diff options
author | Vicențiu Ciorbaru <cvicentiu@gmail.com> | 2022-06-16 16:35:55 +0300 |
---|---|---|
committer | Vicențiu Ciorbaru <cvicentiu@gmail.com> | 2022-06-16 16:35:55 +0300 |
commit | 75e98cbd5c5325e7d4596c5387cc5c3cc14936f1 (patch) | |
tree | d22f0392a329ddd2f72fad7c44cfb31c7d06204e | |
parent | 33b8ac0286a41aec0278a704804bd07edefa9218 (diff) | |
download | mariadb-git-75e98cbd5c5325e7d4596c5387cc5c3cc14936f1.tar.gz |
INFORMATION_SCHEMA.views denies with show view and selectpreview-10.10-MDEV-14443-deny-command
This commit fixes show view and select privilege denies.
Other cleanup work:
- TODO(cvicentiu) describe this when rebasing.
- get_table_grant -> Take in table strings, not a TABLE_LIST
- get_column_grant -> Take in sctx.
- check_some_grants_remain -> take in role denies.
- check_column_access_denied -> Utility function for show view.
-rw-r--r-- | mysql-test/suite/deny/show_view.result | 101 | ||||
-rw-r--r-- | mysql-test/suite/deny/show_view.test | 106 | ||||
-rw-r--r-- | sql/item.cc | 3 | ||||
-rw-r--r-- | sql/sql_acl.cc | 196 | ||||
-rw-r--r-- | sql/sql_acl.h | 18 | ||||
-rw-r--r-- | sql/sql_base.cc | 2 | ||||
-rw-r--r-- | sql/sql_show.cc | 41 | ||||
-rw-r--r-- | sql/sql_view.cc | 2 |
8 files changed, 399 insertions, 70 deletions
diff --git a/mysql-test/suite/deny/show_view.result b/mysql-test/suite/deny/show_view.result index 50a65773c56..0b5ac0644a5 100644 --- a/mysql-test/suite/deny/show_view.result +++ b/mysql-test/suite/deny/show_view.result @@ -198,3 +198,104 @@ drop view v1, v2; drop table t1; drop user foo; drop database some_db; +# +# Test showing / not showing view body with SHOW VIEW and SELECT +# privilege denies. +# +connection default; +create user foo; +create database some_db; +create table some_db.t1 (a int, b int); +create view some_db.v1 as (select * from some_db.t1); +grant select, show view on *.* to foo; +grant select, show view on some_db.* to foo; +grant select, show view on some_db.v1 to foo; +connect con1,localhost,foo,,; +select * from information_schema.views where table_schema = 'some_db'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME VIEW_DEFINITION CHECK_OPTION IS_UPDATABLE DEFINER SECURITY_TYPE CHARACTER_SET_CLIENT COLLATION_CONNECTION ALGORITHM +def some_db v1 (select `some_db`.`t1`.`a` AS `a`,`some_db`.`t1`.`b` AS `b` from `some_db`.`t1`) NONE YES root@localhost DEFINER latin1 latin1_swedish_ci UNDEFINED +disconnect con1; +# +# Table level denies. +# +connection default; +deny show view on some_db.v1 to foo; +connect con1,localhost,foo,,; +select * from information_schema.views where table_schema = 'some_db'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME VIEW_DEFINITION CHECK_OPTION IS_UPDATABLE DEFINER SECURITY_TYPE CHARACTER_SET_CLIENT COLLATION_CONNECTION ALGORITHM +def some_db v1 NONE YES root@localhost DEFINER latin1 latin1_swedish_ci UNDEFINED +disconnect con1; +connection default; +revoke deny show view on some_db.v1 from foo; +deny select on some_db.v1 to foo; +connect con1,localhost,foo,,; +select * from information_schema.views where table_schema = 'some_db'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME VIEW_DEFINITION CHECK_OPTION IS_UPDATABLE DEFINER SECURITY_TYPE CHARACTER_SET_CLIENT COLLATION_CONNECTION ALGORITHM +def some_db v1 NONE YES root@localhost DEFINER latin1 latin1_swedish_ci UNDEFINED +disconnect con1; +# +# Database level denies. +# +connection default; +revoke deny select on some_db.v1 from foo; +deny select on some_db.* to foo; +connect con1,localhost,foo,,; +select * from information_schema.views where table_schema = 'some_db'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME VIEW_DEFINITION CHECK_OPTION IS_UPDATABLE DEFINER SECURITY_TYPE CHARACTER_SET_CLIENT COLLATION_CONNECTION ALGORITHM +def some_db v1 NONE YES root@localhost DEFINER latin1 latin1_swedish_ci UNDEFINED +disconnect con1; +connection default; +revoke deny select on some_db.* from foo; +deny show view on some_db.* to foo; +connect con1,localhost,foo,,; +select * from information_schema.views where table_schema = 'some_db'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME VIEW_DEFINITION CHECK_OPTION IS_UPDATABLE DEFINER SECURITY_TYPE CHARACTER_SET_CLIENT COLLATION_CONNECTION ALGORITHM +def some_db v1 NONE YES root@localhost DEFINER latin1 latin1_swedish_ci UNDEFINED +disconnect con1; +# +# Global level denies. +# +connection default; +revoke deny show view on some_db.* from foo; +deny show view on *.* to foo; +connect con1,localhost,foo,,; +select * from information_schema.views where table_schema = 'some_db'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME VIEW_DEFINITION CHECK_OPTION IS_UPDATABLE DEFINER SECURITY_TYPE CHARACTER_SET_CLIENT COLLATION_CONNECTION ALGORITHM +def some_db v1 NONE YES root@localhost DEFINER latin1 latin1_swedish_ci UNDEFINED +disconnect con1; +connection default; +revoke deny show view on *.* from foo; +deny select on *.* to foo; +connect con1,localhost,foo,,; +select * from information_schema.views where table_schema = 'some_db'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME VIEW_DEFINITION CHECK_OPTION IS_UPDATABLE DEFINER SECURITY_TYPE CHARACTER_SET_CLIENT COLLATION_CONNECTION ALGORITHM +def some_db v1 NONE YES root@localhost DEFINER latin1 latin1_swedish_ci UNDEFINED +disconnect con1; +# +# Column level denies. +# +connection default; +revoke deny select on *.* from foo; +# +# All denies have been revoked from the user, back to regular +# privilege checking. +# +connect con1,localhost,foo,,; +select * from information_schema.views where table_schema = 'some_db'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME VIEW_DEFINITION CHECK_OPTION IS_UPDATABLE DEFINER SECURITY_TYPE CHARACTER_SET_CLIENT COLLATION_CONNECTION ALGORITHM +def some_db v1 (select `some_db`.`t1`.`a` AS `a`,`some_db`.`t1`.`b` AS `b` from `some_db`.`t1`) NONE YES root@localhost DEFINER latin1 latin1_swedish_ci UNDEFINED +disconnect con1; +connection default; +# +# Here because we have column level denies, a user is not allowed +# to see the view. +# +deny select (a) on some_db.v1 to foo; +connect con1,localhost,foo,,; +select * from information_schema.views where table_schema = 'some_db'; +TABLE_CATALOG TABLE_SCHEMA TABLE_NAME VIEW_DEFINITION CHECK_OPTION IS_UPDATABLE DEFINER SECURITY_TYPE CHARACTER_SET_CLIENT COLLATION_CONNECTION ALGORITHM +def some_db v1 NONE YES root@localhost DEFINER latin1 latin1_swedish_ci UNDEFINED +disconnect con1; +connection default; +drop user foo; +drop database some_db; diff --git a/mysql-test/suite/deny/show_view.test b/mysql-test/suite/deny/show_view.test index 9d4bc4a3158..12a86987e80 100644 --- a/mysql-test/suite/deny/show_view.test +++ b/mysql-test/suite/deny/show_view.test @@ -175,3 +175,109 @@ drop view v1, v2; drop table t1; drop user foo; drop database some_db; + +--echo # +--echo # Test showing / not showing view body with SHOW VIEW and SELECT +--echo # privilege denies. +--echo # +connection default; +create user foo; +create database some_db; +create table some_db.t1 (a int, b int); + +create view some_db.v1 as (select * from some_db.t1); + +grant select, show view on *.* to foo; +grant select, show view on some_db.* to foo; +grant select, show view on some_db.v1 to foo; + +--connect (con1,localhost,foo,,) +select * from information_schema.views where table_schema = 'some_db'; +disconnect con1; + +--echo # +--echo # Table level denies. +--echo # +connection default; +deny show view on some_db.v1 to foo; + +--connect (con1,localhost,foo,,) +select * from information_schema.views where table_schema = 'some_db'; +disconnect con1; + +connection default; +revoke deny show view on some_db.v1 from foo; +deny select on some_db.v1 to foo; + +--connect (con1,localhost,foo,,) +select * from information_schema.views where table_schema = 'some_db'; +disconnect con1; + +--echo # +--echo # Database level denies. +--echo # +connection default; +revoke deny select on some_db.v1 from foo; +deny select on some_db.* to foo; + +--connect (con1,localhost,foo,,) +select * from information_schema.views where table_schema = 'some_db'; +disconnect con1; + +connection default; +revoke deny select on some_db.* from foo; +deny show view on some_db.* to foo; + +--connect (con1,localhost,foo,,) +select * from information_schema.views where table_schema = 'some_db'; +disconnect con1; + +--echo # +--echo # Global level denies. +--echo # +connection default; +revoke deny show view on some_db.* from foo; +deny show view on *.* to foo; + +--connect (con1,localhost,foo,,) +select * from information_schema.views where table_schema = 'some_db'; +disconnect con1; + +connection default; +revoke deny show view on *.* from foo; +deny select on *.* to foo; + +--connect (con1,localhost,foo,,) +select * from information_schema.views where table_schema = 'some_db'; +disconnect con1; + +--echo # +--echo # Column level denies. +--echo # + +connection default; +revoke deny select on *.* from foo; + +--echo # +--echo # All denies have been revoked from the user, back to regular +--echo # privilege checking. +--echo # + +--connect (con1,localhost,foo,,) +select * from information_schema.views where table_schema = 'some_db'; +disconnect con1; + +connection default; +--echo # +--echo # Here because we have column level denies, a user is not allowed +--echo # to see the view. +--echo # +deny select (a) on some_db.v1 to foo; + +--connect (con1,localhost,foo,,) +select * from information_schema.views where table_schema = 'some_db'; +disconnect con1; + +connection default; +drop user foo; +drop database some_db; diff --git a/sql/item.cc b/sql/item.cc index 4eb8e50be84..90d63a91691 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -6181,7 +6181,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference) #ifndef NO_EMBEDDED_ACCESS_CHECKS if (any_privileges) { - if (!(have_privileges= (get_column_grant(thd, &field->table->grant, + if (!(have_privileges= (get_column_grant(thd->security_context(), + &field->table->grant, field->table->s->db, field->table->s->table_name, field_name) & 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, diff --git a/sql/sql_acl.h b/sql/sql_acl.h index dcf9ee2a09a..0fff913c2f0 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -130,6 +130,12 @@ bool check_table_access(THD *thd, privilege_t requirements,TABLE_LIST *tables, bool any_combination_of_privileges_will_do, uint number, bool no_errors); + +bool check_if_any_column_is_denied(Security_context *ctx, + privilege_t privilege_needed, + const LEX_CSTRING &db, const LEX_CSTRING &table, + Field **fields); + #else inline bool check_access(THD *thd, privilege_t want_access, const char *db, privilege_t *save_priv, @@ -168,10 +174,18 @@ check_table_access(THD *thd, privilege_t requirements,TABLE_LIST *tables, uint number, bool no_errors) { return false; } +inline bool +check_if_any_column_is_denied(Security_context *sctx, + privilege_t privilege_needed, + const LEX_CSTRING &db, const LEX_CSTRING &table, + Field **fields); + #endif /*NO_EMBEDDED_ACCESS_CHECKS*/ -privilege_t get_table_grant(THD *thd, TABLE_LIST *table); -privilege_t get_column_grant(THD *thd, GRANT_INFO *grant, +privilege_t get_table_grant(Security_context *sctx, + privilege_t db_access, + const LEX_CSTRING &db, const LEX_CSTRING &table); +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); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1d92a1a2a03..e4defdff2c7 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8491,7 +8491,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if (!tables->schema_table && !(fld->have_privileges= - (get_column_grant(thd, field_iterator.grant(), + (get_column_grant(thd->security_context(), field_iterator.grant(), field_db_name, field_table_name, fld->field_name) & VIEW_ANY_ACL))) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 01fb7813d7a..cb6fc67cdb6 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -6123,7 +6123,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, ulonglong col_access; check_access(thd,SELECT_ACL, db_name->str, &tables->grant.privilege, 0, 0, MY_TEST(tables->schema_table)); - col_access= get_column_grant(thd, &tables->grant, + col_access= get_column_grant(thd->security_context(), &tables->grant, *db_name, *table_name, field->field_name) & COL_ACLS; if (!tables->schema_table && !col_access) @@ -6917,19 +6917,38 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables, else { if ((thd->col_access & (SHOW_VIEW_ACL|SELECT_ACL)) == - (SHOW_VIEW_ACL|SELECT_ACL)) + (SHOW_VIEW_ACL|SELECT_ACL) && + !(thd->security_ctx->denies_active & (TABLE_PRIV | COLUMN_PRIV))) + { tables->allowed_show= TRUE; + } else { - TABLE_LIST table_list; - table_list.reset(); - table_list.db= tables->db; - table_list.table_name= tables->table_name; - table_list.grant.privilege= thd->col_access; - privilege_t view_access(get_table_grant(thd, &table_list)); - if ((view_access & (SHOW_VIEW_ACL|SELECT_ACL)) == - (SHOW_VIEW_ACL|SELECT_ACL)) - tables->allowed_show= TRUE; + privilege_t view_access(get_table_grant(thd->security_context(), + thd->col_access, tables->db, + tables->table_name)); + if ((view_access & (SHOW_VIEW_ACL|SELECT_ACL)) == + (SHOW_VIEW_ACL|SELECT_ACL)) + { + if (thd->security_context()->denies_active & COLUMN_PRIV) + { + /* + Only allow show of the view's definition *if* we have + TABLE or higher SELECT & SHOW VIEW privilege *and* + we do not have any columns in the view with a denied + SELECT. + */ + if (!check_if_any_column_is_denied(thd->security_context(), + SELECT_ACL, + tables->db, tables->table_name, + tables->table->field)) + tables->allowed_show= TRUE; + } + else + { + tables->allowed_show= TRUE; + } + } } } #endif diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 90c4e4fe0dc..ba1b4d7688e 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -624,7 +624,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, while ((item= it++)) { Item_field *fld= item->field_for_view_update(); - privilege_t priv(get_column_grant(thd, &view->grant, + privilege_t priv(get_column_grant(thd->security_context(), &view->grant, view->db, view->table_name, item->name) & |