Commit fe521dc2 authored by Vicențiu Ciorbaru's avatar Vicențiu Ciorbaru Committed by Sergei Golubchik

Implemented _non recursive_ role specific grants for table/column level privileges

parent 20609373
...@@ -1690,7 +1690,7 @@ bool acl_getroot(Security_context *sctx, char *user, char *host, ...@@ -1690,7 +1690,7 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
sctx->master_access= 0; sctx->master_access= 0;
sctx->db_access= 0; sctx->db_access= 0;
*sctx->priv_user= *sctx->priv_host= 0; *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0;
/* /*
Find acl entry in user database. Find acl entry in user database.
...@@ -1821,10 +1821,24 @@ end: ...@@ -1821,10 +1821,24 @@ end:
int acl_setrole(THD *thd, char *rolename, ulonglong access) { int acl_setrole(THD *thd, char *rolename, ulonglong access) {
/* merge the privileges */ /* merge the privileges */
thd->security_ctx->master_access= access; Security_context *sctx= thd->security_ctx;
/* mark the current role */ sctx->master_access= access;
strmake(thd->security_ctx->priv_role, rolename, if (thd->db)
sizeof(thd->security_ctx->priv_role)-1); {
sctx->db_access= acl_get(sctx->host,
sctx->ip, sctx->user, thd->db, FALSE);
sctx->db_access= acl_get("", "", rolename, thd->db, FALSE);
}
if (!strcasecmp(rolename, "NONE"))
{
thd->security_ctx->priv_role[0]= 0;
}
else
{
/* mark the current role */
strmake(thd->security_ctx->priv_role, rolename,
sizeof(thd->security_ctx->priv_role)-1);
}
return 0; return 0;
} }
...@@ -2144,12 +2158,15 @@ ulong acl_get(const char *host, const char *ip, ...@@ -2144,12 +2158,15 @@ ulong acl_get(const char *host, const char *ip,
{ {
if (compare_hostname(&acl_db->host,host,ip)) if (compare_hostname(&acl_db->host,host,ip))
{ {
if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern)) if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
{ {
db_access=acl_db->access; db_access=acl_db->access;
if (acl_db->host.hostname) if (acl_db->host.hostname)
goto exit; // Fully specified. Take it goto exit; // Fully specified. Take it
break; /* purecov: tested */ /* XXX is this an alright way to bypass the host table for roles? */
if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user))
goto exit;
break; /* purecov: tested */
} }
} }
} }
...@@ -3035,6 +3052,8 @@ static bool test_if_create_new_users(THD *thd) ...@@ -3035,6 +3052,8 @@ static bool test_if_create_new_users(THD *thd)
db_access=acl_get(sctx->host, sctx->ip, db_access=acl_get(sctx->host, sctx->ip,
sctx->priv_user, tl.db, 0); sctx->priv_user, tl.db, 0);
if (sctx->priv_role[0])
db_access|= acl_get("", "", sctx->priv_role, tl.db, 0);
if (!(db_access & INSERT_ACL)) if (!(db_access & INSERT_ACL))
{ {
if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE)) if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
...@@ -5330,6 +5349,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, ...@@ -5330,6 +5349,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
ulong orig_want_access= want_access; ulong orig_want_access= want_access;
my_bool locked= 0; my_bool locked= 0;
GRANT_TABLE *grant_table; GRANT_TABLE *grant_table;
GRANT_TABLE *grant_table_role= NULL;
DBUG_ENTER("check_grant"); DBUG_ENTER("check_grant");
DBUG_ASSERT(number > 0); DBUG_ASSERT(number > 0);
...@@ -5415,14 +5435,21 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, ...@@ -5415,14 +5435,21 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
mysql_rwlock_rdlock(&LOCK_grant); mysql_rwlock_rdlock(&LOCK_grant);
} }
if (!(grant_table= table_hash_search(sctx->host, sctx->ip, grant_table= table_hash_search(sctx->host, sctx->ip,
tl->get_db_name(), tl->get_db_name(),
sctx->priv_user, sctx->priv_user,
tl->get_table_name(), tl->get_table_name(),
FALSE))) FALSE);
if (sctx->priv_role[0])
grant_table_role= table_hash_search("", "", tl->get_db_name(),
sctx->priv_role,
tl->get_table_name(),
TRUE);
if (!grant_table && !grant_table_role)
{ {
want_access &= ~tl->grant.privilege; want_access&= ~tl->grant.privilege;
goto err; // No grants goto err;
} }
/* /*
...@@ -5432,15 +5459,19 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, ...@@ -5432,15 +5459,19 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
if (any_combination_will_do) if (any_combination_will_do)
continue; continue;
tl->grant.grant_table= grant_table; // Remember for column test tl->grant.grant_table_user= grant_table; // Remember for column test
tl->grant.grant_table_role= grant_table_role;
tl->grant.version= grant_version; tl->grant.version= grant_version;
tl->grant.privilege|= grant_table->privs; tl->grant.privilege|= grant_table ? grant_table->privs : 0;
tl->grant.privilege|= grant_table_role ? grant_table_role->privs : 0;
tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege); tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege);
if (!(~tl->grant.privilege & want_access)) if (!(~tl->grant.privilege & want_access))
continue; continue;
if (want_access & ~(grant_table->cols | tl->grant.privilege)) if (want_access & ~((grant_table ? grant_table->cols : 0) |
(grant_table_role ? grant_table_role->cols : 0) |
tl->grant.privilege))
{ {
want_access &= ~(grant_table->cols | tl->grant.privilege); want_access &= ~(grant_table->cols | tl->grant.privilege);
goto err; // impossible goto err; // impossible
...@@ -5492,6 +5523,7 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant, ...@@ -5492,6 +5523,7 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
const char *name, uint length, Security_context *sctx) const char *name, uint length, Security_context *sctx)
{ {
GRANT_TABLE *grant_table; GRANT_TABLE *grant_table;
GRANT_TABLE *grant_table_role;
GRANT_COLUMN *grant_column; GRANT_COLUMN *grant_column;
ulong want_access= grant->want_privilege & ~grant->privilege; ulong want_access= grant->want_privilege & ~grant->privilege;
DBUG_ENTER("check_grant_column"); DBUG_ENTER("check_grant_column");
...@@ -5506,17 +5538,37 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant, ...@@ -5506,17 +5538,37 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
if (grant->version != grant_version) if (grant->version != grant_version)
{ {
grant->grant_table= grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip, db_name, table_hash_search(sctx->host, sctx->ip, db_name,
sctx->priv_user, sctx->priv_user,
table_name, 0); /* purecov: inspected */ table_name, 0); /* purecov: inspected */
grant->grant_table_role=
sctx->priv_role[0] ? table_hash_search("", "", db_name,
sctx->priv_role,
table_name, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */ grant->version= grant_version; /* purecov: inspected */
} }
if (!(grant_table= grant->grant_table)) if (!(grant_table= grant->grant_table_user) &&
!(grant_table_role= grant->grant_table_role))
goto err; /* purecov: deadcode */ goto err; /* purecov: deadcode */
grant_column=column_hash_search(grant_table, name, length); if (grant_table)
if (grant_column && !(~grant_column->rights & want_access)) {
grant_column= column_hash_search(grant_table, name, length);
if (grant_column)
{
want_access&= ~grant_column->rights;
}
}
if (grant_table_role)
{
grant_column= column_hash_search(grant_table_role, name, length);
if (grant_column)
{
want_access&= ~grant_column->rights;
}
}
if (!want_access)
{ {
mysql_rwlock_unlock(&LOCK_grant); mysql_rwlock_unlock(&LOCK_grant);
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -5526,6 +5578,7 @@ err: ...@@ -5526,6 +5578,7 @@ err:
mysql_rwlock_unlock(&LOCK_grant); mysql_rwlock_unlock(&LOCK_grant);
char command[128]; char command[128];
get_privilege_desc(command, sizeof(command), want_access); get_privilege_desc(command, sizeof(command), want_access);
/* TODO perhaps error should print current rolename aswell */
my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
command, command,
sctx->priv_user, sctx->priv_user,
...@@ -5632,6 +5685,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, ...@@ -5632,6 +5685,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
GRANT_INFO *grant; GRANT_INFO *grant;
/* Initialized only to make gcc happy */ /* Initialized only to make gcc happy */
GRANT_TABLE *grant_table= NULL; GRANT_TABLE *grant_table= NULL;
GRANT_TABLE *grant_table_role= NULL;
/* /*
Flag that gets set if privilege checking has to be performed on column Flag that gets set if privilege checking has to be performed on column
level. level.
...@@ -5656,15 +5710,20 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, ...@@ -5656,15 +5710,20 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
/* reload table if someone has modified any grants */ /* reload table if someone has modified any grants */
if (grant->version != grant_version) if (grant->version != grant_version)
{ {
grant->grant_table= grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip, db_name, table_hash_search(sctx->host, sctx->ip, db_name,
sctx->priv_user, sctx->priv_user,
table_name, 0); /* purecov: inspected */ table_name, 0); /* purecov: inspected */
grant->grant_table_role=
sctx->priv_role[0] ? table_hash_search("", "", db_name,
sctx->priv_role,
table_name, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */ grant->version= grant_version; /* purecov: inspected */
} }
grant_table= grant->grant_table; grant_table= grant->grant_table_user;
DBUG_ASSERT (grant_table); DBUG_ASSERT (grant_table);
grant_table_role= grant->grant_table_role;
} }
} }
...@@ -5674,8 +5733,23 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, ...@@ -5674,8 +5733,23 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
column_hash_search(grant_table, field_name, column_hash_search(grant_table, field_name,
(uint) strlen(field_name)); (uint) strlen(field_name));
if (grant_column) if (grant_column)
{
using_column_privileges= TRUE; using_column_privileges= TRUE;
if (!grant_column || (~grant_column->rights & want_access)) want_access&= ~grant_column->rights;
}
if (grant_table_role)
{
grant_column=
column_hash_search(grant_table_role, field_name,
(uint) strlen(field_name));
if (grant_column)
{
using_column_privileges= TRUE;
want_access&= ~grant_column->rights;
}
}
if (!want_access)
goto err; goto err;
} }
} }
...@@ -5882,18 +5956,26 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table) ...@@ -5882,18 +5956,26 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
Security_context *sctx= thd->security_ctx; Security_context *sctx= thd->security_ctx;
const char *db = table->db ? table->db : thd->db; const char *db = table->db ? table->db : thd->db;
GRANT_TABLE *grant_table; GRANT_TABLE *grant_table;
GRANT_TABLE *grant_table_role= NULL;
mysql_rwlock_rdlock(&LOCK_grant); mysql_rwlock_rdlock(&LOCK_grant);
#ifdef EMBEDDED_LIBRARY #ifdef EMBEDDED_LIBRARY
grant_table= NULL; grant_table= NULL;
grant_table_role= NULL;
#else #else
grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user, grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
table->table_name, 0); table->table_name, 0);
if (sctx->priv_role[0])
grant_table_role= table_hash_search("", "", db, sctx->priv_role,
table->table_name, 0);
#endif #endif
table->grant.grant_table=grant_table; // Remember for column test table->grant.grant_table_user= grant_table; // Remember for column test
table->grant.grant_table_role= grant_table_role;
table->grant.version=grant_version; table->grant.version=grant_version;
if (grant_table) if (grant_table)
table->grant.privilege|= grant_table->privs; table->grant.privilege|= grant_table->privs;
if (grant_table_role)
table->grant.privilege|= grant_table_role->privs;
privilege= table->grant.privilege; privilege= table->grant.privilege;
mysql_rwlock_unlock(&LOCK_grant); mysql_rwlock_unlock(&LOCK_grant);
return privilege; return privilege;
...@@ -5923,31 +6005,50 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant, ...@@ -5923,31 +6005,50 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant,
const char *field_name) const char *field_name)
{ {
GRANT_TABLE *grant_table; GRANT_TABLE *grant_table;
GRANT_TABLE *grant_table_role;
GRANT_COLUMN *grant_column; GRANT_COLUMN *grant_column;
ulong priv; ulong priv= 0;
mysql_rwlock_rdlock(&LOCK_grant); mysql_rwlock_rdlock(&LOCK_grant);
/* reload table if someone has modified any grants */ /* reload table if someone has modified any grants */
if (grant->version != grant_version) if (grant->version != grant_version)
{ {
Security_context *sctx= thd->security_ctx; Security_context *sctx= thd->security_ctx;
grant->grant_table= grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip, table_hash_search(sctx->host, sctx->ip,
db_name, sctx->priv_user, db_name, sctx->priv_user,
table_name, 0); /* purecov: inspected */ table_name, 0); /* purecov: inspected */
grant->grant_table_role=
sctx->priv_role[0] ? table_hash_search("", "", db_name,
sctx->priv_role,
table_name, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */ grant->version= grant_version; /* purecov: inspected */
} }
if (!(grant_table= grant->grant_table)) if (!(grant_table= grant->grant_table_user) &&
!(grant_table_role= grant->grant_table_role))
priv= grant->privilege; priv= grant->privilege;
else else
{ {
grant_column= column_hash_search(grant_table, field_name, if (grant_table)
(uint) strlen(field_name)); {
if (!grant_column) grant_column= column_hash_search(grant_table, field_name,
priv= (grant->privilege | grant_table->privs); (uint) strlen(field_name));
else if (!grant_column)
priv= (grant->privilege | grant_table->privs | grant_column->rights); priv= (grant->privilege | grant_table->privs);
else
priv= (grant->privilege | grant_table->privs | grant_column->rights);
}
if (grant_table_role)
{
grant_column= column_hash_search(grant_table_role, field_name,
(uint) strlen(field_name));
if (!grant_column)
priv|= (grant->privilege | grant_table_role->privs);
else
priv|= (grant->privilege | grant_table->privs | grant_column->rights);
}
} }
mysql_rwlock_unlock(&LOCK_grant); mysql_rwlock_unlock(&LOCK_grant);
return priv; return priv;
...@@ -8661,20 +8762,31 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, ...@@ -8661,20 +8762,31 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
/* db privileges */ /* db privileges */
grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0); grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
/* db privileges for role */
if (sctx->priv_role[0])
grant->privilege|= acl_get("", "", sctx->priv_role, db, 0);
/* table privileges */ /* table privileges */
mysql_rwlock_rdlock(&LOCK_grant); mysql_rwlock_rdlock(&LOCK_grant);
if (grant->version != grant_version) if (grant->version != grant_version)
{ {
grant->grant_table= grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip, db, table_hash_search(sctx->host, sctx->ip, db,
sctx->priv_user, sctx->priv_user,
table, 0); /* purecov: inspected */ table, 0); /* purecov: inspected */
grant->grant_table_role=
sctx->priv_role[0] ? table_hash_search("", "", db,
sctx->priv_role,
table, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */ grant->version= grant_version; /* purecov: inspected */
} }
if (grant->grant_table != 0) if (grant->grant_table_user != 0)
{
grant->privilege|= grant->grant_table_user->privs;
}
if (grant->grant_table_role != 0)
{ {
grant->privilege|= grant->grant_table->privs; grant->privilege|= grant->grant_table_role->privs;
} }
mysql_rwlock_unlock(&LOCK_grant); mysql_rwlock_unlock(&LOCK_grant);
......
...@@ -1478,6 +1478,11 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) ...@@ -1478,6 +1478,11 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
sctx->priv_user, sctx->priv_user,
new_db_file_name.str, new_db_file_name.str,
FALSE) | sctx->master_access; FALSE) | sctx->master_access;
if (sctx->priv_role)
{
/* include a possible currently set role for access */
db_access|= acl_get("", "", sctx->priv_role, new_db_file_name.str, FALSE);
}
if (!force_switch && if (!force_switch &&
!(db_access & DB_ACLS) && !(db_access & DB_ACLS) &&
......
...@@ -5200,8 +5200,12 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, ...@@ -5200,8 +5200,12 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if (!(sctx->master_access & SELECT_ACL)) if (!(sctx->master_access & SELECT_ACL))
{ {
if (db && (!thd->db || db_is_pattern || strcmp(db, thd->db))) if (db && (!thd->db || db_is_pattern || strcmp(db, thd->db)))
{
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
db_is_pattern); db_is_pattern);
if (sctx->priv_role)
db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern);
}
else else
{ {
/* get access for current db */ /* get access for current db */
...@@ -5245,8 +5249,14 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, ...@@ -5245,8 +5249,14 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
} }
if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db))) if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
{
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
db_is_pattern); db_is_pattern);
if (sctx->priv_role)
{
db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern);
}
}
else else
db_access= sctx->db_access; db_access= sctx->db_access;
DBUG_PRINT("info",("db_access: %lu want_access: %lu", DBUG_PRINT("info",("db_access: %lu want_access: %lu",
......
...@@ -251,7 +251,8 @@ typedef struct st_grant_info ...@@ -251,7 +251,8 @@ typedef struct st_grant_info
@details The version of this copy is found in GRANT_INFO::version. @details The version of this copy is found in GRANT_INFO::version.
*/ */
GRANT_TABLE *grant_table; GRANT_TABLE *grant_table_user;
GRANT_TABLE *grant_table_role;
/** /**
@brief Used for cache invalidation when caching privilege information. @brief Used for cache invalidation when caching privilege information.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment