summaryrefslogtreecommitdiff
path: root/sql/sql_acl.cc
diff options
context:
space:
mode:
authorAnel Husakovic <anel@mariadb.org>2020-05-12 16:16:05 +0200
committerAnel Husakovic <anel@mariadb.org>2020-05-28 17:08:40 +0200
commit957cb7b7ba355184aebf0f5dc91b7f2aa620c0e0 (patch)
tree365a8d1e21f7d3b6ab4ce142b1119d25d17a756e /sql/sql_acl.cc
parentdbe447a78908214614db53061dccbc6bde52764e (diff)
downloadmariadb-git-957cb7b7ba355184aebf0f5dc91b7f2aa620c0e0.tar.gz
MDEV-22312: Bad error message for SET DEFAULT ROLE when user account is not granted the role
- `SET DEFAULT ROLE xxx [FOR yyy]` should say: "User yyy has not been granted a role xxx" if: - The current user (not the user `yyy` in the FOR clause) can see the role xxx. It can see the role if: * role exists in `mysql.roles_mappings` (traverse the graph), * If the current user has read access on `mysql.user` table - in that case, it can see all roles, granted or not. - Otherwise it should be "Invalid role specification". In other words, it should not be possible to use `SET DEFAULT ROLE` to discover whether a specific role exist or not.
Diffstat (limited to 'sql/sql_acl.cc')
-rw-r--r--sql/sql_acl.cc120
1 files changed, 82 insertions, 38 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index b6a6f806e50..af8685c458b 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -201,6 +201,8 @@ LEX_STRING current_user= { C_STRING_WITH_LEN("*current_user") };
LEX_STRING current_role= { C_STRING_WITH_LEN("*current_role") };
LEX_STRING current_user_and_current_role= { C_STRING_WITH_LEN("*current_user_and_current_role") };
+class ACL_USER;
+static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static plugin_ref old_password_plugin;
@@ -2034,8 +2036,21 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
DBUG_RETURN(res);
}
-static int check_user_can_set_role(const char *user, const char *host,
- const char *ip, const char *rolename, ulonglong *access)
+static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data)
+{
+ LEX_CSTRING *rolename= static_cast<LEX_CSTRING *>(data);
+ if (rolename->length == grantee->user.length &&
+ !strcmp(rolename->str, grantee->user.str))
+ return -1; // End search, we've found our role.
+
+ /* Keep looking, we haven't found our role yet. */
+ return 0;
+}
+
+
+static int check_user_can_set_role(THD *thd, const char *user, const char *host,
+ const char *ip, const char *rolename,
+ ulonglong *access)
{
ACL_ROLE *role;
ACL_USER_BASE *acl_user_base;
@@ -2053,8 +2068,7 @@ static int check_user_can_set_role(const char *user, const char *host,
acl_user= find_user_wild(host, user, ip);
if (acl_user == NULL)
{
- my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename);
- result= -1;
+ result= ER_INVALID_CURRENT_USER;
}
else if (access)
*access= acl_user->access;
@@ -2065,9 +2079,9 @@ static int check_user_can_set_role(const char *user, const char *host,
role= find_acl_role(rolename);
/* According to SQL standard, the same error message must be presented */
- if (role == NULL) {
- my_error(ER_INVALID_ROLE, MYF(0), rolename);
- result= -1;
+ if (role == NULL)
+ {
+ result= ER_INVALID_ROLE;
goto end;
}
@@ -2088,7 +2102,6 @@ static int check_user_can_set_role(const char *user, const char *host,
/* According to SQL standard, the same error message must be presented */
if (!is_granted)
{
- my_error(ER_INVALID_ROLE, MYF(0), rolename);
result= 1;
goto end;
}
@@ -2097,17 +2110,66 @@ static int check_user_can_set_role(const char *user, const char *host,
{
*access = acl_user->access | role->access;
}
+
end:
mysql_mutex_unlock(&acl_cache->lock);
- return result;
+ /* We present different error messages depending if the user has sufficient
+ privileges to know if the INVALID_ROLE exists. */
+ switch (result)
+ {
+ case ER_INVALID_CURRENT_USER:
+ my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename);
+ break;
+ case ER_INVALID_ROLE:
+ /* Role doesn't exist at all */
+ my_error(ER_INVALID_ROLE, MYF(0), rolename);
+ break;
+ case 1:
+ StringBuffer<1024> c_usr;
+ LEX_CSTRING role_lex;
+ /* First, check if current user can see mysql database. */
+ bool read_access= !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 1);
+
+ role_lex.str= rolename;
+ role_lex.length= strlen(rolename);
+ mysql_mutex_lock(&acl_cache->lock);
+ ACL_USER *cur_user= find_user_or_anon(thd->security_ctx->priv_host,
+ thd->security_ctx->priv_user,
+ thd->security_ctx->ip);
+
+ /* If the current user does not have select priv to mysql database,
+ see if the current user can discover the role if it was granted to him.
+ */
+ if (cur_user && (read_access ||
+ traverse_role_graph_down(cur_user, &role_lex,
+ check_role_is_granted_callback,
+ NULL) == -1))
+ {
+ /* Role is not granted but current user can see the role */
+ c_usr.append(user, strlen(user));
+ c_usr.append('@');
+ c_usr.append(host, strlen(host));
+ my_printf_error(ER_INVALID_ROLE, "User %`s has not been granted role %`s",
+ MYF(0), c_usr.c_ptr(), rolename);
+ }
+ else
+ {
+ /* Role is not granted and current user cannot see the role */
+ my_error(ER_INVALID_ROLE, MYF(0), rolename);
+ }
+ mysql_mutex_unlock(&acl_cache->lock);
+ break;
+ }
+
+ return result;
}
+
int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
{
- /* Yes! priv_user@host. Don't ask why - that's what check_access() does. */
- return check_user_can_set_role(thd->security_ctx->priv_user,
- thd->security_ctx->host, thd->security_ctx->ip, rolename, access);
+ return check_user_can_set_role(thd, thd->security_ctx->priv_user,
+ thd->security_ctx->host, thd->security_ctx->ip, rolename, access);
}
@@ -2886,9 +2948,12 @@ WSREP_ERROR_LABEL:
DBUG_RETURN(result);
}
-int acl_check_set_default_role(THD *thd, const char *host, const char *user)
+int acl_check_set_default_role(THD *thd, const char *host, const char *user,
+ const char *role)
{
- return check_alter_user(thd, host, user);
+ DBUG_ENTER("acl_check_set_default_role");
+ DBUG_RETURN(check_alter_user(thd, host, user) ||
+ check_user_can_set_role(thd, user, host, NULL, role, NULL));
}
int acl_set_default_role(THD *thd, const char *host, const char *user,
@@ -2910,16 +2975,6 @@ int acl_set_default_role(THD *thd, const char *host, const char *user,
DBUG_PRINT("enter",("host: '%s' user: '%s' rolename: '%s'",
safe_str(user), safe_str(host), safe_str(rolename)));
- if (rolename == current_role.str) {
- if (!thd->security_ctx->priv_role[0])
- rolename= "NONE";
- else
- rolename= thd->security_ctx->priv_role;
- }
-
- if (check_user_can_set_role(user, host, host, rolename, NULL))
- DBUG_RETURN(result);
-
if (!strcasecmp(rolename, "NONE"))
clear_role= TRUE;
@@ -3370,7 +3425,7 @@ static bool test_if_create_new_users(THD *thd)
if (!(db_access & INSERT_ACL))
{
if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
- create_new_users=0;
+ create_new_users=0;
}
}
return create_new_users;
@@ -8508,17 +8563,6 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
mysql_mutex_unlock(&acl_cache->lock);
}
-static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data)
-{
- LEX_CSTRING *rolename= static_cast<LEX_CSTRING *>(data);
- if (rolename->length == grantee->user.length &&
- !strcmp(rolename->str, grantee->user.str))
- return -1; // End search, we've found our role.
-
- /* Keep looking, we haven't found our role yet. */
- return 0;
-}
-
/*
Initialize a TABLE_LIST array and open grant tables
@@ -10329,7 +10373,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
Security context in THD contains two pairs of (user,host):
1. (user,host) pair referring to inbound connection.
2. (priv_user,priv_host) pair obtained from mysql.user table after doing
- authnetication of incoming connection.
+ authentication of incoming connection.
Privileges should be checked wrt (priv_user, priv_host) tuple, because
(user,host) pair obtained from inbound connection may have different
values than what is actually stored in mysql.user table and while granting
@@ -10746,7 +10790,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
ulong j,test_access= want_access & ~GRANT_ACL;
for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
{
- if (test_access & j)
+ if (test_access & j)
{
if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
command_array[priv_id],