diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result index 44b930e0705804cabb12cceb8b9d7d6e94b8fe07..a7f0594588dc51b5b55fee1f81d82f2372dba8c1 100644 --- a/mysql-test/r/events_bugs.result +++ b/mysql-test/r/events_bugs.result @@ -325,4 +325,57 @@ drop event e22830_3; drop event e22830_4; drop table t1; drop table t2; +DROP USER mysqltest_u1@localhost; +CREATE USER mysqltest_u1@localhost; +GRANT EVENT ON events_test.* TO mysqltest_u1@localhost; +CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +event_name definer +e1 root@localhost +DROP EVENT e1; +CREATE DEFINER=CURRENT_USER EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +event_name definer +e1 root@localhost +ALTER DEFINER=mysqltest_u1@localhost EVENT e1 ON SCHEDULE EVERY 1 HOUR; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +event_name definer +e1 mysqltest_u1@localhost +DROP EVENT e1; +CREATE DEFINER=CURRENT_USER() EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +event_name definer +e1 root@localhost +DROP EVENT e1; +CREATE DEFINER=mysqltest_u1@localhost EVENT e1 ON SCHEDULE EVERY 1 DAY DO +SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +event_name definer +e1 mysqltest_u1@localhost +DROP EVENT e1; +CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +event_name definer +e1 mysqltest_u1@localhost +DROP EVENT e1; +CREATE DEFINER=CURRENT_USER EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +event_name definer +e1 mysqltest_u1@localhost +ALTER DEFINER=root@localhost EVENT e1 ON SCHEDULE EVERY 1 HOUR; +ERROR 42000: Access denied; you need the SUPER privilege for this operation +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +event_name definer +e1 mysqltest_u1@localhost +DROP EVENT e1; +CREATE DEFINER=CURRENT_USER() EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +event_name definer +e1 mysqltest_u1@localhost +DROP EVENT e1; +CREATE DEFINER=root@localhost EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +ERROR 42000: Access denied; you need the SUPER privilege for this operation +DROP EVENT e1; +ERROR HY000: Unknown event 'e1' +DROP USER mysqltest_u1@localhost; drop database events_test; diff --git a/mysql-test/t/events_bugs.test b/mysql-test/t/events_bugs.test index 26abf663ce15cd4461f87806e35ecaf7e97f60d2..0790999d7207d7a14c0cea0bbc7d8eead8d0efda 100644 --- a/mysql-test/t/events_bugs.test +++ b/mysql-test/t/events_bugs.test @@ -364,6 +364,63 @@ drop event e22830_4; drop table t1; drop table t2; + +# +# BUG#16425: Events: no DEFINER clause +# +--error 0,ER_CANNOT_USER +DROP USER mysqltest_u1@localhost; + +CREATE USER mysqltest_u1@localhost; +GRANT EVENT ON events_test.* TO mysqltest_u1@localhost; + +CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +DROP EVENT e1; + +CREATE DEFINER=CURRENT_USER EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +ALTER DEFINER=mysqltest_u1@localhost EVENT e1 ON SCHEDULE EVERY 1 HOUR; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +DROP EVENT e1; + +CREATE DEFINER=CURRENT_USER() EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +DROP EVENT e1; + +CREATE DEFINER=mysqltest_u1@localhost EVENT e1 ON SCHEDULE EVERY 1 DAY DO + SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +DROP EVENT e1; + +connect (conn1, localhost, mysqltest_u1, , events_test); + +CREATE EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +DROP EVENT e1; + +CREATE DEFINER=CURRENT_USER EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +ALTER DEFINER=root@localhost EVENT e1 ON SCHEDULE EVERY 1 HOUR; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +DROP EVENT e1; + +CREATE DEFINER=CURRENT_USER() EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +SELECT event_name, definer FROM INFORMATION_SCHEMA.EVENTS; +DROP EVENT e1; + +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +CREATE DEFINER=root@localhost EVENT e1 ON SCHEDULE EVERY 1 DAY DO SELECT 1; +--error ER_EVENT_DOES_NOT_EXIST +DROP EVENT e1; + +disconnect conn1; +connection default; + +DROP USER mysqltest_u1@localhost; + + # # End of tests # diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index b1724a28820d64623721932c63c6ccc45e89b2b7..42f8d2959b03ebd4e4e006bf564a31aa4e353969 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -611,16 +611,18 @@ Event_parse_data::check_parse_data(THD *thd) void Event_parse_data::init_definer(THD *thd) { - int definer_user_len; - int definer_host_len; DBUG_ENTER("Event_parse_data::init_definer"); - DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx " - "thd->sec_ctx->priv_user: 0x%lx", (long) thd->mem_root, - (long) thd->security_ctx->priv_user)); + DBUG_ASSERT(thd->lex->definer); + + const char *definer_user= thd->lex->definer->user.str; + const char *definer_host= thd->lex->definer->host.str; + int definer_user_len= thd->lex->definer->user.length; + int definer_host_len= thd->lex->definer->host.length; - definer_user_len= strlen(thd->security_ctx->priv_user); - definer_host_len= strlen(thd->security_ctx->priv_host); + DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx " + "definer_user: 0x%lx", (long) thd->mem_root, + (long) definer_user)); /* + 1 for @ */ DBUG_PRINT("info",("init definer as whole")); @@ -628,12 +630,11 @@ Event_parse_data::init_definer(THD *thd) definer.str= thd->alloc(definer.length + 1); DBUG_PRINT("info",("copy the user")); - memcpy(definer.str, thd->security_ctx->priv_user, definer_user_len); + memcpy(definer.str, definer_user, definer_user_len); definer.str[definer_user_len]= '@'; DBUG_PRINT("info",("copy the host")); - memcpy(definer.str + definer_user_len + 1, thd->security_ctx->priv_host, - definer_host_len); + memcpy(definer.str + definer_user_len + 1, definer_host, definer_host_len); definer.str[definer.length]= '\0'; DBUG_PRINT("info",("definer [%s] initted", definer.str)); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 9307836731a794f28f1bfc541b0fdb2daf59ca76..580be4d5c1f3fab421981b84ec399d9d8666e84e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2492,6 +2492,91 @@ static void reset_one_shot_variables(THD *thd) } +static +bool sp_process_definer(THD *thd) +{ + DBUG_ENTER("sp_process_definer"); + + LEX *lex= thd->lex; + + /* + If the definer is not specified, this means that CREATE-statement missed + DEFINER-clause. DEFINER-clause can be missed in two cases: + + - The user submitted a statement w/o the clause. This is a normal + case, we should assign CURRENT_USER as definer. + + - Our slave received an updated from the master, that does not + replicate definer for stored rountines. We should also assign + CURRENT_USER as definer here, but also we should mark this routine + as NON-SUID. This is essential for the sake of backward + compatibility. + + The problem is the slave thread is running under "special" user (@), + that actually does not exist. In the older versions we do not fail + execution of a stored routine if its definer does not exist and + continue the execution under the authorization of the invoker + (BUG#13198). And now if we try to switch to slave-current-user (@), + we will fail. + + Actually, this leads to the inconsistent state of master and + slave (different definers, different SUID behaviour), but it seems, + this is the best we can do. + */ + + if (!lex->definer) + { + Query_arena original_arena; + Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena); + + lex->definer= create_default_definer(thd); + + if (ps_arena) + thd->restore_active_arena(ps_arena, &original_arena); + + /* Error has been already reported. */ + if (lex->definer == NULL) + DBUG_RETURN(TRUE); + + if (thd->slave_thread) + lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; + } + else + { + /* + If the specified definer differs from the current user, 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)) + { + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); + DBUG_RETURN(TRUE); + } + } + + /* Check that the specified definer exists. Emit a warning if not. */ + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (!is_acl_user(lex->definer->host.str, lex->definer->user.str)) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NO_SUCH_USER, + ER(ER_NO_SUCH_USER), + lex->definer->user.str, + lex->definer->host.str); + } +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ + + DBUG_RETURN(FALSE); +} + + /* Execute command saved in thd and lex->sql_command @@ -3992,6 +4077,11 @@ mysql_execute_command(THD *thd) "function calls as part of this statement"); break; } + + res= sp_process_definer(thd); + if (res) + break; + switch (lex->sql_command) { case SQLCOM_CREATE_EVENT: res= Events::get_instance()-> @@ -4487,83 +4577,8 @@ mysql_execute_command(THD *thd) } #endif - /* - If the definer is not specified, this means that CREATE-statement missed - DEFINER-clause. DEFINER-clause can be missed in two cases: - - - The user submitted a statement w/o the clause. This is a normal - case, we should assign CURRENT_USER as definer. - - - Our slave received an updated from the master, that does not - replicate definer for stored rountines. We should also assign - CURRENT_USER as definer here, but also we should mark this routine - as NON-SUID. This is essential for the sake of backward - compatibility. - - The problem is the slave thread is running under "special" user (@), - that actually does not exist. In the older versions we do not fail - execution of a stored routine if its definer does not exist and - continue the execution under the authorization of the invoker - (BUG#13198). And now if we try to switch to slave-current-user (@), - we will fail. - - Actually, this leads to the inconsistent state of master and - slave (different definers, different SUID behaviour), but it seems, - this is the best we can do. - */ - - if (!lex->definer) - { - bool res= FALSE; - Query_arena original_arena; - Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena); - - if (!(lex->definer= create_default_definer(thd))) - res= TRUE; - - if (ps_arena) - thd->restore_active_arena(ps_arena, &original_arena); - - /* Error has been already reported. */ - if (res) - goto create_sp_error; - - if (thd->slave_thread) - lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; - } - - /* - If the specified definer differs from the current user, 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). - */ - - else 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)) - { - if (check_global_access(thd, SUPER_ACL)) - { - my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); - goto create_sp_error; - } - } - - /* Check that the specified definer exists. Emit a warning if not. */ - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!is_acl_user(lex->definer->host.str, - lex->definer->user.str)) - { - push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_NO_SUCH_USER, - ER(ER_NO_SUCH_USER), - lex->definer->user.str, - lex->definer->host.str); - } -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ + if (sp_process_definer(thd)) + goto create_sp_error; res= (result= lex->sphead->create(thd)); switch (result) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index cd11bb543efab634d0a3c0958d2237a3078b3436..89f30f8b4527d068eea3d49fafdfd432be18986b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5104,7 +5104,7 @@ alter: } view_list_opt AS view_select view_check_option {} - | ALTER EVENT_SYM sp_name + | ALTER definer EVENT_SYM sp_name /* BE CAREFUL when you add a new rule to update the block where YYTHD->client_capabilities is set back to original value @@ -5120,7 +5120,7 @@ alter: if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) YYABORT; - Lex->event_parse_data->identifier= $3; + Lex->event_parse_data->identifier= $4; /* We have to turn off CLIENT_MULTI_QUERIES while parsing a @@ -5140,13 +5140,14 @@ alter: { /* $1 - ALTER - $2 - EVENT_SYM - $3 - sp_name - $4 - the block above + $2 - definer + $3 - EVENT_SYM + $4 - sp_name + $5 - the block above */ - YYTHD->client_capabilities |= $<ulong_num>4; + YYTHD->client_capabilities |= $<ulong_num>5; - if (!($5 || $6 || $7 || $8 || $9)) + if (!($6 || $7 || $8 || $9 || $10)) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT;