From a44a924a407ae1f1135a14ee750468e6fc88d9b0 Mon Sep 17 00:00:00 2001 From: unknown <anozdrin@mysql.com> Date: Wed, 1 Mar 2006 14:13:07 +0300 Subject: [PATCH] Fix for BUG#16266: Definer is not fully qualified error during replication. The idea of the fix is to extend support of non-SUID triggers for backward compatibility. Formerly non-SUID triggers were appeared when "new" server is being started against "old" database. Now, they are also created when "new" slave receives updates from "old" master. mysql-test/r/rpl_trigger.result: Updated the result file with the results of the test for BUG#16266. mysql-test/t/rpl_trigger.test: Added the test case for BUG#16266. sql/mysql_priv.h: Added an utility operation to be used from sql_yacc.yy. sql/sql_parse.cc: Add a utility operation to be used from sql_yacc.yy. sql/sql_trigger.cc: Extend support of non-SUID triggers. sql/sql_view.cc: Initialize LEX::definer if DEFINER-clause is missing. sql/sql_yacc.yy: Extended support of non-SUID triggers. mysql-test/std_data/bug16266.000001: A new binlog file for testing a patch for BUG#16266. --- mysql-test/r/rpl_trigger.result | 41 +++++++++++ mysql-test/std_data/bug16266.000001 | Bin 0 -> 532 bytes mysql-test/t/rpl_trigger.test | 74 +++++++++++++++++++ sql/mysql_priv.h | 1 + sql/sql_parse.cc | 30 +++++++- sql/sql_trigger.cc | 108 +++++++++++++++++++++------- sql/sql_view.cc | 20 ++++++ sql/sql_yacc.yy | 42 ++++------- 8 files changed, 264 insertions(+), 52 deletions(-) create mode 100644 mysql-test/std_data/bug16266.000001 diff --git a/mysql-test/r/rpl_trigger.result b/mysql-test/r/rpl_trigger.result index ccd880c1e0..185862097b 100644 --- a/mysql-test/r/rpl_trigger.result +++ b/mysql-test/r/rpl_trigger.result @@ -855,3 +855,44 @@ f3 drop trigger trg11; drop table t21,t31; drop table t11; +STOP SLAVE; +FLUSH LOGS; +RESET SLAVE; +START SLAVE; +SELECT MASTER_POS_WAIT('master-bin.000001', 513) >= 0; +MASTER_POS_WAIT('master-bin.000001', 513) >= 0 +1 +SHOW TABLES; +Tables_in_test +t1 +t2 +SHOW TRIGGERS; +Trigger Event Table Statement Timing Created sql_mode Definer +trg1 INSERT t1 INSERT INTO t2 VALUES(CURRENT_USER()) AFTER NULL +SELECT * FROM t1; +c +1 +SELECT * FROM t2; +s +@ +INSERT INTO t1 VALUES(2); +SELECT * FROM t1; +c +1 +2 +SELECT * FROM t2; +s +@ +root@localhost +DROP TRIGGER trg1; +Warnings: +Warning 1454 No definer attribute for trigger 'test'.'trg1'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger. +DROP TABLE t1; +DROP TABLE t2; +STOP SLAVE; +RESET SLAVE; +SHOW TABLES; +Tables_in_test +SHOW TRIGGERS; +Trigger Event Table Statement Timing Created sql_mode Definer +RESET MASTER; diff --git a/mysql-test/std_data/bug16266.000001 b/mysql-test/std_data/bug16266.000001 new file mode 100644 index 0000000000000000000000000000000000000000..1b24d231511ff1505b232ef852b6473852afd480 GIT binary patch literal 532 zcmeyDl$p2U!XIaTMg|6kI3P{}Vg?2l22(u)Jwr3yl+>isblsf%bg(d90%VM^1up{! zgAfA?5C}0acr&m_Z3PN5flLnt;_YA)Ks*p&VPKE~QVt9ZFcAhe=HikR77m~x91JC? z#U%{RL9UJ=t_mTJPCl**C59Tw3Z8x;ng?NqMg#Q=fpx=-Al*<Sjba67562)4BLf3X z%||e!X8?^}MYhpFp6>3hK?)^B>4pl9ZXrOXr(dvZP>6!RAJ7{LZvH_Eu8z(g3PJwi zFd3l7{1r-!6v7;RLS2J3oI`_xT>V1gLxI{fG&Nts+!YRV7b6ppfQK3}UWVEZvBMB% UhoPp&#Xru>AYTdtu?W~$0PX--`v3p{ literal 0 HcmV?d00001 diff --git a/mysql-test/t/rpl_trigger.test b/mysql-test/t/rpl_trigger.test index f3db1cb584..90822e0654 100644 --- a/mysql-test/t/rpl_trigger.test +++ b/mysql-test/t/rpl_trigger.test @@ -162,6 +162,7 @@ use test; drop table t1,t2; drop database other; + # # Test specific triggers including SELECT into var with replication # BUG#13227: @@ -257,6 +258,79 @@ while ($rnd) } +# +# BUG#16266: Definer is not fully qualified error during replication. +# +# The idea of this test is to emulate replication of a trigger from the old +# master (master w/o "DEFINER in triggers" support) to the new slave and check +# that: +# 1. the trigger on the slave will be replicated w/o errors; +# 2. the trigger on the slave will be non-SUID (will have no DEFINER); +# 3. the trigger can be activated later on the slave w/o errors. +# +# In order to emulate this kind of replication, we make the slave playing the binlog, +# recorded by 5.0.16 master. This binlog contains the following statements: +# CREATE TABLE t1(c INT); +# CREATE TABLE t2(s CHAR(200)); +# CREATE TRIGGER trg1 AFTER INSERT ON t1 +# FOR EACH ROW +# INSERT INTO t2 VALUES(CURRENT_USER()); +# INSERT INTO t1 VALUES(1); +# + +# 1. Check that the trigger's replication is succeeded. + +# Stop the slave. + +connection slave; +STOP SLAVE; + +# Replace master's binlog. + +connection master; +FLUSH LOGS; +exec cp $MYSQL_TEST_DIR/std_data/bug16266.000001 $MYSQLTEST_VARDIR/log/master-bin.000001; + +# Make the slave to replay the new binlog. + +connection slave; +RESET SLAVE; +START SLAVE; + +SELECT MASTER_POS_WAIT('master-bin.000001', 513) >= 0; + +# Check that the replication succeeded. + +SHOW TABLES; +SHOW TRIGGERS; +SELECT * FROM t1; +SELECT * FROM t2; + +# 2. Check that the trigger is non-SUID on the slave; +# 3. Check that the trigger can be activated on the slave. + +INSERT INTO t1 VALUES(2); + +SELECT * FROM t1; +SELECT * FROM t2; + +# That's all, cleanup. + +DROP TRIGGER trg1; +DROP TABLE t1; +DROP TABLE t2; + +STOP SLAVE; +RESET SLAVE; + +# The master should be clean. + +connection master; +SHOW TABLES; +SHOW TRIGGERS; + +RESET MASTER; + # # End of tests diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2c817ae54c..9559b0be76 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -531,6 +531,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table); bool get_default_definer(THD *thd, LEX_USER *definer); +LEX_USER *create_default_definer(THD *thd); LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name); enum enum_mysql_completiontype { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b2066953cf..0494ccf985 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -7206,6 +7206,34 @@ bool get_default_definer(THD *thd, LEX_USER *definer) } +/* + Create default definer for the specified THD. Also check that the current + user is conformed to the definers requirements. + + SYNOPSIS + create_default_definer() + thd [in] thread handler + + RETURN + On success, return a valid pointer to the created and initialized + LEX_USER, which contains definer information. + On error, return 0. +*/ + +LEX_USER *create_default_definer(THD *thd) +{ + LEX_USER *definer; + + if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))) + return 0; + + if (get_default_definer(thd, definer)) + return 0; + + return definer; +} + + /* Create definer with the given user and host names. Also check that the user and host names satisfy definers requirements. @@ -7218,7 +7246,7 @@ bool get_default_definer(THD *thd, LEX_USER *definer) RETURN On success, return a valid pointer to the created and initialized - LEX_STRING, which contains definer information. + LEX_USER, which contains definer information. On error, return 0. */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 4c3ae5c032..9ccc70d8cc 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -264,7 +264,17 @@ end: log_query.set((char *) 0, 0, system_charset_info); /* reset log_query */ log_query.append(STRING_WITH_LEN("CREATE ")); - append_definer(thd, &log_query, &definer_user, &definer_host); + + if (definer_user.str && definer_host.str) + { + /* + Append definer-clause if the trigger is SUID (a usual trigger in + new MySQL versions). + */ + + append_definer(thd, &log_query, &definer_user, &definer_host); + } + log_query.append(thd->lex->trigger_definition_begin); } @@ -289,17 +299,30 @@ end: LEX) tables - table list containing one open table for which the trigger is created. - definer_user - [out] after a call it points to 0-terminated string, - which contains user name part of the actual trigger - definer. The caller is responsible to provide memory for + definer_user - [out] after a call it points to 0-terminated string or + contains the NULL-string: + - 0-terminated is returned if the trigger is SUID. The + string contains user name part of the actual trigger + definer. + - NULL-string is returned if the trigger is non-SUID. + Anyway, the caller is responsible to provide memory for storing LEX_STRING object. - definer_host - [out] after a call it points to 0-terminated string, - which contains host name part of the actual trigger - definer. The caller is responsible to provide memory for + definer_host - [out] after a call it points to 0-terminated string or + contains the NULL-string: + - 0-terminated string is returned if the trigger is + SUID. The string contains host name part of the + actual trigger definer. + - NULL-string is returned if the trigger is non-SUID. + Anyway, the caller is responsible to provide memory for storing LEX_STRING object. NOTE - Assumes that trigger name is fully qualified. + - Assumes that trigger name is fully qualified. + - NULL-string means the following LEX_STRING instance: + { str = 0; length = 0 }. + - In other words, definer_user and definer_host should contain + simultaneously NULL-strings (non-SUID/old trigger) or valid strings + (SUID/new trigger). RETURN VALUE False - success @@ -336,12 +359,30 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, return 1; } - /* - Definer attribute of the Lex instance is always set in sql_yacc.yy when - trigger is created. - */ + if (!lex->definer) + { + /* + DEFINER-clause is missing. - DBUG_ASSERT(lex->definer); + If we are in slave thread, this means that we received CREATE TRIGGER + from the master, that does not support definer in triggers. So, we + should mark this trigger as non-SUID. Note that this does not happen + when we parse triggers' definitions during opening .TRG file. + LEX::definer is ignored in that case. + + Otherwise, we should use CURRENT_USER() as definer. + + NOTE: when CREATE TRIGGER statement is allowed to be executed in PS/SP, + it will be required to create the definer below in persistent MEM_ROOT + of PS/SP. + */ + + if (!thd->slave_thread) + { + if (!(lex->definer= create_default_definer(thd))) + return 1; + } + } /* If the specified definer differs from the current user, we should check @@ -349,10 +390,11 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, 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)) + if (lex->definer && + (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)) { @@ -446,8 +488,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, *trg_sql_mode= thd->variables.sql_mode; #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!is_acl_user(lex->definer->host.str, - lex->definer->user.str)) + if (lex->definer && !is_acl_user(lex->definer->host.str, + lex->definer->user.str)) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, @@ -458,12 +500,30 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, } #endif /* NO_EMBEDDED_ACCESS_CHECKS */ - *definer_user= lex->definer->user; - *definer_host= lex->definer->host; + if (lex->definer) + { + /* SUID trigger. */ + + *definer_user= lex->definer->user; + *definer_host= lex->definer->host; - trg_definer->str= trg_definer_holder; - trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@", - definer_host->str, NullS) - trg_definer->str; + trg_definer->str= trg_definer_holder; + trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@", + definer_host->str, NullS) - trg_definer->str; + } + else + { + /* non-SUID trigger. */ + + definer_user->str= 0; + definer_user->length= 0; + + definer_host->str= 0; + definer_host->length= 0; + + trg_definer->str= (char*) ""; + trg_definer->length= 0; + } if (!sql_create_definition_file(&dir, &file, &triggers_file_type, (gptr)this, triggers_file_parameters, 0)) diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 4f62a80cfd..2178b5d00a 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -208,6 +208,26 @@ bool mysql_create_view(THD *thd, if (mode != VIEW_CREATE_NEW) sp_cache_invalidate(); + if (!lex->definer) + { + /* + DEFINER-clause is missing; we have to create default definer in + persistent arena to be PS/SP friendly. + */ + + 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); + + if (res) + goto err; + } + #ifndef NO_EMBEDDED_ACCESS_CHECKS /* check definer of view: diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index d6d2939bed..7e93b9c8e7 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -778,7 +778,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <symbol> FUNC_ARG0 FUNC_ARG1 FUNC_ARG2 FUNC_ARG3 keyword keyword_sp -%type <lex_user> user grant_user get_definer +%type <lex_user> user grant_user %type <charset> opt_collate @@ -8960,41 +8960,29 @@ subselect_end: }; definer: - get_definer + /* empty */ { - THD *thd= YYTHD; - - if (! (thd->lex->definer= create_definer(thd, &$1->user, &$1->host))) - YYABORT; + /* + We have to distinguish missing DEFINER-clause from case when + CURRENT_USER specified as definer explicitly in order to properly + handle CREATE TRIGGER statements which come to replication thread + from older master servers (i.e. to create non-suid trigger in this + case). + */ + YYTHD->lex->definer= 0; } - ; - -get_definer: - opt_current_definer + | DEFINER_SYM EQ CURRENT_USER optional_braces { - THD *thd= YYTHD; - - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - YYABORT; - - if (get_default_definer(thd, $$)) - YYABORT; + if (! (YYTHD->lex->definer= create_default_definer(YYTHD))) + YYABORT; } | DEFINER_SYM EQ ident_or_text '@' ident_or_text { - if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user)))) - YYABORT; - - $$->user= $3; - $$->host= $5; + if (!(YYTHD->lex->definer= create_definer(YYTHD, &$3, &$5))) + YYABORT; } ; -opt_current_definer: - /* empty */ - | DEFINER_SYM EQ CURRENT_USER optional_braces - ; - /************************************************************************** CREATE VIEW statement options. -- 2.30.9