mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
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.
This commit is contained in:
parent
6560c2aa04
commit
2b48825bd6
5 changed files with 221 additions and 94 deletions
|
@ -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;
|
||||||
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)
|
||||||
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));
|
|
||||||
|
|
||||||
definer_user_len= strlen(thd->security_ctx->priv_user);
|
const char *definer_user= thd->lex->definer->user.str;
|
||||||
definer_host_len= strlen(thd->security_ctx->priv_host);
|
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;
|
||||||
|
|
||||||
|
DBUG_PRINT("info",("init definer_user thd->mem_root: 0x%lx "
|
||||||
|
"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)
|
||||||
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));
|
||||||
|
|
||||||
|
|
169
sql/sql_parse.cc
169
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
|
Execute command saved in thd and lex->sql_command
|
||||||
|
|
||||||
|
@ -3992,6 +4077,11 @@ end_with_restore_list:
|
||||||
"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,83 +4577,8 @@ end_with_restore_list:
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
if (sp_process_definer(thd))
|
||||||
If the definer is not specified, this means that CREATE-statement missed
|
goto create_sp_error;
|
||||||
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 */
|
|
||||||
|
|
||||||
res= (result= lex->sphead->create(thd));
|
res= (result= lex->sphead->create(thd));
|
||||||
switch (result) {
|
switch (result) {
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
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:
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
$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;
|
||||||
|
|
Loading…
Reference in a new issue