diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 497 |
1 files changed, 321 insertions, 176 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7b9e31e84f0..bd417231d4b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -27,6 +27,7 @@ #include "sp_cache.h" #include "events.h" #include "sql_trigger.h" +#include "sql_prepare.h" #include "probes_mysql.h" /** @@ -45,7 +46,6 @@ "FUNCTION" : "PROCEDURE") static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables); -static bool check_show_create_table_access(THD *thd, TABLE_LIST *table); const char *any_db="*any*"; // Special symbol for check_access @@ -375,7 +375,7 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var, Vio* save_vio; ulong save_client_capabilities; -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.start_new_query(); thd->profiling.set_query_source(init_command_var->value, init_command_var->value_length); @@ -403,7 +403,7 @@ void execute_init_command(THD *thd, sys_var_str *init_command_var, thd->client_capabilities= save_client_capabilities; thd->net.vio= save_vio; -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); #endif } @@ -453,7 +453,7 @@ static void handle_bootstrap_impl(THD *thd) /* purecov: begin tested */ if (net_realloc(&(thd->net), 2 * thd->net.max_packet)) { - net_end_statement(thd); + thd->protocol->end_statement(); bootstrap_error= 1; break; } @@ -479,7 +479,7 @@ static void handle_bootstrap_impl(THD *thd) QUERY_CACHE_FLAGS_SIZE); thd->set_query(query, length); DBUG_PRINT("query",("%-.4096s",thd->query)); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.start_new_query(); thd->profiling.set_query_source(thd->query, length); #endif @@ -494,9 +494,9 @@ static void handle_bootstrap_impl(THD *thd) close_thread_tables(thd); // Free tables bootstrap_error= thd->is_error(); - net_end_statement(thd); + thd->protocol->end_statement(); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); #endif @@ -589,7 +589,7 @@ static bool check_merge_table_access(THD *thd, char *db, tlist->db= db; /* purecov: inspected */ } error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, - table_list, UINT_MAX, FALSE); + table_list, FALSE, UINT_MAX, FALSE); } return error; } @@ -806,7 +806,7 @@ bool do_command(THD *thd) net_new_transaction(net); packet_length= my_net_read(net); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.start_new_query(); #endif if (packet_length == packet_error) @@ -819,7 +819,7 @@ bool do_command(THD *thd) /* The error must be set. */ DBUG_ASSERT(thd->is_error()); - net_end_statement(thd); + thd->protocol->end_statement(); if (net->error != 3) { @@ -866,7 +866,7 @@ bool do_command(THD *thd) return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1)); out: -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); #endif DBUG_RETURN(return_value); @@ -1223,7 +1223,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, general_log_write(thd, command, thd->query, thd->query_length); DBUG_PRINT("query",("%-.4096s",thd->query)); -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.set_query_source(thd->query, thd->query_length); #endif @@ -1236,7 +1236,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { char *beginning_of_next_stmt= (char*) end_of_stmt; - net_end_statement(thd); + thd->protocol->end_statement(); query_cache_end_of_result(thd); /* Multiple queries exits, execute them individually @@ -1258,7 +1258,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, MYSQL_QUERY_DONE(thd->is_error()); } -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.finish_current_query(); thd->profiling.start_new_query("continuing"); thd->profiling.set_query_source(beginning_of_next_stmt, length); @@ -1334,7 +1334,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (check_access(thd,SELECT_ACL,table_list.db,&table_list.grant.privilege, 0, 0, test(table_list.schema_table))) break; - if (check_grant(thd, SELECT_ACL, &table_list, 2, UINT_MAX, 0)) + if (check_grant(thd, SELECT_ACL, &table_list, TRUE, UINT_MAX, FALSE)) break; /* init structures for VIEW processing */ table_list.select_lex= &(thd->lex->select_lex); @@ -1630,7 +1630,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->transaction.stmt.reset(); - net_end_statement(thd); + thd->protocol->end_statement(); query_cache_end_of_result(thd); thd->proc_info= "closing tables"; @@ -1799,7 +1799,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, Mark this current profiling record to be discarded. We don't wish to have SHOW commands show up in profiling. */ -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.discard_current_query(); #endif break; @@ -2214,14 +2214,16 @@ mysql_execute_command(THD *thd) #endif case SQLCOM_SHOW_STATUS_PROC: case SQLCOM_SHOW_STATUS_FUNC: - if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))) + if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE, + UINT_MAX, FALSE))) res= execute_sqlcom_select(thd, all_tables); break; case SQLCOM_SHOW_STATUS: { system_status_var old_status_var= thd->status_var; thd->initial_status_var= &old_status_var; - if (!(res= check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE))) + if (!(res= check_table_access(thd, SELECT_ACL, all_tables, FALSE, + UINT_MAX, FALSE))) res= execute_sqlcom_select(thd, all_tables); /* Don't log SHOW STATUS commands to slow query log */ thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | @@ -2251,18 +2253,22 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_STORAGE_ENGINES: case SQLCOM_SHOW_PROFILE: case SQLCOM_SELECT: + { thd->status_var.last_query_cost= 0.0; + + /* + lex->exchange != NULL implies SELECT .. INTO OUTFILE and this + requires FILE_ACL access. + */ + ulong privileges_requested= lex->exchange ? SELECT_ACL | FILE_ACL : + SELECT_ACL; + if (all_tables) - { res= check_table_access(thd, - lex->exchange ? SELECT_ACL | FILE_ACL : - SELECT_ACL, - all_tables, UINT_MAX, FALSE); - } + privileges_requested, + all_tables, FALSE, UINT_MAX, FALSE); else - res= check_access(thd, - lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, - any_db, 0, 0, 0, 0); + res= check_access(thd, privileges_requested, any_db, 0, 0, 0, UINT_MAX); if (res) break; @@ -2273,7 +2279,8 @@ mysql_execute_command(THD *thd) res= execute_sqlcom_select(thd, all_tables); break; - case SQLCOM_PREPARE: + } +case SQLCOM_PREPARE: { mysql_sql_stmt_prepare(thd); break; @@ -2289,8 +2296,8 @@ mysql_execute_command(THD *thd) break; } case SQLCOM_DO: - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || - open_and_lock_tables(thd, all_tables)) + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || open_and_lock_tables(thd, all_tables)) goto error; res= mysql_do(thd, *lex->insert_list); @@ -2354,7 +2361,7 @@ mysql_execute_command(THD *thd) } case SQLCOM_SHOW_PROFILES: { -#if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) +#if defined(ENABLED_PROFILING) thd->profiling.discard_current_query(); res= thd->profiling.show_profiles(); if (res) @@ -2399,8 +2406,8 @@ mysql_execute_command(THD *thd) case SQLCOM_BACKUP_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || - check_global_access(thd, FILE_ACL)) + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_backup_table(thd, first_table); @@ -2411,8 +2418,8 @@ mysql_execute_command(THD *thd) case SQLCOM_RESTORE_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, INSERT_ACL, all_tables, UINT_MAX, FALSE) || - check_global_access(thd, FILE_ACL)) + if (check_table_access(thd, INSERT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || check_global_access(thd, FILE_ACL)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_restore_table(thd, first_table); @@ -2511,7 +2518,7 @@ mysql_execute_command(THD *thd) test(first_table->schema_table))) goto error; /* purecov: inspected */ /* Check that the first table has CREATE privilege */ - if (check_grant(thd, CREATE_ACL, all_tables, 0, 1, 0)) + if (check_grant(thd, CREATE_ACL, all_tables, FALSE, 1, FALSE)) goto error; pthread_mutex_lock(&LOCK_active_mi); @@ -2881,7 +2888,7 @@ end_with_restore_list: (TABLE_LIST *) create_info.merge_list.first)) goto error; /* purecov: inspected */ - if (check_grant(thd, priv_needed, all_tables, 0, UINT_MAX, 0)) + if (check_grant(thd, priv_needed, all_tables, FALSE, UINT_MAX, FALSE)) goto error; if (lex->name.str && !test_all_bits(priv,INSERT_ACL | CREATE_ACL)) { // Rename of table @@ -2890,8 +2897,8 @@ end_with_restore_list: tmp_table.table_name= lex->name.str; tmp_table.db=select_lex->db; tmp_table.grant.privilege=priv; - if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, 0, - UINT_MAX, 0)) + if (check_grant(thd, INSERT_ACL | CREATE_ACL, &tmp_table, FALSE, + UINT_MAX, FALSE)) goto error; } @@ -2945,10 +2952,11 @@ end_with_restore_list: */ old_list= table[0]; new_list= table->next_local[0]; - if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, 0, 1, 0) || + if (check_grant(thd, ALTER_ACL | DROP_ACL, &old_list, FALSE, 1, FALSE) || (!test_all_bits(table->next_local->grant.privilege, INSERT_ACL | CREATE_ACL) && - check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, 0, 1, 0))) + check_grant(thd, INSERT_ACL | CREATE_ACL, &new_list, FALSE, 1, + FALSE))) goto error; } @@ -2979,11 +2987,75 @@ end_with_restore_list: goto error; #else { - /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ + /* + Access check: + SHOW CREATE TABLE require any privileges on the table level (ie + effecting all columns in the table). + SHOW CREATE VIEW require the SHOW_VIEW and SELECT ACLs on the table + level. + NOTE: SHOW_VIEW ACL is checked when the view is created. + */ + if (lex->only_view) + { + if (check_table_access(thd, SELECT_ACL, first_table, FALSE, 1, FALSE)) + { + my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), + "SHOW", thd->security_ctx->priv_user, + thd->security_ctx->host_or_ip, first_table->alias); + goto error; + } + + /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ first_table->skip_temporary= 1; - if (check_show_create_table_access(thd, first_table)) - goto error; + } + else + { + ulong save_priv; + + /* + If it is an INFORMATION_SCHEMA table, SELECT_ACL privilege is the + only privilege allowed. For any other privilege check_access() + reports an error. That's how internal implementation protects + INFORMATION_SCHEMA from updates. + + For ordinary tables any privilege from the SHOW_CREATE_TABLE_ACLS + set is sufficient. + */ + + ulong check_privs= test(first_table->schema_table) ? + SELECT_ACL : SHOW_CREATE_TABLE_ACLS; + + if (check_access(thd, check_privs, first_table->db, + &save_priv, FALSE, FALSE, + test(first_table->schema_table))) + goto error; + + /* + save_priv contains any privileges actually granted by check_access + (i.e. save_priv contains global (user- and database-level) + privileges). + + The fact that check_access() returned FALSE does not mean that + access is granted. We need to check if save_priv contains any + table-specific privilege. If not, we need to check table-level + privileges. + + If there are no global privileges and no table-level privileges, + access is denied. + */ + + if (!(save_priv & (SHOW_CREATE_TABLE_ACLS)) && + !has_any_table_level_privileges(thd, SHOW_CREATE_TABLE_ACLS, first_table)) + { + my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), + "SHOW", thd->security_ctx->priv_user, + thd->security_ctx->host_or_ip, first_table->alias); + goto error; + } + } + + /* Access is granted. Execute the command. */ res= mysqld_show_create(thd, first_table); break; } @@ -2991,8 +3063,8 @@ end_with_restore_list: case SQLCOM_CHECKSUM: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL | EXTRA_ACL, all_tables, - UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, all_tables, + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ res = mysql_checksum_table(thd, first_table, &lex->check_opt); break; @@ -3001,7 +3073,7 @@ end_with_restore_list: { DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_repair_table(thd, first_table, &lex->check_opt); @@ -3020,8 +3092,8 @@ end_with_restore_list: case SQLCOM_CHECK: { DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL | EXTRA_ACL , all_tables, - UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, all_tables, + TRUE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res = mysql_check_table(thd, first_table, &lex->check_opt); @@ -3033,7 +3105,7 @@ end_with_restore_list: { DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= mysql_analyze_table(thd, first_table, &lex->check_opt); @@ -3054,7 +3126,7 @@ end_with_restore_list: { DBUG_ASSERT(first_table == all_tables && first_table != 0); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ? @@ -3427,7 +3499,7 @@ end_with_restore_list: DBUG_ASSERT(first_table == all_tables && first_table != 0); if (!lex->drop_temporary) { - if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE)) + if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ if (end_active_trans(thd)) goto error; @@ -3535,8 +3607,8 @@ end_with_restore_list: if (lex->autocommit && end_active_trans(thd)) goto error; - if ((check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || - open_and_lock_tables(thd, all_tables))) + if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || open_and_lock_tables(thd, all_tables))) goto error; if (lex->one_shot_set && not_all_support_one_shot(lex_var_list)) { @@ -3590,7 +3662,7 @@ end_with_restore_list: if (end_active_trans(thd)) goto error; if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, - UINT_MAX, FALSE)) + FALSE, UINT_MAX, FALSE)) goto error; if (lex->protect_against_global_read_lock && !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1))) @@ -3986,7 +4058,7 @@ end_with_restore_list: else { if (check_grant(thd,(lex->grant | lex->grant_tot_col | GRANT_ACL), - all_tables, 0, UINT_MAX, 0)) + all_tables, FALSE, UINT_MAX, FALSE)) goto error; /* Conditionally writes to binlog */ res= mysql_table_grant(thd, all_tables, lex->users_list, @@ -4095,7 +4167,7 @@ end_with_restore_list: #endif case SQLCOM_HA_OPEN: DBUG_ASSERT(first_table == all_tables && first_table != 0); - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)) goto error; res= mysql_ha_open(thd, first_table, 0); break; @@ -4381,7 +4453,8 @@ create_sp_error: This will cache all SP and SF and open and lock all tables required for execution. */ - if (check_table_access(thd, SELECT_ACL, all_tables, UINT_MAX, FALSE) || + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, + UINT_MAX, FALSE) || open_and_lock_tables(thd, all_tables)) goto error; @@ -4709,8 +4782,8 @@ create_sp_error: } case SQLCOM_DROP_VIEW: { - if (check_table_access(thd, DROP_ACL, all_tables, UINT_MAX, FALSE) || - end_active_trans(thd)) + if (check_table_access(thd, DROP_ACL, all_tables, FALSE, UINT_MAX, FALSE) + || end_active_trans(thd)) goto error; /* Conditionally writes to binlog. */ res= mysql_drop_view(thd, first_table, thd->lex->drop_mode); @@ -4920,7 +4993,7 @@ create_sp_error: res= mysql_xa_recover(thd); break; case SQLCOM_ALTER_TABLESPACE: - if (check_access(thd, ALTER_ACL, thd->db, 0, 1, 0, thd->db ? is_schema_db(thd->db) : 0)) + if (check_global_access(thd, CREATE_TABLESPACE_ACL)) break; if (!(res= mysql_alter_tablespace(thd, lex->alter_tablespace_info))) my_ok(thd); @@ -5159,7 +5232,7 @@ bool check_single_table_access(THD *thd, ulong privilege, (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) && !(all_tables->view && all_tables->effective_algorithm == VIEW_ALGORITHM_TMPTABLE) && - check_grant(thd, privilege, all_tables, 0, 1, no_errors)) + check_grant(thd, privilege, all_tables, FALSE, 1, no_errors)) goto deny; thd->security_ctx= backup_ctx; @@ -5204,7 +5277,8 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) subselects_tables= subselects_tables->next_global; } if (subselects_tables && - (check_table_access(thd, SELECT_ACL, subselects_tables, UINT_MAX, FALSE))) + (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE, + UINT_MAX, FALSE))) return 1; } return 0; @@ -5212,46 +5286,54 @@ bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) /** - Get the user (global) and database privileges for all used tables. - - @param save_priv In this we store global and db level grants for the - table. Note that we don't store db level grants if the - global grants is enough to satisfy the request and the - global grants contains a SELECT grant. - - @note - The idea of EXTRA_ACL is that one will be granted access to the table if - one has the asked privilege on any column combination of the table; For - example to be able to check a table one needs to have SELECT privilege on - any column of the table. - - @retval - 0 ok - @retval - 1 If we can't get the privileges and we don't use table/column - grants. + @brief Compare requested privileges with the privileges acquired from the + User- and Db-tables. + @param thd Thread handler + @param want_access The requested access privileges. + @param db A pointer to the Db name. + @param[out] save_priv A pointer to the granted privileges will be stored. + @param dont_check_global_grants True if no global grants are checked. + @param no_error True if no errors should be sent to the client. + @param schema_db True if the db specified belongs to the meta data tables. + + 'save_priv' is used to save the User-table (global) and Db-table grants for + the supplied db name. Note that we don't store db level grants if the global + grants is enough to satisfy the request AND the global grants contains a + SELECT grant. + + A meta data table (from INFORMATION_SCHEMA) can always be accessed with + a SELECT_ACL. + + @see check_grant + + @return Status of denial of access by exclusive ACLs. + @retval FALSE Access can't exclusively be denied by Db- and User-table + access unless Column- and Table-grants are checked too. + @retval TRUE Access denied. */ + bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, bool dont_check_global_grants, bool no_errors, bool schema_db) { Security_context *sctx= thd->security_ctx; ulong db_access; + /* GRANT command: In case of database level grant the database name may be a pattern, in case of table|column level grant the database name can not be a pattern. We use 'dont_check_global_grants' as a flag to determine - if it's database level grant command + if it's database level grant command (see SQLCOM_GRANT case, mysql_execute_command() function) and set db_is_pattern according to 'dont_check_global_grants' value. */ - bool db_is_pattern= (test(want_access & GRANT_ACL) && - dont_check_global_grants); + bool db_is_pattern= ((want_access & GRANT_ACL) && dont_check_global_grants); ulong dummy; DBUG_ENTER("check_access"); DBUG_PRINT("enter",("db: %s want_access: %lu master_access: %lu", db ? db : "", want_access, sctx->master_access)); + if (save_priv) *save_priv=0; else @@ -5269,8 +5351,12 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if (schema_db) { - if ((!(sctx->master_access & FILE_ACL) && (want_access & FILE_ACL)) || - (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL))) + /* + We don't allow any simple privileges but SELECT_ACL or CREATE_VIEW_ACL + on the information_schema database. + */ + want_access &= ~SELECT_ACL; + if (want_access & DB_ACLS) { if (!no_errors) { @@ -5278,10 +5364,15 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, db_name); } + /* + Access denied; + [out] *save_privileges= 0 + */ DBUG_RETURN(TRUE); } else { + /* Access granted */ *save_priv= SELECT_ACL; DBUG_RETURN(FALSE); } @@ -5289,20 +5380,27 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if ((sctx->master_access & want_access) == want_access) { + /* get access for current db */ + db_access= sctx->db_access; /* - If we don't have a global SELECT privilege, we have to get the database - specific access rights to be able to handle queries of type + 1. If we don't have a global SELECT privilege, we have to get the + database specific access rights to be able to handle queries of type UPDATE t1 SET a=1 WHERE b > 0 + 2. Change db access if it isn't current db which is being addressed */ - db_access= sctx->db_access; if (!(sctx->master_access & SELECT_ACL) && (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))) db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); + + /* + The effective privileges are the union of the global privileges + and the the intersection of db- and host-privileges. + */ *save_priv=sctx->master_access | db_access; DBUG_RETURN(FALSE); } - if (((want_access & ~sctx->master_access) & ~(DB_ACLS | EXTRA_ACL)) || + if (((want_access & ~sctx->master_access) & ~DB_ACLS) || (! db && dont_check_global_grants)) { // We can never grant this DBUG_PRINT("error",("No possible access")); @@ -5317,33 +5415,66 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, } if (db == any_db) - DBUG_RETURN(FALSE); // Allow select on anything + { + /* + Access granted; Allow select on *any* db. + [out] *save_privileges= 0 + */ + DBUG_RETURN(FALSE); + } if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db))) db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); else db_access= sctx->db_access; - DBUG_PRINT("info",("db_access: %lu", db_access)); - /* Remove SHOW attribute and access rights we already have */ - want_access &= ~(sctx->master_access | EXTRA_ACL); DBUG_PRINT("info",("db_access: %lu want_access: %lu", db_access, want_access)); - db_access= ((*save_priv=(db_access | sctx->master_access)) & want_access); - if (db_access == want_access || + /* + Save the union of User-table and the intersection between Db-table and + Host-table privileges. + */ + db_access= (db_access | sctx->master_access); + *save_priv= db_access; + + /* + We need to investigate column- and table access if all requested privileges + belongs to the bit set of . + */ + bool need_table_or_column_check= + (want_access & (TABLE_ACLS | PROC_ACLS | db_access)) == want_access; + + /* + Grant access if the requested access is in the intersection of + host- and db-privileges (as retrieved from the acl cache), + also grant access if all the requested privileges are in the union of + TABLES_ACLS and PROC_ACLS; see check_grant. + */ + if ( (db_access & want_access) == want_access || (!dont_check_global_grants && - !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS)))) - DBUG_RETURN(FALSE); /* Ok */ + need_table_or_column_check)) + { + /* + Ok; but need to check table- and column privileges. + [out] *save_privileges is (User-priv | (Db-priv & Host-priv)) + */ + DBUG_RETURN(FALSE); + } + /* + Access is denied; + [out] *save_privileges is (User-priv | (Db-priv & Host-priv)) + */ DBUG_PRINT("error",("Access denied")); if (!no_errors) my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, (db ? db : (thd->db ? thd->db : - "unknown"))); /* purecov: tested */ - DBUG_RETURN(TRUE); /* purecov: tested */ + "unknown"))); + DBUG_RETURN(TRUE); + } @@ -5389,14 +5520,20 @@ static bool check_show_access(THD *thd, TABLE_LIST *table) DBUG_ASSERT(dst_table); - if (check_access(thd, SELECT_ACL | EXTRA_ACL, - dst_table->db, - &dst_table->grant.privilege, - FALSE, FALSE, + if (check_access(thd, SELECT_ACL, dst_table->db, + &dst_table->grant.privilege, FALSE, FALSE, test(dst_table->schema_table))) - return FALSE; + return TRUE; /* Access denied */ - return (check_grant(thd, SELECT_ACL, dst_table, 2, UINT_MAX, FALSE)); + /* + Check_grant will grant access if there is any column privileges on + all of the tables thanks to the fourth parameter (bool show_table). + */ + if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE)) + return TRUE; /* Access denied */ + + /* Access granted */ + return FALSE; } default: break; @@ -5406,30 +5543,46 @@ static bool check_show_access(THD *thd, TABLE_LIST *table) } -/** - Check the privilege for all used tables. - @param thd Thread context - @param want_access Privileges requested - @param tables List of tables to be checked - @param number Check at most this number of tables. - @param no_errors FALSE/TRUE - report/don't report error to - the client (using my_error() call). +/** + @brief Check if the requested privileges exists in either User-, Host- or + Db-tables. + @param thd Thread context + @param want_access Privileges requested + @param tables List of tables to be compared against + @param no_errors Don't report error to the client (using my_error() call). + @param any_combination_of_privileges_will_do TRUE if any privileges on any + column combination is enough. + @param number Only the first 'number' tables in the linked list are + relevant. + + The suppled table list contains cached privileges. This functions calls the + help functions check_access and check_grant to verify the first three steps + in the privileges check queue: + 1. Global privileges + 2. OR (db privileges AND host privileges) + 3. OR table privileges + 4. OR column privileges (not checked by this function!) + 5. OR routine privileges (not checked by this function!) + + @see check_access + @see check_grant + + @note This functions assumes that table list used and + thd->lex->query_tables_own_last value correspond to each other + (the latter should be either 0 or point to next_global member + of one of elements of this table list). - @note - Table privileges are cached in the table list for GRANT checking. - This functions assumes that table list used and - thd->lex->query_tables_own_last value correspond to each other - (the latter should be either 0 or point to next_global member - of one of elements of this table list). - - @retval FALSE OK - @retval TRUE Access denied + @return + @retval FALSE OK + @retval TRUE Access denied; But column or routine privileges might need to + be checked also. */ bool -check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, - uint number, bool no_errors) +check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, + bool any_combination_of_privileges_will_do, + uint number, bool no_errors) { TABLE_LIST *org_tables= tables; TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); @@ -5440,22 +5593,31 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, the given table list refers to the list for prelocking (contains tables of other queries). For simple queries first_not_own_table is 0. */ - for (; i < number && tables != first_not_own_table; + for (; i < number && tables != first_not_own_table && tables; tables= tables->next_global, i++) { + ulong want_access= requirements; if (tables->security_ctx) sctx= tables->security_ctx; else sctx= backup_ctx; - if (tables->schema_table && - (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL))) + /* + Always allow SELECT on schema tables. This is done by removing the + required SELECT_ACL privilege in the want_access parameter. + Disallow any other DDL or DML operation on any schema table. + */ + if (tables->schema_table) { - if (!no_errors) - my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - sctx->priv_user, sctx->priv_host, - INFORMATION_SCHEMA_NAME.str); - return TRUE; + want_access &= ~SELECT_ACL; + if (want_access & DB_ACLS) + { + if (!no_errors) + my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), + sctx->priv_user, sctx->priv_host, + INFORMATION_SCHEMA_NAME.str); + goto deny; + } } /* Register access for view underlying table. @@ -5467,33 +5629,34 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, { if (check_show_access(thd, tables)) goto deny; - continue; } + DBUG_PRINT("info", ("derived: %d view: %d", tables->derived != 0, + tables->view != 0)); if (tables->is_anonymous_derived_table() || - (tables->table && (int)tables->table->s->tmp_table)) + (tables->table && tables->table->s && + (int)tables->table->s->tmp_table)) continue; thd->security_ctx= sctx; - if ((sctx->master_access & want_access) == - (want_access & ~EXTRA_ACL) && - thd->db) + if ((sctx->master_access & want_access) == want_access && + thd->db) tables->grant.privilege= want_access; else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0) { if (check_access(thd, want_access, tables->get_db_name(), - &tables->grant.privilege, 0, no_errors, + &tables->grant.privilege, 0, no_errors, test(tables->schema_table))) goto deny; // Access denied } else if (check_access(thd, want_access, tables->get_db_name(), - &tables->grant.privilege, 0, no_errors, - test(tables->schema_table))) + &tables->grant.privilege, 0, no_errors, 0)) goto deny; } thd->security_ctx= backup_ctx; - return check_grant(thd,want_access & ~EXTRA_ACL,org_tables, - test(want_access & EXTRA_ACL), number, no_errors); + return check_grant(thd,requirements,org_tables, + any_combination_of_privileges_will_do, + number, no_errors); deny: thd->security_ctx= backup_ctx; return TRUE; @@ -5581,7 +5744,7 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) if (!check_access(thd, access, table->db, &table->grant.privilege, 0, 1, test(table->schema_table)) && - !check_grant(thd, access, table, 0, 1, 1)) + !check_grant(thd, access, table, FALSE, 1, TRUE)) DBUG_RETURN(0); } } @@ -6047,7 +6210,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); - query_cache_abort(&thd->net); + query_cache_abort(&thd->query_cache_tls); } if (thd->lex->sphead) { @@ -6131,8 +6294,8 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, if (type_modifier & PRI_KEY_FLAG) { Key *key; - lex->col_list.push_back(new Key_part_spec(field_name->str, 0)); - key= new Key(Key::PRIMARY, NullS, + lex->col_list.push_back(new Key_part_spec(*field_name, 0)); + key= new Key(Key::PRIMARY, null_lex_str, &default_key_create_info, 0, lex->col_list); lex->alter_info.key_list.push_back(key); @@ -6141,8 +6304,8 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG)) { Key *key; - lex->col_list.push_back(new Key_part_spec(field_name->str, 0)); - key= new Key(Key::UNIQUE, NullS, + lex->col_list.push_back(new Key_part_spec(*field_name, 0)); + key= new Key(Key::UNIQUE, null_lex_str, &default_key_create_info, 0, lex->col_list); lex->alter_info.key_list.push_back(key); @@ -7275,11 +7438,11 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) else if ((check_access(thd, UPDATE_ACL, table->db, &table->grant.privilege, 0, 1, test(table->schema_table)) || - check_grant(thd, UPDATE_ACL, table, 0, 1, 1)) && + check_grant(thd, UPDATE_ACL, table, FALSE, 1, TRUE)) && (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0, test(table->schema_table)) || - check_grant(thd, SELECT_ACL, table, 0, 1, 0))) + check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE))) DBUG_RETURN(TRUE); table->table_in_first_from_clause= 1; @@ -7297,7 +7460,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables) if (check_access(thd, SELECT_ACL, table->db, &table->grant.privilege, 0, 0, test(table->schema_table)) || - check_grant(thd, SELECT_ACL, table, 0, 1, 0)) + check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)) DBUG_RETURN(TRUE); } } @@ -7337,7 +7500,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) /* sql_yacc guarantees that tables and aux_tables are not zero */ DBUG_ASSERT(aux_tables != 0); - if (check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) + if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) DBUG_RETURN(TRUE); /* @@ -7346,7 +7509,7 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables) call check_table_access() safely. */ thd->lex->query_tables_own_last= 0; - if (check_table_access(thd, DELETE_ACL, aux_tables, UINT_MAX, FALSE)) + if (check_table_access(thd, DELETE_ACL, aux_tables, FALSE, UINT_MAX, FALSE)) { thd->lex->query_tables_own_last= save_query_tables_own_last; DBUG_RETURN(TRUE); @@ -7500,25 +7663,6 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables) /** - @brief Check privileges for SHOW CREATE TABLE statement. - - @param thd Thread context - @param table Target table - - @retval TRUE Failure - @retval FALSE Success -*/ - -static bool check_show_create_table_access(THD *thd, TABLE_LIST *table) -{ - return check_access(thd, SELECT_ACL | EXTRA_ACL, table->db, - &table->grant.privilege, 0, 0, - test(table->schema_table)) || - check_grant(thd, SELECT_ACL, table, 2, UINT_MAX, 0); -} - - -/** CREATE TABLE query pre-check. @param thd Thread handler @@ -7557,7 +7701,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, lex->create_info.merge_list.first)) goto err; if (want_priv != CREATE_TMP_ACL && - check_grant(thd, want_priv, create_table, 0, 1, 0)) + check_grant(thd, want_priv, create_table, FALSE, 1, FALSE)) goto err; if (select_lex->item_list.elements) @@ -7585,12 +7729,13 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, } } #endif - if (tables && check_table_access(thd, SELECT_ACL, tables, UINT_MAX, FALSE)) + if (tables && check_table_access(thd, SELECT_ACL, tables, FALSE, + UINT_MAX, FALSE)) goto err; } else if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE) { - if (check_show_create_table_access(thd, tables)) + if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) goto err; } error= FALSE; |