1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-01 03:47:19 +03:00

Added default role implementation

This commit is contained in:
Vicentiu Ciorbaru
2014-07-13 23:57:10 +00:00
committed by Sergei Golubchik
parent 43351faf2b
commit 64b27c734e
19 changed files with 761 additions and 52 deletions

View File

@ -265,6 +265,7 @@ public:
const char *ssl_cipher, *x509_issuer, *x509_subject;
LEX_STRING plugin;
LEX_STRING auth_string;
LEX_STRING default_rolename;
ACL_USER *copy(MEM_ROOT *root)
{
@ -284,6 +285,8 @@ public:
dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
dst->auth_string.str= safe_strdup_root(root, auth_string.str);
dst->host.hostname= safe_strdup_root(root, host.hostname);
dst->default_rolename.str= safe_strdup_root(root, default_rolename.str);
dst->default_rolename.length= default_rolename.length;
bzero(&dst->role_grants, sizeof(role_grants));
return dst;
}
@ -702,6 +705,7 @@ bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, char *username,
#define NORMAL_HANDSHAKE_SIZE 6
#define ROLE_ASSIGN_COLUMN_IDX 43
#define DEFAULT_ROLE_COLUMN_IDX 44
/* various flags valid for ACL_USER */
#define IS_ROLE (1L << 0)
/* Flag to mark that a ROLE is on the recursive DEPTH_FIRST_SEARCH stack */
@ -1347,6 +1351,14 @@ static bool acl_load(THD *thd, TABLE_LIST *tables)
(void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *),
8, 8, MYF(0));
/* check default role, if any */
if (!is_role && table->s->fields >= 45)
{
user.default_rolename.str= get_field(&acl_memroot, table->field[44]);
user.default_rolename.length= user.default_rolename.str ?
strlen(user.default_rolename.str) : 0;
}
if (is_role)
{
DBUG_PRINT("info", ("Found role %s", user.user.str));
@ -1867,7 +1879,8 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
DBUG_RETURN(res);
}
int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
int check_user_can_set_role(const char *host, const char *user,
const char *rolename, ulonglong *access)
{
ACL_ROLE *role;
ACL_USER_BASE *acl_user_base;
@ -1882,8 +1895,7 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
{
/* have to clear the privileges */
/* get the current user */
acl_user= find_user_exact(thd->security_ctx->priv_host,
thd->security_ctx->priv_user);
acl_user= find_user_exact(host, user);
if (acl_user == NULL)
{
my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename);
@ -1911,9 +1923,7 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
continue;
acl_user= (ACL_USER *)acl_user_base;
/* Yes! priv_user@host. Don't ask why - that's what check_access() does. */
if (acl_user->wild_eq(thd->security_ctx->priv_user,
thd->security_ctx->host))
if (acl_user->wild_eq(user, host))
{
is_granted= TRUE;
break;
@ -1935,6 +1945,15 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
end:
mysql_mutex_unlock(&acl_cache->lock);
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->host,
thd->security_ctx->priv_user,
rolename, access);
}
@ -1960,7 +1979,6 @@ int acl_setrole(THD *thd, char *rolename, ulonglong access)
return 0;
}
static uchar* check_get_key(ACL_USER *buff, size_t *length,
my_bool not_used __attribute__((unused)))
{
@ -2068,6 +2086,7 @@ static void acl_insert_user(const char *user, const char *host,
mysql_mutex_assert_owner(&acl_cache->lock);
bzero(&acl_user, sizeof(acl_user));
acl_user.user.str=*user ? strdup_root(&acl_memroot,user) : 0;
acl_user.user.length= strlen(user);
update_hostname(&acl_user.host, safe_strdup_root(&acl_memroot, host));
@ -2518,7 +2537,55 @@ bool acl_check_host(const char *host, const char *ip)
return 1; // Host is not allowed
}
/**
Check if the user is allowed to alter the mysql.user table
@param thd THD
@param host Hostname for the user
@param user User name
@return Error status
@retval 0 OK
@retval 1 skip grant tables error
@retval 2 ANONYMOUS user error
@retval 3 the entry to change is a role, not a user
@retval 4 no UPDATE_ACL error
*/
int check_alter_user(THD *thd, const char *host, const char *user)
{
int error = 1;
if (!initialized)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
goto end;
}
if (!thd->slave_thread && !thd->security_ctx->priv_user[0])
{
my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
MYF(0));
goto end;
}
if (!host) // Role
{
my_error(ER_PASSWORD_NO_MATCH, MYF(0));
goto end;
}
if (!thd->slave_thread &&
(strcmp(thd->security_ctx->priv_user, user) ||
my_strcasecmp(system_charset_info, host,
thd->security_ctx->priv_host)))
{
if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
goto end;
}
error = 0;
end:
return error;
}
/**
Check if the user is allowed to change password
@ -2538,38 +2605,17 @@ bool acl_check_host(const char *host, const char *ip)
int check_change_password(THD *thd, const char *host, const char *user,
char *new_password, uint new_password_len)
{
if (!initialized)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
return(1);
}
if (!thd->slave_thread && !thd->security_ctx->priv_user[0])
{
my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
MYF(0));
return(1);
}
if (!host) // Role
{
my_error(ER_PASSWORD_NO_MATCH, MYF(0));
if (check_alter_user(thd, host, user))
return 1;
}
if (!thd->slave_thread &&
(strcmp(thd->security_ctx->priv_user, user) ||
my_strcasecmp(system_charset_info, host,
thd->security_ctx->priv_host)))
{
if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
return(1);
}
size_t len= strlen(new_password);
if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
{
my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
return -1;
return 1;
}
return(0);
return 0;
}
@ -2669,6 +2715,152 @@ end:
DBUG_RETURN(result);
}
int acl_check_set_default_role(THD *thd, const char *host, const char *user)
{
return check_alter_user(thd, host, user);
}
int acl_set_default_role(THD *thd, const char *host, const char *user,
const char *rolename)
{
TABLE_LIST tables;
TABLE *table;
char user_key[MAX_KEY_LENGTH];
int result= 1;
int error;
bool clear_role= FALSE;
Rpl_filter *rpl_filter;
enum_binlog_format save_binlog_format;
DBUG_ENTER("acl_set_default_role");
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(host, user, rolename, NULL))
DBUG_RETURN(result);
if (!strcasecmp(rolename, "NONE"))
clear_role= TRUE;
tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
#ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in/exclusion rules as they are
some kind of updates to the mysql.% tables.
*/
if (thd->slave_thread &&
(rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter)->is_on())
{
/*
The tables must be marked "updating" so that tables_ok() takes them into
account in tests. It's ok to leave 'updating' set after tables_ok.
*/
tables.updating= 1;
/* Thanks to bzero, tables.next==0 */
if (!(thd->spcont || rpl_filter->tables_ok(0, &tables)))
DBUG_RETURN(0);
}
#endif
if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
DBUG_RETURN(1);
/*
This statement will be replicated as a statement, even when using
row-based replication. The flag will be reset at the end of the
statement.
This has to be handled here as it's called by set_var.cc, which is
not automaticly handled by sql_parse.cc
*/
save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
mysql_mutex_lock(&acl_cache->lock);
ACL_USER *acl_user;
if (!(acl_user= find_user_exact(host, user)))
{
mysql_mutex_unlock(&acl_cache->lock);
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
goto end;
}
if (!clear_role) {
/* set new default_rolename */
acl_user->default_rolename.str= safe_strdup_root(&acl_memroot, rolename);
acl_user->default_rolename.length= strlen(rolename);
}
else
{
/* clear the default_rolename */
acl_user->default_rolename.str = NULL;
acl_user->default_rolename.length = 0;
}
/* update the mysql.user table with the new default role */
table->use_all_columns();
if (table->s->fields < 45)
{
my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
table->alias.c_ptr(), DEFAULT_ROLE_COLUMN_IDX + 1, table->s->fields,
static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
mysql_mutex_unlock(&acl_cache->lock);
goto end;
}
table->field[0]->store(host,(uint) strlen(host), system_charset_info);
table->field[1]->store(user,(uint) strlen(user), system_charset_info);
key_copy((uchar *) user_key, table->record[0], table->key_info,
table->key_info->key_length);
if (table->file->ha_index_read_idx_map(table->record[0], 0,
(uchar *) user_key, HA_WHOLE_KEY,
HA_READ_KEY_EXACT))
{
mysql_mutex_unlock(&acl_cache->lock);
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
goto end;
}
store_record(table, record[1]);
table->field[DEFAULT_ROLE_COLUMN_IDX]->store(acl_user->default_rolename.str,
acl_user->default_rolename.length,
system_charset_info);
if ((error=table->file->ha_update_row(table->record[1],table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
{
mysql_mutex_unlock(&acl_cache->lock);
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
goto end;
}
acl_cache->clear(1);
mysql_mutex_unlock(&acl_cache->lock);
result= 0;
if (mysql_bin_log.is_open())
{
char buff[512];
int query_length=
sprintf(buff,"SET DEFAULT ROLE '%-.120s' FOR '%-.120s'@'%-.120s'",
safe_str(acl_user->default_rolename.str),
safe_str(acl_user->user.str),
safe_str(acl_user->host.hostname));
thd->clear_error();
result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
FALSE, FALSE, FALSE, 0);
}
end:
close_mysql_tables(thd);
thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(result);
}
/*
Find user in ACL
@ -12075,6 +12267,22 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
*/
sctx->db_access=0;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
In case the user has a default role set, attempt to set that role
*/
if (acl_user->default_rolename.length) {
ulonglong access= 0;
int result;
result= acl_check_setrole(thd, acl_user->default_rolename.str, &access);
if (!result)
result= acl_setrole(thd, acl_user->default_rolename.str, access);
if (result)
thd->clear_error(); // even if the default role was not granted, do not
// close the connection
}
#endif
/* Change a database if necessary */
if (mpvio.db.length)
{