mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Bug#27145 EXTRA_ACL troubles
The flag EXTRA_ACL is used in conjugation with our access checks, yet it is not clear what impact this flag has. This is a code clean up which replaces use of EXTRA_ACL with an explicit function parameter. The patch also fixes privilege checks for: - SHOW CREATE TABLE: The new privilege requirement is any privilege on the table-level. - CHECKSUM TABLE: Requires SELECT on the table level. - SHOW CREATE VIEW: Requires SHOW_VIEW and SELECT on the table level (just as the manual claims) - SHOW INDEX: Requires any privilege on any column combination.
This commit is contained in:
427
sql/sql_parse.cc
427
sql/sql_parse.cc
@ -45,7 +45,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
|
||||
|
||||
@ -589,7 +588,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;
|
||||
}
|
||||
@ -1334,7 +1333,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);
|
||||
@ -2193,14 +2192,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 |
|
||||
@ -2230,18 +2231,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;
|
||||
@ -2252,7 +2257,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;
|
||||
@ -2268,8 +2274,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);
|
||||
@ -2378,8 +2384,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);
|
||||
@ -2390,8 +2396,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);
|
||||
@ -2490,7 +2496,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);
|
||||
@ -2860,7 +2866,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
|
||||
@ -2869,8 +2875,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;
|
||||
}
|
||||
|
||||
@ -2924,10 +2930,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;
|
||||
}
|
||||
|
||||
@ -2958,11 +2965,54 @@ 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 (check_access(thd, SELECT_ACL, first_table->db,
|
||||
&save_priv, FALSE, FALSE,
|
||||
test(first_table->schema_table)))
|
||||
goto error;
|
||||
/*
|
||||
save_priv contains any privileges actually granted by check_access.
|
||||
If there are no global privileges (save_priv == 0) and no table level
|
||||
privileges, access is denied.
|
||||
*/
|
||||
if (!save_priv &&
|
||||
!has_any_table_level_privileges(thd, 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 command.
|
||||
*/
|
||||
res= mysqld_show_create(thd, first_table);
|
||||
break;
|
||||
}
|
||||
@ -2970,8 +3020,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;
|
||||
@ -2980,7 +3030,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);
|
||||
@ -2999,8 +3049,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);
|
||||
@ -3012,7 +3062,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);
|
||||
@ -3033,7 +3083,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)) ?
|
||||
@ -3406,7 +3456,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;
|
||||
@ -3514,8 +3564,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))
|
||||
{
|
||||
@ -3569,7 +3619,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)))
|
||||
@ -3965,7 +4015,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,
|
||||
@ -4074,7 +4124,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;
|
||||
@ -4360,7 +4410,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;
|
||||
|
||||
@ -4688,8 +4739,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);
|
||||
@ -5138,7 +5189,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;
|
||||
@ -5183,7 +5234,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;
|
||||
@ -5191,46 +5243,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.
|
||||
@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.
|
||||
|
||||
@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.
|
||||
'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.
|
||||
|
||||
@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.
|
||||
A meta data table (from INFORMATION_SCHEMA) can always be accessed with
|
||||
a SELECT_ACL.
|
||||
|
||||
@retval
|
||||
0 ok
|
||||
@retval
|
||||
1 If we can't get the privileges and we don't use table/column
|
||||
grants.
|
||||
@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
|
||||
@ -5248,8 +5308,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)
|
||||
{
|
||||
@ -5257,10 +5321,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);
|
||||
}
|
||||
@ -5268,20 +5337,27 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
|
||||
|
||||
if ((sctx->master_access & want_access) == want_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
|
||||
UPDATE t1 SET a=1 WHERE b > 0
|
||||
*/
|
||||
/* get access for current db */
|
||||
db_access= sctx->db_access;
|
||||
/*
|
||||
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
|
||||
*/
|
||||
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"));
|
||||
@ -5296,33 +5372,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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -5368,14 +5477,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;
|
||||
@ -5385,30 +5500,46 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Check the privilege for all used tables.
|
||||
@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.
|
||||
|
||||
@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).
|
||||
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!)
|
||||
|
||||
@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).
|
||||
@see check_access
|
||||
@see check_grant
|
||||
|
||||
@retval FALSE OK
|
||||
@retval TRUE Access denied
|
||||
@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).
|
||||
|
||||
@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();
|
||||
@ -5419,22 +5550,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.
|
||||
@ -5446,33 +5586,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;
|
||||
@ -5560,7 +5701,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);
|
||||
}
|
||||
}
|
||||
@ -7253,11 +7394,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;
|
||||
@ -7275,7 +7416,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);
|
||||
}
|
||||
}
|
||||
@ -7315,7 +7456,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);
|
||||
|
||||
/*
|
||||
@ -7324,7 +7465,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);
|
||||
@ -7477,25 +7618,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.
|
||||
|
||||
@ -7535,7 +7657,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)
|
||||
@ -7563,12 +7685,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;
|
||||
|
Reference in New Issue
Block a user