diff options
Diffstat (limited to 'sql/sql_acl.cc')
-rw-r--r-- | sql/sql_acl.cc | 757 |
1 files changed, 439 insertions, 318 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ea781080a38..86f62d9bf72 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000-2003 MySQL AB +/* Copyright (C) 2000-2003 MySQL AB, 2008-2009 Sun Microsystems, Inc This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,6 +30,9 @@ #include <stdarg.h> #include "sp_head.h" #include "sp.h" +#include "transaction.h" + +bool mysql_user_table_is_in_short_password_format= false; static const TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = { @@ -219,25 +222,6 @@ set_user_salt(ACL_USER *acl_user, const char *password, uint password_len) } /* - This after_update function is used when user.password is less than - SCRAMBLE_LENGTH bytes. -*/ - -static void restrict_update_of_old_passwords_var(THD *thd, - enum_var_type var_type) -{ - if (var_type == OPT_GLOBAL) - { - pthread_mutex_lock(&LOCK_global_system_variables); - global_system_variables.old_passwords= 1; - pthread_mutex_unlock(&LOCK_global_system_variables); - } - else - thd->variables.old_passwords= 1; -} - - -/* Initialize structures responsible for user/db-level privilege checking and load privilege information for them from tables in the 'mysql' database. @@ -277,7 +261,6 @@ my_bool acl_init(bool dont_read_acl_tables) DBUG_RETURN(1); /* purecov: inspected */ thd->thread_stack= (char*) &thd; thd->store_globals(); - lex_start(thd); /* It is safe to call acl_reload() since acl_* arrays and hashes which will be freed there are global static objects and thus are initialized @@ -327,7 +310,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0, FALSE); table->use_all_columns(); - VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50)); + (void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50); while (!(read_record_info.read_record(&read_record_info))) { ACL_HOST host; @@ -367,7 +350,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL; } #endif - VOID(push_dynamic(&acl_hosts,(uchar*) &host)); + (void) push_dynamic(&acl_hosts,(uchar*) &host); } my_qsort((uchar*) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements, sizeof(ACL_HOST),(qsort_cmp) acl_compare); @@ -376,7 +359,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0,FALSE); table->use_all_columns(); - VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100)); + (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100); password_length= table->field[2]->field_length / table->field[2]->charset()->mbmaxlen; if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323) @@ -389,23 +372,23 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) DBUG_PRINT("info",("user table fields: %d, password length: %d", table->s->fields, password_length)); - pthread_mutex_lock(&LOCK_global_system_variables); + mysql_mutex_lock(&LOCK_global_system_variables); if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH) { if (opt_secure_auth) { - pthread_mutex_unlock(&LOCK_global_system_variables); + mysql_mutex_unlock(&LOCK_global_system_variables); sql_print_error("Fatal error: mysql.user table is in old format, " "but server started with --secure-auth option."); goto end; } - sys_old_passwords.after_update= restrict_update_of_old_passwords_var; + mysql_user_table_is_in_short_password_format= true; if (global_system_variables.old_passwords) - pthread_mutex_unlock(&LOCK_global_system_variables); + mysql_mutex_unlock(&LOCK_global_system_variables); else { global_system_variables.old_passwords= 1; - pthread_mutex_unlock(&LOCK_global_system_variables); + mysql_mutex_unlock(&LOCK_global_system_variables); sql_print_warning("mysql.user table is not updated to new password format; " "Disabling new password usage until " "mysql_fix_privilege_tables is run"); @@ -414,8 +397,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) } else { - sys_old_passwords.after_update= 0; - pthread_mutex_unlock(&LOCK_global_system_variables); + mysql_user_table_is_in_short_password_format= false; + mysql_mutex_unlock(&LOCK_global_system_variables); } allow_all_hosts=0; @@ -551,7 +534,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) user.access|= SUPER_ACL | EXECUTE_ACL; #endif } - VOID(push_dynamic(&acl_users,(uchar*) &user)); + (void) push_dynamic(&acl_users,(uchar*) &user); if (!user.host.hostname || (user.host.hostname[0] == wild_many && !user.host.hostname[1])) allow_all_hosts=1; // Anyone can connect @@ -564,7 +547,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0,FALSE); table->use_all_columns(); - VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100)); + (void) my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100); while (!(read_record_info.read_record(&read_record_info))) { ACL_DB db; @@ -614,7 +597,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; } #endif - VOID(push_dynamic(&acl_dbs,(uchar*) &db)); + (void) push_dynamic(&acl_dbs,(uchar*) &db); } my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements, sizeof(ACL_DB),(qsort_cmp) acl_compare); @@ -677,13 +660,6 @@ my_bool acl_reload(THD *thd) my_bool return_val= TRUE; DBUG_ENTER("acl_reload"); - if (thd->locked_tables) - { // Can't have locked tables here - thd->lock=thd->locked_tables; - thd->locked_tables=0; - close_thread_tables(thd); - } - /* To avoid deadlocks we should obtain table locks before obtaining acl_cache->lock mutex. @@ -696,10 +672,10 @@ my_bool acl_reload(THD *thd) tables[0].next_local= tables[0].next_global= tables+1; tables[1].next_local= tables[1].next_global= tables+2; tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; - tables[0].skip_temporary= tables[1].skip_temporary= - tables[2].skip_temporary= TRUE; + tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY; + init_mdl_requests(tables); - if (simple_open_n_lock_tables(thd, tables)) + if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { /* Execution might have been interrupted; only print the error message @@ -712,7 +688,7 @@ my_bool acl_reload(THD *thd) } if ((old_initialized=initialized)) - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_mutex_lock(&acl_cache->lock); old_acl_hosts=acl_hosts; old_acl_users=acl_users; @@ -739,9 +715,11 @@ my_bool acl_reload(THD *thd) delete_dynamic(&old_acl_dbs); } if (old_initialized) - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); end: + trans_commit_implicit(thd); close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); DBUG_RETURN(return_val); } @@ -891,7 +869,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, DBUG_RETURN(0); } - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_mutex_lock(&acl_cache->lock); /* Find acl entry in user database. Note, that find_acl_user is not the same, @@ -1070,7 +1048,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, else *sctx->priv_host= 0; } - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); DBUG_RETURN(res); } @@ -1117,7 +1095,7 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, DBUG_RETURN(FALSE); } - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_mutex_lock(&acl_cache->lock); sctx->master_access= 0; sctx->db_access= 0; @@ -1171,7 +1149,7 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, else *sctx->priv_host= 0; } - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); DBUG_RETURN(res); } @@ -1192,7 +1170,7 @@ static void acl_update_user(const char *user, const char *host, USER_RESOURCES *mqh, ulong privileges) { - safe_mutex_assert_owner(&acl_cache->lock); + mysql_mutex_assert_owner(&acl_cache->lock); for (uint i=0 ; i < acl_users.elements ; i++) { @@ -1244,7 +1222,7 @@ static void acl_insert_user(const char *user, const char *host, { ACL_USER acl_user; - safe_mutex_assert_owner(&acl_cache->lock); + mysql_mutex_assert_owner(&acl_cache->lock); acl_user.user=*user ? strdup_root(&mem,user) : 0; update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0); @@ -1260,7 +1238,7 @@ static void acl_insert_user(const char *user, const char *host, set_user_salt(&acl_user, password, password_len); - VOID(push_dynamic(&acl_users,(uchar*) &acl_user)); + (void) push_dynamic(&acl_users,(uchar*) &acl_user); if (!acl_user.host.hostname || (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1])) allow_all_hosts=1; // Anyone can connect /* purecov: tested */ @@ -1275,7 +1253,7 @@ static void acl_insert_user(const char *user, const char *host, static void acl_update_db(const char *user, const char *host, const char *db, ulong privileges) { - safe_mutex_assert_owner(&acl_cache->lock); + mysql_mutex_assert_owner(&acl_cache->lock); for (uint i=0 ; i < acl_dbs.elements ; i++) { @@ -1320,13 +1298,13 @@ static void acl_insert_db(const char *user, const char *host, const char *db, ulong privileges) { ACL_DB acl_db; - safe_mutex_assert_owner(&acl_cache->lock); + mysql_mutex_assert_owner(&acl_cache->lock); acl_db.user=strdup_root(&mem,user); update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0); acl_db.db=strdup_root(&mem,db); acl_db.access=privileges; acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user); - VOID(push_dynamic(&acl_dbs,(uchar*) &acl_db)); + (void) push_dynamic(&acl_dbs,(uchar*) &acl_db); my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements, sizeof(ACL_DB),(qsort_cmp) acl_compare); } @@ -1350,7 +1328,7 @@ ulong acl_get(const char *host, const char *ip, acl_entry *entry; DBUG_ENTER("acl_get"); - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_mutex_lock(&acl_cache->lock); end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db); if (lower_case_table_names) { @@ -1362,7 +1340,7 @@ ulong acl_get(const char *host, const char *ip, key_length))) { db_access=entry->access; - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); DBUG_PRINT("exit", ("access: 0x%lx", db_access)); DBUG_RETURN(db_access); } @@ -1416,7 +1394,7 @@ exit: memcpy((uchar*) entry->key,key,key_length); acl_cache->add(entry); } - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access)); DBUG_RETURN(db_access & host_access); } @@ -1432,10 +1410,11 @@ exit: static void init_check_host(void) { DBUG_ENTER("init_check_host"); - VOID(my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip), - acl_users.elements,1)); - VOID(my_hash_init(&acl_check_hosts,system_charset_info,acl_users.elements,0,0, - (my_hash_get_key) check_get_key,0,0)); + (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip), + acl_users.elements,1); + (void) my_hash_init(&acl_check_hosts,system_charset_info, + acl_users.elements, 0, 0, + (my_hash_get_key) check_get_key, 0, 0); if (!allow_all_hosts) { for (uint i=0 ; i < acl_users.elements ; i++) @@ -1497,12 +1476,12 @@ bool acl_check_host(const char *host, const char *ip) { if (allow_all_hosts) return 0; - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_mutex_lock(&acl_cache->lock); if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) || (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip)))) { - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); return 0; // Found host } for (uint i=0 ; i < acl_wild_hosts.elements ; i++) @@ -1510,11 +1489,11 @@ bool acl_check_host(const char *host, const char *ip) acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*); if (compare_hostname(acl, host, ip)) { - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); return 0; // Host ok } } - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); return 1; // Host is not allowed } @@ -1550,7 +1529,7 @@ int check_change_password(THD *thd, const char *host, const char *user, my_strcasecmp(system_charset_info, host, thd->security_ctx->priv_host))) { - if (check_access(thd, UPDATE_ACL, "mysql",0,1,0,0)) + if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0)) return(1); } if (!thd->slave_thread && !thd->security_ctx->user[0]) @@ -1603,9 +1582,7 @@ bool change_password(THD *thd, const char *host, const char *user, if (check_change_password(thd, host, user, new_password, new_password_len)) DBUG_RETURN(1); - bzero((char*) &tables, sizeof(tables)); - tables.alias= tables.table_name= (char*) "user"; - tables.db= (char*) "mysql"; + tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE); #ifdef HAVE_REPLICATION /* @@ -1625,14 +1602,14 @@ bool change_password(THD *thd, const char *host, const char *user, } #endif - if (!(table= open_ltable(thd, &tables, TL_WRITE, 0))) + if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT))) DBUG_RETURN(1); - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_mutex_lock(&acl_cache->lock); ACL_USER *acl_user; if (!(acl_user= find_acl_user(host, user, TRUE))) { - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); goto end; } @@ -1644,12 +1621,12 @@ bool change_password(THD *thd, const char *host, const char *user, acl_user->user ? acl_user->user : "", new_password, new_password_len)) { - VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */ + mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */ goto end; } acl_cache->clear(1); // Clear locked hostname cache - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); result= 0; if (mysql_bin_log.is_open()) { @@ -1660,8 +1637,8 @@ bool change_password(THD *thd, const char *host, const char *user, acl_user->host.hostname ? acl_user->host.hostname : "", new_password)); thd->clear_error(); - result= thd->binlog_query(THD::MYSQL_QUERY_TYPE, buff, query_length, - FALSE, FALSE, 0); + result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length, + FALSE, FALSE, FALSE, 0); } end: close_thread_tables(thd); @@ -1690,9 +1667,9 @@ bool is_acl_user(const char *host, const char *user) if (!initialized) return TRUE; - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_mutex_lock(&acl_cache->lock); res= find_acl_user(host, user, TRUE) != NULL; - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); return res; } @@ -1707,7 +1684,7 @@ find_acl_user(const char *host, const char *user, my_bool exact) DBUG_ENTER("find_acl_user"); DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user)); - safe_mutex_assert_owner(&acl_cache->lock); + mysql_mutex_assert_owner(&acl_cache->lock); for (uint i=0 ; i < acl_users.elements ; i++) { @@ -1790,24 +1767,83 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, (ip && !wild_compare(ip, host->hostname, 0))); } +/** + Check if the given host name needs to be resolved or not. + Host name has to be resolved if it actually contains *name*. + + For example: + 192.168.1.1 --> FALSE + 192.168.1.0/255.255.255.0 --> FALSE + % --> FALSE + 192.168.1.% --> FALSE + AB% --> FALSE + + AAAAFFFF --> TRUE (Hostname) + AAAA:FFFF:1234:5678 --> FALSE + ::1 --> FALSE + + This function does not check if the given string is a valid host name or + not. It assumes that the argument is a valid host name. + + @param hostname the string to check. + + @return a flag telling if the argument needs to be resolved or not. + @retval TRUE the argument is a host name and needs to be resolved. + @retval FALSE the argument is either an IP address, or a patter and + should not be resolved. +*/ + bool hostname_requires_resolving(const char *hostname) { - char cur; if (!hostname) return FALSE; - size_t namelen= strlen(hostname); - size_t lhlen= strlen(my_localhost); - if ((namelen == lhlen) && - !my_strnncoll(system_charset_info, (const uchar *)hostname, namelen, - (const uchar *)my_localhost, strlen(my_localhost))) + + /* Check if hostname is the localhost. */ + + size_t hostname_len= strlen(hostname); + size_t localhost_len= strlen(my_localhost); + + if (hostname == my_localhost || + (hostname_len == localhost_len && + !my_strnncoll(system_charset_info, + (const uchar *) hostname, hostname_len, + (const uchar *) my_localhost, strlen(my_localhost)))) + { return FALSE; - for (; (cur=*hostname); hostname++) + } + + /* + If the string contains any of {':', '%', '_', '/'}, it is definitely + not a host name: + - ':' means that the string is an IPv6 address; + - '%' or '_' means that the string is a pattern; + - '/' means that the string is an IPv4 network address; + */ + + for (const char *p= hostname; *p; ++p) { - if ((cur != '%') && (cur != '_') && (cur != '.') && (cur != '/') && - ((cur < '0') || (cur > '9'))) - return TRUE; + switch (*p) { + case ':': + case '%': + case '_': + case '/': + return FALSE; + } } - return FALSE; + + /* + Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address + (12.34.56.78). The assumption is that if the string contains only + digits and dots, it is an IPv4 address. Otherwise -- a host name. + */ + + for (const char *p= hostname; *p; ++p) + { + if (*p != '.' && !my_isdigit(&my_charset_latin1, *p)) + return TRUE; /* a "letter" has been found. */ + } + + return FALSE; /* all characters are either dots or digits. */ } @@ -1910,7 +1946,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, LEX *lex= thd->lex; DBUG_ENTER("replace_user_table"); - safe_mutex_assert_owner(&acl_cache->lock); + mysql_mutex_assert_owner(&acl_cache->lock); if (combo.password.str && combo.password.str[0]) { @@ -3004,7 +3040,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, class LEX_COLUMN *column; List_iterator <LEX_COLUMN> column_iter(columns); - if (open_and_lock_tables(thd, table_list)) + if (open_and_lock_tables(thd, table_list, TRUE, 0)) DBUG_RETURN(TRUE); while ((column = column_iter++)) @@ -3071,14 +3107,15 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, ? tables+2 : 0); tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; + init_mdl_requests(tables); /* This statement will be replicated as a statement, even when using row-based replication. The flag will be reset at the end of the statement. */ - save_binlog_row_based= thd->current_stmt_binlog_row_based; - thd->clear_current_stmt_binlog_row_based(); + if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) + thd->clear_current_stmt_binlog_format_row(); #ifdef HAVE_REPLICATION /* @@ -3095,7 +3132,9 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, if (!(thd->spcont || rpl_filter->tables_ok(0, tables))) { /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(FALSE); } } @@ -3107,19 +3146,21 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, */ Query_tables_list backup; thd->lex->reset_n_backup_query_tables_list(&backup); - if (simple_open_n_lock_tables(thd,tables)) + if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { // Should never happen close_thread_tables(thd); /* purecov: deadcode */ /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(TRUE); /* purecov: deadcode */ } if (!revoke_grant) create_new_users= test_if_create_new_users(thd); bool result= FALSE; - rw_wrlock(&LOCK_grant); - pthread_mutex_lock(&acl_cache->lock); + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); MEM_ROOT *old_root= thd->mem_root; thd->mem_root= &memex; grant_version++; @@ -3224,14 +3265,14 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, } } thd->mem_root= old_root; - pthread_mutex_unlock(&acl_cache->lock); + mysql_mutex_unlock(&acl_cache->lock); if (!result) /* success */ { result= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); } - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); if (!result) /* success */ my_ok(thd); @@ -3239,7 +3280,9 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, /* Tables are automatically closed */ thd->lex->restore_backup_query_tables_list(&backup); /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(result); } @@ -3298,14 +3341,15 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + init_mdl_requests(tables); /* This statement will be replicated as a statement, even when using row-based replication. The flag will be reset at the end of the statement. */ - save_binlog_row_based= thd->current_stmt_binlog_row_based; - thd->clear_current_stmt_binlog_row_based(); + if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) + thd->clear_current_stmt_binlog_format_row(); #ifdef HAVE_REPLICATION /* @@ -3322,24 +3366,28 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, if (!(thd->spcont || rpl_filter->tables_ok(0, tables))) { /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(FALSE); } } #endif - if (simple_open_n_lock_tables(thd,tables)) + if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { // Should never happen close_thread_tables(thd); /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(TRUE); } if (!revoke_grant) create_new_users= test_if_create_new_users(thd); - rw_wrlock(&LOCK_grant); - pthread_mutex_lock(&acl_cache->lock); + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); MEM_ROOT *old_root= thd->mem_root; thd->mem_root= &memex; @@ -3400,7 +3448,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, } } thd->mem_root= old_root; - pthread_mutex_unlock(&acl_cache->lock); + mysql_mutex_unlock(&acl_cache->lock); if (write_to_binlog) { @@ -3408,9 +3456,11 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, result= TRUE; } - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); /* Tables are automatically closed */ DBUG_RETURN(result); @@ -3448,14 +3498,15 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; + init_mdl_requests(tables); /* This statement will be replicated as a statement, even when using row-based replication. The flag will be reset at the end of the statement. */ - save_binlog_row_based= thd->current_stmt_binlog_row_based; - thd->clear_current_stmt_binlog_row_based(); + if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) + thd->clear_current_stmt_binlog_format_row(); #ifdef HAVE_REPLICATION /* @@ -3472,17 +3523,21 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, if (!(thd->spcont || rpl_filter->tables_ok(0, tables))) { /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(FALSE); } } #endif - if (simple_open_n_lock_tables(thd,tables)) + if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { // This should never happen close_thread_tables(thd); /* purecov: deadcode */ /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(TRUE); /* purecov: deadcode */ } @@ -3490,8 +3545,8 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, create_new_users= test_if_create_new_users(thd); /* go through users in user_list */ - rw_wrlock(&LOCK_grant); - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); grant_version++; int result=0; @@ -3530,20 +3585,22 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, } } } - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); if (!result) { result= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); } - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); close_thread_tables(thd); if (!result) my_ok(thd); /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(result); } @@ -3581,7 +3638,6 @@ my_bool grant_init() DBUG_RETURN(1); /* purecov: deadcode */ thd->thread_stack= (char*) &thd; thd->store_globals(); - lex_start(thd); return_val= grant_reload(thd); delete thd; /* Remember that we don't have a THD */ @@ -3792,19 +3848,18 @@ static my_bool grant_reload_procs_priv(THD *thd) my_bool return_val= FALSE; DBUG_ENTER("grant_reload_procs_priv"); - bzero((char*) &table, sizeof(table)); - table.alias= table.table_name= (char*) "procs_priv"; - table.db= (char *) "mysql"; - table.lock_type= TL_READ; - table.skip_temporary= 1; + table.init_one_table("mysql", 5, "procs_priv", + strlen("procs_priv"), "procs_priv", + TL_READ); + table.open_type= OT_BASE_ONLY; - if (simple_open_n_lock_tables(thd, &table)) + if (open_and_lock_tables(thd, &table, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { close_thread_tables(thd); DBUG_RETURN(TRUE); } - rw_wrlock(&LOCK_grant); + mysql_rwlock_wrlock(&LOCK_grant); /* Save a copy of the current hash if we need to undo the grant load */ old_proc_priv_hash= proc_priv_hash; old_func_priv_hash= func_priv_hash; @@ -3822,7 +3877,7 @@ static my_bool grant_reload_procs_priv(THD *thd) my_hash_free(&old_proc_priv_hash); my_hash_free(&old_func_priv_hash); } - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); close_thread_tables(thd); DBUG_RETURN(return_val); @@ -3862,15 +3917,17 @@ my_bool grant_reload(THD *thd) tables[0].db= tables[1].db= (char *) "mysql"; tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type= tables[1].lock_type= TL_READ; - tables[0].skip_temporary= tables[1].skip_temporary= TRUE; + tables[0].open_type= tables[1].open_type= OT_BASE_ONLY; + init_mdl_requests(tables); + /* To avoid deadlocks we should obtain table locks before obtaining LOCK_grant rwlock. */ - if (simple_open_n_lock_tables(thd, tables)) + if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) goto end; - rw_wrlock(&LOCK_grant); + mysql_rwlock_wrlock(&LOCK_grant); old_column_priv_hash= column_priv_hash; /* @@ -3892,8 +3949,10 @@ my_bool grant_reload(THD *thd) my_hash_free(&old_column_priv_hash); free_root(&old_mem,MYF(0)); } - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); + trans_commit_implicit(thd); close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); /* It is OK failing to load procs_priv table because we may be @@ -3902,9 +3961,9 @@ my_bool grant_reload(THD *thd) if (grant_reload_procs_priv(thd)) return_val= 1; - rw_wrlock(&LOCK_grant); + mysql_rwlock_wrlock(&LOCK_grant); grant_version++; - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); end: DBUG_RETURN(return_val); @@ -3956,6 +4015,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table(); Security_context *sctx= thd->security_ctx; uint i; + ulong orig_want_access= want_access; DBUG_ENTER("check_grant"); DBUG_ASSERT(number > 0); @@ -3980,7 +4040,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); } - rw_rdlock(&LOCK_grant); + mysql_rwlock_rdlock(&LOCK_grant); for (table= tables; table && number-- && table != first_not_own_table; table= table->next_global) @@ -3989,6 +4049,33 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, sctx = test(table->security_ctx) ? table->security_ctx : thd->security_ctx; + const ACL_internal_table_access *access; + access= get_cached_table_access(&table->grant.m_internal, + table->get_db_name(), + table->get_table_name()); + + if (access) + { + switch(access->check(orig_want_access, &table->grant.privilege)) + { + case ACL_INTERNAL_ACCESS_GRANTED: + /* + Currently, + - the information_schema does not subclass ACL_internal_table_access, + there are no per table privilege checks for I_S, + - the performance schema does use per tables checks, but at most + returns 'CHECK_GRANT', and never 'ACCESS_GRANTED'. + so this branch is not used. + */ + DBUG_ASSERT(0); + case ACL_INTERNAL_ACCESS_DENIED: + goto err; + case ACL_INTERNAL_ACCESS_CHECK_GRANT: + break; + } + } + + want_access= orig_want_access; want_access&= ~sctx->master_access; if (!want_access) continue; // ok @@ -4041,11 +4128,11 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, goto err; // impossible } } - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); DBUG_RETURN(FALSE); err: - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); if (!no_errors) // Not a silent skip of table { char command[128]; @@ -4060,96 +4147,6 @@ err: } -/** - Check if all tables in the table list has any of the requested table level - privileges matching the current user. - - @param thd A pointer to the thread context. - @param required_access Set of privileges to compare against. - @param tables[in,out] A list of tables to be checked. - - @note If the table grant hash contains any grant table, this table will be - attached to the corresponding TABLE_LIST object in 'tables'. - - @return - @retval TRUE There is a privilege on the table level granted to the - current user. - @retval FALSE There are no privileges on the table level granted to the - current user. -*/ - -bool has_any_table_level_privileges(THD *thd, ulong required_access, - TABLE_LIST *tables) -{ - - Security_context *sctx; - GRANT_TABLE *grant_table; - TABLE_LIST *table; - - /* For each table in tables */ - for (table= tables; table; table= table->next_global) - { - /* - If this table is a VIEW, then it will supply its own security context. - This is because VIEWs can have a DEFINER or an INVOKER security role. - */ - sctx= table->security_ctx ? table->security_ctx : thd->security_ctx; - - /* - Get privileges from table_priv and column_priv tables by searching - the cache. - */ - rw_rdlock(&LOCK_grant); - grant_table= table_hash_search(sctx->host, sctx->ip, - table->db, sctx->priv_user, - table->table_name,0); - rw_unlock(&LOCK_grant); - - /* Stop if there are no grants for the current user */ - if (!grant_table) - return FALSE; - - /* - Save a pointer to the found grant_table in the table object. - This pointer can later be used to verify other access requirements - without having to look up the grant table in the hash. - */ - table->grant.grant_table= grant_table; - table->grant.version= grant_version; - table->grant.privilege|= grant_table->privs; - /* - Save all privileges which might be subject to column privileges - but not which aren't yet granted by table level ACLs. - This is can later be used for column privilege checks. - */ - table->grant.want_privilege= ((required_access & COL_ACLS) - & ~table->grant.privilege); - - /* - If the requested privileges share any intersection with the current - table privileges we have found at least one common privilege on the - table level. - */ - if (grant_table->privs & required_access) - continue; /* Check next table */ - - /* - There are no table level privileges which satisfies any of the - requested privileges. There might still be column privileges which - does though. - */ - return FALSE; - } - - /* - All tables in TABLE_LIST satisfy the requirement of having any - privilege on the table level. - */ - - return TRUE; -} - - /* Check column rights in given security context @@ -4181,7 +4178,7 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant, if (!want_access) DBUG_RETURN(0); // Already checked - rw_rdlock(&LOCK_grant); + mysql_rwlock_rdlock(&LOCK_grant); /* reload table if someone has modified any grants */ @@ -4199,12 +4196,12 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant, grant_column=column_hash_search(grant_table, name, length); if (grant_column && !(~grant_column->rights & want_access)) { - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); DBUG_RETURN(0); } err: - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); char command[128]; get_privilege_desc(command, sizeof(command), want_access); my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), @@ -4319,7 +4316,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, */ bool using_column_privileges= FALSE; - rw_rdlock(&LOCK_grant); + mysql_rwlock_rdlock(&LOCK_grant); for (; !fields->end_of_fields(); fields->next()) { @@ -4360,11 +4357,11 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, goto err; } } - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); return 0; err: - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); char command[128]; get_privilege_desc(command, sizeof(command), want_access); @@ -4422,7 +4419,7 @@ bool check_grant_db(THD *thd,const char *db) len= (uint) (strmov(strmov(helping, sctx->priv_user) + 1, db) - helping) + 1; - rw_rdlock(&LOCK_grant); + mysql_rwlock_rdlock(&LOCK_grant); for (uint idx=0 ; idx < column_priv_hash.records ; idx++) { @@ -4442,7 +4439,7 @@ bool check_grant_db(THD *thd,const char *db) error= check_grant_db_routine(thd, db, &proc_priv_hash) && check_grant_db_routine(thd, db, &func_priv_hash); - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); return error; } @@ -4478,7 +4475,7 @@ bool check_grant_routine(THD *thd, ulong want_access, if (!want_access) DBUG_RETURN(0); // ok - rw_rdlock(&LOCK_grant); + mysql_rwlock_rdlock(&LOCK_grant); for (table= procs; table; table= table->next_global) { GRANT_NAME *grant_proc; @@ -4492,10 +4489,10 @@ bool check_grant_routine(THD *thd, ulong want_access, goto err; } } - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); DBUG_RETURN(0); err: - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); if (!no_errors) { char buff[1024]; @@ -4536,13 +4533,13 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, bool no_routine_acl= 1; GRANT_NAME *grant_proc; Security_context *sctx= thd->security_ctx; - rw_rdlock(&LOCK_grant); + mysql_rwlock_rdlock(&LOCK_grant); if ((grant_proc= routine_hash_search(sctx->priv_host, sctx->ip, db, sctx->priv_user, name, is_proc, 0))) no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); return no_routine_acl; } @@ -4558,7 +4555,7 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table) const char *db = table->db ? table->db : thd->db; GRANT_TABLE *grant_table; - rw_rdlock(&LOCK_grant); + mysql_rwlock_rdlock(&LOCK_grant); #ifdef EMBEDDED_LIBRARY grant_table= NULL; #else @@ -4570,7 +4567,7 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table) if (grant_table) table->grant.privilege|= grant_table->privs; privilege= table->grant.privilege; - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); return privilege; } @@ -4601,7 +4598,7 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant, GRANT_COLUMN *grant_column; ulong priv; - rw_rdlock(&LOCK_grant); + mysql_rwlock_rdlock(&LOCK_grant); /* reload table if someone has modified any grants */ if (grant->version != grant_version) { @@ -4624,7 +4621,7 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant, else priv= (grant->privilege | grant_table->privs | grant_column->rights); } - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); return priv; } @@ -4691,14 +4688,14 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) DBUG_RETURN(TRUE); } - rw_rdlock(&LOCK_grant); - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_rwlock_rdlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE); if (!acl_user) { - VOID(pthread_mutex_unlock(&acl_cache->lock)); - rw_unlock(&LOCK_grant); + mysql_mutex_unlock(&acl_cache->lock); + mysql_rwlock_unlock(&LOCK_grant); my_error(ER_NONEXISTING_GRANT, MYF(0), lex_user->user.str, lex_user->host.str); @@ -4715,8 +4712,8 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) { - VOID(pthread_mutex_unlock(&acl_cache->lock)); - rw_unlock(&LOCK_grant); + mysql_mutex_unlock(&acl_cache->lock); + mysql_rwlock_unlock(&LOCK_grant); DBUG_RETURN(TRUE); } @@ -5026,8 +5023,8 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) } end: - VOID(pthread_mutex_unlock(&acl_cache->lock)); - rw_unlock(&LOCK_grant); + mysql_mutex_unlock(&acl_cache->lock); + mysql_rwlock_unlock(&LOCK_grant); my_eof(thd); DBUG_RETURN(error); @@ -5151,14 +5148,14 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc) { ACL_USER *acl_user; - pthread_mutex_lock(&acl_cache->lock); + mysql_mutex_lock(&acl_cache->lock); if (initialized && (acl_user= find_acl_user(host,user, FALSE))) uc->user_resources= acl_user->user_resource; else bzero((char*) &uc->user_resources, sizeof(uc->user_resources)); - pthread_mutex_unlock(&acl_cache->lock); + mysql_mutex_unlock(&acl_cache->lock); } /* @@ -5208,6 +5205,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) (tables+4)->lock_type= TL_WRITE; tables->db= (tables+1)->db= (tables+2)->db= (tables+3)->db= (tables+4)->db= (char*) "mysql"; + init_mdl_requests(tables); #ifdef HAVE_REPLICATION /* @@ -5229,7 +5227,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) } #endif - if (simple_open_n_lock_tables(thd, tables)) + if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { // This should never happen close_thread_tables(thd); DBUG_RETURN(-1); @@ -5244,7 +5242,7 @@ ACL_USER *check_acl_user(LEX_USER *user_name, ACL_USER *acl_user= 0; uint counter; - safe_mutex_assert_owner(&acl_cache->lock); + mysql_mutex_assert_owner(&acl_cache->lock); for (counter= 0 ; counter < acl_users.elements ; counter++) { @@ -5513,7 +5511,7 @@ static int handle_grant_struct(uint struct_no, bool drop, LINT_INIT(user); LINT_INIT(host); - safe_mutex_assert_owner(&acl_cache->lock); + mysql_mutex_assert_owner(&acl_cache->lock); /* Get the number of elements in the in-memory structure. */ switch (struct_no) { @@ -5818,19 +5816,21 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list) row-based replication. The flag will be reset at the end of the statement. */ - save_binlog_row_based= thd->current_stmt_binlog_row_based; - thd->clear_current_stmt_binlog_row_based(); + if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) + thd->clear_current_stmt_binlog_format_row(); /* CREATE USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) { /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(result != 1); } - rw_wrlock(&LOCK_grant); - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); while ((tmp_user_name= user_list++)) { @@ -5860,7 +5860,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list) } } - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); if (result) my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe()); @@ -5868,10 +5868,12 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list) if (some_users_created) result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); close_thread_tables(thd); /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(result); } @@ -5906,21 +5908,23 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list) row-based replication. The flag will be reset at the end of the statement. */ - save_binlog_row_based= thd->current_stmt_binlog_row_based; - thd->clear_current_stmt_binlog_row_based(); + if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) + thd->clear_current_stmt_binlog_format_row(); /* DROP USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) { /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(result != 1); } thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; - rw_wrlock(&LOCK_grant); - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); while ((tmp_user_name= user_list++)) { @@ -5941,7 +5945,7 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list) /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ rebuild_check_host(); - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); if (result) my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe()); @@ -5949,11 +5953,13 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list) if (some_users_deleted) result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); close_thread_tables(thd); thd->variables.sql_mode= old_sql_mode; /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(result); } @@ -5988,19 +5994,21 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) row-based replication. The flag will be reset at the end of the statement. */ - save_binlog_row_based= thd->current_stmt_binlog_row_based; - thd->clear_current_stmt_binlog_row_based(); + if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) + thd->clear_current_stmt_binlog_format_row(); /* RENAME USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) { /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(result != 1); } - rw_wrlock(&LOCK_grant); - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); while ((tmp_user_from= user_list++)) { @@ -6034,7 +6042,7 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ rebuild_check_host(); - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); if (result) my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe()); @@ -6042,10 +6050,12 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) if (some_users_renamed && mysql_bin_log.is_open()) result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); close_thread_tables(thd); /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(result); } @@ -6078,18 +6088,20 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) row-based replication. The flag will be reset at the end of the statement. */ - save_binlog_row_based= thd->current_stmt_binlog_row_based; - thd->clear_current_stmt_binlog_row_based(); + if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) + thd->clear_current_stmt_binlog_format_row(); if ((result= open_grant_tables(thd, tables))) { /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(result != 1); } - rw_wrlock(&LOCK_grant); - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); LEX_USER *lex_user, *tmp_lex_user; List_iterator <LEX_USER> user_list(list); @@ -6228,19 +6240,21 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) } while (revoked); } - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); int binlog_error= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); close_thread_tables(thd); /* error for writing binary log has already been reported */ if (result && !binlog_error) my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0)); /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(result || binlog_error); } @@ -6341,16 +6355,16 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, /* Be sure to pop this before exiting this scope! */ thd->push_internal_handler(&error_handler); - rw_wrlock(&LOCK_grant); - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); /* This statement will be replicated as a statement, even when using row-based replication. The flag will be reset at the end of the statement. */ - save_binlog_row_based= thd->current_stmt_binlog_row_based; - thd->clear_current_stmt_binlog_row_based(); + if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row())) + thd->clear_current_stmt_binlog_format_row(); /* Remove procedure access */ do @@ -6381,13 +6395,15 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, } } while (revoked); - VOID(pthread_mutex_unlock(&acl_cache->lock)); - rw_unlock(&LOCK_grant); + mysql_mutex_unlock(&acl_cache->lock); + mysql_rwlock_unlock(&LOCK_grant); close_thread_tables(thd); thd->pop_internal_handler(); /* Restore the state of binlog format */ - thd->current_stmt_binlog_row_based= save_binlog_row_based; + DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); + if (save_binlog_row_based) + thd->set_current_stmt_binlog_format_row(); DBUG_RETURN(error_handler.has_errors()); } @@ -6424,7 +6440,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, combo->user.str= sctx->user; - VOID(pthread_mutex_lock(&acl_cache->lock)); + mysql_mutex_lock(&acl_cache->lock); if ((au= find_acl_user(combo->host.str=(char*)sctx->host_or_ip,combo->user.str,FALSE))) goto found_acl; @@ -6435,11 +6451,11 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, if((au= find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE))) goto found_acl; - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); DBUG_RETURN(TRUE); found_acl: - VOID(pthread_mutex_unlock(&acl_cache->lock)); + mysql_mutex_unlock(&acl_cache->lock); bzero((char*)tables, sizeof(TABLE_LIST)); user_list.empty(); @@ -6592,13 +6608,14 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) ulong want_access; char buff[100]; TABLE *table= tables->table; - bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0); + bool no_global_access= check_access(thd, SELECT_ACL, "mysql", + NULL, NULL, 1, 1); char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_user_privileges"); if (!initialized) DBUG_RETURN(0); - pthread_mutex_lock(&acl_cache->lock); + mysql_mutex_lock(&acl_cache->lock); for (counter=0 ; counter < acl_users.elements ; counter++) { @@ -6648,7 +6665,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) } } err: - pthread_mutex_unlock(&acl_cache->lock); + mysql_mutex_unlock(&acl_cache->lock); DBUG_RETURN(error); #else @@ -6666,13 +6683,14 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond) ulong want_access; char buff[100]; TABLE *table= tables->table; - bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0); + bool no_global_access= check_access(thd, SELECT_ACL, "mysql", + NULL, NULL, 1, 1); char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_schema_privileges"); if (!initialized) DBUG_RETURN(0); - pthread_mutex_lock(&acl_cache->lock); + mysql_mutex_lock(&acl_cache->lock); for (counter=0 ; counter < acl_dbs.elements ; counter++) { @@ -6725,7 +6743,7 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond) } } err: - pthread_mutex_unlock(&acl_cache->lock); + mysql_mutex_unlock(&acl_cache->lock); DBUG_RETURN(error); #else @@ -6741,11 +6759,12 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) uint index; char buff[100]; TABLE *table= tables->table; - bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0); + bool no_global_access= check_access(thd, SELECT_ACL, "mysql", + NULL, NULL, 1, 1); char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_table_privileges"); - rw_rdlock(&LOCK_grant); + mysql_rwlock_rdlock(&LOCK_grant); for (index=0 ; index < column_priv_hash.records ; index++) { @@ -6808,7 +6827,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) } } err: - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); DBUG_RETURN(error); #else @@ -6824,11 +6843,12 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) uint index; char buff[100]; TABLE *table= tables->table; - bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0); + bool no_global_access= check_access(thd, SELECT_ACL, "mysql", + NULL, NULL, 1, 1); char *curr_host= thd->security_ctx->priv_host_name(); DBUG_ENTER("fill_schema_table_privileges"); - rw_rdlock(&LOCK_grant); + mysql_rwlock_rdlock(&LOCK_grant); for (index=0 ; index < column_priv_hash.records ; index++) { @@ -6889,7 +6909,7 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) } } err: - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); DBUG_RETURN(error); #else @@ -6941,7 +6961,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0); /* table privileges */ - rw_rdlock(&LOCK_grant); + mysql_rwlock_rdlock(&LOCK_grant); if (grant->version != grant_version) { grant->grant_table= @@ -6954,7 +6974,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, { grant->privilege|= grant->grant_table->privs; } - rw_unlock(&LOCK_grant); + mysql_rwlock_unlock(&LOCK_grant); DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege)); DBUG_VOID_RETURN; @@ -6973,3 +6993,104 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, } #endif + +struct ACL_internal_schema_registry_entry +{ + const LEX_STRING *m_name; + const ACL_internal_schema_access *m_access; +}; + +/** + Internal schema registered. + Currently, this is only: + - performance_schema + - information_schema, + This can be reused later for: + - mysql +*/ +static ACL_internal_schema_registry_entry registry_array[2]; +static uint m_registry_array_size= 0; + +/** + Add an internal schema to the registry. + @param name the schema name + @param access the schema ACL specific rules +*/ +void ACL_internal_schema_registry::register_schema + (const LEX_STRING *name, const ACL_internal_schema_access *access) +{ + DBUG_ASSERT(m_registry_array_size < array_elements(registry_array)); + + /* Not thread safe, and does not need to be. */ + registry_array[m_registry_array_size].m_name= name; + registry_array[m_registry_array_size].m_access= access; + m_registry_array_size++; +} + +/** + Search per internal schema ACL by name. + @param name a schema name + @return per schema rules, or NULL +*/ +const ACL_internal_schema_access * +ACL_internal_schema_registry::lookup(const char *name) +{ + DBUG_ASSERT(name != NULL); + + uint i; + + for (i= 0; i<m_registry_array_size; i++) + { + if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str, + name) == 0) + return registry_array[i].m_access; + } + return NULL; +} + +/** + Get a cached internal schema access. + @param grant_internal_info the cache + @param schema_name the name of the internal schema +*/ +const ACL_internal_schema_access * +get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info, + const char *schema_name) +{ + if (grant_internal_info) + { + if (! grant_internal_info->m_schema_lookup_done) + { + grant_internal_info->m_schema_access= + ACL_internal_schema_registry::lookup(schema_name); + grant_internal_info->m_schema_lookup_done= TRUE; + } + return grant_internal_info->m_schema_access; + } + return ACL_internal_schema_registry::lookup(schema_name); +} + +/** + Get a cached internal table access. + @param grant_internal_info the cache + @param schema_name the name of the internal schema + @param table_name the name of the internal table +*/ +const ACL_internal_table_access * +get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info, + const char *schema_name, + const char *table_name) +{ + DBUG_ASSERT(grant_internal_info); + if (! grant_internal_info->m_table_lookup_done) + { + const ACL_internal_schema_access *schema_access; + schema_access= get_cached_schema_access(grant_internal_info, schema_name); + if (schema_access) + grant_internal_info->m_table_access= schema_access->lookup(table_name); + grant_internal_info->m_table_lookup_done= TRUE; + } + return grant_internal_info->m_table_access; +} + + |