diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item_func.cc | 10 | ||||
-rw-r--r-- | sql/sp.cc | 28 | ||||
-rw-r--r-- | sql/sp_head.cc | 102 | ||||
-rw-r--r-- | sql/sp_head.h | 35 | ||||
-rw-r--r-- | sql/sql_acl.cc | 60 | ||||
-rw-r--r-- | sql/sql_acl.h | 1 | ||||
-rw-r--r-- | sql/sql_parse.cc | 5 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 2 |
8 files changed, 212 insertions, 31 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc index 10e405393a2..428ca5aa90e 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3069,13 +3069,21 @@ Item_func_sp::execute(Item **itp) { DBUG_ENTER("Item_func_sp::execute"); THD *thd= current_thd; + st_sp_security_context save_ctx; + int res; if (! m_sp) m_sp= sp_find_function(thd, &m_name); if (! m_sp) DBUG_RETURN(-1); - DBUG_RETURN(m_sp->execute_function(thd, args, arg_count, itp)); + sp_change_security_context(thd, m_sp, &save_ctx); + + res= m_sp->execute_function(thd, args, arg_count, itp); + + sp_restore_security_context(thd, m_sp, &save_ctx); + + DBUG_RETURN(res); } enum enum_field_types diff --git a/sql/sp.cc b/sql/sp.cc index bc7f0bf32d7..10eb9c0c6f0 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -121,10 +121,10 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) const char *params, *returns, *body; int ret; bool opened; - const char *creator; + const char *definer; longlong created; longlong modified; - st_sp_chistics *chistics; + st_sp_chistics chistics; char *ptr; uint length; char buff[65]; @@ -147,8 +147,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) ret= SP_GET_FIELD_FAILED; goto done; } - chistics= (st_sp_chistics *)thd->alloc(sizeof(st_sp_chistics)); - chistics->detistic= (ptr[0] == 'N' ? FALSE : TRUE); + bzero((char *)&chistics, sizeof(chistics)); + chistics.detistic= (ptr[0] == 'N' ? FALSE : TRUE); if ((ptr= get_field(&thd->mem_root, table->field[MYSQL_PROC_FIELD_SECURITY_TYPE])) == NULL) @@ -156,7 +156,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) ret= SP_GET_FIELD_FAILED; goto done; } - chistics->suid= (ptr[0] == 'I' ? IS_NOT_SUID : IS_SUID); + chistics.suid= (ptr[0] == 'I' ? IS_NOT_SUID : IS_SUID); if ((params= get_field(&thd->mem_root, table->field[MYSQL_PROC_FIELD_PARAM_LIST])) == NULL) @@ -181,7 +181,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) } // Get additional information - if ((creator= get_field(&thd->mem_root, + if ((definer= get_field(&thd->mem_root, table->field[MYSQL_PROC_FIELD_DEFINER])) == NULL) { ret= SP_GET_FIELD_FAILED; @@ -198,8 +198,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) ptr= 0; if ((length= str.length())) ptr= thd->strmake(str.ptr(), length); - chistics->comment.str= ptr; - chistics->comment.length= length; + chistics.comment.str= ptr; + chistics.comment.length= length; if (opened) { @@ -224,7 +224,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) params, strlen(params), returns, strlen(returns), body, strlen(body), - chistics); + &chistics); lex_start(thd, (uchar*)defstr, deflen); if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL) { @@ -243,8 +243,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) else { *sphp= thd->lex->sphead; - (*sphp)->set_info((char *) creator, (uint) strlen(creator), - created, modified, chistics); + (*sphp)->set_info((char *)definer, (uint)strlen(definer), + created, modified, &chistics); } thd->lex->sql_command= oldcmd; thd->variables.sql_mode= old_sql_mode; @@ -265,6 +265,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) int ret; TABLE *table; TABLE_LIST tables; + char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; memset(&tables, 0, sizeof(tables)); tables.db= (char*)"mysql"; @@ -275,6 +276,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) else { restore_record(table, default_values); // Get default values for fields + strxmov(definer, thd->priv_user, "@", thd->priv_host, NullS); if (table->fields != MYSQL_PROC_FIELD_COUNT) { @@ -300,7 +302,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) table->field[MYSQL_PROC_FIELD_BODY]-> store(sp->m_body.str, sp->m_body.length, system_charset_info); table->field[MYSQL_PROC_FIELD_DEFINER]-> - store(thd->user, (uint)strlen(thd->user), system_charset_info); + store(definer, (uint)strlen(definer), system_charset_info); ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time(); table->field[MYSQL_PROC_FIELD_SQL_MODE]-> store((longlong)thd->variables.sql_mode); @@ -853,7 +855,7 @@ create_string(THD *thd, ulong *lenp, ptr+= my_sprintf(ptr, (ptr, (char *)" DETERMINISTIC\n")); if (chistics->suid == IS_NOT_SUID) ptr+= my_sprintf(ptr, (ptr, (char *)" SQL SECURITY INVOKER\n")); - if (chistics->comment.str) + if (chistics->comment.length) ptr+= my_sprintf(ptr, (ptr, (char *)" COMMENT '%*s'\n", chistics->comment.length, chistics->comment.str)); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index cfaf08b7972..66c811c1e19 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -19,6 +19,7 @@ #endif #include "mysql_priv.h" +#include "sql_acl.h" #include "sp_head.h" #include "sp.h" #include "sp_pcontext.h" @@ -183,9 +184,10 @@ sp_head::init_strings(LEX_STRING *name, LEX *lex) DBUG_PRINT("info", ("name: %*s", name->length, name->str)); m_name.length= name->length; - m_name.str= lex->thd->strmake(name->str, name->length); + m_name.str= strmake_root(&m_mem_root, name->str, name->length); m_params.length= m_param_end- m_param_begin; - m_params.str= lex->thd->strmake((char *)m_param_begin, m_params.length); + m_params.str= strmake_root(&m_mem_root, + (char *)m_param_begin, m_params.length); if (m_returns_begin && m_returns_end) { /* QQ KLUDGE: We can't seem to cut out just the type in the parser @@ -202,12 +204,13 @@ sp_head::init_strings(LEX_STRING *name, LEX *lex) p-= 1; m_returns_end= (uchar *)p+1; m_retstr.length= m_returns_end - m_returns_begin; - m_retstr.str= lex->thd->strmake((char *)m_returns_begin, m_retstr.length); + m_retstr.str= strmake_root(&m_mem_root, + (char *)m_returns_begin, m_retstr.length); } m_body.length= lex->end_of_query - m_body_begin; - m_body.str= lex->thd->strmake((char *)m_body_begin, m_body.length); + m_body.str= strmake_root(&m_mem_root, (char *)m_body_begin, m_body.length); m_defstr.length= lex->end_of_query - lex->buf; - m_defstr.str= lex->thd->strmake((char *)lex->buf, m_defstr.length); + m_defstr.str= strmake_root(&m_mem_root, (char *)lex->buf, m_defstr.length); DBUG_VOID_RETURN; } @@ -664,6 +667,34 @@ sp_head::backpatch(sp_label_t *lab) } } +void +sp_head::set_info(char *definer, uint definerlen, + longlong created, longlong modified, + st_sp_chistics *chistics) +{ + char *p= strchr(definer, '@'); + uint len; + + if (! p) + p= definer; // Weird... + len= p-definer; + m_definer_user.str= strmake_root(&m_mem_root, definer, len); + m_definer_user.length= len; + len= definerlen-len-1; + m_definer_host.str= strmake_root(&m_mem_root, p+1, len); + m_definer_host.length= len; + m_created= created; + m_modified= modified; + m_chistics= (st_sp_chistics *)alloc_root(&m_mem_root, sizeof(st_sp_chistics)); + memcpy(m_chistics, chistics, sizeof(st_sp_chistics)); + if (m_chistics->comment.length == 0) + m_chistics->comment.str= 0; + else + m_chistics->comment.str= strmake_root(&m_mem_root, + m_chistics->comment.str, + m_chistics->comment.length); +} + int sp_head::show_create_procedure(THD *thd) { @@ -1041,3 +1072,64 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp) *nextp= m_ip+1; DBUG_RETURN(res); } + + +// +// Security context swapping +// + +void +sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) +{ + ctxp->changed= (sp->m_chistics->suid != IS_NOT_SUID && + (strcmp(sp->m_definer_user.str, thd->priv_user) || + strcmp(sp->m_definer_host.str, thd->priv_host))); + + if (ctxp->changed) + { + ctxp->master_access= thd->master_access; + ctxp->db_access= thd->db_access; + ctxp->db= thd->db; + ctxp->db_length= thd->db_length; + ctxp->priv_user= thd->priv_user; + strncpy(ctxp->priv_host, thd->priv_host, sizeof(ctxp->priv_host)); + ctxp->user= thd->user; + ctxp->host= thd->host; + ctxp->ip= thd->ip; + + /* Change thise just to do the acl_getroot_no_password */ + thd->user= sp->m_definer_user.str; + thd->host= thd->ip = sp->m_definer_host.str; + + if (acl_getroot_no_password(thd)) + { // Failed, run as invoker for now + ctxp->changed= FALSE; + thd->master_access= ctxp->master_access; + thd->db_access= ctxp->db_access; + thd->db= ctxp->db; + thd->db_length= ctxp->db_length; + thd->priv_user= ctxp->priv_user; + strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host)); + } + + /* Restore these immiediately */ + thd->user= ctxp->user; + thd->host= ctxp->host; + thd->ip= ctxp->ip; + } +} + +void +sp_restore_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp) +{ + if (ctxp->changed) + { + ctxp->changed= FALSE; + thd->master_access= ctxp->master_access; + thd->db_access= ctxp->db_access; + thd->db= ctxp->db; + thd->db_length= ctxp->db_length; + thd->priv_user= ctxp->priv_user; + strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host)); + } +} diff --git a/sql/sp_head.h b/sql/sp_head.h index 33c6bd4fe38..bf3dc012a08 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -61,8 +61,8 @@ public: LEX_STRING m_retstr; // For FUNCTIONs only LEX_STRING m_body; LEX_STRING m_defstr; - char *m_creator; - uint m_creatorlen; + LEX_STRING m_definer_user; + LEX_STRING m_definer_host; longlong m_created; longlong m_modified; // Pointers set during parsing @@ -159,16 +159,9 @@ public: return sp_map_result_type(m_returns); } - void set_info(char *creator, uint creatorlen, + void set_info(char *definer, uint definerlen, longlong created, longlong modified, - st_sp_chistics *chistics) - { - m_creator= creator; - m_creatorlen= creatorlen; - m_created= created; - m_modified= modified; - m_chistics= chistics; - } + st_sp_chistics *chistics); inline void reset_thd_mem_root(THD *thd) { @@ -642,4 +635,24 @@ private: }; // class sp_instr_cfetch : public sp_instr +struct st_sp_security_context +{ + bool changed; + uint master_access; + uint db_access; + char *db; + uint db_length; + char *priv_user; + char priv_host[MAX_HOSTNAME]; + char *user; + char *host; + char *ip; +}; + +void +sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp); + +void +sp_restore_security_context(THD *thd, sp_head *sp,st_sp_security_context *ctxp); + #endif /* _SP_HEAD_H_ */ diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 479c0e5af7b..2527edc1643 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -778,6 +778,66 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, } +/* + * This is like acl_getroot() above, but it doesn't check password, + * and we don't care about the user resources. + * Used to get access rights for SQL SECURITY DEFINER invokation of + * stored procedures. + */ +int acl_getroot_no_password(THD *thd) +{ + ulong user_access= NO_ACCESS; + int res= 1; + ACL_USER *acl_user= 0; + DBUG_ENTER("acl_getroot_no_password"); + + if (!initialized) + { + /* + here if mysqld's been started with --skip-grant-tables option. + */ + thd->priv_user= (char *) ""; // privileges for + *thd->priv_host= '\0'; // the user are unknown + thd->master_access= ~NO_ACCESS; // everything is allowed + DBUG_RETURN(0); + } + + VOID(pthread_mutex_lock(&acl_cache->lock)); + + /* + Find acl entry in user database. + This is specially tailored to suit the check we do for CALL of + a stored procedure; thd->user is set to what is actually a + priv_user, which can be ''. + */ + for (uint i=0 ; i < acl_users.elements ; i++) + { + acl_user= dynamic_element(&acl_users,i,ACL_USER*); + if ((!acl_user->user && (!thd->user || !thd->user[0])) || + (acl_user->user && strcmp(thd->user, acl_user->user) == 0)) + { + if (compare_hostname(&acl_user->host, thd->host, thd->ip)) + { + res= 0; + break; + } + } + } + + if (acl_user) + { + thd->master_access= acl_user->access; + thd->priv_user= acl_user->user ? thd->user : (char *) ""; + + if (acl_user->host.hostname) + strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME); + else + *thd->priv_host= 0; + } + VOID(pthread_mutex_unlock(&acl_cache->lock)); + DBUG_RETURN(res); +} + static byte* check_get_key(ACL_USER *buff,uint *length, my_bool not_used __attribute__((unused))) { diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 8b8115b10db..9858b403d27 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -139,6 +139,7 @@ ulong acl_get(const char *host, const char *ip, const char *user, const char *db, my_bool db_is_pattern); int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd, uint passwd_len); +int acl_getroot_no_password(THD *thd); bool acl_check_host(const char *host, const char *ip); bool check_change_password(THD *thd, const char *host, const char *user); bool change_password(THD *thd, const char *host, const char *user, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 08a11d7c729..f5f07a651bd 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3495,6 +3495,7 @@ mysql_execute_command(THD *thd) } else { + st_sp_security_context save_ctx; uint smrx; LINT_INIT(smrx); @@ -3526,8 +3527,12 @@ mysql_execute_command(THD *thd) thd->server_status |= SERVER_MORE_RESULTS_EXISTS; } + sp_change_security_context(thd, sp, &save_ctx); + res= sp->execute_procedure(thd, &lex->value_list); + sp_restore_security_context(thd, sp, &save_ctx); + #ifndef EMBEDDED_LIBRARY thd->net.no_send_ok= nsok; #endif diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index cbafd69b9ac..a4832e1d913 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2712,7 +2712,7 @@ alter: LEX *lex= Lex; bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - Lex->name= 0; + lex->name= 0; } sp_a_chistics { |