summaryrefslogtreecommitdiff
path: root/sql/sql_acl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_acl.cc')
-rw-r--r--sql/sql_acl.cc196
1 files changed, 142 insertions, 54 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index df9409800ba..919417b5fa1 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -198,6 +198,11 @@ public:
};
+enum object_type {
+ TABLE_TYPE, PROCEDURE_TYPE, FUNCTION_TYPE, PACKAGE_SPEC_TYPE,
+ PACKAGE_BODY_TYPE
+};
+
class Deny_spec
{
typedef std::pair<LEX_CSTRING, privilege_t> deny_entry;
@@ -4238,6 +4243,13 @@ check_routine_access(THD *thd, privilege_t want_access, const LEX_CSTRING *db,
return check_grant_routine(thd, want_access, tables, sph, no_errors);
}
+static privilege_t acl_get_effective_deny_mask_impl(
+ const Deny_spec &denies,
+ const Deny_spec *role_denies,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &object_name,
+ const LEX_CSTRING &column_name,
+ enum object_type type);
/**
Check if the routine has any of the routine privileges.
@@ -4275,6 +4287,52 @@ bool check_some_routine_access(THD *thd,
}
+bool check_if_any_column_is_denied(Security_context *sctx,
+ privilege_t privilege_needed,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &table,
+ Field **fields)
+{
+ const Deny_spec *denies;
+ const Deny_spec *role_denies= nullptr;
+
+ mysql_mutex_lock(&acl_cache->lock);
+ /*
+ TODO(cvicentiu) this snippet really repeats in too many places. Factor
+ it out into a function.
+ */
+ ACL_USER *acl_user= find_user_exact(sctx->priv_host, sctx->priv_user);
+ if (!acl_user)
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ return true;
+ }
+ denies= acl_user->denies;
+ /* This function should only be called when denies are active. */
+ DBUG_ASSERT(denies);
+
+ ACL_ROLE *role= find_acl_role(sctx->priv_role);
+ if (role)
+ role_denies= role->denies;
+
+ while (*fields)
+ {
+ privilege_t deny_mask= acl_get_effective_deny_mask_impl(
+ *denies, role_denies, db, table, (*fields)->field_name, TABLE_TYPE);
+
+ if (privilege_needed & deny_mask)
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ return true;
+ }
+ fields++;
+ }
+
+ mysql_mutex_unlock(&acl_cache->lock);
+ return false;
+}
+
+
/*
Check if the given table has any of the asked privileges
@@ -5218,11 +5276,6 @@ static bool compute_acl_cache_key(const char *ip,
}
-enum object_type {
- TABLE_TYPE, PROCEDURE_TYPE, FUNCTION_TYPE, PACKAGE_SPEC_TYPE,
- PACKAGE_BODY_TYPE
-};
-
static enum object_type get_corresponding_object_type(const Sp_handler &sph)
{
switch (sph.type())
@@ -5283,7 +5336,7 @@ static enum PRIV_TYPE get_corresponding_priv_spec_type(const Sp_handler &sph)
return NO_PRIV;
}
-static privilege_t acl_get_effective_deny_mask_impl(
+static privilege_t get_deny_mask(
const Deny_spec &denies,
const LEX_CSTRING &db,
const LEX_CSTRING &object_name,
@@ -5318,6 +5371,21 @@ static privilege_t acl_get_effective_deny_mask_impl(
return result;
}
+static privilege_t acl_get_effective_deny_mask_impl(
+ const Deny_spec &denies,
+ const Deny_spec *role_denies,
+ const LEX_CSTRING &db,
+ const LEX_CSTRING &object_name,
+ const LEX_CSTRING &column_name,
+ enum object_type type)
+{
+ privilege_t result= get_deny_mask(denies, db, object_name, column_name, type);
+ if (role_denies)
+ result|= get_deny_mask(*role_denies, db, object_name, column_name, type);
+ return result;
+}
+
+
privilege_t acl_get_effective_deny_mask_impl(const Security_context *ctx,
const LEX_CSTRING &db,
const LEX_CSTRING &object_name,
@@ -5371,13 +5439,11 @@ privilege_t acl_get_effective_deny_mask_impl(const Security_context *ctx,
return ALL_KNOWN_ACL;
}
- result= acl_get_effective_deny_mask_impl(*acl_user->denies,
- db, object_name, column_name, type);
+ result= get_deny_mask(*acl_user->denies, db, object_name, column_name, type);
ACL_ROLE *role= find_acl_role(ctx->priv_role);
if (role)
- result|= acl_get_effective_deny_mask_impl(*role->denies, db,
- object_name, column_name, type);
+ result|= get_deny_mask(*role->denies, db, object_name, column_name, type);
mysql_mutex_unlock(&acl_cache->lock);
return result;
@@ -9947,9 +10013,9 @@ bool grant_reload(THD *thd)
}
-/* TODO(cvicentiu) extend this to pass in both user and role denies. */
static
bool check_some_grants_remain(const Deny_spec &denies,
+ const Deny_spec *role_denies,
privilege_t table_deny_mask, GRANT_TABLE *table)
{
if (!table)
@@ -9968,6 +10034,9 @@ bool check_some_grants_remain(const Deny_spec &denies,
privilege_t col_deny_mask=
table_deny_mask | denies.get_column_deny(db, table_name, column_name);
+ if (role_denies)
+ col_deny_mask|= role_denies->get_column_deny(db, table_name, column_name);
+
if (grant_column->rights & ~col_deny_mask)
return true;
}
@@ -9994,13 +10063,26 @@ bool check_some_grants_remain(Security_context *sctx,
}
DBUG_ASSERT(sctx->denies_active && acl_user->denies);
- const Deny_spec& denies= *acl_user->denies;
+ const Deny_spec &denies= *acl_user->denies;
+ const Deny_spec *role_denies= nullptr;
+
privilege_t deny_mask= denies.get_global() |
denies.get_db_deny(db) |
denies.get_table_deny(db, table);
- result= check_some_grants_remain(denies, deny_mask, grant_table) ||
- check_some_grants_remain(denies, deny_mask, grant_table_role);
+ ACL_ROLE *role= find_acl_role(sctx->priv_role);
+ if (role && role->denies)
+ {
+ role_denies= role->denies;
+ deny_mask|= role_denies->get_global() |
+ role_denies->get_db_deny(db) |
+ role_denies->get_table_deny(db, table);
+ }
+
+ result= check_some_grants_remain(denies, role_denies,
+ deny_mask, grant_table) ||
+ check_some_grants_remain(denies, role_denies,
+ deny_mask, grant_table_role);
mysql_mutex_unlock(&acl_cache->lock);
return result;
@@ -10294,7 +10376,6 @@ bool check_grant(THD *thd, privilege_t want_access, TABLE_LIST *tables,
t_ref->grant.version= grant_version;
t_ref->grant.privilege|= grant_table ? grant_table->privs : NO_ACL;
t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : NO_ACL;
- // TODO(cvicentiu) here the deny mask for the role must be created too.
t_ref->grant.privilege&= ~deny_mask;
/*
@@ -10494,7 +10575,10 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
if (table_ref->belong_to_view &&
thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
{
- view_privs= get_column_grant(thd, grant,
+ /* !!! TODO(cvicentiu) This changes from always thd->security_context
+ * to also point to table_ref->security_ctx, CHECK! Perhaps
+ * this is not the intent *here* !*/
+ view_privs= get_column_grant(sctx, grant,
*db_name, *table_name, {name, length});
if (view_privs & VIEW_ANY_ACL)
{
@@ -10548,6 +10632,7 @@ bool check_grant_all_columns(THD *thd, privilege_t want_access_arg,
GRANT_TABLE *UNINIT_VAR(grant_table);
GRANT_TABLE *UNINIT_VAR(grant_table_role);
const Deny_spec *denies= nullptr;
+ const Deny_spec *role_denies= nullptr;
const bool denies_could_interfere=
sctx->denies_active & (GLOBAL_PRIV | DATABASE_PRIV | TABLE_PRIV |
COLUMN_PRIV);
@@ -10568,6 +10653,7 @@ bool check_grant_all_columns(THD *thd, privilege_t want_access_arg,
mysql_mutex_lock(&acl_cache->lock);
/* TODO(cvicentiu) modify this for role denies too! */
ACL_USER *acl_user= find_user_exact(sctx->priv_host, sctx->priv_user);
+ ACL_ROLE *acl_role= find_acl_role(sctx->priv_role);
if (!acl_user)
goto err; /* See acl_get_effective_deny_mask_impl, same shortcut. */
/*
@@ -10575,6 +10661,8 @@ bool check_grant_all_columns(THD *thd, privilege_t want_access_arg,
it again.
*/
denies= acl_user->denies;
+ if (acl_role)
+ role_denies= acl_role->denies;
}
for (; !fields->end_of_fields(); fields->next())
@@ -10594,10 +10682,10 @@ bool check_grant_all_columns(THD *thd, privilege_t want_access_arg,
if (sctx->denies_active & (GLOBAL_PRIV | DATABASE_PRIV | TABLE_PRIV |
COLUMN_PRIV))
{
- /* TODO(cvicentiu) -> Role denies too... */
field_deny_mask=
acl_get_effective_deny_mask_impl(
*denies,
+ role_denies,
{fields->get_db_name(), strlen(fields->get_db_name())},
{fields->get_table_name(), strlen(fields->get_table_name())},
*field_name,
@@ -10709,33 +10797,32 @@ static bool check_grant_db_routine(Security_context *sctx,
HASH *hash= get_corresponding_routine_hash(routine_type);
for (uint idx= 0; idx < hash->records; ++idx)
{
+ privilege_t usable_mask= db_deny_mask;
GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
+ /*
+ Only re-compute the deny mask if there are specific denies active
+ that might impact it.
+ */
+ if (sctx->denies_active & get_corresponding_priv_spec_type(routine_type))
+ {
+ LEX_CSTRING routine_name= {item->tname, strlen(item->tname)};
+ usable_mask= acl_get_effective_deny_mask_impl(sctx, db, routine_name,
+ {nullptr, 0},
+ routine_type);
+ }
if (strcmp(item->user, sctx->priv_user) == 0 &&
strcmp(item->db, db.str) == 0 &&
compare_hostname(&item->host, sctx->host, sctx->ip))
{
- privilege_t usable_mask= db_deny_mask;
- /*
- Only re-compute the deny mask if there are specific denies active
- that might impact it.
- */
- if (sctx->denies_active & get_corresponding_priv_spec_type(routine_type))
- {
- LEX_CSTRING routine_name= {item->tname, strlen(item->tname)};
- usable_mask= acl_get_effective_deny_mask_impl(sctx, db, routine_name,
- {nullptr, 0},
- routine_type);
- }
if (item->privs & ~usable_mask)
return FALSE;
}
- /* TODO(cvicentiu) make this work for role denies too. */
if (sctx->priv_role[0] && strcmp(item->user, sctx->priv_role) == 0 &&
strcmp(item->db, db.str) == 0 &&
(!item->host.hostname || !item->host.hostname[0]))
{
- if (item->privs & ~db_deny_mask)
+ if (item->privs & ~usable_mask)
return FALSE; /* Found current role match */
}
}
@@ -10761,6 +10848,7 @@ bool check_grant_db(Security_context *sctx,
bool denies_at_table_or_column_level=
sctx->denies_active & (TABLE_PRIV | COLUMN_PRIV);
const Deny_spec *denies= nullptr;
+ const Deny_spec *role_denies= nullptr;
LEX_CSTRING lower_case_db= db;
@@ -10802,11 +10890,15 @@ bool check_grant_db(Security_context *sctx,
ACL_USER *acl_user= find_user_exact(sctx->priv_host, sctx->priv_user);
if (!acl_user)
goto error; /* See acl_get_effective_deny_mask_impl, same shortcut. */
+
+ ACL_ROLE *role= find_acl_role(sctx->priv_role);
/*
Caching denies here, vs for every hash entry means we don't need to find
it again.
*/
denies= acl_user->denies;
+ if (role)
+ role_denies= role->denies;
}
for (size_t idx=0 ; idx < column_priv_hash.records ; idx++)
@@ -10823,6 +10915,8 @@ bool check_grant_db(Security_context *sctx,
{
table_name= {grant_table->tname, strlen(grant_table->tname)};
deny_mask|= denies->get_table_deny(db, table_name);
+ if (role_denies)
+ deny_mask|= role_denies->get_table_deny(db, table_name);
}
if (grant_table->privs & ~deny_mask)
@@ -10852,7 +10946,7 @@ bool check_grant_db(Security_context *sctx,
to check if there is an explicit deny on each of those columns
and compute the effective privileges.
*/
- if (check_some_grants_remain(*denies, deny_mask, grant_table))
+ if (check_some_grants_remain(*denies, role_denies, deny_mask, grant_table))
{
error= false;
goto error;
@@ -10864,7 +10958,6 @@ bool check_grant_db(Security_context *sctx,
!memcmp(grant_table->hash_key, helping2, len2) &&
(!grant_table->host.hostname || !grant_table->host.hostname[0]))
{
- //TODO(cvicentiu) implement for roles...
if (grant_table->privs & ~deny_mask || grant_table->cols & ~deny_mask)
{
error= FALSE; /* Found role match. */
@@ -11027,39 +11120,36 @@ bool check_routine_level_acl(THD *thd,
Functions to retrieve the grant for a table/column (for SHOW functions)
*****************************************************************************/
-privilege_t get_table_grant(THD *thd, TABLE_LIST *table)
+privilege_t get_table_grant(Security_context *sctx,
+ privilege_t db_access,
+ const LEX_CSTRING &db, const LEX_CSTRING &table)
{
- Security_context *sctx= thd->security_ctx;
- const char *db = table->db.str ? table->db.str : thd->db.str;
GRANT_TABLE *grant_table;
GRANT_TABLE *grant_table_role= NULL;
+ privilege_t result= db_access;
+
//TODO(cvicentiu) table level and column level deny mask recompute per table.
- privilege_t deny_mask= acl_get_effective_deny_mask(sctx, {db, strlen(db)});
+ privilege_t deny_mask= acl_get_effective_deny_mask(sctx, db, table);
mysql_rwlock_rdlock(&LOCK_grant);
#ifdef EMBEDDED_LIBRARY
grant_table= NULL;
grant_table_role= NULL;
#else
- grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
- table->table_name.str, 0);
+ grant_table= table_hash_search(sctx->host, sctx->ip, db.str, sctx->priv_user,
+ table.str, 0);
if (sctx->priv_role[0])
- grant_table_role= table_hash_search("", "", db, sctx->priv_role,
- table->table_name.str, 0);
+ grant_table_role= table_hash_search("", "", db.str, sctx->priv_role,
+ table.str, 0);
#endif
- table->grant.grant_table_user= grant_table; // Remember for column test
- table->grant.grant_table_role= grant_table_role;
- table->grant.version=grant_version;
if (grant_table)
- table->grant.privilege|= grant_table->privs;
+ result|= grant_table->privs;
if (grant_table_role)
- table->grant.privilege|= grant_table_role->privs;
+ result|= grant_table_role->privs;
- table->grant.privilege &= ~deny_mask;
-
- privilege_t privilege(table->grant.privilege);
+ result&= ~deny_mask;
mysql_rwlock_unlock(&LOCK_grant);
- return privilege;
+ return result;
}
@@ -11081,7 +11171,7 @@ privilege_t get_table_grant(THD *thd, TABLE_LIST *table)
The access priviliges for the field db_name.table_name.field_name
*/
-privilege_t get_column_grant(THD *thd, GRANT_INFO *grant,
+privilege_t get_column_grant(Security_context *sctx, GRANT_INFO *grant,
const LEX_CSTRING &db_name,
const LEX_CSTRING &table_name,
const LEX_CSTRING &field_name)
@@ -11094,14 +11184,12 @@ privilege_t get_column_grant(THD *thd, GRANT_INFO *grant,
mysql_rwlock_rdlock(&LOCK_grant);
- deny_mask= acl_get_effective_deny_mask(thd->security_ctx,
+ deny_mask= acl_get_effective_deny_mask(sctx,
db_name, table_name, field_name);
/* reload table if someone has modified any grants */
if (grant->version != grant_version)
{
- Security_context *sctx= thd->security_ctx;
-
grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip,
db_name.str, sctx->priv_user,