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