diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 174 |
1 files changed, 109 insertions, 65 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0353ec7fde9..92d628bbfe1 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -401,8 +401,11 @@ void init_update_queries(void) sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_ROLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_GRANT_ROLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_REVOKE_ROLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA; /* @todo SQLCOM_CREATE_FUNCTION should have CF_AUTO_COMMIT_TRANS @@ -461,9 +464,13 @@ void init_update_queries(void) sql_command_flags[SQLCOM_CREATE_USER]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_USER]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_RENAME_USER]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_ROLE]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_ROLE]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_REVOKE]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_REVOKE_ROLE]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_GRANT]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_GRANT_ROLE]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_PRELOAD_KEYS]= CF_AUTO_COMMIT_TRANS; @@ -664,7 +671,8 @@ static void handle_bootstrap_impl(THD *thd) #endif /* EMBEDDED_LIBRARY */ thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME)); - thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=0; + thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]= + thd->security_ctx->priv_role[0]= 0; /* Make the "client" handle multiple results. This is necessary to enable stored procedures with SELECTs and Dynamic SQL @@ -1878,8 +1886,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, DBUG_RETURN(1); lex->query_tables_last= query_tables_last; break; - } #endif + } case SCH_PROFILES: /* Mark this current profiling record to be discarded. We don't @@ -2006,7 +2014,6 @@ static void reset_one_shot_variables(THD *thd) } -static bool sp_process_definer(THD *thd) { DBUG_ENTER("sp_process_definer"); @@ -2043,7 +2050,7 @@ bool sp_process_definer(THD *thd) Query_arena original_arena; Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena); - lex->definer= create_default_definer(thd); + lex->definer= create_default_definer(thd, false); if (ps_arena) thd->restore_active_arena(ps_arena, &original_arena); @@ -2057,20 +2064,24 @@ bool sp_process_definer(THD *thd) } else { + LEX_USER *d= lex->definer= get_current_user(thd, lex->definer); + if (!d) + DBUG_RETURN(TRUE); + /* - If the specified definer differs from the current user, we + If the specified definer differs from the current user or role, we should check that the current user has SUPER privilege (in order to create a stored routine under another user one must have SUPER privilege). */ - if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) || - my_strcasecmp(system_charset_info, lex->definer->host.str, - thd->security_ctx->priv_host)) && - check_global_access(thd, SUPER_ACL, true)) - { - my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); + bool curuser= !strcmp(d->user.str, thd->security_ctx->priv_user); + bool currole= !curuser && !strcmp(d->user.str, thd->security_ctx->priv_role); + bool curuserhost= curuser && d->host.str && + !my_strcasecmp(system_charset_info, d->host.str, + thd->security_ctx->priv_host); + if (!curuserhost && !currole && + check_global_access(thd, SUPER_ACL, false)) DBUG_RETURN(TRUE); - } } /* Check that the specified definer exists. Emit a warning if not. */ @@ -3991,22 +4002,26 @@ end_with_restore_list: } #ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_CREATE_USER: + case SQLCOM_CREATE_ROLE: { if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 1) && check_global_access(thd,CREATE_USER_ACL)) break; /* Conditionally writes to binlog */ - if (!(res= mysql_create_user(thd, lex->users_list))) + if (!(res= mysql_create_user(thd, lex->users_list, + lex->sql_command == SQLCOM_CREATE_ROLE))) my_ok(thd); break; } case SQLCOM_DROP_USER: + case SQLCOM_DROP_ROLE: { if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) && check_global_access(thd,CREATE_USER_ACL)) break; /* Conditionally writes to binlog */ - if (!(res= mysql_drop_user(thd, lex->users_list))) + if (!(res= mysql_drop_user(thd, lex->users_list, + lex->sql_command == SQLCOM_DROP_ROLE))) my_ok(thd); break; } @@ -4026,9 +4041,6 @@ end_with_restore_list: check_global_access(thd,CREATE_USER_ACL)) break; - /* Replicate current user as grantor */ - thd->binlog_invoker(); - /* Conditionally writes to binlog */ if (!(res = mysql_revoke_all(thd, lex->users_list))) my_ok(thd); @@ -4046,42 +4058,58 @@ end_with_restore_list: goto error; /* Replicate current user as grantor */ - thd->binlog_invoker(); + thd->binlog_invoker(false); if (thd->security_ctx->user) // If not replication { - LEX_USER *user, *tmp_user; + LEX_USER *user; bool first_user= TRUE; List_iterator <LEX_USER> user_list(lex->users_list); - while ((tmp_user= user_list++)) + while ((user= user_list++)) { - if (!(user= get_current_user(thd, tmp_user))) - goto error; if (specialflag & SPECIAL_NO_RESOLVE && hostname_requires_resolving(user->host.str)) push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_HOSTNAME_WONT_WORK, ER(ER_WARN_HOSTNAME_WONT_WORK)); - // Are we trying to change a password of another user - DBUG_ASSERT(user->host.str != 0); /* GRANT/REVOKE PROXY has the target user as a first entry in the list. */ if (lex->type == TYPE_ENUM_PROXY && first_user) { + if (!(user= get_current_user(thd, user)) || !user->host.str) + goto error; + first_user= FALSE; if (acl_check_proxy_grant_access (thd, user->host.str, user->user.str, lex->grant & GRANT_ACL)) goto error; } - else if (is_acl_user(user->host.str, user->user.str) && - user->password.str && - check_change_password (thd, user->host.str, user->user.str, - user->password.str, - user->password.length)) - goto error; + else if (user->password.str) + { + // Are we trying to change a password of another user? + const char *hostname= user->host.str, *username=user->user.str; + bool userok; + if (username == current_user.str) + { + username= thd->security_ctx->priv_user; + hostname= thd->security_ctx->priv_host; + userok= true; + } + else + { + if (!hostname) + hostname= host_not_specified.str; + userok= is_acl_user(hostname, username); + } + + if (userok && check_change_password (thd, hostname, username, + user->password.str, + user->password.length)) + goto error; + } } } if (first_table) @@ -4125,9 +4153,9 @@ end_with_restore_list: else { /* Conditionally writes to binlog */ - res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant, - lex->sql_command == SQLCOM_REVOKE, - lex->type == TYPE_ENUM_PROXY); + res= mysql_grant(thd, select_lex->db, lex->users_list, lex->grant, + lex->sql_command == SQLCOM_REVOKE, + lex->type == TYPE_ENUM_PROXY); } if (!res) { @@ -4146,6 +4174,14 @@ end_with_restore_list: } break; } + case SQLCOM_REVOKE_ROLE: + case SQLCOM_GRANT_ROLE: + { + if (!(res= mysql_grant_role(thd, lex->users_list, + lex->sql_command != SQLCOM_GRANT_ROLE))) + my_ok(thd); + break; + } #endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ case SQLCOM_RESET: /* @@ -4243,11 +4279,17 @@ end_with_restore_list: #ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_SHOW_GRANTS: { - LEX_USER *grant_user= get_current_user(thd, lex->grant_user); + LEX_USER *grant_user= lex->grant_user; if (!grant_user) goto error; - if ((thd->security_ctx->priv_user && - !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) || + + if (grant_user->user.str && + !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) + grant_user->user= current_user; + + if (grant_user->user.str == current_user.str || + grant_user->user.str == current_role.str || + grant_user->user.str == current_user_and_current_role.str || !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 0)) { res = mysql_show_grants(thd, grant_user); @@ -5363,8 +5405,12 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if (!(sctx->master_access & SELECT_ACL)) { if (db && (!thd->db || db_is_pattern || strcmp(db, thd->db))) + { db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); + if (sctx->priv_role[0]) + db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern); + } else { /* get access for current db */ @@ -5408,8 +5454,14 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, } if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db))) + { db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); + if (sctx->priv_role[0]) + { + db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern); + } + } else db_access= sctx->db_access; DBUG_PRINT("info",("db_access: %lu want_access: %lu", @@ -8016,16 +8068,23 @@ Item *negate_expression(THD *thd, Item *expr) @param[out] definer definer */ -void get_default_definer(THD *thd, LEX_USER *definer) +void get_default_definer(THD *thd, LEX_USER *definer, bool role) { const Security_context *sctx= thd->security_ctx; - definer->user.str= (char *) sctx->priv_user; + if (role) + { + definer->user.str= const_cast<char*>(sctx->priv_role); + definer->host= empty_lex_str; + } + else + { + definer->user.str= const_cast<char*>(sctx->priv_user); + definer->host.str= const_cast<char*>(sctx->priv_host); + definer->host.length= strlen(definer->host.str); + } definer->user.length= strlen(definer->user.str); - definer->host.str= (char *) sctx->priv_host; - definer->host.length= strlen(definer->host.str); - definer->password= null_lex_str; definer->plugin= empty_lex_str; definer->auth= empty_lex_str; @@ -8043,16 +8102,22 @@ void get_default_definer(THD *thd, LEX_USER *definer) - On error, return 0. */ -LEX_USER *create_default_definer(THD *thd) +LEX_USER *create_default_definer(THD *thd, bool role) { LEX_USER *definer; if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))) return 0; - thd->get_definer(definer); + thd->get_definer(definer, role); - return definer; + if (role && definer->user.length == 0) + { + my_error(ER_MALFORMED_DEFINER, MYF(0)); + return 0; + } + else + return definer; } @@ -8088,27 +8153,6 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name) /** - Retuns information about user or current user. - - @param[in] thd thread handler - @param[in] user user - - @return - - On success, return a valid pointer to initialized - LEX_USER, which contains user information. - - On error, return 0. -*/ - -LEX_USER *get_current_user(THD *thd, LEX_USER *user) -{ - if (!user->user.str) // current_user - return create_default_definer(thd); - - return user; -} - - -/** Check that byte length of a string does not exceed some limit. @param str string to be checked |