Commit 2b48825b authored by unknown's avatar unknown

BUG#16425: Events: no DEFINER clause

There was already support for CREATE DEFINER=... EVENT syntax in the
parser, but DEFINER information was ignored.

This patch adds processing of DEFINER, and a new ALTER DEFINER=...
EVENT syntax.


mysql-test/r/events_bugs.result:
  Add result for bug#16425: Events: no DEFINER clause.
mysql-test/t/events_bugs.test:
  Add test case for bug#16425: Events: no DEFINER clause.
sql/event_data_objects.cc:
  Event_parse_data::init_definer() looks for DEFINER in
  thd->lex->definer, which is always set now.
sql/sql_parse.cc:
  Move DEFINER processing into the sp_process_definer().  Call this
  function for CREATE EVENT/ALTER EVENT, as well as for CREATE
  PROCEDURE/FUNCTION.
sql/sql_yacc.yy:
  Add 'alter DEFINER=... event', update rule references accordingly.
parent 6560c2aa
...@@ -325,4 +325,57 @@ drop event e22830_3; ...@@ -325,4 +325,57 @@ drop event e22830_3;
drop event e22830_4; drop event e22830_4;
drop table t1; drop table t1;
drop table t2; 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; drop database events_test;
...@@ -364,6 +364,63 @@ drop event e22830_4; ...@@ -364,6 +364,63 @@ drop event e22830_4;
drop table t1; drop table t1;
drop table t2; 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 # End of tests
# #
......
...@@ -611,16 +611,18 @@ Event_parse_data::check_parse_data(THD *thd) ...@@ -611,16 +611,18 @@ Event_parse_data::check_parse_data(THD *thd)
void void
Event_parse_data::init_definer(THD *thd) Event_parse_data::init_definer(THD *thd)
{ {
int definer_user_len;
int definer_host_len;
DBUG_ENTER("Event_parse_data::init_definer"); DBUG_ENTER("Event_parse_data::init_definer");
DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx " DBUG_ASSERT(thd->lex->definer);
"thd->sec_ctx->priv_user: 0x%lx", (long) thd->mem_root,
(long) thd->security_ctx->priv_user)); 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); DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx "
definer_host_len= strlen(thd->security_ctx->priv_host); "definer_user: 0x%lx", (long) thd->mem_root,
(long) definer_user));
/* + 1 for @ */ /* + 1 for @ */
DBUG_PRINT("info",("init definer as whole")); DBUG_PRINT("info",("init definer as whole"));
...@@ -628,12 +630,11 @@ Event_parse_data::init_definer(THD *thd) ...@@ -628,12 +630,11 @@ Event_parse_data::init_definer(THD *thd)
definer.str= thd->alloc(definer.length + 1); definer.str= thd->alloc(definer.length + 1);
DBUG_PRINT("info",("copy the user")); 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]= '@'; definer.str[definer_user_len]= '@';
DBUG_PRINT("info",("copy the host")); DBUG_PRINT("info",("copy the host"));
memcpy(definer.str + definer_user_len + 1, thd->security_ctx->priv_host, memcpy(definer.str + definer_user_len + 1, definer_host, definer_host_len);
definer_host_len);
definer.str[definer.length]= '\0'; definer.str[definer.length]= '\0';
DBUG_PRINT("info",("definer [%s] initted", definer.str)); DBUG_PRINT("info",("definer [%s] initted", definer.str));
......
...@@ -2492,6 +2492,91 @@ static void reset_one_shot_variables(THD *thd) ...@@ -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 Execute command saved in thd and lex->sql_command
...@@ -3992,6 +4077,11 @@ mysql_execute_command(THD *thd) ...@@ -3992,6 +4077,11 @@ mysql_execute_command(THD *thd)
"function calls as part of this statement"); "function calls as part of this statement");
break; break;
} }
res= sp_process_definer(thd);
if (res)
break;
switch (lex->sql_command) { switch (lex->sql_command) {
case SQLCOM_CREATE_EVENT: case SQLCOM_CREATE_EVENT:
res= Events::get_instance()-> res= Events::get_instance()->
...@@ -4487,84 +4577,9 @@ mysql_execute_command(THD *thd) ...@@ -4487,84 +4577,9 @@ mysql_execute_command(THD *thd)
} }
#endif #endif
/* if (sp_process_definer(thd))
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; 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 */
res= (result= lex->sphead->create(thd)); res= (result= lex->sphead->create(thd));
switch (result) { switch (result) {
case SP_OK: case SP_OK:
......
...@@ -5104,7 +5104,7 @@ alter: ...@@ -5104,7 +5104,7 @@ alter:
} }
view_list_opt AS view_select view_check_option 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 BE CAREFUL when you add a new rule to update the block where
YYTHD->client_capabilities is set back to original value YYTHD->client_capabilities is set back to original value
...@@ -5120,7 +5120,7 @@ alter: ...@@ -5120,7 +5120,7 @@ alter:
if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD))) if (!(Lex->event_parse_data= Event_parse_data::new_instance(YYTHD)))
YYABORT; YYABORT;
Lex->event_parse_data->identifier= $3; Lex->event_parse_data->identifier= $4;
/* /*
We have to turn off CLIENT_MULTI_QUERIES while parsing a We have to turn off CLIENT_MULTI_QUERIES while parsing a
...@@ -5140,13 +5140,14 @@ alter: ...@@ -5140,13 +5140,14 @@ alter:
{ {
/* /*
$1 - ALTER $1 - ALTER
$2 - EVENT_SYM $2 - definer
$3 - sp_name $3 - EVENT_SYM
$4 - the block above $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)); yyerror(ER(ER_SYNTAX_ERROR));
YYABORT; YYABORT;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment