in sql/auth/sql_user.cc [1341:2045]
bool set_and_validate_user_attributes(
THD *thd, LEX_USER *Str, acl_table::Pod_user_what_to_update &what_to_set,
bool is_privileged_user, bool is_role, Table_ref *history_table,
bool *history_check_done, const char *cmd,
Userhostpassword_list &generated_passwords, I_multi_factor_auth **i_mfa,
bool if_not_exists) {
bool user_exists = false;
ACL_USER *acl_user;
plugin_ref plugin = nullptr;
char outbuf[MAX_FIELD_WIDTH] = {0};
unsigned int buflen = MAX_FIELD_WIDTH, inbuflen;
const char *inbuf;
const char *password = nullptr;
const enum_sql_command command = thd->lex->sql_command;
bool current_password_empty = false;
bool new_password_empty = false;
char new_password[MAX_FIELD_WIDTH]{0};
unsigned int new_password_length = 0;
authentication_policy::Factors policy_factors;
authentication_policy::get_policy_factors(policy_factors);
what_to_set.m_what = NONE_ATTR;
what_to_set.m_user_attributes = acl_table::USER_ATTRIBUTE_NONE;
assert(assert_acl_cache_read_lock(thd) || assert_acl_cache_write_lock(thd));
if (history_check_done) *history_check_done = false;
/* update plugin,auth str attributes */
if (Str->first_factor_auth_info.uses_identified_by_clause ||
Str->first_factor_auth_info.uses_identified_with_clause ||
Str->first_factor_auth_info.uses_authentication_string_clause)
what_to_set.m_what |= PLUGIN_ATTR;
else
what_to_set.m_what |= DEFAULT_AUTH_ATTR;
/* update ssl attributes */
if (thd->lex->ssl_type != SSL_TYPE_NOT_SPECIFIED)
what_to_set.m_what |= SSL_ATTR;
/* update connection attributes */
if (thd->lex->mqh.specified_limits) what_to_set.m_what |= RESOURCE_ATTR;
if ((acl_user = find_acl_user(Str->host.str, Str->user.str, true)))
user_exists = true;
/* copy password expire attributes to individual user */
Str->alter_status = thd->lex->alter_password;
if ((!user_exists && thd->lex->ignore_unknown_user) ||
(user_exists && thd->lex->grant_if_exists)) {
/*
REVOKE IF EXISTS ... with missing privilege AND
REVOKE ... IGNORE UNKNOWN USER with missing user account
should be a no-op and be ignored.
*/
if (command == SQLCOM_REVOKE) {
what_to_set.m_what = NONE_ATTR;
return false;
}
}
if (user_exists && if_not_exists) {
/*
CREATE USER/ROLE IF NOT EXISTS ... when the account exists
should be a no-op and be ignored.
*/
assert(command == SQLCOM_CREATE_USER || command == SQLCOM_CREATE_ROLE);
/* use the default plugin when it's not supplied */
if (!Str->first_factor_auth_info.uses_identified_with_clause)
authentication_policy::get_first_factor_default_plugin(
thd->mem_root, &Str->first_factor_auth_info.plugin);
/*
Make sure the hashed credentials are set so the statement is logged
correctly. We need the plugin reference for this.
*/
st_mysql_auth *auth = nullptr;
assert(plugin == nullptr);
if (Str->first_factor_auth_info.uses_identified_by_clause ||
Str->first_factor_auth_info.uses_authentication_string_clause) {
plugin =
my_plugin_lock_by_name(nullptr, Str->first_factor_auth_info.plugin,
MYSQL_AUTHENTICATION_PLUGIN);
/* check if plugin is loaded */
if (!plugin) {
what_to_set.m_what = NONE_ATTR;
my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0),
Str->first_factor_auth_info.plugin.str);
return true;
}
auth = (st_mysql_auth *)plugin_decl(plugin)->info;
}
if (Str->first_factor_auth_info.uses_identified_by_clause) {
inbuf = Str->first_factor_auth_info.auth.str;
inbuflen = (unsigned)Str->first_factor_auth_info.auth.length;
if (auth->generate_authentication_string(outbuf, &buflen, inbuf,
inbuflen)) {
plugin_unlock(nullptr, plugin);
what_to_set.m_what = NONE_ATTR;
/*
generate_authentication_string may return error status
without setting actual error.
*/
if (!thd->is_error()) {
String error_user;
log_user(thd, &error_user, Str, false);
my_error(ER_CANNOT_USER, MYF(0), cmd, error_user.c_ptr_safe());
}
return true;
}
password = strmake_root(thd->mem_root, outbuf, buflen);
Str->first_factor_auth_info.auth = {password, buflen};
} else if (Str->first_factor_auth_info.uses_authentication_string_clause) {
assert(!is_role);
if (auth->validate_authentication_string(
const_cast<char *>(Str->first_factor_auth_info.auth.str),
(unsigned)Str->first_factor_auth_info.auth.length)) {
my_error(ER_PASSWORD_FORMAT, MYF(0));
plugin_unlock(nullptr, plugin);
what_to_set.m_what = NONE_ATTR;
return true;
}
}
if (plugin) plugin_unlock(nullptr, plugin);
what_to_set.m_what = NONE_ATTR;
return false;
}
mysql_mutex_lock(&LOCK_password_history);
Str->alter_status.password_history_length =
Str->alter_status.use_default_password_history
? global_password_history
: Str->alter_status.password_history_length;
mysql_mutex_unlock(&LOCK_password_history);
mysql_mutex_lock(&LOCK_password_reuse_interval);
Str->alter_status.password_reuse_interval =
Str->alter_status.use_default_password_reuse_interval
? global_password_reuse_interval
: Str->alter_status.password_reuse_interval;
mysql_mutex_unlock(&LOCK_password_reuse_interval);
/* update password expire attributes */
if (Str->alter_status.update_password_expired_column ||
!Str->alter_status.use_default_password_lifetime ||
Str->alter_status.expire_after_days)
what_to_set.m_what |= PASSWORD_EXPIRE_ATTR;
/* update account lock attribute */
if (Str->alter_status.update_account_locked_column)
what_to_set.m_what |= ACCOUNT_LOCK_ATTR;
if (Str->first_factor_auth_info.plugin.length)
optimize_plugin_compare_by_pointer(&Str->first_factor_auth_info.plugin);
if (user_exists) {
switch (command) {
case SQLCOM_CREATE_USER: {
/*
Since user exists, we are likely going to fail
unless IF NOT EXISTS is specified. In that case
we need to use default plugin to generate password
so that binlog entry is correct.
*/
if (!Str->first_factor_auth_info.uses_identified_with_clause)
authentication_policy::get_first_factor_default_plugin(
thd->mem_root, &Str->first_factor_auth_info.plugin);
break;
}
case SQLCOM_ALTER_USER: {
if (!Str->first_factor_auth_info.uses_identified_with_clause) {
/* If no plugin is given, get existing plugin */
Str->first_factor_auth_info.plugin = acl_user->plugin;
} else if (!(Str->first_factor_auth_info.uses_identified_by_clause ||
Str->first_factor_auth_info
.uses_authentication_string_clause) &&
auth_plugin_supports_expiration(
Str->first_factor_auth_info.plugin.str)) {
/*
This is an attempt to change existing users authentication plugin
without specifying any password. In such cases, expire user's
password so we can force password change on next login
*/
Str->alter_status.update_password_expired_column = true;
what_to_set.m_what |= PASSWORD_EXPIRE_ATTR;
}
/*
always check for password expire/interval attributes as there is no
way to differentiate NEVER EXPIRE and EXPIRE DEFAULT scenario
*/
if (Str->alter_status.update_password_expired_fields)
what_to_set.m_what |= PASSWORD_EXPIRE_ATTR;
/* detect changes in the plugin name */
if (Str->first_factor_auth_info.plugin.str != acl_user->plugin.str) {
what_to_set.m_what |= DIFFERENT_PLUGIN_ATTR;
if (Str->retain_current_password) {
my_error(ER_PASSWORD_CANNOT_BE_RETAINED_ON_PLUGIN_CHANGE, MYF(0),
Str->user.str, Str->host.str);
what_to_set.m_what = NONE_ATTR;
return true;
}
if (acl_user->credentials[SECOND_CRED].m_auth_string.length) {
what_to_set.m_what |= USER_ATTRIBUTES;
what_to_set.m_user_attributes |=
acl_table::USER_ATTRIBUTE_DISCARD_PASSWORD;
}
}
if (Str->retain_current_password || Str->discard_old_password) {
assert(!(Str->retain_current_password && Str->discard_old_password));
what_to_set.m_what |= USER_ATTRIBUTES;
if (Str->retain_current_password)
what_to_set.m_user_attributes |=
acl_table::USER_ATTRIBUTE_RETAIN_PASSWORD;
if (Str->discard_old_password)
what_to_set.m_user_attributes |=
acl_table::USER_ATTRIBUTE_DISCARD_PASSWORD;
current_password_empty =
acl_user->credentials[PRIMARY_CRED].m_auth_string.length ? false
: true;
}
break;
}
case SQLCOM_SET_PASSWORD: {
if (Str->retain_current_password) {
what_to_set.m_what |= USER_ATTRIBUTES;
what_to_set.m_user_attributes |=
acl_table::USER_ATTRIBUTE_RETAIN_PASSWORD;
current_password_empty =
acl_user->credentials[PRIMARY_CRED].m_auth_string.length ? false
: true;
}
break;
}
/*
We need to fill in the elements of the LEX_USER structure even for
GRANT and REVOKE.
*/
case SQLCOM_GRANT:
[[fallthrough]];
case SQLCOM_REVOKE:
what_to_set.m_what = NONE_ATTR;
Str->first_factor_auth_info.plugin = acl_user->plugin;
Str->first_factor_auth_info.auth.str =
acl_user->credentials[PRIMARY_CRED].m_auth_string.str;
Str->first_factor_auth_info.auth.length =
acl_user->credentials[PRIMARY_CRED].m_auth_string.length;
break;
default: {
if (!Str->first_factor_auth_info.uses_identified_with_clause) {
/* if IDENTIFIED WITH is not specified set plugin from cache */
Str->first_factor_auth_info.plugin = acl_user->plugin;
/* set auth str from cache when not specified for existing user */
if (!(Str->first_factor_auth_info.uses_identified_by_clause ||
Str->first_factor_auth_info
.uses_authentication_string_clause)) {
Str->first_factor_auth_info.auth.str =
acl_user->credentials[PRIMARY_CRED].m_auth_string.str;
Str->first_factor_auth_info.auth.length =
acl_user->credentials[PRIMARY_CRED].m_auth_string.length;
}
} else if (!(Str->first_factor_auth_info.uses_identified_by_clause ||
Str->first_factor_auth_info
.uses_authentication_string_clause) &&
auth_plugin_supports_expiration(
Str->first_factor_auth_info.plugin.str)) {
/*
This is an attempt to change existing users authentication plugin
without specifying any password. In such cases, expire user's
password so we can force password change on next login
*/
Str->alter_status.update_password_expired_column = true;
what_to_set.m_what |= PASSWORD_EXPIRE_ATTR;
}
}
};
/* if we don't update password history take the user's password history */
if (!Str->alter_status.update_password_history) {
if (acl_user->use_default_password_history) {
mysql_mutex_lock(&LOCK_password_history);
Str->alter_status.password_history_length = global_password_history;
mysql_mutex_unlock(&LOCK_password_history);
} else
Str->alter_status.password_history_length =
acl_user->password_history_length;
}
/* if we don't update password reuse interval take the user's interval */
if (!Str->alter_status.update_password_reuse_interval) {
if (acl_user->use_default_password_reuse_interval) {
mysql_mutex_lock(&LOCK_password_reuse_interval);
Str->alter_status.password_reuse_interval =
global_password_reuse_interval;
mysql_mutex_unlock(&LOCK_password_reuse_interval);
} else
Str->alter_status.password_reuse_interval =
acl_user->password_reuse_interval;
}
} else { /* User does not exist */
if (add_default_auth_plugins(thd, Str, policy_factors)) return true;
if (command == SQLCOM_GRANT) {
my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
return true;
}
}
optimize_plugin_compare_by_pointer(&Str->first_factor_auth_info.plugin);
/*
Check if non-default password expiraition option
is passed to a plugin that does not support it and raise
and error if it is.
*/
if (Str->alter_status.update_password_expired_fields &&
!Str->alter_status.use_default_password_lifetime &&
Str->alter_status.expire_after_days != 0 &&
!auth_plugin_supports_expiration(
Str->first_factor_auth_info.plugin.str)) {
my_error(ER_PASSWORD_EXPIRATION_NOT_SUPPORTED_BY_AUTH_METHOD, MYF(0),
Str->first_factor_auth_info.plugin.length,
Str->first_factor_auth_info.plugin.str);
return true;
}
plugin = my_plugin_lock_by_name(nullptr, Str->first_factor_auth_info.plugin,
MYSQL_AUTHENTICATION_PLUGIN);
/* check if plugin is loaded */
if (!plugin) {
what_to_set.m_what = NONE_ATTR;
my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0),
Str->first_factor_auth_info.plugin.str);
return (true);
}
st_mysql_auth *auth = (st_mysql_auth *)plugin_decl(plugin)->info;
if (user_exists && (what_to_set.m_what & PLUGIN_ATTR)) {
if (auth->authentication_flags &
AUTH_FLAG_PRIVILEGED_USER_FOR_PASSWORD_CHANGE) {
if (!is_privileged_user &&
(command == SQLCOM_ALTER_USER || command == SQLCOM_GRANT)) {
/*
An external plugin that prevents user
to change authentication_string information
unless user is privileged.
*/
what_to_set.m_what = NONE_ATTR;
my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
thd->security_context()->priv_user().str,
thd->security_context()->priv_host().str,
thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
plugin_unlock(nullptr, plugin);
return (true);
}
}
if (!(auth->authentication_flags & AUTH_FLAG_USES_INTERNAL_STORAGE) &&
command == SQLCOM_SET_PASSWORD) {
/*
A plugin that does not use internal storage and
hence does not support SET PASSWORD
*/
my_error(ER_SET_PASSWORD_AUTH_PLUGIN_ERROR, MYF(0), Str->user.str,
Str->host.str);
plugin_unlock(nullptr, plugin);
what_to_set.m_what = NONE_ATTR;
return (true);
}
}
if (!(auth->authentication_flags & AUTH_FLAG_USES_INTERNAL_STORAGE)) {
if (Str->alter_status.password_history_length ||
Str->alter_status.password_reuse_interval) {
/*
A plugin that does not use internal storage and
hence does not support password history is passed a password history
*/
if (Str->alter_status.update_password_history ||
Str->alter_status.update_password_reuse_interval)
push_warning_printf(
thd, Sql_condition::SL_WARNING,
ER_WARNING_PASSWORD_HISTORY_CLAUSES_VOID,
ER_THD(thd, ER_WARNING_PASSWORD_HISTORY_CLAUSES_VOID),
Str->user.str, Str->host.str, plugin_decl(plugin)->name);
/* reset back the password history clauses for that user */
Str->alter_status.password_history_length = 0;
Str->alter_status.password_reuse_interval = 0;
Str->alter_status.update_password_history = true;
Str->alter_status.update_password_reuse_interval = true;
Str->alter_status.use_default_password_history = true;
Str->alter_status.use_default_password_reuse_interval = true;
}
}
if ((auth->authentication_flags & AUTH_FLAG_REQUIRES_REGISTRATION) &&
command == SQLCOM_CREATE_USER) {
/*
Plugin which requires registration step is not allowed as part of first
factor auth method if user does not have PASSWORDLESS_USER_ADMIN privilege
*/
if (!(thd->security_context()
->has_global_grant(STRING_WITH_LEN("PASSWORDLESS_USER_ADMIN"))
.first)) {
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
"PASSWORDLESS_USER_ADMIN");
plugin_unlock(nullptr, plugin);
what_to_set.m_what = NONE_ATTR;
return (false);
}
}
/*
If auth string is specified, change it to hash.
Validate empty credentials for new user ex: CREATE USER u1;
We skip authentication string generation if the issued statement was
CREATE ROLE.
*/
if (!is_role &&
(Str->first_factor_auth_info.uses_identified_by_clause ||
(Str->first_factor_auth_info.auth.length == 0 && !user_exists))) {
inbuf = Str->first_factor_auth_info.auth.str;
inbuflen = (unsigned)Str->first_factor_auth_info.auth.length;
std::string gen_password;
if (Str->first_factor_auth_info.has_password_generator) {
thd->m_disable_password_validation = true;
generate_random_password(&gen_password,
thd->variables.generated_random_password_length);
inbuf = gen_password.c_str();
inbuflen = gen_password.length();
random_password_info p{std::string(Str->user.str),
std::string(Str->host.str), gen_password, 1};
generated_passwords.push_back(p);
}
if (auth->generate_authentication_string(outbuf, &buflen, inbuf,
inbuflen) ||
auth_verify_password_history(thd, &Str->user, &Str->host,
Str->alter_status.password_history_length,
Str->alter_status.password_reuse_interval,
auth, inbuf, inbuflen, outbuf, buflen,
history_table, what_to_set.m_what)) {
plugin_unlock(nullptr, plugin);
what_to_set.m_what = NONE_ATTR;
/*
generate_authentication_string may return error status
without setting actual error.
*/
if (!thd->is_error()) {
String error_user;
log_user(thd, &error_user, Str, false);
my_error(ER_CANNOT_USER, MYF(0), cmd, error_user.c_ptr_safe());
}
return (true);
}
/* Allow for password validation in case it was disabled before */
thd->m_disable_password_validation = false;
if (history_check_done) *history_check_done = true;
if (buflen) {
password = strmake_root(thd->mem_root, outbuf, buflen);
} else
password = const_cast<char *>("");
/*
Erase in memory copy of plain text password, unless we need it
later to send to client as a result set.
*/
if (Str->first_factor_auth_info.auth.length > 0) {
if (user_exists && Str->uses_replace_clause) {
assert(Str->first_factor_auth_info.auth.length < MAX_FIELD_WIDTH);
new_password_length = Str->first_factor_auth_info.auth.length;
strncpy(new_password, Str->first_factor_auth_info.auth.str,
std::min(static_cast<size_t>(MAX_FIELD_WIDTH),
Str->first_factor_auth_info.auth.length));
}
my_cleanse(const_cast<char *>(Str->first_factor_auth_info.auth.str),
Str->first_factor_auth_info.auth.length);
}
/* Use the authentication_string field as password */
Str->first_factor_auth_info.auth = {password, buflen};
new_password_empty = buflen ? false : true;
}
/* Check iff the REPLACE clause is specified correctly for the user */
if ((what_to_set.m_what & PLUGIN_ATTR) &&
validate_password_require_current(thd, Str, acl_user, auth, new_password,
new_password_length, is_privileged_user,
user_exists)) {
plugin_unlock(nullptr, plugin);
what_to_set.m_what = NONE_ATTR;
return (true);
}
/* Validate hash string */
if (Str->first_factor_auth_info.uses_authentication_string_clause) {
/*
The statement CREATE ROLE calls mysql_create_user() with a set of
lexicographic parameters: users_identified_by_password_caluse= false etc
It also sets is_role= true. We don't have to check this parameter here
since we're already know that the above parameters will be false
but we place an extra assert here to remind us about the complex
interdependencies if mysql_create_user() is refactored.
*/
assert(!is_role);
if (auth->validate_authentication_string(
const_cast<char *>(Str->first_factor_auth_info.auth.str),
(unsigned)Str->first_factor_auth_info.auth.length)) {
my_error(ER_PASSWORD_FORMAT, MYF(0));
plugin_unlock(nullptr, plugin);
what_to_set.m_what = NONE_ATTR;
return (true);
}
/*
Call the password history validation so that it can store the incoming
hash into the password history table.
Here we can't check if the password was used since we don't have the
cleartext password, but we still want to record it into the history table.
Covers replication scenario too since the IDENTIFIED BY will get
rewritten to IDENTIFIED ... WITH ... AS
*/
if (auth_verify_password_history(
thd, &Str->user, &Str->host,
Str->alter_status.password_history_length,
Str->alter_status.password_reuse_interval, auth, nullptr, 0,
Str->first_factor_auth_info.auth.str,
(unsigned)Str->first_factor_auth_info.auth.length, history_table,
what_to_set.m_what)) {
/* we should have an error generated here already */
plugin_unlock(nullptr, plugin);
what_to_set.m_what = NONE_ATTR;
return (true);
}
if (history_check_done) *history_check_done = true;
}
if (user_exists && (what_to_set.m_user_attributes &
(acl_table::USER_ATTRIBUTE_RETAIN_PASSWORD |
acl_table::USER_ATTRIBUTE_DISCARD_PASSWORD))) {
if (!(auth->authentication_flags & AUTH_FLAG_USES_INTERNAL_STORAGE)) {
/* We do not support multiple passwords */
if (Str->retain_current_password) {
push_warning_printf(
thd, Sql_condition::SL_WARNING,
ER_WARNING_RETAIN_CURRENT_PASSWORD_CLAUSE_VOID,
ER_THD(thd, ER_WARNING_RETAIN_CURRENT_PASSWORD_CLAUSE_VOID),
Str->user.str, Str->host.str, plugin_decl(plugin)->name);
what_to_set.m_user_attributes &=
~acl_table::USER_ATTRIBUTE_RETAIN_PASSWORD;
}
if (Str->discard_old_password) {
push_warning_printf(
thd, Sql_condition::SL_WARNING,
ER_WARNING_DISCARD_OLD_PASSWORD_CLAUSE_VOID,
ER_THD(thd, ER_WARNING_DISCARD_OLD_PASSWORD_CLAUSE_VOID),
Str->user.str, Str->host.str, plugin_decl(plugin)->name);
what_to_set.m_user_attributes &=
~acl_table::USER_ATTRIBUTE_DISCARD_PASSWORD;
}
} else if (what_to_set.m_user_attributes &
acl_table::USER_ATTRIBUTE_RETAIN_PASSWORD) {
if (current_password_empty) {
my_error(ER_SECOND_PASSWORD_CANNOT_BE_EMPTY, MYF(0), Str->user.str,
Str->host.str);
plugin_unlock(nullptr, plugin);
what_to_set.m_what = NONE_ATTR;
return true;
}
if (what_to_set.m_what & PLUGIN_ATTR && new_password_empty) {
my_error(ER_CURRENT_PASSWORD_CANNOT_BE_RETAINED, MYF(0), Str->user.str,
Str->host.str);
plugin_unlock(nullptr, plugin);
what_to_set.m_what = NONE_ATTR;
return true;
}
}
}
if (user_exists && (what_to_set.m_what & PLUGIN_ATTR) &&
!Str->first_factor_auth_info.auth.length &&
(auth->authentication_flags & AUTH_FLAG_USES_INTERNAL_STORAGE)) {
if (acl_user->credentials[SECOND_CRED].m_auth_string.length) {
what_to_set.m_what |= USER_ATTRIBUTES;
what_to_set.m_user_attributes |=
acl_table::USER_ATTRIBUTE_DISCARD_PASSWORD;
}
}
if (Str->alter_status.update_failed_login_attempts) {
what_to_set.m_what |= USER_ATTRIBUTES;
what_to_set.m_user_attributes |=
acl_table::USER_ATTRIBUTE_FAILED_LOGIN_ATTEMPTS;
}
if (Str->alter_status.update_password_lock_time) {
what_to_set.m_what |= USER_ATTRIBUTES;
what_to_set.m_user_attributes |=
acl_table::USER_ATTRIBUTE_PASSWORD_LOCK_TIME;
}
/*
We issued a ALTER USER x ATTRIBUTE or COMMENT statement and need
to update the user attributes.
*/
if (thd->lex->alter_user_attribute !=
enum_alter_user_attribute::ALTER_USER_COMMENT_NOT_USED) {
what_to_set.m_what |= USER_ATTRIBUTES;
}
plugin_unlock(nullptr, plugin);
if (check_for_authentication_policy(thd, Str, command,
(acl_user ? acl_user->m_mfa : nullptr),
policy_factors))
return true;
/* initialize MFA */
if (Str->mfa_list.size()) {
if (user_exists && command == SQLCOM_CREATE_USER) return false;
/* Update Multi factor authentication details */
I_multi_factor_auth *mfa = *i_mfa;
LEX_MFA *tmp_lex_mfa;
List_iterator<LEX_MFA> mfa_list_it(Str->mfa_list);
mfa = new (&global_acl_memory) Multi_factor_auth_list(&global_acl_memory);
while ((tmp_lex_mfa = mfa_list_it++))
mfa->add_factor(new (&global_acl_memory) Multi_factor_auth_info(
&global_acl_memory, tmp_lex_mfa));
if (user_exists) {
if (acl_user->m_mfa) {
if (acl_user->m_mfa->is_alter_allowed(thd, Str)) return true;
/*
copy attributes from in memory copy of ACL_USER::m_mfa to new Multi
factor authentication method interface.
*/
acl_user->m_mfa->alter_mfa(mfa);
} else {
MEM_ROOT mr(PSI_NOT_INSTRUMENTED, 256);
I_multi_factor_auth *tmp = new (&mr) Multi_factor_auth_list(&mr);
if (tmp->is_alter_allowed(thd, Str)) {
mr.Clear();
return true;
}
}
/* perform regestration step */
mfa_list_it = Str->mfa_list;
while ((tmp_lex_mfa = mfa_list_it++)) {
if (tmp_lex_mfa->init_registration) {
/* initiate registration step */
if (mfa->init_registration(thd, tmp_lex_mfa->nth_factor)) return true;
break;
} else if (tmp_lex_mfa->finish_registration) {
/* finish registration step */
if (mfa->finish_registration(thd, Str, tmp_lex_mfa->nth_factor))
return true;
}
if (mfa->is_passwordless()) {
/* save auth string in LEX_USER::mfa_list before replacing */
mfa->get_info_for_query_rewrite(thd, Str);
/*
On replica, ALTER USER ... FINISH REGISTRATION is converted to
ALTER USER .. MODIFY 2 FACTOR IDENTIFIED WITH ..., thus copy
plugin and auth string into first factor auth method
*/
if (tmp_lex_mfa->modify_factor) {
lex_string_strmake(
thd->mem_root, &Str->first_factor_auth_info.plugin,
tmp_lex_mfa->plugin.str, tmp_lex_mfa->plugin.length);
lex_string_strmake(thd->mem_root, &Str->first_factor_auth_info.auth,
tmp_lex_mfa->auth.str, tmp_lex_mfa->auth.length);
}
what_to_set.m_what |= PLUGIN_ATTR;
mfa = nullptr;
}
}
/* if no mfa methods exists, set mfa to nullptr */
if (mfa && mfa->get_multi_factor_auth_list()->get_mfa_list_size() == 0)
mfa = nullptr;
}
if (mfa) {
/* validate auth plugins in Multi factor authentication methods */
if (mfa->validate_plugins_in_auth_chain(thd, policy_factors)) return true;
/*
Once alter is done check that new mfa methods are inline with
authentication policy.
*/
if (command == SQLCOM_ALTER_USER &&
mfa->validate_against_authentication_policy(thd, policy_factors))
return true;
/*
Fill in details related to Multi factor authentication methods into
LEX_USER::mfa_list, to be consumed by query rewrite methods.
*/
mfa->get_info_for_query_rewrite(thd, Str);
}
*i_mfa = mfa;
/* update MFA details in user attributes */
what_to_set.m_what |= USER_ATTRIBUTES;
what_to_set.m_user_attributes |= acl_table::USER_ATTRIBUTE_UPDATE_MFA;
} else {
/* Update i_mfa in case Multi factor auth methods are not touched */
if (user_exists && i_mfa) *i_mfa = acl_user->m_mfa;
}
return (false);
}