1 /* Copyright (c) 2000, 2020, Oracle and/or its affiliates.
2    This program is free software; you can redistribute it and/or modify
3    it under the terms of the GNU General Public License, version 2.0,
4    as published by the Free Software Foundation.
5 
6    This program is also distributed with certain software (including
7    but not limited to OpenSSL) that is licensed under separate terms,
8    as designated in a particular file or component or in included license
9    documentation.  The authors of MySQL hereby grant you an additional
10    permission to link the program and your derivative works with the
11    separately licensed software that they have included with MySQL.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License, version 2.0, for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
21 
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <algorithm>
26 #include <memory>
27 #include <set>
28 #include <string>
29 #include <unordered_map>
30 #include <utility>
31 #include <vector>
32 
33 #include "lex_string.h"
34 #include "m_ctype.h"
35 #include "m_string.h"
36 #include "map_helpers.h"
37 #include "my_alloc.h"
38 #include "my_base.h"
39 #include "my_compiler.h"
40 #include "my_dbug.h"
41 #include "my_inttypes.h"
42 #include "my_loglevel.h"
43 #include "my_sqlcommand.h"
44 #include "my_sys.h"
45 #include "my_time.h"
46 #include "mysql/components/services/log_builtins.h"
47 #include "mysql/components/services/log_shared.h"
48 #include "mysql/mysql_lex_string.h"
49 #include "mysql/plugin.h"
50 #include "mysql/plugin_audit.h"
51 #include "mysql/plugin_auth.h"
52 #include "mysql/psi/mysql_mutex.h"
53 #include "mysql/psi/psi_base.h"
54 #include "mysql_com.h"
55 #include "mysql_time.h"
56 #include "mysqld_error.h"
57 #include "password.h" /* my_make_scrambled_password */
58 #include "sql/auth/auth_acls.h"
59 #include "sql/auth/auth_common.h"
60 #include "sql/auth/dynamic_privilege_table.h"
61 #include "sql/auth/sql_security_ctx.h"
62 #include "sql/debug_sync.h"  // DEBUG_SYNC
63 #include "sql/field.h"
64 #include "sql/handler.h"
65 #include "sql/item.h"
66 #include "sql/key.h"
67 #include "sql/log_event.h" /* append_query_string */
68 #include "sql/protocol.h"
69 #include "sql/sql_audit.h"
70 #include "sql/sql_base.h"  // open table
71 #include "sql/sql_class.h"
72 #include "sql/sql_connect.h"
73 #include "sql/sql_const.h"
74 #include "sql/sql_error.h"
75 #include "sql/sql_lex.h"
76 #include "sql/sql_list.h"
77 #include "sql/sql_parse.h"  /* check_access */
78 #include "sql/sql_plugin.h" /* lock_plugin_data etc. */
79 #include "sql/sql_plugin_ref.h"
80 #include "sql/strfunc.h"
81 #include "sql/system_variables.h"
82 #include "sql/table.h"
83 #include "sql/thd_raii.h"
84 #include "sql_string.h"
85 #include "violite.h"
86 /* key_restore */
87 
88 #include "prealloced_array.h"
89 #include "sql/auth/auth_internal.h"
90 #include "sql/auth/sql_auth_cache.h"
91 #include "sql/auth/sql_authentication.h"
92 #include "sql/auth/sql_user_table.h"
93 #include "sql/current_thd.h"
94 #include "sql/derror.h" /* ER_THD */
95 #include "sql/log.h"
96 #include "sql/mysqld.h"
97 #include "sql/sql_rewrite.h"
98 
99 #include <openssl/rand.h>  // RAND_bytes
100 /**
101   Auxiliary function for constructing a  user list string.
102   This function is used for error reporting and logging.
103 
104   @param thd     Thread context
105   @param str     A String to store the user list.
106   @param user    A LEX_USER which will be appended into user list.
107   @param comma   If true, append a ',' before the the user.
108  */
log_user(THD * thd,String * str,LEX_USER * user,bool comma=true)109 void log_user(THD *thd, String *str, LEX_USER *user, bool comma = true) {
110   String from_user(user->user.str, user->user.length, system_charset_info);
111   String from_plugin(user->plugin.str, user->plugin.length,
112                      system_charset_info);
113   String from_auth(user->auth.str, user->auth.length, system_charset_info);
114   String from_host(user->host.str, user->host.length, system_charset_info);
115 
116   if (comma) str->append(',');
117   append_query_string(thd, system_charset_info, &from_user, str);
118   str->append(STRING_WITH_LEN("@"));
119   append_query_string(thd, system_charset_info, &from_host, str);
120 }
121 
122 extern bool initialized;
123 
124 /*
125  Enumeration of various ACL's and Hashes used in handle_grant_struct()
126 */
127 enum enum_acl_lists {
128   USER_ACL = 0,
129   DB_ACL,
130   COLUMN_PRIVILEGES_HASH,
131   PROC_PRIVILEGES_HASH,
132   FUNC_PRIVILEGES_HASH,
133   PROXY_USERS_ACL
134 };
135 
check_change_password(THD * thd,const char * host,const char * user,bool retain_current_password)136 bool check_change_password(THD *thd, const char *host, const char *user,
137                            bool retain_current_password) {
138   Security_context *sctx;
139   DBUG_ASSERT(initialized);
140   sctx = thd->security_context();
141   if (!thd->slave_thread &&
142       (strcmp(sctx->user().str, user) ||
143        my_strcasecmp(system_charset_info, host, sctx->priv_host().str))) {
144     if (sctx->password_expired()) {
145       my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
146       return true;
147     }
148     if (check_access(thd, UPDATE_ACL, consts::mysql.c_str(), nullptr, nullptr,
149                      true, false))
150       return true;
151 
152     if (sctx->can_operate_with({user, host}, consts::system_user)) return true;
153   }
154 
155   if (retain_current_password) {
156     if (check_access(thd, UPDATE_ACL, consts::mysql.c_str(), nullptr, nullptr,
157                      true, true) &&
158         !(sctx->check_access(CREATE_USER_ACL, consts::mysql)) &&
159         !(sctx->has_global_grant(STRING_WITH_LEN("APPLICATION_PASSWORD_ADMIN"))
160               .first)) {
161       my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
162                "CREATE USER or APPLICATION_PASSWORD_ADMIN");
163       return true;
164     }
165   }
166 
167   if (!thd->slave_thread && likely((get_server_state() == SERVER_OPERATING)) &&
168       !strcmp(thd->security_context()->priv_user().str, "")) {
169     my_error(ER_PASSWORD_ANONYMOUS_USER, MYF(0));
170     return true;
171   }
172 
173   return false;
174 }
175 
176 /**
177   Auxiliary function for constructing CREATE USER sql for a given user.
178 
179   @param thd                    Thread context
180   @param user_name              user for which the sql should be constructed.
181   @param are_both_users_same    If the command is issued for self or not.
182 
183   @retval
184     0         OK.
185     1         Error.
186  */
187 
mysql_show_create_user(THD * thd,LEX_USER * user_name,bool are_both_users_same)188 bool mysql_show_create_user(THD *thd, LEX_USER *user_name,
189                             bool are_both_users_same) {
190   int error = 0;
191   ACL_USER *acl_user;
192   LEX *lex = thd->lex;
193   Protocol *protocol = thd->get_protocol();
194   USER_RESOURCES tmp_user_resource;
195   enum SSL_type ssl_type;
196   const char *ssl_cipher, *x509_issuer, *x509_subject;
197   static const int COMMAND_BUFFER_LENGTH = 2048;
198   char buff[COMMAND_BUFFER_LENGTH];
199   Item_string *field = nullptr;
200   List<Item> field_list;
201   String sql_text(buff, sizeof(buff), system_charset_info);
202   LEX_ALTER alter_info;
203   List_of_auth_id_refs default_roles;
204   List<LEX_USER> *old_default_roles = lex->default_roles;
205   bool hide_password_hash = false;
206 
207   DBUG_TRACE;
208   TABLE_LIST table_list("mysql", "user", TL_READ, MDL_SHARED_READ_ONLY);
209   if (are_both_users_same) {
210     hide_password_hash =
211         check_table_access(thd, SELECT_ACL, &table_list, false, UINT_MAX, true);
212   }
213 
214   /*
215      Open user table so we later can read the JSON data in the user_attribute
216      field. All tables must be opened before the acl_cache_lock
217   */
218   if (open_and_lock_tables(thd, &table_list, MYSQL_LOCK_IGNORE_TIMEOUT)) {
219     if (!is_expected_or_transient_error(thd)) {
220       LogErr(ERROR_LEVEL, ER_AUTHCACHE_CANT_OPEN_AND_LOCK_PRIVILEGE_TABLES,
221              thd->get_stmt_da()->message_text());
222     }
223     return true;
224   }
225 
226   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
227   if (!acl_cache_lock.lock()) return true;
228 
229   if (!(acl_user =
230             find_acl_user(user_name->host.str, user_name->user.str, true))) {
231     String wrong_users;
232     log_user(thd, &wrong_users, user_name, wrong_users.length() > 0);
233     my_error(ER_CANNOT_USER, MYF(0), "SHOW CREATE USER",
234              wrong_users.c_ptr_safe());
235     return true;
236   }
237   /* fill in plugin, auth_str from acl_user */
238   user_name->auth.str = acl_user->credentials[PRIMARY_CRED].m_auth_string.str;
239   user_name->auth.length =
240       acl_user->credentials[PRIMARY_CRED].m_auth_string.length;
241   user_name->plugin = acl_user->plugin;
242   user_name->uses_identified_by_clause = true;
243   user_name->uses_identified_with_clause = false;
244   user_name->uses_authentication_string_clause = false;
245   user_name->retain_current_password = false;
246   user_name->discard_old_password = false;
247 
248   /* make a copy of user resources, ssl and password expire attributes */
249   tmp_user_resource = lex->mqh;
250   lex->mqh = acl_user->user_resource;
251 
252   /* Set specified_limits flags so user resources are shown properly. */
253   if (lex->mqh.user_conn)
254     lex->mqh.specified_limits |= USER_RESOURCES::USER_CONNECTIONS;
255   if (lex->mqh.questions)
256     lex->mqh.specified_limits |= USER_RESOURCES::QUERIES_PER_HOUR;
257   if (lex->mqh.updates)
258     lex->mqh.specified_limits |= USER_RESOURCES::UPDATES_PER_HOUR;
259   if (lex->mqh.conn_per_hour)
260     lex->mqh.specified_limits |= USER_RESOURCES::CONNECTIONS_PER_HOUR;
261 
262   ssl_type = lex->ssl_type;
263   ssl_cipher = lex->ssl_cipher;
264   x509_issuer = lex->x509_issuer;
265   x509_subject = lex->x509_subject;
266 
267   lex->ssl_type = acl_user->ssl_type;
268   lex->ssl_cipher = acl_user->ssl_cipher;
269   lex->x509_issuer = acl_user->x509_issuer;
270   lex->x509_subject = acl_user->x509_subject;
271 
272   alter_info = lex->alter_password;
273 
274   lex->alter_password.update_password_expired_column =
275       acl_user->password_expired;
276   lex->alter_password.use_default_password_lifetime =
277       acl_user->use_default_password_lifetime;
278   lex->alter_password.expire_after_days = acl_user->password_lifetime;
279   lex->alter_password.update_account_locked_column = true;
280   lex->alter_password.account_locked = acl_user->account_locked;
281   lex->alter_password.update_password_expired_fields = true;
282 
283   lex->alter_password.password_history_length =
284       acl_user->password_history_length;
285   lex->alter_password.use_default_password_history =
286       acl_user->use_default_password_history;
287   lex->alter_password.update_password_history =
288       !acl_user->use_default_password_history;
289 
290   lex->alter_password.password_reuse_interval =
291       acl_user->password_reuse_interval;
292   lex->alter_password.use_default_password_reuse_interval =
293       acl_user->use_default_password_reuse_interval;
294   lex->alter_password.update_password_reuse_interval =
295       !acl_user->use_default_password_reuse_interval;
296   lex->alter_password.update_password_require_current =
297       acl_user->password_require_current;
298 
299   lex->alter_password.failed_login_attempts =
300       acl_user->password_locked_state.get_failed_login_attempts();
301   lex->alter_password.password_lock_time =
302       acl_user->password_locked_state.get_password_lock_time_days();
303 
304   lex->alter_password.update_failed_login_attempts =
305       lex->alter_password.failed_login_attempts != 0;
306   lex->alter_password.update_password_lock_time =
307       lex->alter_password.password_lock_time != 0;
308 
309   /* send the metadata to client */
310   field = new Item_string("", 0, &my_charset_latin1);
311   field->max_length = 256;
312   strxmov(buff, "CREATE USER for ", user_name->user.str, "@",
313           user_name->host.str, NullS);
314   field->item_name.set(buff);
315   field_list.push_back(field);
316   if (thd->send_result_metadata(&field_list,
317                                 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) {
318     error = 1;
319     goto err;
320   }
321   sql_text.length(0);
322   if (lex->sql_command == SQLCOM_SHOW_CREATE_USER ||
323       lex->sql_command == SQLCOM_CREATE_USER) {
324     /*
325       Recreate LEX for default roles given an ACL_USER. This will later be used
326       by rewrite_default_roles() called by Rewriter_show_create_user::rewrite()
327     */
328     get_default_roles(create_authid_from(acl_user), default_roles);
329     if (default_roles.size() > 0) {
330       LEX_STRING *tmp_user = nullptr;
331       LEX_STRING *tmp_host = nullptr;
332       /*
333         Make sure we reallocate the default_roles list when using it outside of
334         parser code so it has the same mem root as its items.
335       */
336       lex->default_roles = new (thd->mem_root) List<LEX_USER>;
337       for (auto &&role : default_roles) {
338         if (!(tmp_user = make_lex_string_root(thd->mem_root, role.first.str,
339                                               role.first.length)) ||
340             !(tmp_host = make_lex_string_root(thd->mem_root, role.second.str,
341                                               role.second.length))) {
342           error = 1;
343           goto err;
344         }
345         LEX_USER *lex_role = LEX_USER::alloc(thd, tmp_user, tmp_host);
346         if (lex_role == nullptr) {
347           error = 1;
348           goto err;
349         }
350         lex->default_roles->push_back(lex_role);
351       }
352     }
353   }
354   lex->users_list.push_back(user_name);
355   {
356     /* Read and extract JSON comments */
357     String metadata_str;
358     if (read_user_application_user_metadata_from_table(
359             user_name->user, user_name->host, &metadata_str, table_list.table,
360             thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)) {
361       error = 1;
362       goto err;
363     }
364     Show_user_params show_user_params(
365         hide_password_hash, thd->variables.print_identified_with_as_hex,
366         &metadata_str);
367     /*
368       By disabling instrumentation, we're requesting a rewrite to our
369       local buffer, sql_text. The value on the THD and those seen in
370       instrumentation remain unchanged.
371     */
372     mysql_rewrite_acl_query(thd, sql_text, Consumer_type::STDOUT,
373                             &show_user_params, false);
374   }
375 
376   /* send the result row to client */
377   protocol->start_row();
378   protocol->store_string(sql_text.ptr(), sql_text.length(), sql_text.charset());
379   if (protocol->end_row()) {
380     error = 1;
381     goto err;
382   }
383 
384 err:
385   commit_and_close_mysql_tables(thd);
386   lex->default_roles = old_default_roles;
387   /* restore user resources, ssl and password expire attributes */
388   lex->mqh = tmp_user_resource;
389   lex->ssl_type = ssl_type;
390   lex->ssl_cipher = ssl_cipher;
391   lex->x509_issuer = x509_issuer;
392   lex->x509_subject = x509_subject;
393 
394   lex->alter_password = alter_info;
395   if (!thd->get_stmt_da()->is_error()) my_eof(thd);
396   return error;
397 }
398 
399 #include "sql/query_result.h"  // Time_zone
400 #include "sql/tztime.h"
401 
402 /**
403   Perform credentials history check and update the password history table
404 
405   Note that the data for the checks are extracted from LEX_USER. So these
406   need to be up to date in all cases.
407 
408   How credential history checks are performed:
409   ~~~
410   count= 0;
411   FOR SELECT * FROM mysql.password_history ORDER BY USER,HOST,TS DESC
412     WHERE USER=::current_user AND HOST=::current_host
413   {
414      if (count >= ::password_history && (NOW() - ts) > ::password_reuse_time)
415      {
416        delete row;
417        continue;
418      }
419 
420      if (CRED was produced by ::password)
421        signal("wrong password");
422 
423      count = count + 1;
424   }
425 
426   INSERT INTO mysql.password_history (USER,HOST,TS,CRED)
427     VALUES (::current_user, ::current_host, NOW(), ::hashed_password);
428   ~~~
429 
430   @param thd      The current thread
431   @param user     The user account user to operate on
432   @param host     The user acount host to operate on
433   @param password_history The effective password history value
434   @param password_reuse_interval The effective password reuse interval value
435   @param auth     auth plugin to use for verification
436   @param cleartext  the clear text password supplied
437   @param cleartext_length length of cleartext password
438   @param cred_hash hash of the credential to be inserted into the history
439   @param cred_hash_length length of cred_hash
440   @param history_table  The opened history table
441   @param what_to_set   The mask of what to set
442   @retval false   Password is OK
443   @retval true    Password is not OK
444 */
auth_verify_password_history(THD * thd,LEX_CSTRING * user,LEX_CSTRING * host,uint32 password_history,long password_reuse_interval,st_mysql_auth * auth,const char * cleartext,unsigned int cleartext_length,const char * cred_hash,unsigned int cred_hash_length,TABLE_LIST * history_table,ulong what_to_set)445 static bool auth_verify_password_history(
446     THD *thd, LEX_CSTRING *user, LEX_CSTRING *host, uint32 password_history,
447     long password_reuse_interval, st_mysql_auth *auth, const char *cleartext,
448     unsigned int cleartext_length, const char *cred_hash,
449     unsigned int cred_hash_length, TABLE_LIST *history_table,
450     ulong what_to_set) {
451   TABLE *table = history_table->table;
452   uchar user_key[MAX_KEY_LENGTH];
453   uint key_prefix_length;
454   int error;
455   Field *user_field, *host_field, *ts_field, *cred_field;
456   bool result = false;
457 
458   Acl_table_intact intact(thd);
459 
460   if (!table) {
461     if ((password_history || password_reuse_interval) && cred_hash_length) {
462       /* fail if there's no history table and we need to update it */
463       my_error(ER_NO_SUCH_TABLE, MYF(0), "mysql", "password_history");
464       return true;
465     }
466     /* threat missing table as absent and empty otherwise */
467     return false;
468   }
469 
470   /* all good: we don't handle empty passwords in history */
471   if (!cleartext_length && !cred_hash_length) return false;
472 
473   /* invalid table causes verification to fail */
474   if (intact.check(history_table->table, ACL_TABLES::TABLE_PASSWORD_HISTORY))
475     return true;
476 
477   user_field = table->field[MYSQL_PASSWORD_HISTORY_FIELD_USER];
478   host_field = table->field[MYSQL_PASSWORD_HISTORY_FIELD_HOST];
479   ts_field = table->field[MYSQL_PASSWORD_HISTORY_FIELD_PASSWORD_TIMESTAMP];
480   cred_field = table->field[MYSQL_PASSWORD_HISTORY_FIELD_PASSWORD];
481 
482   table->use_all_columns();
483 
484   /* create the search key on user and host */
485   user_field->store(user->str, user->length, system_charset_info);
486   host_field->store(host->str, host->length, system_charset_info);
487   key_prefix_length = (table->key_info->key_part[0].store_length +
488                        table->key_info->key_part[1].store_length);
489   key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
490 
491   uint32 count = 0;
492 
493   int rc = table->file->ha_index_init(0, true);
494 
495   if (rc) {
496     table->file->print_error(rc, MYF(0));
497     result = true;
498     goto end;
499   }
500 
501   /* find the first matching record by the first 2 fields of a key */
502   error = table->file->ha_index_read_idx_map(
503       table->record[0], 0, user_key, (key_part_map)((1L << 0) | (1L << 1)),
504       HA_READ_KEY_EXACT);
505 
506   /* fetch the current day */
507   MYSQL_TIME tm_now;
508   long now_day;
509   thd->time_zone()->gmt_sec_to_TIME(&tm_now, thd->query_start_timeval_trunc(6));
510   now_day = calc_daynr(tm_now.year, tm_now.month, tm_now.day);
511 
512   /* iterate over the password history rows for the user */
513   while (!error) {
514     MYSQL_TIME ts_val;
515     char outbuf[MAX_FIELD_WIDTH] = {0};
516     long ts_day, date_diff;
517     String cred_val(&outbuf[0], sizeof(outbuf), &my_charset_bin);
518     int is_error = 0;
519 
520     /* fetch the recorded time */
521     if (ts_field->get_date(&ts_val, 0)) goto get_next_row;
522 
523     /* convert to a day number */
524     ts_day = calc_daynr(ts_val.year, ts_val.month, ts_val.day);
525 
526     /* get the difference in days */
527     date_diff = now_day - ts_day;
528 
529     count++;
530 
531     /*
532        We check everything that's in any range, including the last row(s)
533     */
534     if (count <= password_history || date_diff < password_reuse_interval) {
535       /* fetch the cred field */
536       cred_field->val_str(&cred_val);
537 
538       /*
539         Check if the password matches the stored hash.
540         There can't possibly be a match when we're altering the plugin
541         used. So we check for that and just delete the rows in this case.
542         But we still check the validity of the hash in case someone has
543         tampered with the history table manually.
544       */
545       if (cleartext_length && cleartext &&
546           0 == (what_to_set & DIFFERENT_PLUGIN_ATTR) &&
547           (auth->authentication_flags & AUTH_FLAG_USES_INTERNAL_STORAGE) &&
548           auth->validate_authentication_string &&
549           !auth->validate_authentication_string(cred_val.c_ptr_safe(),
550                                                 (unsigned)cred_val.length()) &&
551           auth->compare_password_with_hash &&
552           !auth->compare_password_with_hash(
553               cred_val.c_ptr_safe(), (unsigned long)cred_val.length(),
554               cleartext, (unsigned long)cleartext_length, &is_error) &&
555           !is_error) {
556         my_error(ER_CREDENTIALS_CONTRADICT_TO_HISTORY, MYF(0), user->length,
557                  user->str, host->length, host->str);
558         /* password found in history */
559         result = true;
560         goto end;
561       }
562     }
563 
564     /*
565       Delete all rows outside all check ranges, including the last row in
566       history range count, since we're to add another one.
567     */
568     if ((count >= password_history &&
569          (!password_reuse_interval || date_diff > password_reuse_interval)) ||
570         0L != (what_to_set & DIFFERENT_PLUGIN_ATTR)) {
571       int ignore_error;
572       /* delete and go on, even if there's an error deleting */
573       if (0 != (ignore_error = table->file->ha_delete_row(table->record[0])))
574         table->file->print_error(ignore_error, MYF(0));
575       goto get_next_row;
576     }
577 
578   get_next_row:
579     error = table->file->ha_index_next_same(table->record[0], user_key,
580                                             key_prefix_length);
581   }
582 
583   /* something went wrong reading */
584   if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) {
585     table->file->print_error(error, MYF(0));
586     result = true;
587     goto end;
588   }
589 
590   /* Update the history if a hash is supplied and the plugin supports it */
591   if ((password_history || password_reuse_interval) && cred_hash_length &&
592       (auth->authentication_flags & AUTH_FLAG_USES_INTERNAL_STORAGE)) {
593     /* add history if needed */
594     restore_record(table, s->default_values);
595     table->field[MYSQL_PASSWORD_HISTORY_FIELD_USER]->store(
596         user->str, user->length, system_charset_info);
597     table->field[MYSQL_PASSWORD_HISTORY_FIELD_HOST]->store(
598         host->str, host->length, system_charset_info);
599     table->field[MYSQL_PASSWORD_HISTORY_FIELD_PASSWORD_TIMESTAMP]->store_time(
600         &tm_now);
601     table->field[MYSQL_PASSWORD_HISTORY_FIELD_PASSWORD]->store(
602         cred_hash, cred_hash_length, &my_charset_utf8_bin);
603     table->field[MYSQL_PASSWORD_HISTORY_FIELD_PASSWORD]->set_notnull();
604 
605     if (0 != (error = table->file->ha_write_row(table->record[0]))) {
606       table->file->print_error(error, MYF(0));
607       result = true;
608     }
609   }
610 end:
611   if (table->file->inited != handler::NONE) {
612     int rc_end = table->file->ha_index_end();
613 
614     if (rc_end) {
615       /* purecov: begin inspected */
616       table->file->print_error(rc_end, MYF(ME_ERRORLOG));
617       DBUG_ASSERT(false);
618       /* purecov: end */
619     }
620   }
621   return result;
622 }
623 
624 /**
625   Updates the password history table for cases of deleting or renaming users
626 
627   This function, unline the other "update" functions does not handle the
628   addition of new data. That's done by auth_verify_password_history().
629   The function only handles renames and deletes of user accounts.
630   It does not go via the normal non-mysql.user handle_grant_data() route
631   since there is a (partial) key on user/host and hence no need to do a
632   full table scan.
633 
634   @param thd the execution context
635   @param tables the list of opened ACL tables
636   @param drop true if it's a drop operation
637   @param user_from the user to rename from or the user to drop
638   @param user_to the user to rename to or the user to add
639   @param[out] row_existed set to true if row matching user_from existed
640   @retval true operation failed
641   @retval false success
642 */
643 
handle_password_history_table(THD * thd,TABLE_LIST * tables,bool drop,LEX_USER * user_from,LEX_USER * user_to,bool * row_existed)644 static bool handle_password_history_table(THD *thd, TABLE_LIST *tables,
645                                           bool drop, LEX_USER *user_from,
646                                           LEX_USER *user_to,
647                                           bool *row_existed) {
648   bool result = false;
649   TABLE *table = tables[ACL_TABLES::TABLE_PASSWORD_HISTORY].table;
650   uchar user_key[MAX_KEY_LENGTH];
651   uint key_prefix_length;
652   int error;
653   Field *user_field, *host_field;
654 
655   Acl_table_intact table_intact(thd);
656 
657   *row_existed = false;
658 
659   if (!table) {
660     /* table not preset is considered empty if not adding to it */
661     return false;
662   }
663 
664   if (table_intact.check(table, ACL_TABLES::TABLE_PASSWORD_HISTORY)) {
665     result = true;
666     goto end;
667   }
668 
669   user_field = table->field[MYSQL_PASSWORD_HISTORY_FIELD_USER];
670   host_field = table->field[MYSQL_PASSWORD_HISTORY_FIELD_HOST];
671 
672   table->use_all_columns();
673 
674   /* create the search key on user and host */
675   user_field->store(user_from->user.str, user_from->user.length,
676                     system_charset_info);
677   host_field->store(user_from->host.str, user_from->host.length,
678                     system_charset_info);
679   key_prefix_length = (table->key_info->key_part[0].store_length +
680                        table->key_info->key_part[1].store_length);
681   key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
682 
683   int rc;
684 
685   rc = table->file->ha_index_init(0, true);
686 
687   if (rc) {
688     table->file->print_error(rc, MYF(0));
689     result = true;
690     goto end;
691   }
692 
693   /* find the first matching record by host/user key prefix */
694   error = table->file->ha_index_read_idx_map(
695       table->record[0], 0, user_key, (key_part_map)((1L << 0) | (1L << 1)),
696       HA_READ_KEY_EXACT);
697 
698   /* iterate over the password history rows for the user */
699   while (!error) {
700     /* found at least 1 row */
701     if (!*row_existed) {
702       *row_existed = true;
703       /* no need to look for more rows if not updating */
704       if (!drop && !user_to) {
705         /* mark the cursor as being at end */
706         error = HA_ERR_KEY_NOT_FOUND;
707         break;
708       }
709     }
710 
711     if (drop) {
712       /* if we're dropping, delete the row */
713       if (0 != (error = table->file->ha_delete_row(table->record[0]))) break;
714     } else if (user_to) {
715       /* we're renaming, set the new user/host values */
716       store_record(table, record[1]);
717       table->field[MYSQL_PASSWORD_HISTORY_FIELD_USER]->store(
718           user_to->user.str, user_to->user.length, system_charset_info);
719       table->field[MYSQL_PASSWORD_HISTORY_FIELD_HOST]->store(
720           user_to->host.str, user_to->host.length, system_charset_info);
721       error = table->file->ha_update_row(table->record[1], table->record[0]);
722       if (error) break;
723     }
724     error = table->file->ha_index_next_same(table->record[0], user_key,
725                                             key_prefix_length);
726   }
727   /* something went wrong reading */
728   if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) {
729     table->file->print_error(error, MYF(0));
730     result = true;
731   }
732 
733 end:
734   if (table->file->inited != handler::NONE) {
735     int rc_end = table->file->ha_index_end();
736 
737     if (rc_end) {
738       /* purecov: begin inspected */
739       table->file->print_error(rc_end, MYF(ME_ERRORLOG));
740       DBUG_ASSERT(false);
741       /* purecov: end */
742     }
743   }
744   return result;
745 }
746 
747 /**
748   Checks, if the REPLACE clause is required, optional or not required.
749   It throws error:
750   If REPLACE clause is required but not specified.
751   If REPLACE clause is not required but specified.
752   If current password specified in the REPLACE clause does not match with
753   authentication string of the user.
754 
755   The plaintext current password is erased from LEX_USER, iff its length > 0 .
756 
757   @param thd      The execution context
758   @param Str      LEX user
759   @param acl_user The associated user which carries the ACL
760   @param auth     Auth plugin to use for verification
761   @param is_privileged_user     Whether caller has CREATE_USER_ACL
762                                 or UPDATE_ACL over mysql.*
763   @param user_exists  Whether user already exists
764 
765   @retval true operation failed
766   @retval false success
767 */
validate_password_require_current(THD * thd,LEX_USER * Str,ACL_USER * acl_user,st_mysql_auth * auth,bool is_privileged_user,bool user_exists)768 static bool validate_password_require_current(THD *thd, LEX_USER *Str,
769                                               ACL_USER *acl_user,
770                                               st_mysql_auth *auth,
771                                               bool is_privileged_user,
772                                               bool user_exists) {
773   if (user_exists) {
774     if (Str->uses_replace_clause) {
775       int is_error = 0;
776       Security_context *sctx = thd->security_context();
777       DBUG_ASSERT(sctx);
778       // If trying to set password for other user
779       if (strcmp(sctx->user().str, Str->user.str) ||
780           my_strcasecmp(system_charset_info, sctx->priv_host().str,
781                         Str->host.str)) {
782         my_error(ER_CURRENT_PASSWORD_NOT_REQUIRED, MYF(0));
783         return (true);
784       }
785 
786       /*
787         Handle the validation of empty current password first as some of
788         authenication plugins do not like to check the empty passwords.
789       */
790       if (acl_user->credentials[PRIMARY_CRED].m_auth_string.length == 0) {
791         if (Str->current_auth.length > 0) {
792           my_error(ER_INCORRECT_CURRENT_PASSWORD, MYF(0));
793           return (true);
794         } else {
795           return (false);
796         }
797       }
798       /*
799         Compare the specified plain text current password with the
800         current auth string.
801       */
802       else if ((auth->authentication_flags & AUTH_FLAG_USES_INTERNAL_STORAGE) &&
803                auth->compare_password_with_hash &&
804                auth->compare_password_with_hash(
805                    acl_user->credentials[PRIMARY_CRED].m_auth_string.str,
806                    (unsigned long)acl_user->credentials[PRIMARY_CRED]
807                        .m_auth_string.length,
808                    Str->current_auth.str,
809                    (unsigned long)Str->current_auth.length, &is_error) &&
810                !is_error) {
811         my_error(ER_INCORRECT_CURRENT_PASSWORD, MYF(0));
812         return (true);
813       }
814 
815       /*
816         Current password is valid plain text password with len > 0.
817         Erase that in memory. We don't need it any further
818      */
819       memset(const_cast<char *>(Str->current_auth.str), 0,
820              Str->current_auth.length);
821     } else if (!is_privileged_user) {
822       /*
823         If the field value is set or field value is NULL and global sys
824         variable flag is ON then REPLACE clause must be specified.
825       */
826       if ((acl_user->password_require_current == Lex_acl_attrib_udyn::YES) ||
827           (acl_user->password_require_current == Lex_acl_attrib_udyn::DEFAULT &&
828            password_require_current)) {
829         my_error(ER_MISSING_CURRENT_PASSWORD, MYF(0));
830         return (true);
831       }
832     }
833   }
834   return (false);
835 }
836 
translate_byte_to_password_char(unsigned char c)837 char translate_byte_to_password_char(unsigned char c) {
838   static const std::string translation = std::string(
839       "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY"
840       "Z,.-;:_+*!%&/(){}[]<>@");
841   int index = round(((float)c * ((float)(translation.length() - 1) / 255.0)));
842   return translation[index];
843 }
844 
845 /**
846   Generates a random password of the length decided by the system variable
847   generated_random_password_length.
848 
849   @param[out] password The generated password.
850   @param length The length of the generated password.
851 
852 */
853 
generate_random_password(std::string * password,uint32_t length)854 void generate_random_password(std::string *password, uint32_t length) {
855   unsigned char buffer[256];
856   if (length > 255) length = 255;
857   RAND_bytes((unsigned char *)&buffer[0], length);
858   password->reserve(length + 1);
859   for (uint32_t i = 0; i < length; ++i) {
860     password->append(1, translate_byte_to_password_char(buffer[i]));
861   }
862 }
863 
864 /**
865   Sends the result set of generated passwords to the client.
866   @param thd The thread handler
867   @param generated_passwords A list of 3-tuple strings containing user, host
868     and plaintext password.
869   @return success state
870     @retval true An error occurred (DA is set)
871     @retval false Success (my_eof)
872 */
873 
send_password_result_set(THD * thd,const Userhostpassword_list & generated_passwords)874 bool send_password_result_set(
875     THD *thd, const Userhostpassword_list &generated_passwords) {
876   List<Item> meta_data;
877   meta_data.push_back(new Item_string("user", 4, system_charset_info));
878   meta_data.push_back(new Item_string("host", 4, system_charset_info));
879   meta_data.push_back(
880       new Item_string("generated password", 18, system_charset_info));
881   List<Item> item_list;
882   Query_result_send output;
883   if (output.send_result_set_metadata(
884           thd, meta_data, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
885     return true;
886   for (auto password : generated_passwords) {
887     Item *item = new Item_string(password[0].c_str(), password[0].length(),
888                                  system_charset_info);
889     item_list.push_back(item);
890     item = new Item_string(password[1].c_str(), password[1].length(),
891                            system_charset_info);
892     item_list.push_back(item);
893     item = new Item_string(password[2].c_str(), password[2].length(),
894                            system_charset_info);
895     item_list.push_back(item);
896     if (output.send_data(thd, item_list)) {
897       item_list.empty();
898       return true;
899     }
900     // items clean themselves up when THD dies.
901     item_list.empty();
902   }
903   my_eof(thd);
904   return false;
905 }
906 
907 /**
908   This function does following:
909    1. Convert plain text password to hash and update the same in
910       user definition.
911    2. Validate hash string if specified in user definition.
912    3. Identify what all fields needs to be updated in mysql.user
913       table based on user definition.
914 
915   If the is_role flag is set, the password validation is not used.
916 
917   The function perform some semantic parsing of the original statement
918   by investigating the syntactic elements found in the LEX_USER object
919   not-so-appropriately named Str.
920 
921   The code fits the purpose as a helper function to mysql_create_user()
922   but it is used from mysql_alter_user(), mysql_grant(), change_password() and
923   mysql_routine_grant() with a slightly varying semantic meaning.
924 
925   @param thd          Thread context
926   @param Str          user on which attributes has to be applied
927   @param what_to_set  User attributes
928   @param is_privileged_user     Whether caller has CREATE_USER_ACL
929                                 or UPDATE_ACL over mysql.*
930   @param is_role      CREATE ROLE was used to create the authid.
931   @param history_table          The table to verify history against.
932   @param[out] history_check_done  Set to on if the history table is updated
933   @param cmd          Command information
934   @param[out] generated_passwords A list of generated random passwords. Depends
935   on LEX_USER.
936 
937   @retval 0 ok
938   @retval 1 ERROR;
939 */
940 
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_LIST * history_table,bool * history_check_done,const char * cmd,Userhostpassword_list & generated_passwords)941 bool set_and_validate_user_attributes(
942     THD *thd, LEX_USER *Str, acl_table::Pod_user_what_to_update &what_to_set,
943     bool is_privileged_user, bool is_role, TABLE_LIST *history_table,
944     bool *history_check_done, const char *cmd,
945     Userhostpassword_list &generated_passwords) {
946   bool user_exists = false;
947   ACL_USER *acl_user;
948   plugin_ref plugin = nullptr;
949   char outbuf[MAX_FIELD_WIDTH] = {0};
950   unsigned int buflen = MAX_FIELD_WIDTH, inbuflen;
951   const char *inbuf;
952   const char *password = nullptr;
953   enum_sql_command command = thd->lex->sql_command;
954   bool current_password_empty = false;
955   bool new_password_empty = false;
956 
957   what_to_set.m_what = NONE_ATTR;
958   what_to_set.m_user_attributes = acl_table::USER_ATTRIBUTE_NONE;
959   DBUG_ASSERT(assert_acl_cache_read_lock(thd) ||
960               assert_acl_cache_write_lock(thd));
961 
962   if (history_check_done) *history_check_done = false;
963   /* update plugin,auth str attributes */
964   if (Str->uses_identified_by_clause || Str->uses_identified_with_clause ||
965       Str->uses_authentication_string_clause)
966     what_to_set.m_what |= PLUGIN_ATTR;
967   else
968     what_to_set.m_what |= DEFAULT_AUTH_ATTR;
969 
970   /* update ssl attributes */
971   if (thd->lex->ssl_type != SSL_TYPE_NOT_SPECIFIED)
972     what_to_set.m_what |= SSL_ATTR;
973   /* update connection attributes */
974   if (thd->lex->mqh.specified_limits) what_to_set.m_what |= RESOURCE_ATTR;
975 
976   if ((acl_user = find_acl_user(Str->host.str, Str->user.str, true)))
977     user_exists = true;
978 
979   /* copy password expire attributes to individual user */
980   Str->alter_status = thd->lex->alter_password;
981 
982   mysql_mutex_lock(&LOCK_password_history);
983   Str->alter_status.password_history_length =
984       Str->alter_status.use_default_password_history
985           ? global_password_history
986           : Str->alter_status.password_history_length;
987   mysql_mutex_unlock(&LOCK_password_history);
988   mysql_mutex_lock(&LOCK_password_reuse_interval);
989   Str->alter_status.password_reuse_interval =
990       Str->alter_status.use_default_password_reuse_interval
991           ? global_password_reuse_interval
992           : Str->alter_status.password_reuse_interval;
993   mysql_mutex_unlock(&LOCK_password_reuse_interval);
994 
995   /* update password expire attributes */
996   if (Str->alter_status.update_password_expired_column ||
997       !Str->alter_status.use_default_password_lifetime ||
998       Str->alter_status.expire_after_days)
999     what_to_set.m_what |= PASSWORD_EXPIRE_ATTR;
1000 
1001   /* update account lock attribute */
1002   if (Str->alter_status.update_account_locked_column)
1003     what_to_set.m_what |= ACCOUNT_LOCK_ATTR;
1004 
1005   if (Str->plugin.length) optimize_plugin_compare_by_pointer(&Str->plugin);
1006 
1007   if (user_exists) {
1008     switch (command) {
1009       case SQLCOM_CREATE_USER: {
1010         /*
1011           Since user exists, we are likely going to fail
1012           unless IF NOT EXISTS is specified. In that case
1013           we need to use default plugin to generate password
1014           so that binlog entry is correct.
1015         */
1016         if (!Str->uses_identified_with_clause)
1017           Str->plugin = default_auth_plugin_name;
1018         break;
1019       }
1020       case SQLCOM_ALTER_USER: {
1021         if (!Str->uses_identified_with_clause) {
1022           /* If no plugin is given, get existing plugin */
1023           Str->plugin = acl_user->plugin;
1024         } else if (!(Str->uses_identified_by_clause ||
1025                      Str->uses_authentication_string_clause) &&
1026                    auth_plugin_supports_expiration(Str->plugin.str)) {
1027           /*
1028             This is an attempt to change existing users authentication plugin
1029             without specifying any password. In such cases, expire user's
1030             password so we can force password change on next login
1031           */
1032           Str->alter_status.update_password_expired_column = true;
1033           what_to_set.m_what |= PASSWORD_EXPIRE_ATTR;
1034         }
1035         /*
1036           always check for password expire/interval attributes as there is no
1037           way to differentiate NEVER EXPIRE and EXPIRE DEFAULT scenario
1038         */
1039         if (Str->alter_status.update_password_expired_fields)
1040           what_to_set.m_what |= PASSWORD_EXPIRE_ATTR;
1041 
1042         /* detect changes in the plugin name */
1043         if (Str->plugin.str != acl_user->plugin.str) {
1044           what_to_set.m_what |= DIFFERENT_PLUGIN_ATTR;
1045           if (Str->retain_current_password) {
1046             my_error(ER_PASSWORD_CANNOT_BE_RETAINED_ON_PLUGIN_CHANGE, MYF(0),
1047                      Str->user.str, Str->host.str);
1048             what_to_set.m_what = NONE_ATTR;
1049             return true;
1050           }
1051           if (acl_user->credentials[SECOND_CRED].m_auth_string.length) {
1052             what_to_set.m_what |= USER_ATTRIBUTES;
1053             what_to_set.m_user_attributes |=
1054                 acl_table::USER_ATTRIBUTE_DISCARD_PASSWORD;
1055           }
1056         }
1057 
1058         if (Str->retain_current_password || Str->discard_old_password) {
1059           DBUG_ASSERT(
1060               !(Str->retain_current_password && Str->discard_old_password));
1061           what_to_set.m_what |= USER_ATTRIBUTES;
1062           if (Str->retain_current_password)
1063             what_to_set.m_user_attributes |=
1064                 acl_table::USER_ATTRIBUTE_RETAIN_PASSWORD;
1065           if (Str->discard_old_password)
1066             what_to_set.m_user_attributes |=
1067                 acl_table::USER_ATTRIBUTE_DISCARD_PASSWORD;
1068           current_password_empty =
1069               acl_user->credentials[PRIMARY_CRED].m_auth_string.length ? false
1070                                                                        : true;
1071         }
1072 
1073         break;
1074       }
1075       case SQLCOM_SET_PASSWORD: {
1076         if (Str->retain_current_password) {
1077           what_to_set.m_what |= USER_ATTRIBUTES;
1078           what_to_set.m_user_attributes |=
1079               acl_table::USER_ATTRIBUTE_RETAIN_PASSWORD;
1080           current_password_empty =
1081               acl_user->credentials[PRIMARY_CRED].m_auth_string.length ? false
1082                                                                        : true;
1083         }
1084         break;
1085       }
1086       /*
1087         We need to fill in the elements of the LEX_USER structure even for GRANT
1088         and REVOKE.
1089       */
1090       case SQLCOM_GRANT:
1091         /* fall through */
1092       case SQLCOM_REVOKE:
1093         what_to_set.m_what = NONE_ATTR;
1094         Str->plugin = acl_user->plugin;
1095         Str->auth.str = acl_user->credentials[PRIMARY_CRED].m_auth_string.str;
1096         Str->auth.length =
1097             acl_user->credentials[PRIMARY_CRED].m_auth_string.length;
1098         break;
1099 
1100       default: {
1101         /*
1102           If we are here, authentication related information can be provided
1103           only if GRANT statement was used to change user's credentials.
1104         */
1105 
1106         if (!Str->uses_identified_with_clause) {
1107           /* if IDENTIFIED WITH is not specified set plugin from cache */
1108           Str->plugin = acl_user->plugin;
1109           /* set auth str from cache when not specified for existing user */
1110           if (!(Str->uses_identified_by_clause ||
1111                 Str->uses_authentication_string_clause)) {
1112             Str->auth.str =
1113                 acl_user->credentials[PRIMARY_CRED].m_auth_string.str;
1114             Str->auth.length =
1115                 acl_user->credentials[PRIMARY_CRED].m_auth_string.length;
1116           }
1117         } else if (!(Str->uses_identified_by_clause ||
1118                      Str->uses_authentication_string_clause) &&
1119                    auth_plugin_supports_expiration(Str->plugin.str)) {
1120           /*
1121             This is an attempt to change existing users authentication plugin
1122             without specifying any password. In such cases, expire user's
1123             password so we can force password change on next login
1124           */
1125           Str->alter_status.update_password_expired_column = true;
1126           what_to_set.m_what |= PASSWORD_EXPIRE_ATTR;
1127         }
1128       }
1129     };
1130 
1131     /* if we don't update password history take the user's password history */
1132     if (!Str->alter_status.update_password_history) {
1133       if (acl_user->use_default_password_history) {
1134         mysql_mutex_lock(&LOCK_password_history);
1135         Str->alter_status.password_history_length = global_password_history;
1136         mysql_mutex_unlock(&LOCK_password_history);
1137       } else
1138         Str->alter_status.password_history_length =
1139             acl_user->password_history_length;
1140     }
1141     /* if we don't update password reuse interval take the user's interval */
1142     if (!Str->alter_status.update_password_reuse_interval) {
1143       if (acl_user->use_default_password_reuse_interval) {
1144         mysql_mutex_lock(&LOCK_password_reuse_interval);
1145         Str->alter_status.password_reuse_interval =
1146             global_password_reuse_interval;
1147         mysql_mutex_unlock(&LOCK_password_reuse_interval);
1148       } else
1149         Str->alter_status.password_reuse_interval =
1150             acl_user->password_reuse_interval;
1151     }
1152   } else {
1153     /* set default plugin for new users if not specified */
1154     if (!Str->uses_identified_with_clause)
1155       Str->plugin = default_auth_plugin_name;
1156 
1157     if (command == SQLCOM_GRANT) {
1158       my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
1159       return true;
1160     }
1161   }
1162 
1163   optimize_plugin_compare_by_pointer(&Str->plugin);
1164 
1165   /*
1166     Check if non-default password expiraition option
1167     is passed to a plugin that does not support it and raise
1168     and error if it is.
1169   */
1170   if (Str->alter_status.update_password_expired_fields &&
1171       !Str->alter_status.use_default_password_lifetime &&
1172       Str->alter_status.expire_after_days != 0 &&
1173       !auth_plugin_supports_expiration(Str->plugin.str)) {
1174     my_error(ER_PASSWORD_EXPIRATION_NOT_SUPPORTED_BY_AUTH_METHOD, MYF(0),
1175              Str->plugin.length, Str->plugin.str);
1176     return true;
1177   }
1178 
1179   plugin =
1180       my_plugin_lock_by_name(nullptr, Str->plugin, MYSQL_AUTHENTICATION_PLUGIN);
1181 
1182   /* check if plugin is loaded */
1183   if (!plugin) {
1184     what_to_set.m_what = NONE_ATTR;
1185     my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), Str->plugin.str);
1186     return (true);
1187   }
1188 
1189   st_mysql_auth *auth = (st_mysql_auth *)plugin_decl(plugin)->info;
1190 
1191   if (user_exists && (what_to_set.m_what & PLUGIN_ATTR)) {
1192     if (auth->authentication_flags &
1193         AUTH_FLAG_PRIVILEGED_USER_FOR_PASSWORD_CHANGE) {
1194       if (!is_privileged_user &&
1195           (command == SQLCOM_ALTER_USER || command == SQLCOM_GRANT)) {
1196         /*
1197           An external plugin that prevents user
1198           to change authentication_string information
1199           unless user is privileged.
1200         */
1201         what_to_set.m_what = NONE_ATTR;
1202         my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
1203                  thd->security_context()->priv_user().str,
1204                  thd->security_context()->priv_host().str,
1205                  thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
1206         plugin_unlock(nullptr, plugin);
1207         return (true);
1208       }
1209     }
1210 
1211     if (!(auth->authentication_flags & AUTH_FLAG_USES_INTERNAL_STORAGE)) {
1212       if (command == SQLCOM_SET_PASSWORD) {
1213         /*
1214           A plugin that does not use internal storage and
1215           hence does not support SET PASSWORD
1216         */
1217         char warning_buffer[MYSQL_ERRMSG_SIZE];
1218         snprintf(warning_buffer, sizeof(warning_buffer),
1219                  "SET PASSWORD has no significance for user '%s'@'%s' as "
1220                  "authentication plugin does not support it.",
1221                  Str->user.str, Str->host.str);
1222         warning_buffer[MYSQL_ERRMSG_SIZE - 1] = '\0';
1223         push_warning(thd, Sql_condition::SL_NOTE, ER_SET_PASSWORD_AUTH_PLUGIN,
1224                      warning_buffer);
1225         plugin_unlock(nullptr, plugin);
1226         what_to_set.m_what = NONE_ATTR;
1227         return (false);
1228       }
1229     }
1230   }
1231 
1232   if (!(auth->authentication_flags & AUTH_FLAG_USES_INTERNAL_STORAGE)) {
1233     if (Str->alter_status.password_history_length ||
1234         Str->alter_status.password_reuse_interval) {
1235       /*
1236         A plugin that does not use internal storage and
1237         hence does not support password history is passed a password history
1238       */
1239       if (Str->alter_status.update_password_history ||
1240           Str->alter_status.update_password_reuse_interval)
1241         push_warning_printf(
1242             thd, Sql_condition::SL_WARNING,
1243             ER_WARNING_PASSWORD_HISTORY_CLAUSES_VOID,
1244             ER_THD(thd, ER_WARNING_PASSWORD_HISTORY_CLAUSES_VOID),
1245             Str->user.str, Str->host.str, plugin_decl(plugin)->name);
1246       /* reset back the password history clauses for that user */
1247       Str->alter_status.password_history_length = 0;
1248       Str->alter_status.password_reuse_interval = 0;
1249       Str->alter_status.update_password_history = true;
1250       Str->alter_status.update_password_reuse_interval = true;
1251       Str->alter_status.use_default_password_history = true;
1252       Str->alter_status.use_default_password_reuse_interval = true;
1253     }
1254   }
1255   /*
1256     If auth string is specified, change it to hash.
1257     Validate empty credentials for new user ex: CREATE USER u1;
1258     We skip authentication string generation if the issued statement was
1259     CREATE ROLE.
1260   */
1261   if (!is_role && (Str->uses_identified_by_clause ||
1262                    (Str->auth.length == 0 && !user_exists))) {
1263     inbuf = Str->auth.str;
1264     inbuflen = (unsigned)Str->auth.length;
1265     std::string gen_password;
1266     if (Str->has_password_generator) {
1267       thd->m_disable_password_validation = true;
1268       generate_random_password(&gen_password,
1269                                thd->variables.generated_random_password_length);
1270       inbuf = gen_password.c_str();
1271       inbuflen = gen_password.length();
1272       generated_passwords.push_back({std::string(Str->user.str),
1273                                      std::string(Str->host.str), gen_password});
1274     }
1275     if (auth->generate_authentication_string(outbuf, &buflen, inbuf,
1276                                              inbuflen) ||
1277         auth_verify_password_history(thd, &Str->user, &Str->host,
1278                                      Str->alter_status.password_history_length,
1279                                      Str->alter_status.password_reuse_interval,
1280                                      auth, inbuf, inbuflen, outbuf, buflen,
1281                                      history_table, what_to_set.m_what)) {
1282       plugin_unlock(nullptr, plugin);
1283       what_to_set.m_what = NONE_ATTR;
1284       /*
1285         generate_authentication_string may return error status
1286         without setting actual error.
1287       */
1288       if (!thd->is_error()) {
1289         String error_user;
1290         log_user(thd, &error_user, Str, false);
1291         my_error(ER_CANNOT_USER, MYF(0), cmd, error_user.c_ptr_safe());
1292       }
1293       return (true);
1294     }
1295     /* Allow for password validation in case it was disabled before */
1296     thd->m_disable_password_validation = false;
1297     if (history_check_done) *history_check_done = true;
1298     if (buflen) {
1299       password = strmake_root(thd->mem_root, outbuf, buflen);
1300     } else
1301       password = const_cast<char *>("");
1302     /*
1303        Erase in memory copy of plain text password, unless we need it
1304        later to send to client as a result set.
1305     */
1306     if (Str->auth.length > 0)
1307       memset(const_cast<char *>(Str->auth.str), 0, Str->auth.length);
1308     /* Use the authentication_string field as password */
1309     Str->auth.str = password;
1310     Str->auth.length = buflen;
1311     new_password_empty = Str->auth.length ? false : true;
1312   }
1313 
1314   /* Check iff the REPLACE clause is specified correctly for the user */
1315   if ((what_to_set.m_what & PLUGIN_ATTR) &&
1316       validate_password_require_current(thd, Str, acl_user, auth,
1317                                         is_privileged_user, user_exists)) {
1318     plugin_unlock(nullptr, plugin);
1319     what_to_set.m_what = NONE_ATTR;
1320     return (true);
1321   }
1322 
1323   /* Validate hash string */
1324   if (Str->uses_authentication_string_clause) {
1325     /*
1326       The statement CREATE ROLE calls mysql_create_user() with a set of
1327       lexicographic parameters: users_identified_by_password_caluse= false etc
1328       It also sets is_role= true. We don't have to check this parameter here
1329       since we're already know that the above parameters will be false
1330       but we place an extra assert here to remind us about the complex
1331       interdependencies if mysql_create_user() is refactored.
1332     */
1333     DBUG_ASSERT(!is_role);
1334     if (auth->validate_authentication_string(const_cast<char *>(Str->auth.str),
1335                                              (unsigned)Str->auth.length)) {
1336       my_error(ER_PASSWORD_FORMAT, MYF(0));
1337       plugin_unlock(nullptr, plugin);
1338       what_to_set.m_what = NONE_ATTR;
1339       return (true);
1340     }
1341     /*
1342       Call the password history validation so that it can store the incoming
1343       hash into the password history table.
1344       Here we can't check if the password was used since we don't have the
1345       cleartext password, but we still want to record it into the history table.
1346       Covers replication scenario too since the IDENTIFIED BY will get
1347       rewritten to IDENTIFIED ... WITH ... AS
1348     */
1349     if (auth_verify_password_history(thd, &Str->user, &Str->host,
1350                                      Str->alter_status.password_history_length,
1351                                      Str->alter_status.password_reuse_interval,
1352                                      auth, nullptr, 0, Str->auth.str,
1353                                      (unsigned)Str->auth.length, history_table,
1354                                      what_to_set.m_what)) {
1355       /* we should have an error generated here already */
1356       plugin_unlock(nullptr, plugin);
1357       what_to_set.m_what = NONE_ATTR;
1358       return (true);
1359     }
1360     if (history_check_done) *history_check_done = true;
1361   }
1362 
1363   if (user_exists && (what_to_set.m_user_attributes &
1364                       (acl_table::USER_ATTRIBUTE_RETAIN_PASSWORD |
1365                        acl_table::USER_ATTRIBUTE_DISCARD_PASSWORD))) {
1366     if (!(auth->authentication_flags & AUTH_FLAG_USES_INTERNAL_STORAGE)) {
1367       /* We do not support multiple passwords */
1368       if (Str->retain_current_password) {
1369         push_warning_printf(
1370             thd, Sql_condition::SL_WARNING,
1371             ER_WARNING_RETAIN_CURRENT_PASSWORD_CLAUSE_VOID,
1372             ER_THD(thd, ER_WARNING_RETAIN_CURRENT_PASSWORD_CLAUSE_VOID),
1373             Str->user.str, Str->host.str, plugin_decl(plugin)->name);
1374         what_to_set.m_user_attributes &=
1375             ~acl_table::USER_ATTRIBUTE_RETAIN_PASSWORD;
1376       }
1377 
1378       if (Str->discard_old_password) {
1379         push_warning_printf(
1380             thd, Sql_condition::SL_WARNING,
1381             ER_WARNING_DISCARD_OLD_PASSWORD_CLAUSE_VOID,
1382             ER_THD(thd, ER_WARNING_DISCARD_OLD_PASSWORD_CLAUSE_VOID),
1383             Str->user.str, Str->host.str, plugin_decl(plugin)->name);
1384         what_to_set.m_user_attributes &=
1385             ~acl_table::USER_ATTRIBUTE_DISCARD_PASSWORD;
1386       }
1387     } else if (what_to_set.m_user_attributes &
1388                acl_table::USER_ATTRIBUTE_RETAIN_PASSWORD) {
1389       if (current_password_empty) {
1390         my_error(ER_SECOND_PASSWORD_CANNOT_BE_EMPTY, MYF(0), Str->user.str,
1391                  Str->host.str);
1392         plugin_unlock(nullptr, plugin);
1393         what_to_set.m_what = NONE_ATTR;
1394         return true;
1395       }
1396 
1397       if (what_to_set.m_what & PLUGIN_ATTR && new_password_empty) {
1398         my_error(ER_CURRENT_PASSWORD_CANNOT_BE_RETAINED, MYF(0), Str->user.str,
1399                  Str->host.str);
1400         plugin_unlock(nullptr, plugin);
1401         what_to_set.m_what = NONE_ATTR;
1402         return true;
1403       }
1404     }
1405   }
1406 
1407   if (user_exists && (what_to_set.m_what & PLUGIN_ATTR) && !Str->auth.length &&
1408       (auth->authentication_flags & AUTH_FLAG_USES_INTERNAL_STORAGE)) {
1409     if (acl_user->credentials[SECOND_CRED].m_auth_string.length) {
1410       what_to_set.m_what |= USER_ATTRIBUTES;
1411       what_to_set.m_user_attributes |=
1412           acl_table::USER_ATTRIBUTE_DISCARD_PASSWORD;
1413     }
1414   }
1415 
1416   if (Str->alter_status.update_failed_login_attempts) {
1417     what_to_set.m_what |= USER_ATTRIBUTES;
1418     what_to_set.m_user_attributes |=
1419         acl_table::USER_ATTRIBUTE_FAILED_LOGIN_ATTEMPTS;
1420   }
1421   if (Str->alter_status.update_password_lock_time) {
1422     what_to_set.m_what |= USER_ATTRIBUTES;
1423     what_to_set.m_user_attributes |=
1424         acl_table::USER_ATTRIBUTE_PASSWORD_LOCK_TIME;
1425   }
1426   /*
1427     We issued a ALTER USER x ATTRIBUTE or COMMENT statement and need
1428     to update the user attributes.
1429   */
1430   if (thd->lex->alter_user_attribute !=
1431       enum_alter_user_attribute::ALTER_USER_COMMENT_NOT_USED) {
1432     what_to_set.m_what |= USER_ATTRIBUTES;
1433   }
1434   plugin_unlock(nullptr, plugin);
1435   return (false);
1436 }
1437 
1438 /**
1439   Change a password hash for a user.
1440 
1441   @param thd Thread handle
1442   @param lex_user LEX_USER
1443   @param new_password New password hash for host\@user
1444   @param current_password Current password for host\@user
1445   @param retain_current_password Preference to retain current password
1446 
1447   Note : it will also reset the change_password flag.
1448   This is safe to do unconditionally since the simple userless form
1449   SET PASSWORD = 'text' will be the only allowed form when
1450   this flag is on. So we don't need to check user names here.
1451 
1452   @see set_var_password::update(THD *thd)
1453 
1454   @return Error code
1455    @retval 0 ok
1456    @retval 1 ERROR; In this case the error is sent to the client.
1457 */
1458 
change_password(THD * thd,LEX_USER * lex_user,const char * new_password,const char * current_password,bool retain_current_password)1459 bool change_password(THD *thd, LEX_USER *lex_user, const char *new_password,
1460                      const char *current_password,
1461                      bool retain_current_password) {
1462   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
1463   TABLE *table;
1464   LEX_USER *combo = nullptr;
1465   std::set<LEX_USER *> users;
1466   acl_table::Pod_user_what_to_update what_to_set;
1467   size_t new_password_len = strlen(new_password);
1468   bool transactional_tables;
1469   bool result = false;
1470   bool commit_result = false;
1471   std::string authentication_plugin;
1472   bool is_role;
1473   int ret;
1474   sql_mode_t old_sql_mode = thd->variables.sql_mode;
1475 
1476   DBUG_TRACE;
1477   DBUG_ASSERT(lex_user && lex_user->host.str);
1478   DBUG_PRINT("enter", ("host: '%s'  user: '%s' current_password: '%s' \
1479                        new_password: '%s'",
1480                        lex_user->host.str, lex_user->user.str, current_password,
1481                        new_password));
1482 
1483   if (check_change_password(thd, lex_user->host.str, lex_user->user.str,
1484                             retain_current_password))
1485     return true;
1486 
1487   Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
1488   /*
1489     This statement will be replicated as a statement, even when using
1490     row-based replication.  The binlog state will be cleared here to
1491     statement based replication and will be reset to the originals
1492     values when we are out of this function scope
1493   */
1494   Save_and_Restore_binlog_format_state binlog_format_state(thd);
1495 
1496   if ((ret = open_grant_tables(thd, tables, &transactional_tables)))
1497     return ret != 1;
1498 
1499   { /* Critical section */
1500 
1501     if (!acl_cache_lock.lock()) {
1502       commit_and_close_mysql_tables(thd);
1503       return true;
1504     }
1505 
1506     table = tables[ACL_TABLES::TABLE_USER].table;
1507 
1508     ACL_USER *acl_user =
1509         find_acl_user(lex_user->host.str, lex_user->user.str, true);
1510     if (acl_user == nullptr) {
1511       my_error(ER_PASSWORD_NO_MATCH, MYF(0));
1512       commit_and_close_mysql_tables(thd);
1513       return true;
1514     }
1515 
1516     DBUG_ASSERT(acl_user->plugin.length != 0);
1517     is_role = acl_user->is_role;
1518 
1519     if (!(combo = (LEX_USER *)thd->alloc(sizeof(LEX_USER)))) return true;
1520 
1521     combo->user.str = lex_user->user.str;
1522     combo->host.str = lex_user->host.str;
1523     combo->user.length = lex_user->user.length;
1524     combo->host.length = lex_user->host.length;
1525     combo->plugin.str = acl_user->plugin.str;
1526     combo->plugin.length = acl_user->plugin.length;
1527 
1528     lex_string_strmake(thd->mem_root, &combo->user, combo->user.str,
1529                        strlen(combo->user.str));
1530     lex_string_strmake(thd->mem_root, &combo->host, combo->host.str,
1531                        strlen(combo->host.str));
1532     lex_string_strmake(thd->mem_root, &combo->plugin, combo->plugin.str,
1533                        strlen(combo->plugin.str));
1534     optimize_plugin_compare_by_pointer(&combo->plugin);
1535     combo->auth.str = new_password;
1536     combo->auth.length = new_password_len;
1537     combo->current_auth.str = current_password;
1538     combo->current_auth.length =
1539         (current_password) ? strlen(current_password) : 0;
1540     combo->uses_identified_by_clause = true;
1541     combo->uses_identified_with_clause = false;
1542     combo->uses_authentication_string_clause = false;
1543     combo->uses_replace_clause = (current_password) ? true : false;
1544     combo->retain_current_password = retain_current_password;
1545     combo->discard_old_password = false;
1546     combo->has_password_generator = false;
1547 
1548     /* set default values */
1549     thd->lex->ssl_type = SSL_TYPE_NOT_SPECIFIED;
1550     memset(&(thd->lex->mqh), 0, sizeof(thd->lex->mqh));
1551     thd->lex->alter_password.cleanup();
1552 
1553     bool is_privileged_user = is_privileged_user_for_credential_change(thd);
1554     /*
1555       Change_password() only sets the password for one user at a time and
1556       it does not support the generation of random passwords. Instead it's
1557       called from set_var_password which might have generated the password.
1558       Since we're falling back on code used by mysql_create_user() we still
1559       supply a list for storing generated password, although password
1560       generation never will happen at this stage.
1561       Calling this function has the side effect that combo->auth is rewritten
1562       into a hash.
1563     */
1564     Userhostpassword_list dummy;
1565     if (set_and_validate_user_attributes(
1566             thd, combo, what_to_set, is_privileged_user, false,
1567             &tables[ACL_TABLES::TABLE_PASSWORD_HISTORY], nullptr,
1568             "SET PASSWORD", dummy)) {
1569       authentication_plugin.assign(combo->plugin.str);
1570       result = true;
1571       goto end;
1572     }
1573 
1574     // We must not have user with plain text password at this point
1575     // unless the password was randomly generated in which case the
1576     // plain text password will live on in the calling function for the
1577     // purpose of returning it to the client.
1578     thd->lex->contains_plaintext_password = false;
1579     authentication_plugin.assign(combo->plugin.str);
1580     thd->variables.sql_mode &= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
1581 
1582     ret = replace_user_table(thd, table, combo, 0, false, false, what_to_set);
1583     thd->variables.sql_mode = old_sql_mode;
1584 
1585     if (ret) {
1586       result = true;
1587       goto end;
1588     }
1589     if (!update_sctx_cache(thd->security_context(), acl_user, false) &&
1590         thd->security_context()->password_expired()) {
1591       /* the current user is not the same as the user we operate on */
1592       my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
1593       result = true;
1594       goto end;
1595     }
1596 
1597     result = false;
1598     users.insert(combo);
1599 
1600   end:
1601 
1602     User_params user_params(&users);
1603     commit_result = log_and_commit_acl_ddl(thd, transactional_tables, &users,
1604                                            &user_params, false, !result);
1605 
1606     mysql_audit_notify(
1607         thd, AUDIT_EVENT(MYSQL_AUDIT_AUTHENTICATION_CREDENTIAL_CHANGE),
1608         thd->is_error() || result, lex_user->user.str, lex_user->host.str,
1609         authentication_plugin.c_str(), is_role, nullptr, nullptr);
1610   } /* Critical section */
1611 
1612   /* Notify storage engines (including rewrite list) */
1613   if (!(result || commit_result)) {
1614     List<LEX_USER> user_list;
1615     user_list.push_back(lex_user);
1616     acl_notify_htons(thd, SQLCOM_SET_PASSWORD, &user_list, &users);
1617   }
1618 
1619   return result || commit_result;
1620 }
1621 
1622 namespace {
1623 
1624 // No need for a return value; the matcher sets its own state.
1625 template <class T, class Matcher>
search_for_matching_grant(const T * hash,Matcher & matches)1626 void search_for_matching_grant(const T *hash, Matcher &matches) {
1627   for (auto it = hash->begin(); it != hash->end(); ++it) {
1628     const GRANT_NAME *grant_name = it->second.get();
1629     if (matches(grant_name->user, grant_name->host.get_host())) return;
1630   }
1631 }
1632 
1633 template <class T, class Matcher>
remove_matching_grants(T * hash,Matcher & matches)1634 void remove_matching_grants(T *hash, Matcher &matches) {
1635   for (auto it = hash->begin(); it != hash->end();) {
1636     const GRANT_NAME *grant_name = it->second.get();
1637     if (matches(grant_name->user, grant_name->host.get_host()))
1638       it = hash->erase(it);
1639     else
1640       ++it;
1641   }
1642 }
1643 
1644 template <class T, class Matcher>
rename_matching_grants(T * hash,Matcher & matches,LEX_USER * user_to)1645 bool rename_matching_grants(T *hash, Matcher &matches, LEX_USER *user_to) {
1646   DBUG_TRACE;
1647 
1648   using Elem = typename T::value_type::second_type::element_type;
1649 
1650   /*
1651     Inserting while traversing a hash table is not valid procedure and
1652     hence we save pointers to GRANT_NAME objects for later processing.
1653 
1654     Prealloced_array can't hold unique_ptr, so we'll need to take them
1655     in and out here.
1656   */
1657   Prealloced_array<Elem *, 16> acl_grant_name(PSI_INSTRUMENT_ME);
1658   for (auto it = hash->begin(); it != hash->end();) {
1659     Elem *grant_name = it->second.get();
1660     if (matches(grant_name->user, grant_name->host.get_host())) {
1661       if (acl_grant_name.push_back(it->second.release())) return true;
1662       it = hash->erase(it);
1663     } else
1664       ++it;
1665   }
1666 
1667   /*
1668     Update the grant structures with the new user name and host name,
1669     then insert them back.
1670   */
1671   for (Elem *grant_name : acl_grant_name) {
1672     grant_name->set_user_details(user_to->host.str, grant_name->db,
1673                                  user_to->user.str, grant_name->tname, true);
1674     hash->emplace(grant_name->hash_key,
1675                   unique_ptr_destroy_only<Elem>(grant_name));
1676   }
1677   return false;
1678 }
1679 
1680 }  // namespace
1681 
1682 /**
1683   Handle an in-memory privilege structure.
1684 
1685   @param struct_no  The number of the structure to handle (0..5).
1686   @param drop       If user_from is to be dropped.
1687   @param user_from  The the user to be searched/dropped/renamed.
1688   @param user_to    The new name for the user if to be renamed, NULL otherwise.
1689   @param on_drop_role_priv  true enabled by the DROP ROLE privilege
1690 
1691   @note
1692     Scan through all elements in an in-memory grant structure and apply
1693     the requested operation.
1694     Delete from grant structure if drop is true.
1695     Update in grant structure if drop is false and user_to is not NULL.
1696     Search in grant structure if drop is false and user_to is NULL.
1697     Structures are enumerated as follows:
1698     0 ACL_USER
1699     1 ACL_DB
1700     2 COLUMN_PRIVILIGES_HASH
1701     3 PROC_PRIVILEGES_HASH
1702     4 FUNC_PRIVILEGES_HASH
1703     5 ACL_PROXY_USERS
1704 
1705   @retval > 0  At least one element matched.
1706   @retval 0    OK, but no element matched.
1707   @retval -1   Wrong arguments to function or Out of Memory.
1708 */
1709 
handle_grant_struct(enum enum_acl_lists struct_no,bool drop,LEX_USER * user_from,LEX_USER * user_to,bool on_drop_role_priv)1710 static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
1711                                LEX_USER *user_from, LEX_USER *user_to,
1712                                bool on_drop_role_priv) {
1713   DBUG_TRACE;
1714   DBUG_PRINT("info", ("scan struct: %u  search: '%s'@'%s'", struct_no,
1715                       user_from->user.str, user_from->host.str));
1716 
1717   int result = 0;
1718   auto matches = [user_from, &result](const char *user, const char *host) {
1719     if (!user) user = "";
1720     if (!host) host = "";
1721     bool match =
1722         strcmp(user_from->user.str, user) == 0 &&
1723         my_strcasecmp(system_charset_info, user_from->host.str, host) == 0;
1724     if (match) result = 1;
1725     return match;
1726   };
1727 
1728   DBUG_ASSERT(assert_acl_cache_write_lock(current_thd));
1729 
1730   switch (struct_no) {
1731     case USER_ACL:
1732       for (uint idx = 0; idx < acl_users->size(); idx++) {
1733         ACL_USER *acl_user = &acl_users->at(idx);
1734         if (!matches(acl_user->user, acl_user->host.get_host())) continue;
1735 
1736         if (drop) {
1737           // if we're dropping roles and the account is not locked (not a role)
1738           // bail off
1739           if (on_drop_role_priv && !acl_user->account_locked) {
1740             char command[128];
1741             get_privilege_desc(command, sizeof(command), CREATE_USER_ACL);
1742             my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command);
1743             return -1;
1744           }
1745           acl_restrictions->remove_restrictions(acl_user);
1746           acl_users->erase(idx);
1747           rebuild_cached_acl_users_for_name();
1748           /*
1749              - If we are iterating through an array then we just have moved all
1750              elements after the current element one position closer to its head.
1751              This means that we have to take another look at the element at
1752              current position as it is a new element from the array's tail.
1753              - This is valid for case USER_ACL, DB_ACL and PROXY_USERS_ACL.
1754            */
1755           idx--;
1756         } else if (user_to) {
1757           auto restrictions = acl_restrictions->find_restrictions(acl_user);
1758           acl_restrictions->remove_restrictions(acl_user);
1759           acl_user->set_user(&global_acl_memory, user_to->user.str);
1760           acl_user->set_host(&global_acl_memory, user_to->host.str);
1761           acl_restrictions->upsert_restrictions(acl_user, restrictions);
1762 
1763           rebuild_cached_acl_users_for_name();
1764         } else {
1765           /* If search is requested, we do not need to search further. */
1766           break;
1767         }
1768       }
1769       break;
1770 
1771     case DB_ACL:
1772       for (uint idx = 0; idx < acl_dbs->size(); idx++) {
1773         ACL_DB *acl_db = &acl_dbs->at(idx);
1774         if (!matches(acl_db->user, acl_db->host.get_host())) continue;
1775 
1776         if (drop) {
1777           acl_dbs->erase(idx);
1778           idx--;
1779         } else if (user_to) {
1780           acl_db->set_user(&global_acl_memory, user_to->user.str);
1781           acl_db->set_host(&global_acl_memory, user_to->host.str);
1782         } else {
1783           /* If search is requested, we do not need to search further. */
1784           break;
1785         }
1786       }
1787       break;
1788 
1789     case COLUMN_PRIVILEGES_HASH:
1790       if (drop)
1791         remove_matching_grants(column_priv_hash.get(), matches);
1792       else if (user_to) {
1793         if (rename_matching_grants(column_priv_hash.get(), matches, user_to))
1794           return -1;
1795       } else
1796         search_for_matching_grant(column_priv_hash.get(), matches);
1797       break;
1798 
1799     case PROC_PRIVILEGES_HASH:
1800       if (drop)
1801         remove_matching_grants(proc_priv_hash.get(), matches);
1802       else if (user_to) {
1803         if (rename_matching_grants(proc_priv_hash.get(), matches, user_to))
1804           return -1;
1805       } else
1806         search_for_matching_grant(proc_priv_hash.get(), matches);
1807       break;
1808 
1809     case FUNC_PRIVILEGES_HASH:
1810       if (drop)
1811         remove_matching_grants(func_priv_hash.get(), matches);
1812       else if (user_to) {
1813         if (rename_matching_grants(func_priv_hash.get(), matches, user_to))
1814           return -1;
1815       } else
1816         search_for_matching_grant(func_priv_hash.get(), matches);
1817       break;
1818 
1819     case PROXY_USERS_ACL:
1820       for (uint idx = 0; idx < acl_proxy_users->size(); idx++) {
1821         ACL_PROXY_USER *acl_proxy_user = &acl_proxy_users->at(idx);
1822         if (!matches(acl_proxy_user->get_user(),
1823                      acl_proxy_user->host.get_host()))
1824           continue;
1825 
1826         if (drop) {
1827           acl_proxy_users->erase(idx);
1828           idx--;
1829         } else if (user_to) {
1830           acl_proxy_user->set_user(&global_acl_memory, user_to->user.str);
1831           acl_proxy_user->set_host(&global_acl_memory, user_to->host.str);
1832         } else {
1833           /* If search is requested, we do not need to search further. */
1834           break;
1835         }
1836       }
1837       break;
1838 
1839     default:
1840       return -1;
1841   }
1842 
1843 #ifdef EXTRA_DEBUG
1844   DBUG_PRINT("loop", ("scan struct: %u  result %d", struct_no, result));
1845 #endif
1846 
1847   return result;
1848 }
1849 
1850 /**
1851   Handle all privilege tables and in-memory privilege structures.
1852     @param  thd                 Thread handle
1853     @param  tables              The array with the seven open tables.
1854     @param  drop                If user_from is to be dropped.
1855     @param  user_from           The the user to be searched/dropped/renamed.
1856     @param  user_to             The new name for the user if to be renamed,
1857                                 NULL otherwise.
1858     @param  on_drop_role_priv   Enabled via the DROP ROLE privilege
1859 
1860   @note
1861     Go through all grant tables and in-memory grant structures and apply
1862     the requested operation.
1863     Delete from grant data if drop is true.
1864     Update in grant data if drop is false and user_to is not NULL.
1865     Search in grant data if drop is false and user_to is NULL.
1866 
1867   @return  operation result
1868     @retval > 0  At least one element matched.
1869     @retval 0  OK, but no element matched.
1870     @retval < 0  System error (OOM, error from storage engine).
1871 */
1872 
handle_grant_data(THD * thd,TABLE_LIST * tables,bool drop,LEX_USER * user_from,LEX_USER * user_to,bool on_drop_role_priv)1873 static int handle_grant_data(THD *thd, TABLE_LIST *tables, bool drop,
1874                              LEX_USER *user_from, LEX_USER *user_to,
1875                              bool on_drop_role_priv) {
1876   int result = 0;
1877   int found;
1878   int ret;
1879   Acl_table_intact table_intact(thd);
1880   DBUG_TRACE;
1881 
1882   if (drop) {
1883     /*
1884       Tables are defined by open_grant_tables()
1885       index 6 := mysql.role_edges
1886       index 7 := mysql.default_roles
1887     */
1888     if (revoke_all_roles_from_user(
1889             thd, tables[ACL_TABLES::TABLE_ROLE_EDGES].table,
1890             tables[ACL_TABLES::TABLE_DEFAULT_ROLES].table, user_from)) {
1891       return -1;
1892     }
1893     /* Remove all associated dynamic privileges on a best effort basis */
1894     Update_dynamic_privilege_table update_table(
1895         thd, tables[ACL_TABLES::TABLE_DYNAMIC_PRIV].table);
1896     result = revoke_all_dynamic_privileges(user_from->user, user_from->host,
1897                                            update_table);
1898   }
1899 
1900   /* Handle user table. */
1901   if ((found = handle_grant_table(thd, tables, ACL_TABLES::TABLE_USER, drop,
1902                                   user_from, user_to)) < 0) {
1903     /* Handle of table failed, don't touch the in-memory array. */
1904     return -1;
1905   } else {
1906     /* Handle user array. */
1907     if ((ret = handle_grant_struct(USER_ACL, drop, user_from, user_to,
1908                                    on_drop_role_priv)) > 0 ||
1909         found) {
1910       result = 1; /* At least one record/element found. */
1911       /* If search is requested, we do not need to search further. */
1912       if (!drop && !user_to) goto end;
1913     } else if (ret < 0) {
1914       result = -1;
1915       goto end;
1916     }
1917   }
1918 
1919   /* Handle db table. */
1920   if ((found = handle_grant_table(thd, tables, ACL_TABLES::TABLE_DB, drop,
1921                                   user_from, user_to)) < 0) {
1922     /* Handle of table failed, don't touch the in-memory array. */
1923     return -1;
1924   } else {
1925     /* Handle db array. */
1926     if ((((ret = handle_grant_struct(DB_ACL, drop, user_from, user_to,
1927                                      on_drop_role_priv)) > 0 &&
1928           !result) ||
1929          found) &&
1930         !result) {
1931       result = 1; /* At least one record/element found. */
1932       /* If search is requested, we do not need to search further. */
1933       if (!drop && !user_to) goto end;
1934     } else if (ret < 0) {
1935       result = -1;
1936       goto end;
1937     }
1938   }
1939 
1940   DBUG_EXECUTE_IF("mysql_handle_grant_data_fail_on_routine_table",
1941                   DBUG_SET("+d,wl7158_handle_grant_table_2"););
1942 
1943   /* Handle stored routines table. */
1944   if ((found = handle_grant_table(thd, tables, ACL_TABLES::TABLE_PROCS_PRIV,
1945                                   drop, user_from, user_to)) < 0) {
1946     /* Handle of table failed, don't touch in-memory array. */
1947     DBUG_EXECUTE_IF("mysql_handle_grant_data_fail_on_routine_table",
1948                     DBUG_SET("-d,wl7158_handle_grant_table_2"););
1949     return -1;
1950   } else {
1951     /* Handle procs array. */
1952     if ((((ret = handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from,
1953                                      user_to, on_drop_role_priv)) > 0 &&
1954           !result) ||
1955          found) &&
1956         !result) {
1957       result = 1; /* At least one record/element found. */
1958       /* If search is requested, we do not need to search further. */
1959       if (!drop && !user_to) goto end;
1960     } else if (ret < 0) {
1961       result = -1;
1962       goto end;
1963     }
1964     /* Handle funcs array. */
1965     if ((((ret = handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from,
1966                                      user_to, on_drop_role_priv)) > 0 &&
1967           !result) ||
1968          found) &&
1969         !result) {
1970       result = 1; /* At least one record/element found. */
1971       /* If search is requested, we do not need to search further. */
1972       if (!drop && !user_to) goto end;
1973     } else if (ret < 0) {
1974       result = -1;
1975       goto end;
1976     }
1977   }
1978 
1979   DBUG_EXECUTE_IF("mysql_handle_grant_data_fail_on_tables_table",
1980                   DBUG_SET("+d,wl7158_handle_grant_table_2"););
1981 
1982   /* Handle tables table. */
1983   if ((found = handle_grant_table(thd, tables, ACL_TABLES::TABLE_TABLES_PRIV,
1984                                   drop, user_from, user_to)) < 0) {
1985     DBUG_EXECUTE_IF("mysql_handle_grant_data_fail_on_tables_table",
1986                     DBUG_SET("-d,wl7158_handle_grant_table_2"););
1987     /* Handle of table failed, don't touch columns and in-memory array. */
1988     return -1;
1989   } else {
1990     if (found && !result) {
1991       result = 1; /* At least one record found. */
1992       /* If search is requested, we do not need to search further. */
1993       if (!drop && !user_to) goto end;
1994     }
1995 
1996     DBUG_EXECUTE_IF("mysql_handle_grant_data_fail_on_columns_table",
1997                     DBUG_SET("+d,wl7158_handle_grant_table_2"););
1998 
1999     /* Handle columns table. */
2000     if ((found = handle_grant_table(thd, tables, ACL_TABLES::TABLE_COLUMNS_PRIV,
2001                                     drop, user_from, user_to)) < 0) {
2002       DBUG_EXECUTE_IF("mysql_handle_grant_data_fail_on_columns_table",
2003                       DBUG_SET("-d,wl7158_handle_grant_table_2"););
2004       /* Handle of table failed, don't touch the in-memory array. */
2005       return -1;
2006     } else {
2007       /* Handle columns hash. */
2008       if ((((ret = handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from,
2009                                        user_to, on_drop_role_priv)) > 0 &&
2010             !result) ||
2011            found) &&
2012           !result)
2013         result = 1; /* At least one record/element found. */
2014       else if (ret < 0)
2015         result = -1;
2016     }
2017   }
2018 
2019   /* Handle proxies_priv table. */
2020   if (tables[ACL_TABLES::TABLE_PROXIES_PRIV].table) {
2021     DBUG_EXECUTE_IF("mysql_handle_grant_data_fail_on_proxies_priv_table",
2022                     DBUG_SET("+d,wl7158_handle_grant_table_2"););
2023 
2024     if (table_intact.check(tables[ACL_TABLES::TABLE_PROXIES_PRIV].table,
2025                            ACL_TABLES::TABLE_PROXIES_PRIV)) {
2026       result = -1;
2027       goto end;
2028     }
2029 
2030     if ((found = handle_grant_table(thd, tables, ACL_TABLES::TABLE_PROXIES_PRIV,
2031                                     drop, user_from, user_to)) < 0) {
2032       DBUG_EXECUTE_IF("mysql_handle_grant_data_fail_on_proxies_priv_table",
2033                       DBUG_SET("-d,wl7158_handle_grant_table_2"););
2034       /* Handle of table failed, don't touch the in-memory array. */
2035       return -1;
2036     } else {
2037       /* Handle proxies_priv array. */
2038       if (((ret = handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to,
2039                                       on_drop_role_priv)) > 0 &&
2040            !result) ||
2041           found)
2042         result = 1; /* At least one record/element found. */
2043       else if (ret < 0)
2044         result = -1;
2045     }
2046   }
2047 
2048   {
2049     bool row_existed;
2050     if (handle_password_history_table(thd, tables, drop, user_from, user_to,
2051                                       &row_existed))
2052       return -1;
2053     else if (row_existed)
2054       return 1;
2055   }
2056 
2057 end:
2058   return result;
2059 }
2060 
2061 /*
2062   Create a list of users.
2063 
2064   On successful completion the function emits my_ok() or my_eof().
2065 
2066   SYNOPSIS
2067     mysql_create_user()
2068     thd                         The current thread.
2069     list                        The users to create.
2070 
2071   RETURN
2072     false       OK.
2073     true        Error.
2074 */
2075 
mysql_create_user(THD * thd,List<LEX_USER> & list,bool if_not_exists,bool is_role)2076 bool mysql_create_user(THD *thd, List<LEX_USER> &list, bool if_not_exists,
2077                        bool is_role) {
2078   int result = 0;
2079   String wrong_users;
2080   LEX_USER *user_name, *tmp_user_name;
2081   List_iterator<LEX_USER> user_list(list);
2082   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
2083   bool transactional_tables;
2084   acl_table::Pod_user_what_to_update what_to_update;
2085   bool is_anonymous_user = false;
2086   std::set<LEX_USER *> extra_users;
2087   std::set<LEX_USER *> reset_users;
2088   Userhostpassword_list generated_passwords;
2089   DBUG_TRACE;
2090 
2091   /*
2092     This statement will be replicated as a statement, even when using
2093     row-based replication.  The binlog state will be cleared here to
2094     statement based replication and will be reset to the originals
2095     values when we are out of this function scope
2096   */
2097   Save_and_Restore_binlog_format_state binlog_format_state(thd);
2098 
2099   /* CREATE USER may be skipped on replication client. */
2100   if ((result = open_grant_tables(thd, tables, &transactional_tables)))
2101     return result != 1;
2102 
2103   { /* Critical section */
2104     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
2105 
2106     if (!acl_cache_lock.lock()) {
2107       commit_and_close_mysql_tables(thd);
2108       return true;
2109     }
2110 
2111     if (check_system_user_privilege(thd, list)) {
2112       commit_and_close_mysql_tables(thd);
2113       return true;
2114     }
2115 
2116     while ((tmp_user_name = user_list++)) {
2117       bool history_check_done = false;
2118       /*
2119         Ignore the current user as it already exists.
2120       */
2121       if (!(user_name = get_current_user(thd, tmp_user_name))) {
2122         result = 1;
2123         log_user(thd, &wrong_users, user_name, wrong_users.length() > 0);
2124         continue;
2125       }
2126       if (set_and_validate_user_attributes(
2127               thd, user_name, what_to_update, true, is_role,
2128               &tables[ACL_TABLES::TABLE_PASSWORD_HISTORY], &history_check_done,
2129               "CREATE USER", generated_passwords)) {
2130         result = 1;
2131         log_user(thd, &wrong_users, user_name, wrong_users.length() > 0);
2132         continue;
2133       }
2134       if (!strcmp(user_name->user.str, "") &&
2135           (what_to_update.m_what & PASSWORD_EXPIRE_ATTR)) {
2136         is_anonymous_user = true;
2137         result = 1;
2138         continue;
2139       }
2140 
2141       /*
2142         Search all in-memory structures and grant tables
2143         for a mention of the new user name.
2144         We may not need to process the password history here since it may have
2145         already been processed in set_and_validate_user_attributes().
2146         Hence we check the flag and temporarily set the history table
2147         to unopened state and then restore it back.
2148       */
2149       int ret;
2150       TABLE *history_tbl = tables[ACL_TABLES::TABLE_PASSWORD_HISTORY].table;
2151       if (history_check_done)
2152         tables[ACL_TABLES::TABLE_PASSWORD_HISTORY].table = nullptr;
2153       ret = handle_grant_data(thd, tables, false, user_name, nullptr, false);
2154       if (history_check_done)
2155         tables[ACL_TABLES::TABLE_PASSWORD_HISTORY].table = history_tbl;
2156       if (ret) {
2157         if (ret < 0) {
2158           result = 1;
2159           break;
2160         }
2161         if (if_not_exists) {
2162           String warn_user;
2163           log_user(thd, &warn_user, user_name, false);
2164           push_warning_printf(
2165               thd, Sql_condition::SL_NOTE, ER_USER_ALREADY_EXISTS,
2166               ER_THD(thd, ER_USER_ALREADY_EXISTS), warn_user.c_ptr_safe());
2167           try {
2168             extra_users.insert(user_name);
2169           } catch (...) {
2170             LogErr(WARNING_LEVEL,
2171                    ER_USER_NOT_IN_EXTRA_USERS_BINLOG_POSSIBLY_INCOMPLETE,
2172                    warn_user.c_ptr_safe());
2173           }
2174           continue;
2175         } else {
2176           log_user(thd, &wrong_users, user_name, wrong_users.length() > 0);
2177           result = 1;
2178         }
2179         continue;
2180       }
2181 
2182       ret = replace_user_table(thd, tables[ACL_TABLES::TABLE_USER].table,
2183                                user_name, 0, false, true, what_to_update);
2184 
2185       if (ret) {
2186         result = 1;
2187         if (ret < 0) break;
2188 
2189         log_user(thd, &wrong_users, user_name, wrong_users.length() > 0);
2190 
2191         continue;
2192       }
2193 
2194       /*
2195         Update default roles if any were specified. If roles doesn't exist we
2196         fail the statement. If the role exists but isn't granted, this statement
2197         performs an implicit GRANT.
2198       */
2199       if (thd->lex->default_roles != nullptr &&
2200           thd->lex->sql_command == SQLCOM_CREATE_USER) {
2201         /* Grantee can not be an anonymous user */
2202         if (tmp_user_name->user.length == 0 ||
2203             *(tmp_user_name->user.str) == '\0') {
2204           my_error(ER_CANNOT_GRANT_ROLES_TO_ANONYMOUS_USER, MYF(0));
2205           result = 1;
2206         }
2207 
2208         List_of_auth_id_refs default_roles;
2209         List_iterator<LEX_USER> role_it(*(thd->lex->default_roles));
2210         LEX_USER *role;
2211 
2212         /* Check for anonymous user in roles' list */
2213         while ((role = role_it++) && result == 0) {
2214           Auth_id role_id(role);
2215           if (role->user.length == 0 || *(role->user.str) == '\0') {
2216             std::string to_user = create_authid_str_from(tmp_user_name);
2217             my_error(ER_FAILED_ROLE_GRANT, MYF(0), role_id.auth_str().c_str(),
2218                      to_user.c_str());
2219             break;
2220           }
2221         }
2222 
2223         /* Check administrative access over roles */
2224         if (result == 0) {
2225           if (!has_grant_role_privilege(thd, thd->lex->default_roles)) {
2226             my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
2227                      "WITH ADMIN, ROLE_ADMIN, SUPER");
2228             result = 1;
2229           }
2230         }
2231 
2232         /* SYSTEM_USER requirement */
2233         role_it.rewind();
2234         while ((role = role_it++) && result == 0) {
2235           Auth_id role_id(role);
2236           if (thd->security_context()->can_operate_with(role_id,
2237                                                         consts::system_user)) {
2238             result = 1;
2239           }
2240         }
2241 
2242         ACL_USER *acl_user = nullptr;
2243         if (result == 0)
2244           acl_user = find_acl_user(tmp_user_name->host.str,
2245                                    tmp_user_name->user.str, true);
2246         if (acl_user == nullptr) {
2247           std::string authid = create_authid_str_from(tmp_user_name);
2248           my_error(ER_USER_DOES_NOT_EXIST, MYF(0), authid.c_str());
2249           result = 1;
2250         }
2251 
2252         /* Perform role grants */
2253         role_it.rewind();
2254         while ((role = role_it++) && result == 0) {
2255           Auth_id role_id(role);
2256           if (!is_granted_role(tmp_user_name->user, tmp_user_name->host,
2257                                role->user, role->host)) {
2258             ACL_USER *acl_role =
2259                 find_acl_user(role->host.str, role->user.str, true);
2260             if (acl_role == nullptr) {
2261               std::string authid = create_authid_str_from(role);
2262               my_error(ER_USER_DOES_NOT_EXIST, MYF(0), authid.c_str());
2263               result = 1;
2264             } else {
2265               DBUG_ASSERT(result == 0);
2266               grant_role(acl_role, acl_user, false);
2267               Auth_id_ref from_user = create_authid_from(role);
2268               Auth_id_ref to_user = create_authid_from(tmp_user_name);
2269               result = modify_role_edges_in_table(
2270                   thd, tables[ACL_TABLES::TABLE_ROLE_EDGES].table, from_user,
2271                   to_user, false, false);
2272             }
2273           }  // end if !is_granted_role()
2274 
2275           default_roles.push_back(create_authid_from(role));
2276         }
2277 
2278         /* Make granted roles default */
2279         if (result == 0)
2280           result = alter_user_set_default_roles(
2281               thd, tables[ACL_TABLES::TABLE_DEFAULT_ROLES].table, tmp_user_name,
2282               default_roles);
2283       }
2284 
2285       /* For connection resources */
2286       reset_users.insert(tmp_user_name);
2287 
2288     }  // END while tmp_user_name= user_lists++
2289 
2290     // We must not have plain text password for any user at this point.
2291     thd->lex->contains_plaintext_password = false;
2292 
2293     /* In case of SE error, we would have raised error before reaching here. */
2294     if (result && !thd->is_error()) {
2295       my_error(ER_CANNOT_USER, MYF(0),
2296                (is_role ? "CREATE ROLE" : "CREATE USER"),
2297                is_anonymous_user ? "anonymous user" : wrong_users.c_ptr_safe());
2298     }
2299 
2300     User_params user_params(&extra_users);
2301     result = log_and_commit_acl_ddl(thd, transactional_tables, &extra_users,
2302                                     &user_params);
2303   } /* Critical section */
2304 
2305   if (!result) {
2306     LEX_USER *reset_user;
2307     DEBUG_SYNC(thd, "before_reset_mqh_in_create_user");
2308     for (LEX_USER *one_user : reset_users) {
2309       if ((reset_user = get_current_user(thd, one_user))) {
2310         reset_mqh(thd, reset_user, false);
2311       }
2312     }
2313     /* Notify storage engines */
2314     acl_notify_htons(thd, SQLCOM_CREATE_USER, &list);
2315   }
2316 
2317   if (result == 0) {
2318     if (generated_passwords.size() == 0) {
2319       my_ok(thd);
2320     } else if (send_password_result_set(thd, generated_passwords)) {
2321       result = 1;
2322     }
2323   }  // end if
2324 
2325   /*
2326     If this is a slave thread we should never have generated random passwords
2327   */
2328   DBUG_ASSERT(!thd->slave_thread ||
2329               (thd->slave_thread && generated_passwords.size() == 0));
2330   return result;
2331 }
2332 
2333 /**
2334   Drop a list of users and all their privileges.
2335 
2336   @param thd                         The current thread.
2337   @param list                        The users to drop.
2338   @param if_exists                   The if exists flag
2339   @param on_drop_role_priv           enabled by the DROP ROLE privilege
2340 
2341   @retval false      OK
2342   @retval true       Error
2343 */
2344 
mysql_drop_user(THD * thd,List<LEX_USER> & list,bool if_exists,bool on_drop_role_priv)2345 bool mysql_drop_user(THD *thd, List<LEX_USER> &list, bool if_exists,
2346                      bool on_drop_role_priv) {
2347   int result = 0;
2348   String wrong_users;
2349   LEX_USER *user_name, *tmp_user_name;
2350   List_iterator<LEX_USER> user_list(list);
2351   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
2352   sql_mode_t old_sql_mode = thd->variables.sql_mode;
2353   bool transactional_tables;
2354   std::set<LEX_USER *> audit_users;
2355   DBUG_TRACE;
2356 
2357   /*
2358     Make sure that none of the authids we're about to drop is used as a
2359     mandatory role. Mandatory roles needs to be disabled first before the
2360     authid can be dropped.
2361   */
2362   LEX_USER *user;
2363   std::vector<Role_id> mandatory_roles;
2364 
2365   /*
2366     This statement will be replicated as a statement, even when using
2367     row-based replication.  The binlog state will be cleared here to
2368     statement based replication and will be reset to the originals
2369     values when we are out of this function scope
2370   */
2371   Save_and_Restore_binlog_format_state binlog_format_state(thd);
2372 
2373   /* DROP USER may be skipped on replication client. */
2374   if ((result = open_grant_tables(thd, tables, &transactional_tables)))
2375     return result != 1;
2376 
2377   { /* Critical section */
2378     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
2379 
2380     if (!acl_cache_lock.lock()) {
2381       commit_and_close_mysql_tables(thd);
2382       return true;
2383     }
2384 
2385     if (check_system_user_privilege(thd, list)) {
2386       commit_and_close_mysql_tables(thd);
2387       return true;
2388     }
2389 
2390     get_mandatory_roles(&mandatory_roles);
2391     while ((user = user_list++) != nullptr) {
2392       if (std::find_if(mandatory_roles.begin(), mandatory_roles.end(),
2393                        [&](Role_id &id) -> bool {
2394                          Role_id id2(user->user, user->host);
2395                          return id == id2;
2396                        }) != mandatory_roles.end()) {
2397         Role_id authid(user->user, user->host);
2398         std::string out;
2399         authid.auth_str(&out);
2400         my_error(ER_MANDATORY_ROLE, MYF(0), out.c_str());
2401         return true;
2402       }
2403     }
2404     user_list.rewind();
2405     thd->variables.sql_mode &= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
2406 
2407     while ((tmp_user_name = user_list++)) {
2408       if (!(user_name = get_current_user(thd, tmp_user_name))) {
2409         result = 1;
2410         continue;
2411       }
2412 
2413       audit_users.insert(tmp_user_name);
2414 
2415       int ret = handle_grant_data(thd, tables, true, user_name, nullptr,
2416                                   on_drop_role_priv);
2417       if (ret <= 0) {
2418         if (ret < 0) {
2419           result = 1;
2420           break;
2421         }
2422         if (if_exists) {
2423           String warn_user;
2424           log_user(thd, &warn_user, user_name, false);
2425           push_warning_printf(
2426               thd, Sql_condition::SL_NOTE, ER_USER_DOES_NOT_EXIST,
2427               ER_THD(thd, ER_USER_DOES_NOT_EXIST), warn_user.c_ptr_safe());
2428         } else {
2429           log_user(thd, &wrong_users, user_name, wrong_users.length() > 0);
2430           result = 1;
2431         }
2432         continue;
2433       }
2434     }
2435 
2436     /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
2437     rebuild_check_host();
2438     rebuild_cached_acl_users_for_name();
2439 
2440     if (result && !thd->is_error()) {
2441       String operation_str;
2442       if (thd->query_plan.get_command() == SQLCOM_DROP_ROLE) {
2443         operation_str.append("DROP ROLE");
2444       } else {
2445         operation_str.append("DROP USER");
2446       }
2447       my_error(ER_CANNOT_USER, MYF(0), operation_str.c_ptr_quick(),
2448                wrong_users.c_ptr_safe());
2449     }
2450 
2451     if (!thd->is_error())
2452       result =
2453           populate_roles_caches(thd, (tables + ACL_TABLES::TABLE_ROLE_EDGES));
2454 
2455     result = log_and_commit_acl_ddl(thd, transactional_tables);
2456 
2457     {
2458       /* Notify audit plugin. We will ignore the return value. */
2459       LEX_USER *audit_user;
2460       for (LEX_USER *one_user : audit_users) {
2461         if ((audit_user = get_current_user(thd, one_user)))
2462           mysql_audit_notify(
2463               thd, AUDIT_EVENT(MYSQL_AUDIT_AUTHENTICATION_AUTHID_DROP),
2464               thd->is_error(), audit_user->user.str, audit_user->host.str,
2465               audit_user->plugin.str, is_role_id(audit_user), nullptr, nullptr);
2466       }
2467     }
2468   } /* Critical section */
2469 
2470   /* Notify storage engines */
2471   if (!result) {
2472     acl_notify_htons(thd, SQLCOM_DROP_USER, &list);
2473   }
2474 
2475   thd->variables.sql_mode = old_sql_mode;
2476   return result;
2477 }
2478 
2479 /*
2480   Rename a user.
2481 
2482   SYNOPSIS
2483     mysql_rename_user()
2484     thd                         The current thread.
2485     list                        The user name pairs: (from, to).
2486 
2487   RETURN
2488     false       OK.
2489     true        Error.
2490 */
2491 
mysql_rename_user(THD * thd,List<LEX_USER> & list)2492 bool mysql_rename_user(THD *thd, List<LEX_USER> &list) {
2493   int result = 0;
2494   String wrong_users;
2495   LEX_USER *tmp_user_from;
2496   LEX_USER *tmp_user_to;
2497   List_iterator<LEX_USER> user_list(list);
2498   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
2499   std::unique_ptr<Security_context> orig_sctx = nullptr;
2500   bool transactional_tables;
2501   DBUG_TRACE;
2502 
2503   { /* Is this auth id a role id? */
2504     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
2505     List_iterator<LEX_USER> authid_list_iterator(list);
2506     LEX_USER *authid;
2507 
2508     if (!acl_cache_lock.lock()) return true;
2509 
2510     if (check_system_user_privilege(thd, list)) return true;
2511 
2512     while ((authid = authid_list_iterator++)) {
2513       if (!(authid = get_current_user(thd, authid))) {
2514         /*
2515           This user is not a role.
2516         */
2517         continue;
2518       }
2519       if (is_role_id(authid)) {
2520         my_error(ER_RENAME_ROLE, MYF(0));
2521         return true;
2522       }
2523     }
2524   }
2525 
2526   /*
2527     This statement will be replicated as a statement, even when using
2528     row-based replication.  The binlog state will be cleared here to
2529     statement based replication and will be reset to the originals
2530     values when we are out of this function scope
2531   */
2532   Save_and_Restore_binlog_format_state binlog_format_state(thd);
2533 
2534   /* RENAME USER may be skipped on replication client. */
2535   if ((result = open_grant_tables(thd, tables, &transactional_tables)))
2536     return result != 1;
2537 
2538   { /* Critical section */
2539     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
2540     if (!acl_cache_lock.lock()) {
2541       commit_and_close_mysql_tables(thd);
2542       return true;
2543     }
2544 
2545     while ((tmp_user_from = user_list++)) {
2546       LEX_USER *user_from;
2547       LEX_USER *user_to;
2548       if (!(user_from = get_current_user(thd, tmp_user_from))) {
2549         result = 1;
2550         continue;
2551       }
2552       tmp_user_to = user_list++;
2553       if (!(user_to = get_current_user(thd, tmp_user_to))) {
2554         result = 1;
2555         continue;
2556       }
2557       DBUG_ASSERT(user_to != nullptr); /* Syntax enforces pairs of users. */
2558 
2559       /*
2560         If we are renaming to anonymous user, make sure no roles are granted.
2561       */
2562       if (user_to->user.length == 0 || *(user_to->user.str) == '\0') {
2563         List_of_granted_roles granted_roles;
2564         get_granted_roles(user_from, &granted_roles);
2565         if (!granted_roles.empty()) {
2566           log_user(thd, &wrong_users, user_from, wrong_users.length() > 0);
2567           result = 1;
2568           continue;
2569         }
2570       }
2571 
2572       /*
2573         Search all in-memory structures and grant tables
2574         for a mention of the new user name.
2575       */
2576       int ret = handle_grant_data(thd, tables, false, user_to, nullptr, false);
2577 
2578       if (ret != 0) {
2579         if (ret < 0) {
2580           result = 1;
2581           break;
2582         }
2583 
2584         log_user(thd, &wrong_users, user_from, wrong_users.length() > 0);
2585         result = 1;
2586         continue;
2587       }
2588 
2589       ret = handle_grant_data(thd, tables, false, user_from, user_to, false);
2590 
2591       if (ret <= 0) {
2592         if (ret < 0) {
2593           result = 1;
2594           break;
2595         }
2596 
2597         log_user(thd, &wrong_users, user_from, wrong_users.length() > 0);
2598         result = 1;
2599         continue;
2600       }
2601 
2602       Update_dynamic_privilege_table update_table(
2603           thd, tables[ACL_TABLES::TABLE_DYNAMIC_PRIV].table);
2604       if (rename_dynamic_grant(user_from->user, user_from->host, user_to->user,
2605                                user_to->host, update_table)) {
2606         result = 1;
2607         break;
2608       }
2609       roles_rename_authid(thd, tables[ACL_TABLES::TABLE_ROLE_EDGES].table,
2610                           tables[ACL_TABLES::TABLE_DEFAULT_ROLES].table,
2611                           user_from, user_to);
2612       /* Update the security context if user renames self */
2613       if (do_update_sctx(thd->security_context(), user_from)) {
2614         /* Keep a copy of original security context if not done already */
2615         if (orig_sctx == nullptr) {
2616           orig_sctx = std::make_unique<Security_context>();
2617           *(orig_sctx.get()) = *(thd->security_context());
2618         }
2619         update_sctx(thd->security_context(), tmp_user_to);
2620       }
2621     }
2622 
2623     /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
2624     rebuild_check_host();
2625     rebuild_cached_acl_users_for_name();
2626 
2627     if (result && !thd->is_error())
2628       my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
2629 
2630     if (!thd->is_error())
2631       result =
2632           populate_roles_caches(thd, (tables + ACL_TABLES::TABLE_ROLE_EDGES));
2633 
2634     /*
2635       Restore the orignal security context temporarily because binlog must
2636       write the original definer/invoker in the binlog in order for slave
2637       to work
2638     */
2639     Security_context *current_sctx = thd->security_context();
2640     current_sctx->restore_security_context(thd, orig_sctx.get());
2641 
2642     result = log_and_commit_acl_ddl(thd, transactional_tables);
2643 
2644     /* Restore the updated security context */
2645     current_sctx->restore_security_context(thd, current_sctx);
2646 
2647     /* Notify audit plugin. We will ignore the return value. */
2648     LEX_USER *user_from, *user_to;
2649     List_iterator<LEX_USER> audit_user_list(list);
2650     LEX_USER *audit_user_from, *audit_user_to;
2651     while ((audit_user_from = audit_user_list++)) {
2652       audit_user_to = audit_user_list++;
2653 
2654       if ((((user_from = get_current_user(thd, audit_user_from)) &&
2655             ((user_to = get_current_user(thd, audit_user_to))))))
2656         mysql_audit_notify(
2657             thd, AUDIT_EVENT(MYSQL_AUDIT_AUTHENTICATION_AUTHID_RENAME),
2658             thd->is_error(), user_from->user.str, user_from->host.str,
2659             user_from->plugin.str, is_role_id(user_from), user_to->user.str,
2660             user_to->user.str);
2661     }
2662   } /* Critical section */
2663 
2664   /* Notify storage engines */
2665   if (!result) {
2666     acl_notify_htons(thd, SQLCOM_RENAME_USER, &list);
2667   }
2668 
2669   return result;
2670 }
2671 
2672 /*
2673   Alter user list.
2674 
2675   SYNOPSIS
2676     mysql_alter_user()
2677     thd                         The current thread.
2678     list                        The user names.
2679 
2680   RETURN
2681     false       OK.
2682     true        Error.
2683 */
2684 
mysql_alter_user(THD * thd,List<LEX_USER> & list,bool if_exists)2685 bool mysql_alter_user(THD *thd, List<LEX_USER> &list, bool if_exists) {
2686   int result = 0;
2687   bool is_anonymous_user = false;
2688   String wrong_users;
2689   LEX_USER *user_from, *tmp_user_from;
2690   List_iterator<LEX_USER> user_list(list);
2691   TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
2692   bool transactional_tables;
2693   bool is_privileged_user = false;
2694   std::set<LEX_USER *> extra_users;
2695   ACL_USER *self = nullptr;
2696   bool password_expire_undo = false;
2697   std::set<LEX_USER *> audit_users;
2698   std::set<LEX_USER *> reset_users;
2699   Userhostpassword_list generated_passwords;
2700   DBUG_TRACE;
2701 
2702   /*
2703     This statement will be replicated as a statement, even when using
2704     row-based replication.  The binlog state will be cleared here to
2705     statement based replication and will be reset to the originals
2706     values when we are out of this function scope
2707   */
2708   Save_and_Restore_binlog_format_state binlog_format_state(thd);
2709 
2710   if ((result = open_grant_tables(thd, tables, &transactional_tables)))
2711     return result != 1;
2712 
2713   { /* Critical section */
2714     Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
2715 
2716     if (!acl_cache_lock.lock()) {
2717       commit_and_close_mysql_tables(thd);
2718       return true;
2719     }
2720 
2721     if (check_system_user_privilege(thd, list)) {
2722       commit_and_close_mysql_tables(thd);
2723       return true;
2724     }
2725 
2726     is_privileged_user = is_privileged_user_for_credential_change(thd);
2727 
2728     while ((tmp_user_from = user_list++)) {
2729       int ret;
2730       ACL_USER *acl_user;
2731       acl_table::Pod_user_what_to_update what_to_alter;
2732       bool history_check_done = false;
2733       TABLE *history_tbl = nullptr;
2734       bool dummy_row_existed = false;
2735 
2736       /* add the defaults where needed */
2737       if (!(user_from = get_current_user(thd, tmp_user_from))) {
2738         log_user(thd, &wrong_users, tmp_user_from, wrong_users.length() > 0);
2739         result = 1;
2740         continue;
2741       }
2742 
2743       /* copy password expire attributes to individual lex user */
2744       user_from->alter_status = thd->lex->alter_password;
2745 
2746       if (set_and_validate_user_attributes(
2747               thd, user_from, what_to_alter, is_privileged_user, false,
2748               &tables[ACL_TABLES::TABLE_PASSWORD_HISTORY], &history_check_done,
2749               "ALTER USER", generated_passwords)) {
2750         result = 1;
2751         continue;
2752       }
2753 
2754       /*
2755         Check if the user's authentication method supports expiration only
2756         if PASSWORD EXPIRE attribute is specified
2757       */
2758       if (user_from->alter_status.update_password_expired_column &&
2759           !auth_plugin_supports_expiration(user_from->plugin.str)) {
2760         result = 1;
2761         log_user(thd, &wrong_users, user_from, wrong_users.length() > 0);
2762         continue;
2763       }
2764 
2765       if (!strcmp(user_from->user.str, "") &&
2766           (what_to_alter.m_what & PASSWORD_EXPIRE_ATTR) &&
2767           user_from->alter_status.update_password_expired_column) {
2768         result = 1;
2769         is_anonymous_user = true;
2770         continue;
2771       }
2772 
2773       acl_user = find_acl_user(user_from->host.str, user_from->user.str, true);
2774 
2775       if (history_check_done) {
2776         /*
2777           If the history check is already done we pretend there's no
2778           history table so we can turn off the eventual check here.
2779         */
2780         history_tbl = tables[ACL_TABLES::TABLE_PASSWORD_HISTORY].table;
2781         tables[ACL_TABLES::TABLE_PASSWORD_HISTORY].table = nullptr;
2782       }
2783       ret = handle_grant_data(thd, tables, false, user_from, nullptr, false);
2784 
2785       /* purge the password history if plugin is different */
2786       if ((what_to_alter.m_what & DIFFERENT_PLUGIN_ATTR) &&
2787           handle_password_history_table(thd, tables, true, user_from, nullptr,
2788                                         &dummy_row_existed)) {
2789         /* can't delete stuff from password history */
2790         result = 1;
2791         break;
2792       }
2793 
2794       if (history_check_done) {
2795         tables[ACL_TABLES::TABLE_PASSWORD_HISTORY].table = history_tbl;
2796       }
2797 
2798       if (!acl_user || ret <= 0) {
2799         if (ret < 0) {
2800           result = 1;
2801           break;
2802         }
2803 
2804         if (if_exists) {
2805           String warn_user;
2806           log_user(thd, &warn_user, user_from, false);
2807           push_warning_printf(
2808               thd, Sql_condition::SL_NOTE, ER_USER_DOES_NOT_EXIST,
2809               ER_THD(thd, ER_USER_DOES_NOT_EXIST), warn_user.c_ptr_safe());
2810           try {
2811             extra_users.insert(tmp_user_from);
2812           } catch (...) {
2813             LogErr(WARNING_LEVEL,
2814                    ER_USER_NOT_IN_EXTRA_USERS_BINLOG_POSSIBLY_INCOMPLETE,
2815                    warn_user.c_ptr_safe());
2816           }
2817         } else {
2818           log_user(thd, &wrong_users, user_from, wrong_users.length() > 0);
2819           result = 1;
2820         }
2821         continue;
2822       }
2823 
2824       /* update the mysql.user table */
2825       ret = replace_user_table(thd, tables[ACL_TABLES::TABLE_USER].table,
2826                                user_from, 0, false, false, what_to_alter);
2827       if (ret) {
2828         if (ret < 0) {
2829           result = 1;
2830           break;
2831         }
2832         if (if_exists) {
2833           String warn_user;
2834           log_user(thd, &warn_user, user_from, false);
2835           push_warning_printf(
2836               thd, Sql_condition::SL_NOTE, ER_USER_DOES_NOT_EXIST,
2837               ER_THD(thd, ER_USER_DOES_NOT_EXIST), warn_user.c_ptr_safe());
2838           try {
2839             extra_users.insert(user_from);
2840           } catch (...) {
2841             LogErr(WARNING_LEVEL,
2842                    ER_USER_NOT_IN_EXTRA_USERS_BINLOG_POSSIBLY_INCOMPLETE,
2843                    warn_user.c_ptr_safe());
2844           }
2845         } else {
2846           log_user(thd, &wrong_users, user_from, wrong_users.length() > 0);
2847           result = 1;
2848         }
2849         continue;
2850       }
2851       if (update_sctx_cache(
2852               thd->security_context(), acl_user,
2853               user_from->alter_status.update_password_expired_column)) {
2854         self = acl_user;
2855         password_expire_undo =
2856             !user_from->alter_status.update_password_expired_column;
2857       }
2858 
2859       /*
2860         If there is change related to authentication plugin,
2861         we would like to notify interested audit plugins.
2862       */
2863       if (what_to_alter.m_what & PLUGIN_ATTR) audit_users.insert(tmp_user_from);
2864 
2865       if (what_to_alter.m_what & RESOURCE_ATTR)
2866         reset_users.insert(tmp_user_from);
2867     }
2868 
2869     // We must not have plain text password for any user at this point.
2870     thd->lex->contains_plaintext_password = false;
2871 
2872     clear_and_init_db_cache();  // Clear locked hostname cache
2873 
2874     if (result && self)
2875       update_sctx_cache(thd->security_context(), self, password_expire_undo);
2876 
2877     if (result && !thd->is_error()) {
2878       if (is_anonymous_user)
2879         my_error(ER_PASSWORD_EXPIRE_ANONYMOUS_USER, MYF(0));
2880       else
2881         my_error(ER_CANNOT_USER, MYF(0), "ALTER USER",
2882                  wrong_users.c_ptr_safe());
2883     }
2884 
2885     User_params user_params(&extra_users);
2886     result = log_and_commit_acl_ddl(thd, transactional_tables, &extra_users,
2887                                     &user_params);
2888     /* Notify audit plugin. We will ignore the return value. */
2889     LEX_USER *audit_user;
2890     for (LEX_USER *one_user : audit_users) {
2891       if ((audit_user = get_current_user(thd, one_user)))
2892         mysql_audit_notify(
2893             thd, AUDIT_EVENT(MYSQL_AUDIT_AUTHENTICATION_CREDENTIAL_CHANGE),
2894             thd->is_error(), audit_user->user.str, audit_user->host.str,
2895             audit_user->plugin.str, is_role_id(audit_user), nullptr, nullptr);
2896     }
2897   } /* Critical section */
2898 
2899   if (!result) {
2900     LEX_USER *extra_user;
2901     DEBUG_SYNC(thd, "before_reset_mqh_in_alter_user");
2902     for (LEX_USER *one_user : reset_users) {
2903       if ((extra_user = get_current_user(thd, one_user))) {
2904         reset_mqh(thd, extra_user, false);
2905       }
2906     }
2907     /* Notify storage engines (including rewrite list) */
2908     acl_notify_htons(thd, SQLCOM_ALTER_USER, &list, &extra_users);
2909   }
2910 
2911   if (result == 0) {
2912     if (generated_passwords.size() == 0) {
2913       my_ok(thd);
2914     } else if (send_password_result_set(thd, generated_passwords)) {
2915       result = 1;
2916     }
2917   }  // end if
2918   return result;
2919 }
2920