Commit b221ec65 authored by Sergei Golubchik's avatar Sergei Golubchik

enforce privileges for GRANT role

parent 2f2699f9
......@@ -19,7 +19,7 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp
grant role1 to foo@localhost with admin option;
grant role2 to foo@localhost;
grant role2 to role1;
grant role3 to role4 with admin option;
grant role4 to role3 with admin option;
grant select on *.* to foo@localhost with admin option;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'admin option' at line 1
show grants for foo@localhost;
......@@ -33,18 +33,18 @@ Grants for role1
GRANT USAGE ON *.* TO 'role1'
GRANT USAGE ON *.* TO 'role2'
GRANT USAGE ON *.* TO 'role3'
GRANT USAGE ON *.* TO 'role4'
GRANT role2 TO 'role1'
GRANT role3 TO 'role1' WITH ADMIN OPTION
GRANT role4 TO 'role3' WITH ADMIN OPTION
show grants for role4;
Grants for role4
GRANT USAGE ON *.* TO 'role3'
GRANT USAGE ON *.* TO 'role4'
GRANT role3 TO 'role4' WITH ADMIN OPTION
select * from mysql.roles_mapping;
Host User Role Admin_option
role1 role2 N
role1 role3 Y
role4 role3 Y
role3 role4 Y
bar foo role6 Y
localhost foo role1 Y
localhost foo role2 N
......@@ -64,25 +64,26 @@ Grants for role1
GRANT USAGE ON *.* TO 'role1'
GRANT USAGE ON *.* TO 'role2'
GRANT USAGE ON *.* TO 'role3'
GRANT USAGE ON *.* TO 'role4'
GRANT role2 TO 'role1'
GRANT role3 TO 'role1' WITH ADMIN OPTION
GRANT role4 TO 'role3' WITH ADMIN OPTION
show grants for role4;
Grants for role4
GRANT USAGE ON *.* TO 'role3'
GRANT USAGE ON *.* TO 'role4'
GRANT role3 TO 'role4' WITH ADMIN OPTION
select * from information_schema.applicable_roles;
GRANTEE ROLE_NAME IS_GRANTABLE
role1 role2 NO
role1 role3 YES
role4 role3 YES
role3 role4 YES
root@localhost role1 YES
root@localhost role2 YES
root@localhost role4 YES
grant role2 to role1 with admin option;
revoke role1 from foo@localhost;
revoke admin option for role3 from role4;
revoke admin option for role4 from role3;
revoke admin option for role2 from foo@localhost;
revoke admin option for role1 from root@localhost;
show grants for foo@localhost;
Grants for foo@localhost
GRANT CREATE USER ON *.* TO 'foo'@'localhost'
......@@ -93,22 +94,22 @@ Grants for role1
GRANT USAGE ON *.* TO 'role1'
GRANT USAGE ON *.* TO 'role2'
GRANT USAGE ON *.* TO 'role3'
GRANT USAGE ON *.* TO 'role4'
GRANT role2 TO 'role1' WITH ADMIN OPTION
GRANT role3 TO 'role1' WITH ADMIN OPTION
GRANT role4 TO 'role3'
show grants for role4;
Grants for role4
GRANT USAGE ON *.* TO 'role3'
GRANT USAGE ON *.* TO 'role4'
GRANT role3 TO 'role4'
select * from mysql.roles_mapping;
Host User Role Admin_option
role1 role2 Y
role1 role3 Y
role4 role3 N
role3 role4 N
bar foo role6 Y
localhost foo role2 N
localhost foo role5 Y
localhost root role1 Y
localhost root role1 N
localhost root role2 Y
localhost root role4 Y
flush privileges;
......@@ -122,20 +123,30 @@ Grants for role1
GRANT USAGE ON *.* TO 'role1'
GRANT USAGE ON *.* TO 'role2'
GRANT USAGE ON *.* TO 'role3'
GRANT USAGE ON *.* TO 'role4'
GRANT role2 TO 'role1' WITH ADMIN OPTION
GRANT role3 TO 'role1' WITH ADMIN OPTION
GRANT role4 TO 'role3'
show grants for role4;
Grants for role4
GRANT USAGE ON *.* TO 'role3'
GRANT USAGE ON *.* TO 'role4'
GRANT role3 TO 'role4'
select * from information_schema.applicable_roles;
GRANTEE ROLE_NAME IS_GRANTABLE
role1 role2 YES
role1 role3 YES
role4 role3 NO
root@localhost role1 YES
role3 role4 NO
root@localhost role1 NO
root@localhost role2 YES
root@localhost role4 YES
grant role1 to role4;
ERROR 28000: Access denied for user 'root'@'localhost'
grant role1 to role4 with admin option;
ERROR 28000: Access denied for user 'root'@'localhost'
grant role3 to role2;
revoke role3 from role2;
grant role4 to role2 with admin option;
revoke role2 from current_user;
revoke role4 from current_user;
grant role4 to current_user;
drop role role1, role2, role3, role4, role5, role6;
drop user foo@localhost;
......@@ -30,7 +30,7 @@ create user bar with admin current_user;
grant role1 to foo@localhost with admin option;
grant role2 to foo@localhost;
grant role2 to role1;
grant role3 to role4 with admin option;
grant role4 to role3 with admin option;
--error ER_PARSE_ERROR
grant select on *.* to foo@localhost with admin option;
......@@ -54,8 +54,9 @@ select * from information_schema.applicable_roles;
grant role2 to role1 with admin option;
revoke role1 from foo@localhost;
revoke admin option for role3 from role4;
revoke admin option for role4 from role3;
revoke admin option for role2 from foo@localhost;
revoke admin option for role1 from root@localhost;
--sorted_result
show grants for foo@localhost;
......@@ -75,6 +76,22 @@ show grants for role4;
--sorted_result
select * from information_schema.applicable_roles;
# Now, root@localhost don't have admin option for role1:
--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
grant role1 to role4;
--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
grant role1 to role4 with admin option;
# but role3 is grantable
grant role3 to role2;
revoke role3 from role2;
# now, a diamond
grant role4 to role2 with admin option;
revoke role2 from current_user;
revoke role4 from current_user;
grant role4 to current_user;
########################################
# cleanup
########################################
......
......@@ -5322,6 +5322,67 @@ static void append_user(String *str, const char *u, const char *h)
str->append('\'');
}
struct IS_GRANTABLE_DATA
{
ACL_ROLE *role;
bool grantable;
};
static void can_grant_role_callback(ACL_ROLE *unuser __attribute__((unused)),
ACL_ROLE *grantee, void *context_data)
{
IS_GRANTABLE_DATA *data= (IS_GRANTABLE_DATA*)context_data;
for (uint i= 0; i < grantee->role_grants.elements; i++)
{
ACL_ROLE *r= *(dynamic_element(&grantee->role_grants, i, ACL_ROLE**));
if (r == data->role)
{
ROLE_GRANT_PAIR *pair=
find_role_grant_pair(&grantee->user, &empty_lex_str, &r->user);
if (pair->with_admin)
data->grantable= true;
}
}
}
/*
One can only grant a role if SELECT * FROM I_S.APPLICABLE_ROLES shows this
role as grantable.
What this really means - we need to traverse role graph for the current user
looking for our role being granted with the admin option.
*/
static bool can_grant_role(THD *thd, ACL_ROLE *role)
{
Security_context *sctx= thd->security_ctx;
ACL_USER *grantee= find_user_no_anon(sctx->priv_host, sctx->priv_user, true);
if (!grantee)
return false;
LEX_STRING host= { grantee->host.hostname, grantee->hostname_length };
IS_GRANTABLE_DATA data= { role, false };
for (uint i= 0; i < grantee->role_grants.elements; i++)
{
ACL_ROLE *r= *(dynamic_element(&grantee->role_grants, i, ACL_ROLE**));
if (r == role)
{
ROLE_GRANT_PAIR *pair=
find_role_grant_pair(&grantee->user, &host, &r->user);
if (pair->with_admin)
return true;
}
traverse_role_graph(r, &data, NULL, NULL, NULL, can_grant_role_callback);
if (data.grantable)
return true;
}
return false;
}
bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
{
......@@ -5371,6 +5432,15 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
DBUG_RETURN(TRUE);
}
if (!can_grant_role(thd, role))
{
mysql_mutex_unlock(&acl_cache->lock);
mysql_rwlock_unlock(&LOCK_grant);
my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
thd->security_ctx->priv_user, thd->security_ctx->priv_host);
DBUG_RETURN(TRUE);
}
if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // Should never happen
mysql_mutex_unlock(&acl_cache->lock);
......
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