summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/set_var.cc24
-rw-r--r--sql/set_var.h12
-rw-r--r--sql/share/errmsg-utf8.txt10
-rw-r--r--sql/sql_acl.cc252
-rw-r--r--sql/sql_acl.h3
-rw-r--r--sql/sql_yacc.yy30
6 files changed, 301 insertions, 30 deletions
diff --git a/sql/set_var.cc b/sql/set_var.cc
index ea577bbfa74..5c1e00af33e 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -852,6 +852,7 @@ int set_var_password::update(THD *thd)
/*****************************************************************************
Functions to handle SET ROLE
*****************************************************************************/
+
int set_var_role::check(THD *thd)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -871,6 +872,29 @@ int set_var_role::update(THD *thd)
#endif
}
+/*****************************************************************************
+ Functions to handle SET DEFAULT ROLE
+*****************************************************************************/
+
+int set_var_default_role::check(THD *thd)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ user= get_current_user(thd, user);
+ int status= acl_check_set_default_role(thd, user->host.str, user->user.str);
+ return status;
+#else
+ return 0;
+#endif
+}
+
+int set_var_default_role::update(THD *thd)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ return acl_set_default_role(thd, user->host.str, user->user.str, role.str);
+#else
+ return 0;
+#endif
+}
/*****************************************************************************
Functions to handle SET NAMES and SET CHARACTER SET
diff --git a/sql/set_var.h b/sql/set_var.h
index 564dac587d9..bb92e555aa7 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -291,6 +291,18 @@ public:
int update(THD *thd);
};
+/* For SET DEFAULT ROLE */
+
+class set_var_default_role: public set_var_base
+{
+ LEX_USER *user;
+ LEX_STRING role;
+public:
+ set_var_default_role(LEX_USER *user_arg, LEX_STRING role_arg) :
+ user(user_arg), role(role_arg) {}
+ int check(THD *thd);
+ int update(THD *thd);
+};
/* For SET NAMES and SET CHARACTER SET */
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 6ee6698bb10..32cdbe138b2 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -2936,7 +2936,7 @@ ER_PASSWORD_ANONYMOUS_USER 42000
cze "Používáte MySQL jako anonymní uživatel a anonymní uživatelé nemají povoleno měnit hesla"
dan "Du bruger MariaDB som anonym bruger. Anonyme brugere må ikke ændre adgangskoder"
nla "U gebruikt MariaDB als anonieme gebruiker en deze mogen geen wachtwoorden wijzigen"
- eng "You are using MariaDB as an anonymous user and anonymous users are not allowed to change passwords"
+ eng "You are using MariaDB as an anonymous user and anonymous users are not allowed to modify user settings"
est "Te kasutate MariaDB-i anonüümse kasutajana, kelledel pole parooli muutmise õigust"
fre "Vous utilisez un utilisateur anonyme et les utilisateurs anonymes ne sont pas autorisés à changer les mots de passe"
ger "Sie benutzen MariaDB als anonymer Benutzer und dürfen daher keine Passwörter ändern"
@@ -2946,7 +2946,7 @@ ER_PASSWORD_ANONYMOUS_USER 42000
jpn "MySQL を匿名ユーザーで使用しているので、パスワードの変更はできません。"
kor "당신은 MariaDB서버에 익명의 사용자로 접속을 하셨습니다.익명의 사용자는 암호를 변경할 수 없습니다."
por "Você está usando o MariaDB como usuário anônimo e usuários anônimos não têm permissão para mudar senhas"
- rum "Dumneavoastra folositi MariaDB ca un utilizator anonim si utilizatorii anonimi nu au voie sa schimbe parolele"
+ rum "Dumneavoastra folositi MariaDB ca un utilizator anonim si utilizatorii anonimi nu au voie sa schimbe setarile utilizatorilor."
rus "Вы используете MariaDB от имени анонимного пользователя, а анонимным пользователям не разрешается менять пароли"
serbian "Vi koristite MariaDB kao anonimni korisnik a anonimnim korisnicima nije dozvoljeno da menjaju lozinke"
spa "Tu estás usando MariaDB como un usuario anonimo y usuarios anonimos no tienen permiso para cambiar las claves"
@@ -7110,9 +7110,3 @@ ER_IT_IS_A_VIEW 42S02
eng "'%-.192s' is a view"
ER_SLAVE_SKIP_NOT_IN_GTID
eng "When using GTID, @@sql_slave_skip_counter can not be used. Instead, setting @@gtid_slave_pos explicitly can be used to skip to after a given GTID position."
-ER_DEFAULT_ROLE_ANONYMOUS_USER
- eng "You are using MariaDB as an anonymous user and anonymous users are not allowed to change default roles."
- rum "Dumneavoastra folositi MariaDB ca un utilizator anonim si utilizatorii anonimi nu au voie sa schimbe rolurile implicite."
-ER_NO_SUCH_ROLE
- eng "The role specified '%-.64s' does not exist."
- rum "Rolul specificat '%-.64s' nu există."
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index eff05745628..9ad78115604 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -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,41 +2537,40 @@ bool acl_check_host(const char *host, const char *ip)
return 1; // Host is not allowed
}
-
/**
- Check if the user is allowed to change password
+ 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
- @param new_password New password
- @param new_password_len The length of the new password
-
- new_password cannot be NULL
@return Error status
@retval 0 OK
- @retval 1 ERROR; In this case the error is sent to the client.
+ @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_change_password(THD *thd, const char *host, const char *user,
- char *new_password, uint new_password_len)
+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");
- return(1);
+ 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));
- return(1);
+ goto end;
}
if (!host) // Role
{
my_error(ER_PASSWORD_NO_MATCH, MYF(0));
- return 1;
+ goto end;
}
if (!thd->slave_thread &&
(strcmp(thd->security_ctx->priv_user, user) ||
@@ -2560,16 +2578,44 @@ int check_change_password(THD *thd, const char *host, const char *user,
thd->security_ctx->priv_host)))
{
if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
- return(1);
+ goto end;
}
+
+ error = 0;
+
+end:
+ return error;
+}
+/**
+ Check if the user is allowed to change password
+
+ @param thd THD
+ @param host Hostname for the user
+ @param user User name
+ @param new_password New password
+ @param new_password_len The length of the new password
+
+ new_password cannot be NULL
+
+ @return Error status
+ @retval 0 OK
+ @retval 1 ERROR; In this case the error is sent to the client.
+*/
+
+int check_change_password(THD *thd, const char *host, const char *user,
+ char *new_password, uint new_password_len)
+{
+ if (check_alter_user(thd, host, user))
+ 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)
{
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 3f628b5a082..7833fb8736b 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -401,6 +401,9 @@ bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user,
bool with_grant);
int acl_setrole(THD *thd, char *rolename, ulonglong access);
int acl_check_setrole(THD *thd, char *rolename, ulonglong *access);
+int acl_check_set_default_role(THD *thd, const char *host, const char *user);
+int acl_set_default_role(THD *thd, const char *host, const char *user,
+ const char *rolename);
#ifndef DBUG_OFF
extern ulong role_global_merges, role_db_merges, role_table_merges,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index db569ad01f7..a261d611aa6 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -14667,10 +14667,39 @@ option_value_no_option_type:
MYSQL_YYABORT;
lex->var_list.push_back(var);
}
+ | DEFAULT ROLE_SYM grant_role
+ {
+ LEX *lex = Lex;
+ LEX_USER *user;
+ if (!(user=(LEX_USER *) thd->calloc(sizeof(LEX_USER))))
+ MYSQL_YYABORT;
+ user->user= current_user;
+ set_var_default_role *var= new set_var_default_role(user,
+ $3->user);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ lex->var_list.push_back(var);
+ thd->lex->autocommit= TRUE;
+ if (lex->sphead)
+ lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
+ }
+ | DEFAULT ROLE_SYM grant_role FOR_SYM user
+ {
+ LEX *lex = Lex;
+ set_var_default_role *var= new set_var_default_role($5, $3->user);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ lex->var_list.push_back(var);
+ thd->lex->autocommit= TRUE;
+ if (lex->sphead)
+ lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
+ }
| ROLE_SYM ident_or_text
{
LEX *lex = Lex;
set_var_role *var= new set_var_role($2);
+ if (var == NULL)
+ MYSQL_YYABORT;
lex->var_list.push_back(var);
}
| PASSWORD equal text_or_password
@@ -14710,6 +14739,7 @@ option_value_no_option_type:
}
;
+
internal_variable_name:
ident
{