diff options
author | unknown <gluh@gluh.mysql.r18.ru> | 2005-03-05 14:35:32 +0300 |
---|---|---|
committer | unknown <gluh@gluh.mysql.r18.ru> | 2005-03-05 14:35:32 +0300 |
commit | 0d7e68c92a4ded30d02a66b434cbe624c3df5e1e (patch) | |
tree | d882344cb6e35451bd9dffbfd69b088912a9fce8 | |
parent | 0ae5efb4dc1df3af598030cf7b96b95841306c70 (diff) | |
download | mariadb-git-0d7e68c92a4ded30d02a66b434cbe624c3df5e1e.tar.gz |
WL2131: Access control for SHOW ... PROCEDURE|FUNCTION ...
-rw-r--r-- | mysql-test/r/information_schema.result | 45 | ||||
-rw-r--r-- | mysql-test/t/information_schema.test | 37 | ||||
-rw-r--r-- | sql/mysql_priv.h | 1 | ||||
-rw-r--r-- | sql/sp_head.cc | 35 | ||||
-rw-r--r-- | sql/sql_acl.cc | 31 | ||||
-rw-r--r-- | sql/sql_acl.h | 4 | ||||
-rw-r--r-- | sql/sql_parse.cc | 32 | ||||
-rw-r--r-- | sql/sql_show.cc | 52 |
8 files changed, 206 insertions, 31 deletions
diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 8e49a3df4c8..0e72de7ab12 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -1,7 +1,8 @@ show variables where variable_name like "skip_show_database"; Variable_name Value skip_show_database OFF -grant all privileges on test.* to mysqltest_1@localhost; +grant select, update, execute on test.* to mysqltest_2@localhost; +grant select, update on test.* to mysqltest_1@localhost; select * from information_schema.SCHEMATA where schema_name > 'm'; CATALOG_NAME SCHEMA_NAME DEFAULT_CHARACTER_SET_NAME SQL_PATH NULL mysql latin1 NULL @@ -229,6 +230,44 @@ sel2 sel2 select count(*) from information_schema.ROUTINES; count(*) 2 +select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES; +ROUTINE_NAME ROUTINE_DEFINITION +show create function sub1; +ERROR 42000: FUNCTION sub1 does not exist +select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES; +ROUTINE_NAME ROUTINE_DEFINITION +sel2 +sub1 +grant all privileges on test.* to mysqltest_1@localhost; +select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES; +ROUTINE_NAME ROUTINE_DEFINITION +sel2 +sub1 +create function sub2(i int) returns int +return i+1; +select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES; +ROUTINE_NAME ROUTINE_DEFINITION +sel2 +sub1 +sub2 return i+1 +show create procedure sel2; +Procedure sql_mode Create Procedure +sel2 +show create function sub1; +Function sql_mode Create Function +sub1 +show create function sub2; +Function sql_mode Create Function +sub2 CREATE FUNCTION `test`.`sub2`(i int) RETURNS int +return i+1 +drop function sub2; +show create procedure sel2; +Procedure sql_mode Create Procedure +sel2 CREATE PROCEDURE `test`.`sel2`() +begin +select * from t1; +select * from t2; +end create view v0 (c) as select schema_name from information_schema.schemata; select * from v0; c @@ -311,8 +350,8 @@ GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME PRIVILEGE_TYPE IS_GRAN 'mysqltest_1'@'localhost' NULL test t1 a INSERT NO 'mysqltest_1'@'localhost' NULL test t1 a UPDATE NO 'mysqltest_1'@'localhost' NULL test t1 a REFERENCES NO -delete from mysql.user where user='mysqltest_1'; -delete from mysql.db where user='mysqltest_1'; +delete from mysql.user where user='mysqltest_1' or user='mysqltest_2'; +delete from mysql.db where user='mysqltest_1' or user='mysqltest_2'; delete from mysql.tables_priv where user='mysqltest_1'; delete from mysql.columns_priv where user='mysqltest_1'; flush privileges; diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 9ff0b1fdf61..4319fec258c 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -3,7 +3,8 @@ # show databases show variables where variable_name like "skip_show_database"; -grant all privileges on test.* to mysqltest_1@localhost; +grant select, update, execute on test.* to mysqltest_2@localhost; +grant select, update on test.* to mysqltest_1@localhost; select * from information_schema.SCHEMATA where schema_name > 'm'; select schema_name from information_schema.schemata; @@ -104,6 +105,30 @@ select a.ROUTINE_NAME, b.name from information_schema.ROUTINES a, mysql.proc b where a.ROUTINE_NAME = convert(b.name using utf8); select count(*) from information_schema.ROUTINES; +connect (user1,localhost,mysqltest_1,,); +connect (user3,localhost,mysqltest_2,,); +connection user1; +select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES; +--error 1305 +show create function sub1; +connection user3; +select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES; +connection default; +grant all privileges on test.* to mysqltest_1@localhost; +connect (user2,localhost,mysqltest_1,,); +connection user2; +select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES; +create function sub2(i int) returns int + return i+1; +select ROUTINE_NAME, ROUTINE_DEFINITION from information_schema.ROUTINES; +show create procedure sel2; +show create function sub1; +show create function sub2; +connection default; +disconnect user1; +drop function sub2; +show create procedure sel2; + # # Test for views # @@ -138,8 +163,8 @@ select * from information_schema.USER_PRIVILEGES where grantee like '%mysqltest_ select * from information_schema.SCHEMA_PRIVILEGES where grantee like '%mysqltest_1%'; select * from information_schema.TABLE_PRIVILEGES where grantee like '%mysqltest_1%'; select * from information_schema.COLUMN_PRIVILEGES where grantee like '%mysqltest_1%'; -delete from mysql.user where user='mysqltest_1'; -delete from mysql.db where user='mysqltest_1'; +delete from mysql.user where user='mysqltest_1' or user='mysqltest_2'; +delete from mysql.db where user='mysqltest_1' or user='mysqltest_2'; delete from mysql.tables_priv where user='mysqltest_1'; delete from mysql.columns_priv where user='mysqltest_1'; flush privileges; @@ -160,13 +185,11 @@ TABLE_SCHEMA= "test"; select * from information_schema.KEY_COLUMN_USAGE where TABLE_SCHEMA= "test"; - -connect (user1,localhost,mysqltest_1,,); -connection user1; +connection user2; select table_name from information_schema.TABLES where table_schema like "test%"; select table_name,column_name from information_schema.COLUMNS where table_schema like "test%"; select ROUTINE_NAME from information_schema.ROUTINES; -disconnect user1; +disconnect user2; connection default; delete from mysql.user where user='mysqltest_1'; drop table t1; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index aef1cd1efec..b978ffe175b 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -453,6 +453,7 @@ bool check_procedure_access(THD *thd,ulong want_access,char *db,char *name, bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list); +bool check_some_routine_access(THD *thd, char *db, char *name); bool multi_update_precheck(THD *thd, TABLE_LIST *tables); bool multi_delete_precheck(THD *thd, TABLE_LIST *tables, uint *table_count); bool mysql_multi_update_prepare(THD *thd); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 59cdac1b153..30f3cac8a0d 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1004,6 +1004,27 @@ sp_head::restore_thd_mem_root(THD *thd) } +bool check_show_routine_acceess(THD *thd, sp_head *sp, bool *full_access) +{ + TABLE_LIST tables; + bzero((char*) &tables,sizeof(tables)); + tables.db= (char*) "mysql"; + tables.table_name= tables.alias= (char*) "proc"; + *full_access= !check_table_access(thd, SELECT_ACL, &tables, 1); + if (!(*full_access)) + *full_access= (!strcmp(sp->m_definer_user.str, thd->priv_user) && + !strcmp(sp->m_definer_host.str, thd->priv_host)); + if (!(*full_access)) + { +#ifndef NO_EMBEDDED_ACCESS_CHECKS + return check_some_routine_access(thd, (char * )sp->m_db.str, + (char * ) sp->m_name.str); +#endif + } + return 0; +} + + int sp_head::show_create_procedure(THD *thd) { @@ -1016,11 +1037,15 @@ sp_head::show_create_procedure(THD *thd) sys_var *sql_mode_var; byte *sql_mode_str; ulong sql_mode_len; + bool full_access; DBUG_ENTER("sp_head::show_create_procedure"); DBUG_PRINT("info", ("procedure %s", m_name.str)); LINT_INIT(sql_mode_str); LINT_INIT(sql_mode_len); + + if (check_show_routine_acceess(thd, this, &full_access)) + return 1; old_sql_mode= thd->variables.sql_mode; thd->variables.sql_mode= m_sql_mode; @@ -1047,7 +1072,8 @@ sp_head::show_create_procedure(THD *thd) protocol->store(m_name.str, m_name.length, system_charset_info); if (sql_mode_var) protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); - protocol->store(m_defstr.str, m_defstr.length, system_charset_info); + if (full_access) + protocol->store(m_defstr.str, m_defstr.length, system_charset_info); res= protocol->write(); send_eof(thd); @@ -1085,11 +1111,15 @@ sp_head::show_create_function(THD *thd) sys_var *sql_mode_var; byte *sql_mode_str; ulong sql_mode_len; + bool full_access; DBUG_ENTER("sp_head::show_create_function"); DBUG_PRINT("info", ("procedure %s", m_name.str)); LINT_INIT(sql_mode_str); LINT_INIT(sql_mode_len); + if (check_show_routine_acceess(thd, this, &full_access)) + return 1; + old_sql_mode= thd->variables.sql_mode; thd->variables.sql_mode= m_sql_mode; sql_mode_var= find_sys_var("SQL_MODE", 8); @@ -1114,7 +1144,8 @@ sp_head::show_create_function(THD *thd) protocol->store(m_name.str, m_name.length, system_charset_info); if (sql_mode_var) protocol->store((char*) sql_mode_str, sql_mode_len, system_charset_info); - protocol->store(m_defstr.str, m_defstr.length, system_charset_info); + if (full_access) + protocol->store(m_defstr.str, m_defstr.length, system_charset_info); res= protocol->write(); send_eof(thd); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 041154c96cc..3db219b5fdc 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3583,6 +3583,37 @@ err: } +/* + Check if routine has any of the + procedure level grants + + SYNPOSIS + bool check_routine_level_acl() + thd Thread handler + db Database name + name Routine name + + RETURN + 1 error + 0 Ok +*/ + +bool check_routine_level_acl(THD *thd, char *db, char *name) +{ + bool no_routine_acl= 1; + if (grant_option) + { + GRANT_NAME *grant_proc; + rw_rdlock(&LOCK_grant); + if ((grant_proc= proc_hash_search(thd->priv_host, thd->ip, db, + thd->priv_user, name, 0))) + no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); + rw_unlock(&LOCK_grant); + } + return no_routine_acl; +} + + /***************************************************************************** Functions to retrieve the grant for a table/column (for SHOW functions) *****************************************************************************/ diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 3a9df84a35d..30e335c7afd 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -63,6 +63,9 @@ #define PROC_ACLS \ (ALTER_PROC_ACL | EXECUTE_ACL | GRANT_ACL) +#define SHOW_PROC_ACLS \ +(ALTER_PROC_ACL | EXECUTE_ACL | CREATE_PROC_ACL) + #define GLOBAL_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL | GRANT_ACL | \ @@ -216,6 +219,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, const char *db, const char *table); bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name); bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name); +bool check_routine_level_acl(THD *thd, char *db, char *name); #ifdef NO_EMBEDDED_ACCESS_CHECKS #define check_grant(A,B,C,D,E,F) 0 diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index e5c29f56305..2ec5e0a4128 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4745,6 +4745,38 @@ check_procedure_access(THD *thd, ulong want_access,char *db, char *name, return FALSE; } + +/* + Check if the routine has any of the routine privileges + + SYNOPSIS + check_some_routine_access() + thd Thread handler + db Database name + name Routine name + + RETURN + 0 ok + 1 error +*/ + +bool check_some_routine_access(THD *thd, char *db, char *name) +{ + ulong save_priv; + if (thd->master_access & SHOW_PROC_ACLS) + return FALSE; + if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, 0, 1) || + (save_priv & SHOW_PROC_ACLS)) + return FALSE; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (grant_option) + return check_routine_level_acl(thd, db, name); +#endif + + return FALSE; +} + + /* Check if the given table has any of the asked privileges diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 7a4ee9f5de3..4ffe7110cfa 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2468,32 +2468,41 @@ int fill_schema_coll_charset_app(THD *thd, TABLE_LIST *tables, COND *cond) } -void store_schema_proc(THD *thd, TABLE *table, - TABLE *proc_table, - const char *wild) +void store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, + const char *wild, bool full_access, const char *sp_user) { String tmp_string; TIME time; LEX *lex= thd->lex; CHARSET_INFO *cs= system_charset_info; - restore_record(table, s->default_values); + const char *sp_db, *sp_name, *definer; + sp_db= get_field(thd->mem_root, proc_table->field[0]); + sp_name= get_field(thd->mem_root, proc_table->field[1]); + definer= get_field(thd->mem_root, proc_table->field[11]); + if (!full_access) + full_access= !strcmp(sp_user, definer); + if (!full_access) + { +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_some_routine_access(thd, (char * )sp_db, (char * )sp_name)) + return; +#endif + } + if (lex->orig_sql_command == SQLCOM_SHOW_STATUS_PROC && proc_table->field[2]->val_int() == TYPE_ENUM_PROCEDURE || lex->orig_sql_command == SQLCOM_SHOW_STATUS_FUNC && proc_table->field[2]->val_int() == TYPE_ENUM_FUNCTION || lex->orig_sql_command == SQLCOM_END) { - tmp_string.length(0); - get_field(thd->mem_root, proc_table->field[1], &tmp_string); - if (!wild || !wild[0] || !wild_compare(tmp_string.ptr(), wild, 0)) + restore_record(table, s->default_values); + if (!wild || !wild[0] || !wild_compare(sp_name, wild, 0)) { - table->field[3]->store(tmp_string.ptr(), tmp_string.length(), cs); + table->field[3]->store(sp_name, strlen(sp_name), cs); tmp_string.length(0); get_field(thd->mem_root, proc_table->field[3], &tmp_string); table->field[0]->store(tmp_string.ptr(), tmp_string.length(), cs); - tmp_string.length(0); - get_field(thd->mem_root, proc_table->field[0], &tmp_string); - table->field[2]->store(tmp_string.ptr(), tmp_string.length(), cs); + table->field[2]->store(sp_db, strlen(sp_db), cs); tmp_string.length(0); get_field(thd->mem_root, proc_table->field[2], &tmp_string); table->field[4]->store(tmp_string.ptr(), tmp_string.length(), cs); @@ -2504,10 +2513,13 @@ void store_schema_proc(THD *thd, TABLE *table, table->field[5]->store(tmp_string.ptr(), tmp_string.length(), cs); table->field[5]->set_notnull(); } + if (full_access) + { + tmp_string.length(0); + get_field(thd->mem_root, proc_table->field[10], &tmp_string); + table->field[7]->store(tmp_string.ptr(), tmp_string.length(), cs); + } table->field[6]->store("SQL", 3, cs); - tmp_string.length(0); - get_field(thd->mem_root, proc_table->field[10], &tmp_string); - table->field[7]->store(tmp_string.ptr(), tmp_string.length(), cs); table->field[10]->store("SQL", 3, cs); tmp_string.length(0); get_field(thd->mem_root, proc_table->field[6], &tmp_string); @@ -2531,9 +2543,7 @@ void store_schema_proc(THD *thd, TABLE *table, tmp_string.length(0); get_field(thd->mem_root, proc_table->field[15], &tmp_string); table->field[18]->store(tmp_string.ptr(), tmp_string.length(), cs); - tmp_string.length(0); - get_field(thd->mem_root, proc_table->field[11], &tmp_string); - table->field[19]->store(tmp_string.ptr(), tmp_string.length(), cs); + table->field[19]->store(definer, strlen(definer), cs); table->file->write_row(table->record[0]); } } @@ -2547,14 +2557,18 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; int res= 0; TABLE *table= tables->table, *old_open_tables= thd->open_tables; + bool full_access; + char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; DBUG_ENTER("fill_schema_proc"); + strxmov(definer, thd->priv_user, "@", thd->priv_host, NullS); bzero((char*) &proc_tables,sizeof(proc_tables)); proc_tables.db= (char*) "mysql"; proc_tables.db_length= 5; proc_tables.table_name= proc_tables.alias= (char*) "proc"; proc_tables.table_name_length= 4; proc_tables.lock_type= TL_READ; + full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1); if (!(proc_table= open_ltable(thd, &proc_tables, TL_READ))) { DBUG_RETURN(1); @@ -2565,9 +2579,9 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) res= (res == HA_ERR_END_OF_FILE) ? 0 : 1; goto err; } - store_schema_proc(thd, table, proc_table, wild); + store_schema_proc(thd, table, proc_table, wild, full_access, definer); while (!proc_table->file->index_next(proc_table->record[0])) - store_schema_proc(thd, table, proc_table, wild); + store_schema_proc(thd, table, proc_table, wild, full_access, definer); err: proc_table->file->ha_index_end(); |