From 6114bee47cbcb085f28844173ad382f865191928 Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Sat, 16 Jul 2005 00:01:44 +0300 Subject: [PATCH] stop evaluation constant functions in WHERE (BUG#4663) correct value of CURRENT_USER() in SP with "security definer" (BUG#7291) --- BitKeeper/etc/config | 2 +- mysql-test/r/sp-security.result | 24 ++++++++++++++++++++ mysql-test/r/view.result | 25 +++++++++++++++++++++ mysql-test/t/sp-security.test | 29 ++++++++++++++++++++++++ mysql-test/t/view.test | 21 +++++++++++++++++ sql/item.cc | 27 ++++++++++++++++++++++ sql/item.h | 1 + sql/item_cmpfunc.cc | 40 ++++++++++++++++++--------------- sql/item_create.cc | 12 ++-------- sql/item_strfunc.cc | 28 +++++++++++++++++------ sql/item_strfunc.h | 26 ++++++++++++++++----- sql/sql_class.h | 2 ++ sql/sql_yacc.yy | 2 +- 13 files changed, 197 insertions(+), 42 deletions(-) diff --git a/BitKeeper/etc/config b/BitKeeper/etc/config index 1ac24031dca..56ae08e5ffa 100644 --- a/BitKeeper/etc/config +++ b/BitKeeper/etc/config @@ -24,7 +24,7 @@ description: MySQL - fast and reliable SQL database # repository is commercial it can be an internal email address or "none" # to disable logging. # -logging: logging@openlogging.org +logging: none # # If this field is set, all checkins will appear to be made by this user, # in effect making this a single user package. Single user packages are diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index ee72fde7324..327d2ef1de1 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -194,3 +194,27 @@ use test; drop database sptest; delete from mysql.user where user='usera' or user='userb' or user='userc'; delete from mysql.procs_priv where user='usera' or user='userb' or user='userc'; +use test; +select current_user(); +current_user() +root@localhost +select user(); +user() +root@localhost +create procedure bug7291_0 () sql security invoker select current_user(), user(); +create procedure bug7291_1 () sql security definer call bug7291_0(); +create procedure bug7291_2 () sql security invoker call bug7291_0(); +grant execute on procedure bug7291_0 to user1@localhost; +grant execute on procedure bug7291_1 to user1@localhost; +grant execute on procedure bug7291_2 to user1@localhost; +call bug7291_2(); +current_user() user() +user1@localhost user1@localhost +call bug7291_1(); +current_user() user() +root@localhost user1@localhost +drop procedure bug7291_1; +drop procedure bug7291_2; +drop procedure bug7291_0; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM user1@localhost; +drop user user1@localhost; diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 68cc0c4cb57..75389483317 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -1831,3 +1831,28 @@ select * from v1; t 01:00 drop view v1; +create table t1 (a timestamp default now()); +create table t2 (b timestamp default now()); +create view v1 as select a,b,t1.a < now() from t1,t2 where t1.a < now(); +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1` AS select sql_no_cache `test`.`t1`.`a` AS `a`,`test`.`t2`.`b` AS `b`,(`test`.`t1`.`a` < now()) AS `t1.a < now()` from `test`.`t1` join `test`.`t2` where (`test`.`t1`.`a` < now()) +drop view v1; +drop table t1, t2; +CREATE TABLE t1 ( a varchar(50) ); +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = CURRENT_USER(); +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1` AS select sql_no_cache `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = current_user()) +DROP VIEW v1; +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = VERSION(); +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1` AS select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = version()) +DROP VIEW v1; +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = DATABASE(); +SHOW CREATE VIEW v1; +View Create View +v1 CREATE ALGORITHM=UNDEFINED VIEW `test`.`v1` AS select sql_no_cache `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` = database()) +DROP VIEW v1; +DROP TABLE t1; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index e1d8043ccda..204ef68ad7e 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -304,3 +304,32 @@ drop database sptest; delete from mysql.user where user='usera' or user='userb' or user='userc'; delete from mysql.procs_priv where user='usera' or user='userb' or user='userc'; +# +# correct value from current_user() in function run from "security definer" +# (BUG#7291) +# +connection con1root; +use test; + +select current_user(); +select user(); +create procedure bug7291_0 () sql security invoker select current_user(), user(); +create procedure bug7291_1 () sql security definer call bug7291_0(); +create procedure bug7291_2 () sql security invoker call bug7291_0(); +grant execute on procedure bug7291_0 to user1@localhost; +grant execute on procedure bug7291_1 to user1@localhost; +grant execute on procedure bug7291_2 to user1@localhost; + +connect (user1,localhost,user1,,); +connection user1; + +call bug7291_2(); +call bug7291_1(); + +connection con1root; +drop procedure bug7291_1; +drop procedure bug7291_2; +drop procedure bug7291_0; +disconnect user1; +REVOKE ALL PRIVILEGES, GRANT OPTION FROM user1@localhost; +drop user user1@localhost; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 13a5f8cef1f..20059eff8d0 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -1673,3 +1673,24 @@ create view v1(k, K) as select 1,2; create view v1 as SELECT TIME_FORMAT(SEC_TO_TIME(3600),'%H:%i') as t; select * from v1; drop view v1; + +# +# evaluation constant functions in WHERE (BUG#4663) +# +create table t1 (a timestamp default now()); +create table t2 (b timestamp default now()); +create view v1 as select a,b,t1.a < now() from t1,t2 where t1.a < now(); +SHOW CREATE VIEW v1; +drop view v1; +drop table t1, t2; +CREATE TABLE t1 ( a varchar(50) ); +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = CURRENT_USER(); +SHOW CREATE VIEW v1; +DROP VIEW v1; +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = VERSION(); +SHOW CREATE VIEW v1; +DROP VIEW v1; +CREATE VIEW v1 AS SELECT * FROM t1 WHERE a = DATABASE(); +SHOW CREATE VIEW v1; +DROP VIEW v1; +DROP TABLE t1; diff --git a/sql/item.cc b/sql/item.cc index d3888cef9d5..7fbeb6f15db 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -635,6 +635,33 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) } +Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs) +{ + Item_string *conv; + uint conv_errors; + String tmp, cstr, *ostr= val_str(&tmp); + cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors); + if (conv_errors || + !(conv= new Item_static_string_func(func_name, + cstr.ptr(), cstr.length(), + cstr.charset(), + collation.derivation))) + { + /* + Safe conversion is not possible (or EOM). + We could not convert a string into the requested character set + without data loss. The target charset does not cover all the + characters from the string. Operation cannot be done correctly. + */ + return NULL; + } + conv->str_value.copy(); + /* Ensure that no one is going to change the result string */ + conv->str_value.mark_as_const(); + return conv; +} + + bool Item_string::eq(const Item *item, bool binary_cmp) const { if (type() == item->type() && item->basic_const_item()) diff --git a/sql/item.h b/sql/item.h index c8180b4932a..dce331c6bc2 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1221,6 +1221,7 @@ public: Derivation dv= DERIVATION_COERCIBLE) :Item_string(NullS, str, length, cs, dv), func_name(name_par) {} + Item *safe_charset_converter(CHARSET_INFO *tocs); void print(String *str) { str->append(func_name); } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 5a2e14eef2e..d2e83264006 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -237,30 +237,33 @@ void Item_bool_func2::fix_length_and_dec() set_cmp_func(); return; } - - if (args[0]->type() == FIELD_ITEM) + + if (!thd->is_context_analysis_only()) { - Field *field=((Item_field*) args[0])->field; - if (field->can_be_compared_as_longlong()) + if (args[0]->type() == FIELD_ITEM) { - if (convert_constant_item(thd, field,&args[1])) + Field *field=((Item_field*) args[0])->field; + if (field->can_be_compared_as_longlong()) { - cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, - INT_RESULT); // Works for all types. - return; + if (convert_constant_item(thd, field,&args[1])) + { + cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, + INT_RESULT); // Works for all types. + return; + } } } - } - if (args[1]->type() == FIELD_ITEM /* && !args[1]->const_item() */) - { - Field *field=((Item_field*) args[1])->field; - if (field->can_be_compared_as_longlong()) + if (args[1]->type() == FIELD_ITEM /* && !args[1]->const_item() */) { - if (convert_constant_item(thd, field,&args[0])) + Field *field=((Item_field*) args[1])->field; + if (field->can_be_compared_as_longlong()) { - cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, - INT_RESULT); // Works for all types. - return; + if (convert_constant_item(thd, field,&args[0])) + { + cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, + INT_RESULT); // Works for all types. + return; + } } } } @@ -991,7 +994,8 @@ void Item_func_between::fix_length_and_dec() if (args[0]->type() == FIELD_ITEM) { Field *field=((Item_field*) args[0])->field; - if (field->can_be_compared_as_longlong()) + if (!thd->is_context_analysis_only() && + field->can_be_compared_as_longlong()) { /* The following can't be recoded with || as convert_constant_item diff --git a/sql/item_create.cc b/sql/item_create.cc index b9073a6c0b3..b7d8d50f9b3 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -299,16 +299,8 @@ Item *create_func_pow(Item* a, Item *b) Item *create_func_current_user() { - THD *thd=current_thd; - char buff[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; - uint length; - - thd->lex->safe_to_cache_query= 0; - length= (uint) (strxmov(buff, thd->priv_user, "@", thd->priv_host, NullS) - - buff); - return new Item_static_string_func("current_user()", - thd->memdup(buff, length), length, - system_charset_info); + current_thd->lex->safe_to_cache_query= 0; + return new Item_func_user(TRUE); } Item *create_func_radians(Item *a) diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 9f7a44f6f47..a68d9fdb895 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1522,9 +1522,11 @@ Item *Item_func_sysconst::safe_charset_converter(CHARSET_INFO *tocs) uint conv_errors; String tmp, cstr, *ostr= val_str(&tmp); cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors); - if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(), - cstr.charset(), - collation.derivation))) + if (conv_errors || + !(conv= new Item_static_string_func(fully_qualified_func_name(), + cstr.ptr(), cstr.length(), + cstr.charset(), + collation.derivation))) { return NULL; } @@ -1554,13 +1556,24 @@ String *Item_func_user::val_str(String *str) DBUG_ASSERT(fixed == 1); THD *thd=current_thd; CHARSET_INFO *cs= system_charset_info; - const char *host= thd->host_or_ip; + const char *host, *user; uint res_length; + if (is_current) + { + user= thd->priv_user; + host= thd->priv_host; + } + else + { + user= thd->user; + host= thd->host_or_ip; + } + // For system threads (e.g. replication SQL thread) user may be empty - if (!thd->user) + if (!user) return &my_empty_string; - res_length= (strlen(thd->user)+strlen(host)+2) * cs->mbmaxlen; + res_length= (strlen(user)+strlen(host)+2) * cs->mbmaxlen; if (str->alloc(res_length)) { @@ -1568,12 +1581,13 @@ String *Item_func_user::val_str(String *str) return 0; } res_length=cs->cset->snprintf(cs, (char*)str->ptr(), res_length, "%s@%s", - thd->user, host); + user, host); str->length(res_length); str->set_charset(cs); return str; } + void Item_func_soundex::fix_length_and_dec() { collation.set(args[0]->collation); diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 8d2eb269915..13073b25130 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -356,8 +356,15 @@ public: Item_func_sysconst() { collation.set(system_charset_info,DERIVATION_SYSCONST); } Item *safe_charset_converter(CHARSET_INFO *tocs); + /* + Used to create correct Item name in new converted item in + safe_charset_converter, return string representation of this function + call + */ + virtual const char *fully_qualified_func_name() const = 0; }; + class Item_func_database :public Item_func_sysconst { public: @@ -369,18 +376,27 @@ public: maybe_null=1; } const char *func_name() const { return "database"; } + const char *fully_qualified_func_name() const { return "database()"; } }; + class Item_func_user :public Item_func_sysconst { + bool is_current; + public: - Item_func_user() :Item_func_sysconst() {} + Item_func_user(bool is_current_arg) + :Item_func_sysconst(), is_current(is_current_arg) {} String *val_str(String *); - void fix_length_and_dec() - { - max_length= (USERNAME_LENGTH+HOSTNAME_LENGTH+1)*system_charset_info->mbmaxlen; + void fix_length_and_dec() + { + max_length= ((USERNAME_LENGTH + HOSTNAME_LENGTH + 1) * + system_charset_info->mbmaxlen); } - const char *func_name() const { return "user"; } + const char *func_name() const + { return is_current ? "current_user" : "user"; } + const char *fully_qualified_func_name() const + { return is_current ? "current_user()" : "user()"; } }; diff --git a/sql/sql_class.h b/sql/sql_class.h index a635a126f84..b00f6bcebd9 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1417,6 +1417,8 @@ public: (variables.sql_mode & MODE_STRICT_ALL_TABLES))); } void set_status_var_init(); + bool is_context_analysis_only() + { return current_arena->is_stmt_prepare() || lex->view_prepare_mode; } }; #define tmp_disable_binlog(A) \ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 360bc421965..4cf4bd3c36b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -4794,7 +4794,7 @@ simple_expr: | UNIX_TIMESTAMP '(' expr ')' { $$= new Item_func_unix_timestamp($3); } | USER '(' ')' - { $$= new Item_func_user(); Lex->safe_to_cache_query=0; } + { $$= new Item_func_user(FALSE); Lex->safe_to_cache_query=0; } | UTC_DATE_SYM optional_braces { $$= new Item_func_curdate_utc(); Lex->safe_to_cache_query=0;} | UTC_TIME_SYM optional_braces