1 /* Copyright (c) 2000, 2020, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #include "sql/auth/sql_auth_cache.h"
24
25 #include <stdarg.h>
26 #include <boost/graph/properties.hpp>
27 #include <new>
28
29 #include "m_ctype.h"
30 #include "m_string.h" // LEX_CSTRING
31 #include "my_base.h"
32 #include "my_compiler.h"
33 #include "my_dbug.h"
34 #include "my_loglevel.h"
35 #include "my_macros.h"
36 #include "mysql/components/services/log_builtins.h"
37 #include "mysql/components/services/psi_mutex_bits.h"
38 #include "mysql/plugin.h"
39 #include "mysql/plugin_audit.h"
40 #include "mysql/plugin_auth.h" // st_mysql_auth
41 #include "mysql/psi/mysql_mutex.h"
42 #include "mysql/psi/psi_base.h"
43 #include "mysql/service_mysql_alloc.h"
44 #include "mysqld_error.h"
45 #include "prealloced_array.h"
46 #include "sql/auth/auth_acls.h"
47 #include "sql/auth/auth_common.h" // ACL_internal_schema_access
48 #include "sql/auth/auth_internal.h" // auth_plugin_is_built_in
49 #include "sql/auth/auth_utility.h"
50 #include "sql/auth/dynamic_privilege_table.h"
51 #include "sql/auth/sql_authentication.h" // g_cached_authentication_plugins
52 #include "sql/auth/sql_security_ctx.h"
53 #include "sql/auth/sql_user_table.h"
54 #include "sql/auth/user_table.h" // read_user_table
55 #include "sql/current_thd.h" // current_thd
56 #include "sql/debug_sync.h"
57 #include "sql/error_handler.h" // Internal_error_handler
58 #include "sql/field.h" // Field
59 #include "sql/handler.h"
60 #include "sql/key.h"
61 #include "sql/mdl.h"
62 #include "sql/mysqld.h" // my_localhost
63 #include "sql/psi_memory_key.h" // key_memory_acl_mem
64 #include "sql/records.h" // unique_ptr_destroy_only<RowIterator>
65 #include "sql/row_iterator.h"
66 #include "sql/set_var.h"
67 #include "sql/sql_audit.h"
68 #include "sql/sql_base.h" // open_and_lock_tables
69 #include "sql/sql_class.h" // THD
70 #include "sql/sql_const.h"
71 #include "sql/sql_error.h"
72 #include "sql/sql_lex.h"
73 #include "sql/sql_plugin.h" // my_plugin_lock_by_name
74 #include "sql/sql_plugin_ref.h"
75 #include "sql/ssl_acceptor_context_operator.h"
76 #include "sql/system_variables.h"
77 #include "sql/table.h" // TABLE
78 #include "sql/thd_raii.h"
79 #include "sql/thr_malloc.h"
80 #include "sql/tztime.h" // Time_zone
81 #include "sql/xa.h"
82 #include "sql_string.h"
83 #include "thr_lock.h"
84 #include "thr_mutex.h"
85
86 #define INVALID_DATE "0000-00-00 00:00:00"
87
88 #include <algorithm>
89 #include <functional>
90 #include <unordered_map>
91 #include <utility>
92 #include <vector>
93
94 using std::make_unique;
95 using std::min;
96 using std::move;
97 using std::string;
98 using std::unique_ptr;
99
100 PSI_mutex_key key_LOCK_acl_cache_flush;
101 PSI_mutex_info all_acl_cache_mutexes[] = {
102 {&key_LOCK_acl_cache_flush, "LOCK_acl_cache_flush", PSI_FLAG_SINGLETON, 0,
103 PSI_DOCUMENT_ME}};
104 Acl_cache *g_acl_cache = nullptr;
get_global_acl_cache()105 Acl_cache *get_global_acl_cache() { return g_acl_cache; }
get_global_acl_cache_size()106 ulong get_global_acl_cache_size() { return g_acl_cache->size(); }
107 void init_acl_cache();
108 extern Role_index_map *g_authid_to_vertex;
109 extern Granted_roles_graph *g_granted_roles;
110 #include <boost/property_map/property_map.hpp>
111
112 struct ACL_internal_schema_registry_entry {
113 const LEX_CSTRING *m_name;
114 const ACL_internal_schema_access *m_access;
115 };
116 /**
117 Internal schema registered.
118 Currently, this is only:
119 - performance_schema
120 - information_schema,
121 This can be reused later for:
122 - mysql
123 */
124 static ACL_internal_schema_registry_entry registry_array[2];
125 static uint m_registry_array_size = 0;
126
127 MEM_ROOT global_acl_memory;
128 MEM_ROOT memex;
129 Prealloced_array<ACL_USER, ACL_PREALLOC_SIZE> *acl_users = nullptr;
130 Prealloced_array<ACL_PROXY_USER, ACL_PREALLOC_SIZE> *acl_proxy_users = nullptr;
131 Prealloced_array<ACL_DB, ACL_PREALLOC_SIZE> *acl_dbs = nullptr;
132 Prealloced_array<ACL_HOST_AND_IP, ACL_PREALLOC_SIZE> *acl_wild_hosts = nullptr;
133 Db_access_map acl_db_map;
134 Default_roles *g_default_roles = nullptr;
135 std::vector<Role_id> *g_mandatory_roles = nullptr;
136
137 unique_ptr<
138 malloc_unordered_multimap<string, unique_ptr_destroy_only<GRANT_TABLE>>>
139 column_priv_hash;
140 unique_ptr<
141 malloc_unordered_multimap<string, unique_ptr_destroy_only<GRANT_NAME>>>
142 proc_priv_hash, func_priv_hash;
143 malloc_unordered_map<std::string, unique_ptr_my_free<acl_entry>> db_cache{
144 key_memory_acl_cache};
145 collation_unordered_map<std::string, ACL_USER *> *acl_check_hosts = nullptr;
146 unique_ptr<Acl_restrictions> acl_restrictions = nullptr;
147
148 /**
149 A hashmap on user part of account name for quick lookup.
150 */
151 typedef std::unordered_map<
152 std::string, Acl_user_ptr_list, std::hash<std::string>,
153 std::equal_to<std::string>,
154 Acl_cache_allocator<std::pair<const std::string, Acl_user_ptr_list>>>
155 Name_to_userlist;
156 Name_to_userlist *name_to_userlist = nullptr;
157
158 bool initialized = false;
skip_grant_tables(void)159 bool skip_grant_tables(void) { return !initialized; }
160 bool acl_cache_initialized = false;
161 bool allow_all_hosts = true;
162 uint grant_version = 0; /* Version of priv tables */
163 bool validate_user_plugins = true;
164
165 #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
166 #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + 1 + USERNAME_LENGTH + 1)
167
168 /** Helper: Set user name */
set_username(char ** user,const char * user_arg,MEM_ROOT * mem)169 static void set_username(char **user, const char *user_arg, MEM_ROOT *mem) {
170 DBUG_ASSERT(user != nullptr);
171 *user = (user_arg && *user_arg) ? strdup_root(mem, user_arg) : nullptr;
172 }
173
174 /** Helper: Set host name */
set_hostname(ACL_HOST_AND_IP * host,const char * host_arg,MEM_ROOT * mem)175 static void set_hostname(ACL_HOST_AND_IP *host, const char *host_arg,
176 MEM_ROOT *mem) {
177 DBUG_ASSERT(host != nullptr);
178 host->update_hostname((host_arg && *host_arg) ? strdup_root(mem, host_arg)
179 : nullptr);
180 }
181
182 /**
183 Allocates the memory in the the global_acl_memory MEM_ROOT.
184 */
init_acl_memory()185 void init_acl_memory() {
186 init_sql_alloc(key_memory_acl_mem, &global_acl_memory, ACL_ALLOC_BLOCK_SIZE,
187 0);
188 }
189
190 /**
191 Add an internal schema to the registry.
192 @param name the schema name
193 @param access the schema ACL specific rules
194 */
register_schema(const LEX_CSTRING & name,const ACL_internal_schema_access * access)195 void ACL_internal_schema_registry::register_schema(
196 const LEX_CSTRING &name, const ACL_internal_schema_access *access) {
197 DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
198
199 /* Not thread safe, and does not need to be. */
200 registry_array[m_registry_array_size].m_name = &name;
201 registry_array[m_registry_array_size].m_access = access;
202 m_registry_array_size++;
203 }
204
205 /**
206 Search per internal schema ACL by name.
207 @param name a schema name
208 @return per schema rules, or NULL
209 */
lookup(const char * name)210 const ACL_internal_schema_access *ACL_internal_schema_registry::lookup(
211 const char *name) {
212 DBUG_ASSERT(name != nullptr);
213
214 uint i;
215
216 for (i = 0; i < m_registry_array_size; i++) {
217 if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
218 name) == 0)
219 return registry_array[i].m_access;
220 }
221 return nullptr;
222 }
223
calc_ip(const char * ip_arg,long * val,char end)224 const char *ACL_HOST_AND_IP::calc_ip(const char *ip_arg, long *val, char end) {
225 long ip_val, tmp;
226 if (!(ip_arg = str2int(ip_arg, 10, 0, 255, &ip_val)) || *ip_arg != '.')
227 return nullptr;
228 ip_val <<= 24;
229 if (!(ip_arg = str2int(ip_arg + 1, 10, 0, 255, &tmp)) || *ip_arg != '.')
230 return nullptr;
231 ip_val += tmp << 16;
232 if (!(ip_arg = str2int(ip_arg + 1, 10, 0, 255, &tmp)) || *ip_arg != '.')
233 return nullptr;
234 ip_val += tmp << 8;
235 if (!(ip_arg = str2int(ip_arg + 1, 10, 0, 255, &tmp)) || *ip_arg != end)
236 return nullptr;
237 *val = ip_val + tmp;
238 return ip_arg;
239 }
240
241 /**
242 @brief Update the hostname. Updates ip and ip_mask accordingly.
243
244 @param host_arg Value to be stored
245 */
update_hostname(const char * host_arg)246 void ACL_HOST_AND_IP::update_hostname(const char *host_arg) {
247 hostname = host_arg; // This will not be modified!
248 hostname_length = hostname ? strlen(hostname) : 0;
249 if (!host_arg || (!(host_arg = calc_ip(host_arg, &ip, '/')) ||
250 !(host_arg = calc_ip(host_arg + 1, &ip_mask, '\0')))) {
251 ip = ip_mask = 0; // Not a masked ip
252 }
253 }
254
255 /*
256 @brief Comparing of hostnames.
257
258 @TODO This function should ideally only
259 be called during authentication and not from authorization code. You may
260 authenticate with a hostmask, but all authentication should be against a
261 specific security context with a specific authentication ID.
262
263 @param host_arg Hostname to be compared with
264 @param ip_arg IP address to be compared with
265
266 @notes
267 A hostname may be of type:
268 1) hostname (May include wildcards); monty.pp.sci.fi
269 2) ip (May include wildcards); 192.168.0.0
270 3) ip/netmask 192.168.0.0/255.255.255.0
271 A net mask of 0.0.0.0 is not allowed.
272
273 @return
274 true if matched
275 false if not matched
276 */
277
compare_hostname(const char * host_arg,const char * ip_arg)278 bool ACL_HOST_AND_IP::compare_hostname(const char *host_arg,
279 const char *ip_arg) {
280 long tmp;
281 if (ip_mask && ip_arg && calc_ip(ip_arg, &tmp, '\0')) {
282 return (tmp & ip_mask) == ip;
283 }
284 return (!hostname ||
285 (host_arg &&
286 !wild_case_compare(system_charset_info, host_arg, hostname)) ||
287 (ip_arg && !wild_compare(ip_arg, strlen(ip_arg), hostname,
288 strlen(hostname), false)));
289 }
290
ACL_USER()291 ACL_USER::ACL_USER() {
292 /* ACL_ACCESS is initialized by its constructor */
293 {
294 /* USER_RESOURCES */
295 user_resource.questions = 0;
296 user_resource.updates = 0;
297 user_resource.conn_per_hour = 0;
298 user_resource.user_conn = 0;
299 user_resource.specified_limits = 0;
300 }
301
302 user = nullptr;
303
304 {
305 /* TLS restrictions */
306 ssl_type = SSL_TYPE_NONE;
307 ssl_cipher = nullptr;
308 x509_issuer = nullptr;
309 x509_subject = nullptr;
310 }
311
312 plugin = EMPTY_CSTR;
313 password_expired = false;
314 can_authenticate = false;
315 password_last_changed.time_type = MYSQL_TIMESTAMP_ERROR;
316 password_lifetime = 0;
317 use_default_password_lifetime = false;
318 account_locked = false;
319 is_role = false;
320 password_history_length = 0;
321 use_default_password_history = false;
322 password_reuse_interval = 0;
323 use_default_password_reuse_interval = false;
324 password_require_current = Lex_acl_attrib_udyn::DEFAULT;
325 /* Acl_credentials is initialized by its constructor */
326 }
327
set_parameters(uint password_lock_time_days,uint failed_login_attempts)328 void ACL_USER::Password_locked_state::set_parameters(
329 uint password_lock_time_days, uint failed_login_attempts) {
330 m_password_lock_time_days = password_lock_time_days;
331 m_remaining_login_attempts = m_failed_login_attempts = failed_login_attempts;
332 m_daynr_locked = 0;
333 }
334
335 /**
336 Updates the password locked state based on the time of day fetched from the
337 THD
338
339 @param thd the session to use to calculate time
340 @param successful_login true if the login succeeded
341 @param[out] ret_days_remaining remaining number of days. Filled only if
342 update returns locked account
343 @retval false account not locked
344 @retval true account locked
345 */
update(THD * thd,bool successful_login,long * ret_days_remaining)346 bool ACL_USER::Password_locked_state::update(THD *thd, bool successful_login,
347 long *ret_days_remaining) {
348 /* stop if the user is not tracking failed logins */
349 if (!is_active()) return false;
350
351 /* reset on a successful login if the account is not locked */
352 if (successful_login && m_daynr_locked == 0) {
353 m_remaining_login_attempts = m_failed_login_attempts;
354 return false;
355 }
356
357 /* decreases the remaining login attempts if any */
358 if (!successful_login && m_remaining_login_attempts > 0) {
359 m_remaining_login_attempts--;
360 DBUG_ASSERT(m_daynr_locked == 0);
361 }
362
363 if (m_remaining_login_attempts) return false;
364
365 long now_day;
366 /* fetch the current day */
367 MYSQL_TIME tm_now;
368 thd->time_zone()->gmt_sec_to_TIME(&tm_now, thd->query_start_timeval_trunc(6));
369 now_day = calc_daynr(tm_now.year, tm_now.month, tm_now.day);
370
371 DBUG_EXECUTE_IF("account_lock_daynr_add_one", { now_day += 1; });
372
373 DBUG_EXECUTE_IF("account_lock_daynr_add_ten", { now_day += 10; });
374
375 /* last unsuccessful login. lock the account */
376 if (m_daynr_locked == 0) {
377 DBUG_ASSERT(!successful_login);
378 m_daynr_locked = now_day;
379 *ret_days_remaining = m_password_lock_time_days;
380 return true;
381 };
382
383 /* if the lock should never expire we stop here */
384 if (m_daynr_locked > 0 && m_password_lock_time_days < 0) return true;
385
386 /* check if the account is still to be locked */
387 if (now_day - m_daynr_locked < (long)m_password_lock_time_days) {
388 *ret_days_remaining =
389 ((long)m_password_lock_time_days) - (now_day - m_daynr_locked);
390 return true;
391 }
392 /* reset the account lock if the time has expired */
393 if (now_day - m_daynr_locked >= (long)m_password_lock_time_days) {
394 m_daynr_locked = 0;
395 m_remaining_login_attempts = m_failed_login_attempts;
396 return false;
397 }
398
399 /* it should never get to here */
400 DBUG_ASSERT(false);
401 return false;
402 }
403
copy(MEM_ROOT * root)404 ACL_USER *ACL_USER::copy(MEM_ROOT *root) {
405 ACL_USER *dst = (ACL_USER *)root->Alloc(sizeof(ACL_USER));
406 if (!dst) return nullptr;
407 *dst = *this;
408 dst->user = safe_strdup_root(root, user);
409 dst->ssl_cipher = safe_strdup_root(root, ssl_cipher);
410 dst->x509_issuer = safe_strdup_root(root, x509_issuer);
411 dst->x509_subject = safe_strdup_root(root, x509_subject);
412 /*
413 If the plugin is built in we don't need to reallocate the name of the
414 plugin.
415 */
416 if (auth_plugin_is_built_in(dst->plugin.str))
417 dst->plugin = plugin;
418 else {
419 dst->plugin.str = strmake_root(root, plugin.str, plugin.length);
420 dst->plugin.length = plugin.length;
421 }
422 for (int i = 0; i < NUM_CREDENTIALS; ++i) {
423 dst->credentials[i].m_auth_string.str =
424 safe_strdup_root(root, credentials[i].m_auth_string.str);
425 dst->credentials[i].m_auth_string.length =
426 credentials[i].m_auth_string.length;
427 dst->credentials[i].m_salt_len = credentials[i].m_salt_len;
428 memcpy(dst->credentials[i].m_salt, credentials[i].m_salt,
429 credentials[i].m_salt_len);
430 }
431 dst->host.update_hostname(safe_strdup_root(root, host.get_host()));
432 dst->password_require_current = password_require_current;
433 dst->password_locked_state = password_locked_state;
434 return dst;
435 }
436
set_user(MEM_ROOT * mem,const char * user_arg)437 void ACL_USER::set_user(MEM_ROOT *mem, const char *user_arg) {
438 set_username(&user, user_arg, mem);
439 }
440
set_host(MEM_ROOT * mem,const char * host_arg)441 void ACL_USER::set_host(MEM_ROOT *mem, const char *host_arg) {
442 set_hostname(&host, host_arg, mem);
443 }
444
init(const char * host_arg,const char * user_arg,const char * proxied_host_arg,const char * proxied_user_arg,bool with_grant_arg)445 void ACL_PROXY_USER::init(const char *host_arg, const char *user_arg,
446 const char *proxied_host_arg,
447 const char *proxied_user_arg, bool with_grant_arg) {
448 user = (user_arg && *user_arg) ? user_arg : nullptr;
449 host.update_hostname((host_arg && *host_arg) ? host_arg : nullptr);
450 proxied_user =
451 (proxied_user_arg && *proxied_user_arg) ? proxied_user_arg : nullptr;
452 proxied_host.update_hostname(
453 (proxied_host_arg && *proxied_host_arg) ? proxied_host_arg : nullptr);
454 with_grant = with_grant_arg;
455 sort =
456 get_sort(4, host.get_host(), user, proxied_host.get_host(), proxied_user);
457 }
458
init(MEM_ROOT * mem,const char * host_arg,const char * user_arg,const char * proxied_host_arg,const char * proxied_user_arg,bool with_grant_arg)459 void ACL_PROXY_USER::init(MEM_ROOT *mem, const char *host_arg,
460 const char *user_arg, const char *proxied_host_arg,
461 const char *proxied_user_arg, bool with_grant_arg) {
462 init((host_arg && *host_arg) ? strdup_root(mem, host_arg) : nullptr,
463 (user_arg && *user_arg) ? strdup_root(mem, user_arg) : nullptr,
464 (proxied_host_arg && *proxied_host_arg)
465 ? strdup_root(mem, proxied_host_arg)
466 : nullptr,
467 (proxied_user_arg && *proxied_user_arg)
468 ? strdup_root(mem, proxied_user_arg)
469 : nullptr,
470 with_grant_arg);
471 }
472
init(TABLE * table,MEM_ROOT * mem)473 void ACL_PROXY_USER::init(TABLE *table, MEM_ROOT *mem) {
474 init(get_field(mem, table->field[MYSQL_PROXIES_PRIV_HOST]),
475 get_field(mem, table->field[MYSQL_PROXIES_PRIV_USER]),
476 get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]),
477 get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]),
478 table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->val_int() != 0);
479 }
480
check_validity(bool check_no_resolve)481 bool ACL_PROXY_USER::check_validity(bool check_no_resolve) {
482 if (check_no_resolve &&
483 (hostname_requires_resolving(host.get_host()) ||
484 hostname_requires_resolving(proxied_host.get_host())) &&
485 strcmp(host.get_host(), "localhost") != 0) {
486 LogErr(WARNING_LEVEL, ER_AUTHCACHE_PROXIES_PRIV_SKIPPED_NEEDS_RESOLVE,
487 proxied_user ? proxied_user : "",
488 proxied_host.get_host() ? proxied_host.get_host() : "",
489 user ? user : "", host.get_host() ? host.get_host() : "");
490 }
491 return false;
492 }
493
matches(const char * host_arg,const char * user_arg,const char * ip_arg,const char * proxied_user_arg,bool any_proxy_user)494 bool ACL_PROXY_USER::matches(const char *host_arg, const char *user_arg,
495 const char *ip_arg, const char *proxied_user_arg,
496 bool any_proxy_user) {
497 DBUG_TRACE;
498 DBUG_PRINT("info",
499 ("compare_hostname(%s,%s,%s) &&"
500 "compare_hostname(%s,%s,%s) &&"
501 "wild_compare (%s,%s) &&"
502 "wild_compare (%s,%s)",
503 host.get_host() ? host.get_host() : "<NULL>",
504 host_arg ? host_arg : "<NULL>", ip_arg ? ip_arg : "<NULL>",
505 proxied_host.get_host() ? proxied_host.get_host() : "<NULL>",
506 host_arg ? host_arg : "<NULL>", ip_arg ? ip_arg : "<NULL>",
507 user_arg ? user_arg : "<NULL>", user ? user : "<NULL>",
508 proxied_user_arg ? proxied_user_arg : "<NULL>",
509 proxied_user ? proxied_user : "<NULL>"));
510 return host.compare_hostname(host_arg, ip_arg) &&
511 proxied_host.compare_hostname(host_arg, ip_arg) &&
512 (!user || (user_arg && !wild_compare(user_arg, strlen(user_arg), user,
513 strlen(user), true))) &&
514 (any_proxy_user || !proxied_user ||
515 (proxied_user &&
516 !wild_compare(proxied_user_arg, strlen(proxied_user_arg),
517 proxied_user, strlen(proxied_user), true)));
518 }
519
pk_equals(ACL_PROXY_USER * grant)520 bool ACL_PROXY_USER::pk_equals(ACL_PROXY_USER *grant) {
521 DBUG_TRACE;
522 DBUG_PRINT("info",
523 ("strcmp(%s,%s) &&"
524 "strcmp(%s,%s) &&"
525 "wild_compare (%s,%s) &&"
526 "wild_compare (%s,%s)",
527 user ? user : "<NULL>", grant->user ? grant->user : "<NULL>",
528 proxied_user ? proxied_user : "<NULL>",
529 grant->proxied_user ? grant->proxied_user : "<NULL>",
530 host.get_host() ? host.get_host() : "<NULL>",
531 grant->host.get_host() ? grant->host.get_host() : "<NULL>",
532 proxied_host.get_host() ? proxied_host.get_host() : "<NULL>",
533 grant->proxied_host.get_host() ? grant->proxied_host.get_host()
534 : "<NULL>"));
535
536 return auth_element_equals(user, grant->user) &&
537 auth_element_equals(proxied_user, grant->proxied_user) &&
538 auth_element_equals(host.get_host(), grant->host.get_host()) &&
539 auth_element_equals(proxied_host.get_host(),
540 grant->proxied_host.get_host());
541 }
542
print_grant(String * str)543 void ACL_PROXY_USER::print_grant(String *str) {
544 str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
545 if (proxied_user) str->append(proxied_user, strlen(proxied_user));
546 str->append(STRING_WITH_LEN("'@'"));
547 if (proxied_host.get_host())
548 str->append(proxied_host.get_host(), strlen(proxied_host.get_host()));
549 str->append(STRING_WITH_LEN("' TO '"));
550 if (user) str->append(user, strlen(user));
551 str->append(STRING_WITH_LEN("'@'"));
552 if (host.get_host()) str->append(host.get_host(), strlen(host.get_host()));
553 str->append(STRING_WITH_LEN("'"));
554 if (with_grant) str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
555 }
556
store_pk(TABLE * table,const LEX_CSTRING & hostname,const LEX_CSTRING & user,const LEX_CSTRING & proxied_host,const LEX_CSTRING & proxied_user)557 int ACL_PROXY_USER::store_pk(TABLE *table, const LEX_CSTRING &hostname,
558 const LEX_CSTRING &user,
559 const LEX_CSTRING &proxied_host,
560 const LEX_CSTRING &proxied_user) {
561 DBUG_TRACE;
562 DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
563 hostname.str ? hostname.str : "<NULL>",
564 user.str ? user.str : "<NULL>",
565 proxied_host.str ? proxied_host.str : "<NULL>",
566 proxied_user.str ? proxied_user.str : "<NULL>"));
567 if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(
568 hostname.str, hostname.length, system_charset_info))
569 return true;
570 if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user.str, user.length,
571 system_charset_info))
572 return true;
573 if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(
574 proxied_host.str, proxied_host.length, system_charset_info))
575 return true;
576 if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(
577 proxied_user.str, proxied_user.length, system_charset_info))
578 return true;
579
580 return false;
581 }
582
store_with_grant(TABLE * table,bool with_grant)583 int ACL_PROXY_USER::store_with_grant(TABLE *table, bool with_grant) {
584 DBUG_TRACE;
585 DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
586 if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
587 true))
588 return true;
589
590 return false;
591 }
592
store_data_record(TABLE * table,const LEX_CSTRING & hostname,const LEX_CSTRING & user,const LEX_CSTRING & proxied_host,const LEX_CSTRING & proxied_user,bool with_grant,const char * grantor)593 int ACL_PROXY_USER::store_data_record(TABLE *table, const LEX_CSTRING &hostname,
594 const LEX_CSTRING &user,
595 const LEX_CSTRING &proxied_host,
596 const LEX_CSTRING &proxied_user,
597 bool with_grant, const char *grantor) {
598 DBUG_TRACE;
599 if (store_pk(table, hostname, user, proxied_host, proxied_user)) return true;
600 if (store_with_grant(table, with_grant)) return true;
601 if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor, strlen(grantor),
602 system_charset_info))
603 return true;
604
605 return false;
606 }
607
set_user(MEM_ROOT * mem,const char * user_arg)608 void ACL_PROXY_USER::set_user(MEM_ROOT *mem, const char *user_arg) {
609 set_username(const_cast<char **>(&user), user_arg, mem);
610 }
611
set_host(MEM_ROOT * mem,const char * host_arg)612 void ACL_PROXY_USER::set_host(MEM_ROOT *mem, const char *host_arg) {
613 set_hostname(&host, host_arg, mem);
614 }
615
set_user(MEM_ROOT * mem,const char * user_arg)616 void ACL_DB::set_user(MEM_ROOT *mem, const char *user_arg) {
617 set_username(&user, user_arg, mem);
618 }
619
set_host(MEM_ROOT * mem,const char * host_arg)620 void ACL_DB::set_host(MEM_ROOT *mem, const char *host_arg) {
621 set_hostname(&host, host_arg, mem);
622 }
623
624 /**
625 Performs wildcard matching, aka globbing, on the input string with
626 the given wildcard pattern, and the specified wildcard characters.
627 This method does case insensitive comparisons.
628
629 @param[in] cs character set of the input string and wildcard pattern
630 @param[in] str input which should be matched against pattern
631 @param[in] str_len length of the input string
632 @param[in] wildstr pattern with wildcards
633 @param[in] wildstr_len length of the wildcards pattern
634
635 @return 0 if input string match with the pattern
636 @return 1 otherwise
637 */
wild_case_compare(CHARSET_INFO * cs,const char * str,size_t str_len,const char * wildstr,size_t wildstr_len)638 int wild_case_compare(CHARSET_INFO *cs, const char *str, size_t str_len,
639 const char *wildstr, size_t wildstr_len) {
640 int flag;
641 DBUG_TRACE;
642 DBUG_PRINT("enter", ("str: '%s' wildstr: '%s'", str, wildstr));
643 const char *wildstr_end = wildstr + wildstr_len;
644 const char *str_end = str + str_len;
645
646 /*
647 Empty string matches only if there is only a wild_many(%) char
648 in the string to be matched with.
649 */
650 if (str_len == 0) {
651 bool ret_value = true;
652 if (wildstr_len == 1) {
653 ret_value = !(*wildstr == wild_many);
654 }
655 return ret_value;
656 }
657
658 while (wildstr != wildstr_end && str != str_end) {
659 while (wildstr != wildstr_end && *wildstr != wild_many &&
660 *wildstr != wild_one && str != str_end) {
661 if (*wildstr == wild_prefix && wildstr[1]) wildstr++;
662 if (my_toupper(cs, *wildstr++) != my_toupper(cs, *str++)) return 1;
663 }
664 if (wildstr == wildstr_end) {
665 return str != str_end;
666 }
667 if (str == str_end) {
668 if (*wildstr == '%' && wildstr + 1 == wildstr_end)
669 return 0; /* % match empty string */
670 return (wildstr != wildstr_end);
671 }
672 if (*wildstr++ == wild_one) {
673 ++str;
674 if (str == str_end) /* One char; skip */
675 {
676 return wildstr != wildstr_end;
677 }
678 } else { /* Found wild_many */
679 if (wildstr == wildstr_end) return 0; // empty matches wild_many
680 flag = (*wildstr != wild_many && *wildstr != wild_one);
681 do {
682 if (flag) {
683 char cmp;
684 if ((cmp = *wildstr) == wild_prefix && wildstr[1]) cmp = wildstr[1];
685 cmp = my_toupper(cs, cmp);
686 while (str != str_end && my_toupper(cs, *str) != cmp) str++;
687 if (str == str_end) return 1;
688 }
689 if (wild_case_compare(cs, str, str_end - str, wildstr,
690 wildstr_end - wildstr) == 0) {
691 return 0;
692 }
693 ++str;
694 } while (str != str_end);
695 return 1;
696 }
697 }
698 return str != str_end;
699 }
700
wild_case_compare(CHARSET_INFO * cs,const char * str,const char * wildstr)701 int wild_case_compare(CHARSET_INFO *cs, const char *str, const char *wildstr) {
702 return wild_case_compare(cs, str, strlen(str), wildstr, strlen(wildstr));
703 }
704
705 /*
706 Return a number which, if sorted 'desc', puts strings in this order:
707 no wildcards
708 strings containg wildcards and non-wildcard characters
709 single muilt-wildcard character('%')
710 empty string
711 */
712
get_sort(uint count,...)713 ulong get_sort(uint count, ...) {
714 va_list args;
715 va_start(args, count);
716 ulong sort = 0;
717
718 /* Should not use this function with more than 4 arguments for compare. */
719 DBUG_ASSERT(count <= 4);
720
721 while (count--) {
722 char *start, *str = va_arg(args, char *);
723 uint chars = 0;
724 uint wild_pos = 0;
725
726 /*
727 wild_pos
728 0 if string is empty
729 1 if string is a single muilt-wildcard
730 character('%')
731 first wildcard position + 1 if string containg wildcards and
732 non-wildcard characters
733 */
734
735 if ((start = str)) {
736 for (; *str; str++) {
737 if (*str == wild_prefix && str[1])
738 str++;
739 else if (*str == wild_many || *str == wild_one) {
740 wild_pos = (uint)(str - start) + 1;
741 if (!(wild_pos == 1 && *str == wild_many && *(++str) == '\0'))
742 wild_pos++;
743 break;
744 }
745 chars = 128; // Marker that chars existed
746 }
747 }
748 sort = (sort << 8) + (wild_pos ? min(wild_pos, 127U) : chars);
749 }
750 va_end(args);
751 return sort;
752 }
753
754 /**
755 Check if the given host name needs to be resolved or not.
756 Host name has to be resolved if it actually contains *name*.
757
758 For example:
759 192.168.1.1 --> false
760 192.168.1.0/255.255.255.0 --> false
761 % --> false
762 192.168.1.% --> false
763 AB% --> false
764
765 AAAAFFFF --> true (Hostname)
766 AAAA:FFFF:1234:5678 --> false
767 ::1 --> false
768
769 This function does not check if the given string is a valid host name or
770 not. It assumes that the argument is a valid host name.
771
772 @param hostname the string to check.
773
774 @return a flag telling if the argument needs to be resolved or not.
775 @retval true the argument is a host name and needs to be resolved.
776 @retval false the argument is either an IP address, or a patter and
777 should not be resolved.
778 */
779
hostname_requires_resolving(const char * hostname)780 bool hostname_requires_resolving(const char *hostname) {
781 /* called only for --skip-name-resolve */
782 DBUG_ASSERT(specialflag & SPECIAL_NO_RESOLVE);
783
784 if (!hostname) return false;
785
786 /*
787 If the string contains any of {':', '%', '_', '/'}, it is definitely
788 not a host name:
789 - ':' means that the string is an IPv6 address;
790 - '%' or '_' means that the string is a pattern;
791 - '/' means that the string is an IPv4 network address;
792 */
793
794 for (const char *p = hostname; *p; ++p) {
795 switch (*p) {
796 case ':':
797 case '%':
798 case '_':
799 case '/':
800 return false;
801 }
802 }
803
804 /*
805 Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
806 (12.34.56.78). The assumption is that if the string contains only
807 digits and dots, it is an IPv4 address. Otherwise -- a host name.
808 */
809
810 for (const char *p = hostname; *p; ++p) {
811 if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
812 return true; /* a "letter" has been found. */
813 }
814
815 return false; /* all characters are either dots or digits. */
816 }
817
GRANT_COLUMN(String & c,ulong y)818 GRANT_COLUMN::GRANT_COLUMN(String &c, ulong y)
819 : rights(y), column(c.ptr(), c.length()) {}
820
set_user_details(const char * h,const char * d,const char * u,const char * t,bool is_routine)821 void GRANT_NAME::set_user_details(const char *h, const char *d, const char *u,
822 const char *t, bool is_routine) {
823 /* Host given by user */
824 set_hostname(&host, h, &memex);
825 if (db != d) {
826 db = strdup_root(&memex, d);
827 if (lower_case_table_names) my_casedn_str(files_charset_info, db);
828 }
829 user = strdup_root(&memex, u);
830 sort = get_sort(3, host.get_host(), db, user);
831 if (tname != t) {
832 tname = strdup_root(&memex, t);
833 if (lower_case_table_names || is_routine)
834 my_casedn_str(files_charset_info, tname);
835 }
836
837 hash_key = user;
838 hash_key.push_back('\0');
839 hash_key.append(db);
840 hash_key.push_back('\0');
841 hash_key.append(tname);
842 hash_key.push_back('\0');
843 }
844
GRANT_NAME(const char * h,const char * d,const char * u,const char * t,ulong p,bool is_routine)845 GRANT_NAME::GRANT_NAME(const char *h, const char *d, const char *u,
846 const char *t, ulong p, bool is_routine)
847 : db(nullptr), tname(nullptr), privs(p) {
848 set_user_details(h, d, u, t, is_routine);
849 }
850
GRANT_TABLE(const char * h,const char * d,const char * u,const char * t,ulong p,ulong c)851 GRANT_TABLE::GRANT_TABLE(const char *h, const char *d, const char *u,
852 const char *t, ulong p, ulong c)
853 : GRANT_NAME(h, d, u, t, p, false),
854 cols(c),
855 hash_columns(system_charset_info, key_memory_acl_memex) {}
856
GRANT_NAME(TABLE * form,bool is_routine)857 GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine) {
858 host.update_hostname(get_field(&memex, form->field[0]));
859 db = get_field(&memex, form->field[1]);
860 user = get_field(&memex, form->field[2]);
861 if (!user) user = "";
862 sort = get_sort(3, host.get_host(), db, user);
863 tname = get_field(&memex, form->field[3]);
864 if (!db || !tname) {
865 /* Wrong table row; Ignore it */
866 privs = 0;
867 return; /* purecov: inspected */
868 }
869 if (lower_case_table_names) {
870 my_casedn_str(files_charset_info, db);
871 }
872 if (lower_case_table_names || is_routine) {
873 my_casedn_str(files_charset_info, tname);
874 }
875
876 hash_key = user;
877 hash_key.push_back('\0');
878 hash_key.append(db);
879 hash_key.push_back('\0');
880 hash_key.append(tname);
881 hash_key.push_back('\0');
882
883 if (form->field[MYSQL_TABLES_PRIV_FIELD_TABLE_PRIV]) {
884 privs = (ulong)form->field[MYSQL_TABLES_PRIV_FIELD_TABLE_PRIV]->val_int();
885 privs = fix_rights_for_table(privs);
886 }
887 }
888
GRANT_TABLE(TABLE * form)889 GRANT_TABLE::GRANT_TABLE(TABLE *form)
890 : GRANT_NAME(form, false),
891 hash_columns(system_charset_info, key_memory_acl_memex) {
892 if (!db || !tname) {
893 /* Wrong table row; Ignore it */
894 cols = 0;
895 return;
896 }
897
898 if (form->field[MYSQL_TABLES_PRIV_FIELD_COLUMN_PRIV]) {
899 cols = (ulong)form->field[MYSQL_TABLES_PRIV_FIELD_COLUMN_PRIV]->val_int();
900 cols = fix_rights_for_column(cols);
901 } else
902 cols = 0;
903 }
904
~GRANT_TABLE()905 GRANT_TABLE::~GRANT_TABLE() {}
906
init(TABLE * col_privs)907 bool GRANT_TABLE::init(TABLE *col_privs) {
908 int error;
909
910 if (cols) {
911 uchar key[MAX_KEY_LENGTH];
912 uint key_prefix_len;
913
914 KEY_PART_INFO *key_part = col_privs->key_info->key_part;
915 col_privs->field[0]->store(host.get_host(),
916 host.get_host() ? host.get_host_len() : 0,
917 system_charset_info);
918 col_privs->field[1]->store(db, strlen(db), system_charset_info);
919 col_privs->field[2]->store(user, strlen(user), system_charset_info);
920 col_privs->field[3]->store(tname, strlen(tname), system_charset_info);
921
922 key_prefix_len = (key_part[0].store_length + key_part[1].store_length +
923 key_part[2].store_length + key_part[3].store_length);
924 key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
925 col_privs->field[4]->store("", 0, &my_charset_latin1);
926
927 error = col_privs->file->ha_index_init(0, true);
928 DBUG_EXECUTE_IF("wl7158_grant_table_1", col_privs->file->ha_index_end();
929 error = HA_ERR_LOCK_DEADLOCK;);
930 if (error) {
931 acl_print_ha_error(error);
932 return true;
933 }
934
935 error =
936 col_privs->file->ha_index_read_map(col_privs->record[0], (uchar *)key,
937 (key_part_map)15, HA_READ_KEY_EXACT);
938 DBUG_ASSERT(col_privs->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
939 error != HA_ERR_LOCK_DEADLOCK);
940 DBUG_ASSERT(col_privs->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
941 error != HA_ERR_LOCK_WAIT_TIMEOUT);
942 DBUG_EXECUTE_IF("wl7158_grant_table_2", error = HA_ERR_LOCK_DEADLOCK;);
943 if (error) {
944 bool ret = false;
945 cols = 0;
946 if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) {
947 acl_print_ha_error(error);
948 ret = true;
949 }
950 col_privs->file->ha_index_end();
951 return ret;
952 }
953
954 do {
955 String *res, column_name;
956 GRANT_COLUMN *mem_check;
957 /* As column name is a string, we don't have to supply a buffer */
958 res = col_privs->field[4]->val_str(&column_name);
959 ulong priv = (ulong)col_privs->field[6]->val_int();
960 DBUG_EXECUTE_IF("mysql_grant_table_init_out_of_memory",
961 DBUG_SET("+d,simulate_out_of_memory"););
962 if (!(mem_check = new (*THR_MALLOC)
963 GRANT_COLUMN(*res, fix_rights_for_column(priv)))) {
964 /* Don't use this entry */
965 col_privs->file->ha_index_end();
966
967 DBUG_EXECUTE_IF("mysql_grant_table_init_out_of_memory",
968 DBUG_SET("-d,simulate_out_of_memory"););
969 return true;
970 }
971 hash_columns.emplace(mem_check->column,
972 unique_ptr_destroy_only<GRANT_COLUMN>(mem_check));
973
974 error = col_privs->file->ha_index_next(col_privs->record[0]);
975 DBUG_ASSERT(col_privs->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
976 error != HA_ERR_LOCK_DEADLOCK);
977 DBUG_ASSERT(col_privs->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
978 error != HA_ERR_LOCK_WAIT_TIMEOUT);
979 DBUG_EXECUTE_IF("wl7158_grant_table_3", error = HA_ERR_LOCK_DEADLOCK;);
980 if (error && error != HA_ERR_END_OF_FILE) {
981 acl_print_ha_error(error);
982 col_privs->file->ha_index_end();
983 return true;
984 }
985 } while (!error && !key_cmp_if_same(col_privs, key, 0, key_prefix_len));
986 col_privs->file->ha_index_end();
987 }
988
989 return false;
990 }
991
992 /**
993 Build the lists of ACL_USERs which share name or have no name
994
995 All accounts with same name will be chained so that they can be
996 retrieved by a single lookup. These entries are sorted using
997 ACL_compare to make sure that most specific account is picked up
998 first. Anonymous user is added to each chain.
999 */
1000
rebuild_cached_acl_users_for_name(void)1001 void rebuild_cached_acl_users_for_name(void) {
1002 DBUG_TRACE;
1003 DBUG_PRINT("enter", ("acl_users size: %zu", acl_users->size()));
1004
1005 DBUG_ASSERT(!current_thd || assert_acl_cache_write_lock(current_thd));
1006
1007 if (name_to_userlist) {
1008 name_to_userlist->clear();
1009 } else {
1010 size_t size = sizeof(Name_to_userlist);
1011 myf_t flags = MYF(MY_WME | ME_FATALERROR);
1012 void *bytes = my_malloc(key_memory_acl_cache, size, flags);
1013 name_to_userlist = new (bytes) Name_to_userlist();
1014 }
1015
1016 std::list<ACL_USER *> anons;
1017
1018 /* first build each named list */
1019 for (ACL_USER *acl_user = acl_users->begin(); acl_user != acl_users->end();
1020 ++acl_user) {
1021 std::string name = acl_user->user ? acl_user->user : "";
1022 (*name_to_userlist)[name].push_back(acl_user);
1023
1024 /* keep track of anonymous acl_users */
1025 if (!name.compare("")) anons.push_back(acl_user);
1026 }
1027
1028 /* add the anonymous acl_users to each non-anon list */
1029 for (auto it = name_to_userlist->begin(); it != name_to_userlist->end();
1030 ++it) {
1031 std::string name = it->first;
1032 if (!name.compare("")) continue;
1033
1034 auto *list = &it->second;
1035 for (auto it2 = anons.begin(); it2 != anons.end(); ++it2) {
1036 list->push_back(*it2);
1037 }
1038
1039 list->sort(ACL_compare());
1040 }
1041 }
1042
1043 /**
1044 Fetch the list of ACL_USERs which share name or have no name
1045
1046 @param [in] name User entry to be searched
1047
1048 @returns List of users that share same name
1049 */
1050
cached_acl_users_for_name(const char * name)1051 Acl_user_ptr_list *cached_acl_users_for_name(const char *name) {
1052 DBUG_TRACE;
1053 DBUG_PRINT("enter", ("name: '%s'", name));
1054
1055 DBUG_ASSERT(!current_thd || assert_acl_cache_read_lock(current_thd));
1056
1057 std::string user_name = name ? name : "";
1058
1059 auto it = name_to_userlist->find(user_name);
1060 if (it != name_to_userlist->end()) return &it->second;
1061
1062 it = name_to_userlist->find("");
1063 if (it != name_to_userlist->end()) return &it->second;
1064
1065 return nullptr;
1066 }
1067
1068 /*
1069 Find first entry that matches the current user
1070 */
1071
find_acl_user(const char * host,const char * user,bool exact)1072 ACL_USER *find_acl_user(const char *host, const char *user, bool exact) {
1073 DBUG_TRACE;
1074 DBUG_PRINT("enter", ("host: '%s' user: '%s'", host, user));
1075
1076 DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
1077
1078 if (likely(acl_users)) {
1079 Acl_user_ptr_list *list = cached_acl_users_for_name(user);
1080 if (!list) {
1081 return nullptr;
1082 }
1083
1084 for (auto it = list->begin(); it != list->end(); ++it) {
1085 ACL_USER *acl_user = (*it);
1086 DBUG_PRINT("info",
1087 ("strcmp('%s','%s'), compare_hostname('%s','%s'),", user,
1088 acl_user->user ? acl_user->user : "", host,
1089 acl_user->host.get_host() ? acl_user->host.get_host() : ""));
1090 if (acl_user->user || (user && !user[0])) {
1091 if (exact ? !my_strcasecmp(system_charset_info, host ? host : "",
1092 acl_user->host.get_host()
1093 ? acl_user->host.get_host()
1094 : "")
1095 : acl_user->host.compare_hostname(host, host)) {
1096 return acl_user;
1097 }
1098 }
1099 }
1100 }
1101 return nullptr;
1102 }
1103
1104 /*
1105 Find user in ACL
1106
1107 SYNOPSIS
1108 is_acl_user()
1109 thd Handle to THD
1110 host host name
1111 user user name
1112
1113 RETURN
1114 false user not fond
1115 true there are such user
1116 */
1117
is_acl_user(THD * thd,const char * host,const char * user)1118 bool is_acl_user(THD *thd, const char *host, const char *user) {
1119 bool res = true;
1120
1121 /* --skip-grants */
1122 if (!initialized) return res;
1123
1124 Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
1125 if (!acl_cache_lock.lock(false)) return res;
1126
1127 res = find_acl_user(host, user, true) != nullptr;
1128 return res;
1129 }
1130
1131 /**
1132 Validate if a user can proxy as another user
1133
1134 @param user the logged in user (proxy user)
1135 @param host the hostname part of the logged in userid
1136 @param ip the ip of the logged in userid
1137 @param authenticated_as the effective user a plugin is trying to
1138 impersonate as (proxied user)
1139 @param [out] proxy_used True if a proxy is found
1140 @return proxy user definition
1141 @retval NULL proxy user definition not found or not applicable
1142 @retval non-null the proxy user data
1143 */
1144
acl_find_proxy_user(const char * user,const char * host,const char * ip,char * authenticated_as,bool * proxy_used)1145 ACL_PROXY_USER *acl_find_proxy_user(const char *user, const char *host,
1146 const char *ip, char *authenticated_as,
1147 bool *proxy_used) {
1148 /* if the proxied and proxy user are the same return OK */
1149 DBUG_TRACE;
1150 DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s", user, host,
1151 ip, authenticated_as));
1152
1153 if (!strcmp(authenticated_as, user)) {
1154 DBUG_PRINT("info", ("user is the same as authenticated_as"));
1155 return nullptr;
1156 }
1157
1158 bool find_any = check_proxy_users && !*authenticated_as;
1159
1160 if (!find_any) *proxy_used = true;
1161 for (ACL_PROXY_USER *proxy = acl_proxy_users->begin();
1162 proxy != acl_proxy_users->end(); ++proxy) {
1163 if (proxy->matches(host, user, ip, authenticated_as, find_any)) {
1164 DBUG_PRINT("info", ("proxy matched=%s@%s", proxy->get_proxied_user(),
1165 proxy->get_proxied_host()));
1166 if (!find_any) {
1167 DBUG_PRINT(
1168 "info",
1169 ("returning specific match as authenticated_as was specified"));
1170 *proxy_used = true;
1171 return proxy;
1172 } else {
1173 // we never use anonymous users when mapping
1174 // proxy users for internal plugins:
1175 if (strcmp(proxy->get_proxied_user() ? proxy->get_proxied_user() : "",
1176 "")) {
1177 if (find_acl_user(proxy->get_proxied_host(),
1178 proxy->get_proxied_user(), true)) {
1179 DBUG_PRINT("info",
1180 ("setting proxy_used to true, as \
1181 find_all search matched real user=%s host=%s",
1182 proxy->get_proxied_user(), proxy->get_proxied_host()));
1183 *proxy_used = true;
1184 strcpy(authenticated_as, proxy->get_proxied_user());
1185 } else {
1186 DBUG_PRINT("info", ("skipping match because ACL user \
1187 does not exist, looking for next match to map"));
1188 }
1189 if (*proxy_used) {
1190 DBUG_PRINT("info", ("returning matching user"));
1191 return proxy;
1192 }
1193 }
1194 }
1195 }
1196 }
1197 DBUG_PRINT("info", ("No matching users found, returning null"));
1198 return nullptr;
1199 }
1200
clear_and_init_db_cache()1201 void clear_and_init_db_cache() { db_cache.clear(); }
1202
1203 /**
1204 Insert a new entry in db_cache
1205
1206 @param [in] thd Handle to THD object
1207 @param [in] entry Entry to be inserted in db_cache
1208 */
1209
insert_entry_in_db_cache(THD * thd,acl_entry * entry)1210 static void insert_entry_in_db_cache(THD *thd, acl_entry *entry) {
1211 DBUG_TRACE;
1212 /* Either have WRITE lock or none at all */
1213 DBUG_ASSERT(assert_acl_cache_write_lock(thd) ||
1214 !assert_acl_cache_read_lock(thd));
1215
1216 Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
1217
1218 /*
1219 In following cases release memory and return
1220 1. Could not lock cache : This is ok because db_cache
1221 second level cache anyways.
1222 2. Someone already inserted a similar entry.
1223 */
1224 unique_ptr_my_free<acl_entry> entry_ptr(entry);
1225 if (!acl_cache_lock.lock(false)) return;
1226 db_cache.emplace(std::string(entry->key, entry->length),
1227 std::move(entry_ptr));
1228 }
1229
1230 /**
1231 Get privilege for a host, user, and db
1232 combination.
1233
1234 @note db_cache is not used if db_is_pattern
1235 is set.
1236
1237 @param thd Thread handler
1238 @param host Host name
1239 @param ip Ip
1240 @param user user name
1241 @param db We look for the ACL of this database
1242 @param db_is_pattern true if @p db can be considered a pattern or false if not
1243
1244 @return Database ACL
1245 */
1246
acl_get(THD * thd,const char * host,const char * ip,const char * user,const char * db,bool db_is_pattern)1247 ulong acl_get(THD *thd, const char *host, const char *ip, const char *user,
1248 const char *db, bool db_is_pattern) {
1249 ulong host_access = ~(ulong)0, db_access = 0;
1250 size_t key_length, copy_length;
1251 char key[ACL_KEY_LENGTH], *tmp_db, *end;
1252 acl_entry *entry;
1253 DBUG_TRACE;
1254
1255 copy_length =
1256 (strlen(ip ? ip : "") + strlen(user ? user : "") + strlen(db ? db : "")) +
1257 2; /* Added 2 at the end to avoid
1258 buffer overflow at strmov()*/
1259 /*
1260 Make sure that my_stpcpy() operations do not result in buffer overflow.
1261 */
1262 if (copy_length >= ACL_KEY_LENGTH) return 0;
1263 Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
1264 if (!acl_cache_lock.lock(false)) return db_access;
1265
1266 end = my_stpcpy(
1267 (tmp_db = my_stpcpy(my_stpcpy(key, ip ? ip : "") + 1, user) + 1), db);
1268 if (lower_case_table_names) {
1269 my_casedn_str(files_charset_info, tmp_db);
1270 db = tmp_db;
1271 }
1272 key_length = (size_t)(end - key);
1273 if (!db_is_pattern) {
1274 const auto it = db_cache.find(std::string(key, key_length));
1275 if (it != db_cache.end()) {
1276 db_access = it->second->access;
1277 DBUG_PRINT("exit", ("access: 0x%lx", db_access));
1278 return db_access;
1279 }
1280 }
1281
1282 /*
1283 Check if there are some access rights for database and user
1284 */
1285 for (ACL_DB *acl_db = acl_dbs->begin(); acl_db != acl_dbs->end(); ++acl_db) {
1286 if (!acl_db->user || !strcmp(user, acl_db->user)) {
1287 if (acl_db->host.compare_hostname(host, ip)) {
1288 /*
1289 Do the usual string comparision if partial_revokes is ON,
1290 otherwise do the wildcard grant comparision
1291 */
1292 if (!acl_db->db ||
1293 (db &&
1294 (mysqld_partial_revokes()
1295 ? (!strcmp(db, acl_db->db))
1296 : (!wild_compare(db, strlen(db), acl_db->db,
1297 strlen(acl_db->db), db_is_pattern))))) {
1298 db_access = acl_db->access;
1299 if (acl_db->host.get_host()) goto exit; // Fully specified. Take it
1300 break; /* purecov: tested */
1301 }
1302 }
1303 }
1304 }
1305 if (!db_access) goto exit; // Can't be better
1306
1307 exit:
1308 /* Save entry in cache for quick retrieval */
1309 if (!db_is_pattern &&
1310 (entry = (acl_entry *)my_malloc(
1311 key_memory_acl_cache, sizeof(acl_entry) + key_length, MYF(0)))) {
1312 entry->access = (db_access & host_access);
1313 entry->length = key_length;
1314 memcpy((uchar *)entry->key, key, key_length);
1315 acl_cache_lock.unlock();
1316 insert_entry_in_db_cache(thd, entry);
1317 }
1318 DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
1319 return db_access & host_access;
1320 }
1321
1322 /*
1323 Check if there are any possible matching entries for this host
1324
1325 NOTES
1326 All host names without wild cards are stored in a hash table,
1327 entries with wildcards are stored in a dynamic array
1328 */
1329
init_check_host(void)1330 static void init_check_host(void) {
1331 DBUG_TRACE;
1332 if (acl_wild_hosts != nullptr)
1333 acl_wild_hosts->clear();
1334 else
1335 acl_wild_hosts = new Prealloced_array<ACL_HOST_AND_IP, ACL_PREALLOC_SIZE>(
1336 key_memory_acl_mem);
1337
1338 size_t acl_users_size = acl_users ? acl_users->size() : 0;
1339
1340 acl_check_hosts = new collation_unordered_map<std::string, ACL_USER *>(
1341 system_charset_info, key_memory_acl_mem);
1342 if (acl_users_size && !allow_all_hosts) {
1343 for (ACL_USER *acl_user = acl_users->begin(); acl_user != acl_users->end();
1344 ++acl_user) {
1345 if (acl_user->host.get_host()) {
1346 if (acl_user->host.has_wildcard()) { // Has wildcard
1347 ACL_HOST_AND_IP *acl = nullptr;
1348 for (acl = acl_wild_hosts->begin(); acl != acl_wild_hosts->end();
1349 ++acl) { // Check if host already exists
1350 if (!my_strcasecmp(system_charset_info, acl_user->host.get_host(),
1351 acl->get_host()))
1352 break; // already stored
1353 }
1354 if (acl == acl_wild_hosts->end()) // If new
1355 acl_wild_hosts->push_back(acl_user->host);
1356 } else {
1357 // Will be ignored if there's already an entry.
1358 acl_check_hosts->emplace(acl_user->host.get_host(), acl_user);
1359 }
1360 }
1361 }
1362 }
1363 acl_wild_hosts->shrink_to_fit();
1364 }
1365
1366 /*
1367 Rebuild lists used for checking of allowed hosts
1368
1369 We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
1370 dropping or renaming user, since they contain pointers to elements of
1371 'acl_user' array, which are invalidated by drop operation, and use
1372 ACL_USER::host::hostname as a key, which is changed by rename.
1373 */
rebuild_check_host(void)1374 void rebuild_check_host(void) {
1375 delete acl_wild_hosts;
1376 acl_wild_hosts = nullptr;
1377 delete acl_check_hosts;
1378 acl_check_hosts = nullptr;
1379 init_check_host();
1380 }
1381
1382 /*
1383 Gets user credentials without authentication and resource limit checks. This
1384 function is used to initialized a new Security_context. It's a terrible
1385 anti-pattern that needs to go.
1386
1387 SYNOPSIS
1388 acl_getroot()
1389 thd Handle to THD
1390 sctx Context which should be initialized
1391 user user name
1392 host host name
1393 ip IP
1394 db current data base name
1395
1396 RETURN
1397 false OK
1398 true Error
1399 */
1400
acl_getroot(THD * thd,Security_context * sctx,const char * user,const char * host,const char * ip,const char * db)1401 bool acl_getroot(THD *thd, Security_context *sctx, const char *user,
1402 const char *host, const char *ip, const char *db) {
1403 int res = 1;
1404 ACL_USER *acl_user = nullptr;
1405 DBUG_TRACE;
1406
1407 DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
1408 (host ? host : "(NULL)"), (ip ? ip : "(NULL)"), user,
1409 (db ? db : "(NULL)")));
1410 sctx->set_user_ptr(user, user ? strlen(user) : 0);
1411 sctx->set_host_ptr(host, host ? strlen(host) : 0);
1412 sctx->set_ip_ptr(ip, ip ? strlen(ip) : 0);
1413 sctx->set_host_or_ip_ptr();
1414
1415 if (!initialized) {
1416 /*
1417 here if mysqld's been started with --skip-grant-tables option.
1418 */
1419 sctx->skip_grants();
1420 return false;
1421 }
1422
1423 sctx->set_master_access(0);
1424 sctx->cache_current_db_access(0);
1425 sctx->assign_priv_user("", 0);
1426 sctx->assign_priv_host("", 0);
1427
1428 Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
1429 if (!acl_cache_lock.lock(false)) return true;
1430
1431 /*
1432 Find acl entry in user database.
1433 This is specially tailored to suit the check we do for CALL of
1434 a stored procedure; user is set to what is actually a
1435 priv_user, which can be ''.
1436 */
1437 for (ACL_USER *acl_user_tmp = acl_users->begin();
1438 acl_user_tmp != acl_users->end(); ++acl_user_tmp) {
1439 if ((!acl_user_tmp->user && !user[0]) ||
1440 (acl_user_tmp->user && strcmp(user, acl_user_tmp->user) == 0)) {
1441 if (acl_user_tmp->host.compare_hostname(host, ip)) {
1442 acl_user = acl_user_tmp;
1443 res = 0;
1444 break;
1445 }
1446 }
1447 }
1448
1449 if (acl_user) {
1450 sctx->clear_active_roles();
1451 List_of_auth_id_refs default_roles;
1452 Auth_id_ref authid = create_authid_from(acl_user);
1453 /* Needs Acl_cache_lock_guard in read mode */
1454 get_default_roles(authid, default_roles);
1455 List_of_auth_id_refs::iterator it = default_roles.begin();
1456 for (; it != default_roles.end(); ++it) {
1457 if (sctx->activate_role(it->first, it->second)) {
1458 sctx->clear_active_roles();
1459 break;
1460 }
1461 }
1462
1463 if (sctx->get_active_roles()->size() == 0) {
1464 for (ACL_DB *acl_db = acl_dbs->begin(); acl_db != acl_dbs->end();
1465 ++acl_db) {
1466 if (!acl_db->user || (user && user[0] && !strcmp(user, acl_db->user))) {
1467 if (acl_db->host.compare_hostname(host, ip)) {
1468 /*
1469 Do the usual string comparision if partial_revokes is ON,
1470 otherwise do the wildcard grant comparision
1471 */
1472 if (!acl_db->db ||
1473 (db && (mysqld_partial_revokes()
1474 ? (!strcmp(db, acl_db->db))
1475 : (!wild_compare(db, strlen(db), acl_db->db,
1476 strlen(acl_db->db), false))))) {
1477 sctx->cache_current_db_access(acl_db->access);
1478 break;
1479 }
1480 }
1481 } // end if
1482 } // end for
1483 sctx->set_master_access(acl_user->access,
1484 acl_restrictions->find_restrictions(acl_user));
1485 } // end if
1486 sctx->assign_priv_user(user, user ? strlen(user) : 0);
1487 sctx->assign_priv_host(
1488 acl_user->host.get_host(),
1489 acl_user->host.get_host() ? strlen(acl_user->host.get_host()) : 0);
1490
1491 sctx->set_password_expired(acl_user->password_expired);
1492 sctx->lock_account(acl_user->account_locked);
1493 } // end if
1494
1495 if (acl_user && sctx->get_active_roles()->size() > 0) {
1496 sctx->checkout_access_maps();
1497 ulong db_acl = db ? sctx->db_acl({db, strlen(db)}) : 0;
1498 sctx->cache_current_db_access(db_acl);
1499 }
1500 return res;
1501 }
1502
1503 /**
1504 Convert scrambled password to binary form, according to scramble type,
1505 Binary form is stored in user.salt.
1506
1507 @param acl_user The object where to store the salt
1508
1509 Despite the name of the function it is used when loading ACLs from disk
1510 to store the password hash in the ACL_USER object.
1511 Note that it works only for native and "old" mysql authentication built-in
1512 plugins.
1513
1514 Assumption : user's authentication plugin information is available.
1515
1516 @return Password hash validation
1517 @retval false Hash is of suitable length
1518 @retval true Hash is of wrong length or format
1519 */
1520
set_user_salt(ACL_USER * acl_user)1521 bool set_user_salt(ACL_USER *acl_user) {
1522 bool result = false;
1523 plugin_ref plugin = nullptr;
1524
1525 plugin = my_plugin_lock_by_name(nullptr, acl_user->plugin,
1526 MYSQL_AUTHENTICATION_PLUGIN);
1527 if (plugin) {
1528 st_mysql_auth *auth = (st_mysql_auth *)plugin_decl(plugin)->info;
1529
1530 for (int i = 0; i < NUM_CREDENTIALS && !result; ++i) {
1531 result = auth->set_salt(acl_user->credentials[i].m_auth_string.str,
1532 acl_user->credentials[i].m_auth_string.length,
1533 acl_user->credentials[i].m_salt,
1534 &acl_user->credentials[i].m_salt_len);
1535 }
1536 plugin_unlock(nullptr, plugin);
1537 }
1538 return result;
1539 }
1540
1541 /**
1542 Iterate over the user records and check for irregularities.
1543 Currently this includes :
1544 - checking if the plugin referenced is present.
1545 - if there's sha256 users and there's neither SSL nor RSA configured
1546 */
validate_user_plugin_records()1547 static void validate_user_plugin_records() {
1548 DBUG_TRACE;
1549 if (!validate_user_plugins) return;
1550
1551 lock_plugin_data();
1552 for (ACL_USER *acl_user = acl_users->begin(); acl_user != acl_users->end();
1553 ++acl_user) {
1554 struct st_plugin_int *plugin;
1555
1556 if (acl_user->plugin.length) {
1557 /* rule 1 : plugin does exit */
1558 if (!auth_plugin_is_built_in(acl_user->plugin.str)) {
1559 plugin =
1560 plugin_find_by_type(acl_user->plugin, MYSQL_AUTHENTICATION_PLUGIN);
1561
1562 if (!plugin) {
1563 LogErr(WARNING_LEVEL, ER_AUTHCACHE_PLUGIN_MISSING,
1564 (int)acl_user->plugin.length, acl_user->plugin.str,
1565 acl_user->user,
1566 static_cast<int>(acl_user->host.get_host_len()),
1567 acl_user->host.get_host());
1568 }
1569 }
1570 if (Cached_authentication_plugins::compare_plugin(PLUGIN_SHA256_PASSWORD,
1571 acl_user->plugin) &&
1572 sha256_rsa_auth_status() && !have_ssl()) {
1573 LogErr(WARNING_LEVEL, ER_AUTHCACHE_PLUGIN_CONFIG, acl_user->plugin.str,
1574 acl_user->user, static_cast<int>(acl_user->host.get_host_len()),
1575 acl_user->host.get_host(), "but neither SSL nor RSA keys are");
1576 }
1577 }
1578 }
1579 unlock_plugin_data();
1580 }
1581
1582 /**
1583 Audit notification for flush
1584
1585 @param [in] thd Handle to THD
1586 */
1587
notify_flush_event(THD * thd)1588 void notify_flush_event(THD *thd) {
1589 mysql_audit_notify(thd, AUDIT_EVENT(MYSQL_AUDIT_AUTHENTICATION_FLUSH), 0,
1590 nullptr, nullptr, nullptr, false, nullptr, nullptr);
1591 }
1592
1593 /**
1594 Initialize roles structures from role tables handle.
1595
1596 This function is called by acl_reload and may fail to
1597 initialize role structures if handle to role_edges and/or
1598 default_roles are NUL
1599
1600 @param [in] thd Handle to THD object
1601 @param [in] tablelst Handle to Roles tables
1602
1603 @returns status of cache update
1604 @retval false Success
1605 @retval true failure
1606 */
reload_roles_cache(THD * thd,TABLE_LIST * tablelst)1607 static bool reload_roles_cache(THD *thd, TABLE_LIST *tablelst) {
1608 DBUG_TRACE;
1609 DBUG_ASSERT(tablelst);
1610 sql_mode_t old_sql_mode = thd->variables.sql_mode;
1611 thd->variables.sql_mode &= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
1612
1613 /*
1614 Attempt to reload the role cache only if the role_edges and
1615 default_roles tables exist.
1616 */
1617 if ((tablelst[0].table) && (tablelst[1].table) &&
1618 populate_roles_caches(thd, tablelst)) {
1619 thd->variables.sql_mode = old_sql_mode;
1620 return true;
1621 }
1622
1623 thd->variables.sql_mode = old_sql_mode;
1624 return false;
1625 }
1626
1627 /*
1628 Initialize structures responsible for user/db-level privilege checking and
1629 load privilege information for them from tables in the 'mysql' database.
1630
1631 SYNOPSIS
1632 acl_init()
1633 dont_read_acl_tables true if we want to skip loading data from
1634 privilege tables and disable privilege checking.
1635
1636 NOTES
1637 This function is mostly responsible for preparatory steps, main work
1638 on initialization and grants loading is done in acl_reload().
1639
1640 RETURN VALUES
1641 0 ok
1642 1 Could not initialize grant's
1643 */
1644
acl_init(bool dont_read_acl_tables)1645 bool acl_init(bool dont_read_acl_tables) {
1646 THD *thd;
1647 bool return_val;
1648 DBUG_TRACE;
1649
1650 init_acl_cache();
1651
1652 acl_cache_initialized = true;
1653
1654 /*
1655 cache built-in authentication plugins,
1656 to avoid hash searches and a global mutex lock on every connect
1657 */
1658 g_cached_authentication_plugins = new Cached_authentication_plugins();
1659 unknown_accounts = new Map_with_rw_lock<Auth_id, uint>(0);
1660 if (!g_cached_authentication_plugins->is_valid()) return true;
1661
1662 if (dont_read_acl_tables) {
1663 return false; /* purecov: tested */
1664 }
1665
1666 /*
1667 To be able to run this from boot, we allocate a temporary THD
1668 */
1669 if (!(thd = new THD)) return true; /* purecov: inspected */
1670 thd->thread_stack = (char *)&thd;
1671 thd->store_globals();
1672
1673 /*
1674 Check storage engine type for every ACL table and output warning
1675 message in case it's different from supported one (InnoDB). We
1676 still allow server to start-up if tables are in different SE
1677 since this is necessary to be able to perform privilege table
1678 upgrade without extra server restarts. Account management
1679 statements do their own checks and refuse to operate if privilege
1680 tables are using unsupported SE.
1681 */
1682 return_val = check_engine_type_for_acl_table(thd, false);
1683
1684 /*
1685 Check all the ACL tables are intact and output warning message in
1686 case any of the ACL tables are corrupted.
1687 */
1688 check_acl_tables_intact(thd, false);
1689
1690 /*
1691 It is safe to call acl_reload() since acl_* arrays and hashes which
1692 will be freed there are global static objects and thus are initialized
1693 by zeros at startup.
1694 */
1695 return_val |= acl_reload(thd, false);
1696 notify_flush_event(thd);
1697 thd->release_resources();
1698 delete thd;
1699
1700 return return_val;
1701 }
1702
clean_user_cache()1703 void clean_user_cache() {
1704 if (name_to_userlist) name_to_userlist->clear();
1705 acl_users->clear();
1706 }
1707
1708 /*
1709 Initialize structures responsible for user/db-level privilege checking
1710 and load information about grants from open privilege tables.
1711
1712 SYNOPSIS
1713 acl_load()
1714 thd Current thread
1715 tables List containing open "mysql.host", "mysql.user",
1716 "mysql.db" and "mysql.proxies_priv", "mysql.global_grants"
1717 tables in that order.
1718
1719 RETURN VALUES
1720 false Success
1721 true Error
1722 */
1723
acl_load(THD * thd,TABLE_LIST * tables)1724 static bool acl_load(THD *thd, TABLE_LIST *tables) {
1725 TABLE *table;
1726 unique_ptr_destroy_only<RowIterator> iterator;
1727 bool return_val = true;
1728 bool check_no_resolve = specialflag & SPECIAL_NO_RESOLVE;
1729 char tmp_name[NAME_LEN + 1];
1730 sql_mode_t old_sql_mode = thd->variables.sql_mode;
1731 DBUG_TRACE;
1732
1733 DBUG_EXECUTE_IF(
1734 "wl_9262_set_max_length_hostname",
1735 thd->security_context()->assign_priv_host("oh_my_gosh_this_is_a_long_"
1736 "hostname_look_at_it_it_has_60"
1737 "_char",
1738 60);
1739 thd->security_context()->assign_host("oh_my_gosh_this_is_a_long_"
1740 "hostname_look_at_it_it_has_60"
1741 "_char",
1742 60);
1743 thd->security_context()->set_host_or_ip_ptr(););
1744
1745 thd->variables.sql_mode &= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
1746
1747 grant_version++; /* Privileges updated */
1748
1749 clear_and_init_db_cache(); // Clear locked hostname cache
1750 init_acl_memory(); // Allocate the memory blocks in the MEM_ROOT
1751
1752 if (read_user_table(thd, tables[0].table)) goto end;
1753
1754 /*
1755 Prepare reading from the mysql.db table
1756 */
1757 iterator = init_table_iterator(thd, table = tables[1].table, nullptr, false,
1758 /*ignore_not_found_rows=*/false);
1759 if (iterator == nullptr) goto end;
1760 table->use_all_columns();
1761 acl_dbs->clear();
1762 int read_rec_errcode;
1763 while (!(read_rec_errcode = iterator->Read())) {
1764 /* Reading record in mysql.db */
1765 ACL_DB db;
1766 db.host.update_hostname(
1767 get_field(&global_acl_memory, table->field[MYSQL_DB_FIELD_HOST]));
1768 db.db = get_field(&global_acl_memory, table->field[MYSQL_DB_FIELD_DB]);
1769 if (!db.db) {
1770 LogErr(WARNING_LEVEL, ER_AUTHCACHE_DB_IGNORED_EMPTY_NAME);
1771 continue;
1772 }
1773 db.user = get_field(&global_acl_memory, table->field[MYSQL_DB_FIELD_USER]);
1774 if (check_no_resolve && hostname_requires_resolving(db.host.get_host()) &&
1775 strcmp(db.host.get_host(), "localhost") != 0) {
1776 LogErr(WARNING_LEVEL, ER_AUTHCACHE_DB_SKIPPED_NEEDS_RESOLVE, db.db,
1777 db.user ? db.user : "",
1778 db.host.get_host() ? db.host.get_host() : "");
1779 }
1780 db.access = get_access(table, 3, nullptr);
1781 db.access = fix_rights_for_db(db.access);
1782 if (lower_case_table_names) {
1783 /*
1784 convert db to lower case and give a warning if the db wasn't
1785 already in lower case
1786 */
1787 (void)my_stpcpy(tmp_name, db.db);
1788 my_casedn_str(files_charset_info, db.db);
1789 if (strcmp(db.db, tmp_name) != 0) {
1790 LogErr(WARNING_LEVEL, ER_AUTHCACHE_DB_ENTRY_LOWERCASED_REVOKE_WILL_FAIL,
1791 db.db, db.user ? db.user : "",
1792 db.host.get_host() ? db.host.get_host() : "");
1793 }
1794 }
1795 db.sort = get_sort(3, db.host.get_host(), db.db, db.user);
1796 if (table->s->fields <= 9) { // Without grant
1797 if (db.access & CREATE_ACL)
1798 db.access |= REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
1799 }
1800 acl_dbs->push_back(db);
1801 } // END reading records from mysql.db tables
1802
1803 iterator.reset();
1804 if (read_rec_errcode > 0) goto end;
1805
1806 std::sort(acl_dbs->begin(), acl_dbs->end(), ACL_compare());
1807 acl_dbs->shrink_to_fit();
1808
1809 /* Prepare to read records from the mysql.proxies_priv table */
1810 acl_proxy_users->clear();
1811
1812 if (tables[2].table) {
1813 iterator = init_table_iterator(thd, table = tables[2].table, nullptr, false,
1814 /*ignore_not_found_rows=*/false);
1815 if (iterator == nullptr) goto end;
1816 table->use_all_columns();
1817 while (!(read_rec_errcode = iterator->Read())) {
1818 /* Reading record in mysql.proxies_priv */
1819 ACL_PROXY_USER proxy;
1820 proxy.init(table, &global_acl_memory);
1821 if (proxy.check_validity(check_no_resolve)) continue;
1822 if (acl_proxy_users->push_back(proxy)) {
1823 goto end;
1824 }
1825 } // END reading records from the mysql.proxies_priv table
1826
1827 iterator.reset();
1828 if (read_rec_errcode > 0) goto end;
1829
1830 std::sort(acl_proxy_users->begin(), acl_proxy_users->end(), ACL_compare());
1831 } else {
1832 LogErr(WARNING_LEVEL, ER_AUTHCACHE_TABLE_PROXIES_PRIV_MISSING);
1833 }
1834 acl_proxy_users->shrink_to_fit();
1835 validate_user_plugin_records();
1836 init_check_host();
1837
1838 /* Load dynamic privileges */
1839 if (tables[3].table) {
1840 if (populate_dynamic_privilege_caches(thd, &tables[3])) {
1841 return_val = true;
1842 goto end;
1843 }
1844 } else {
1845 LogErr(WARNING_LEVEL, ER_MISSING_GRANT_SYSTEM_TABLE);
1846 }
1847
1848 initialized = true;
1849 return_val = false;
1850
1851 end:
1852 thd->variables.sql_mode = old_sql_mode;
1853 DBUG_EXECUTE_IF("induce_acl_load_failure", return_val = true;);
1854 return return_val;
1855 }
1856
1857 /**
1858 Clear second level cache on account names.
1859 */
1860
free_name_to_userlist()1861 void free_name_to_userlist() {
1862 if (!name_to_userlist) return;
1863
1864 name_to_userlist->~unordered_map();
1865 my_free(name_to_userlist);
1866 name_to_userlist = nullptr;
1867 }
1868
acl_free(bool end)1869 void acl_free(bool end /*= false*/) {
1870 free_name_to_userlist();
1871 delete acl_users;
1872 acl_users = nullptr;
1873 delete acl_dbs;
1874 acl_dbs = nullptr;
1875 delete acl_wild_hosts;
1876 acl_wild_hosts = nullptr;
1877 delete acl_proxy_users;
1878 acl_proxy_users = nullptr;
1879 delete acl_check_hosts;
1880 acl_check_hosts = nullptr;
1881 if (!end)
1882 clear_and_init_db_cache();
1883 else {
1884 shutdown_acl_cache();
1885 if (acl_cache_initialized == true) {
1886 db_cache.clear();
1887 delete g_cached_authentication_plugins;
1888 g_cached_authentication_plugins = nullptr;
1889 delete unknown_accounts;
1890 unknown_accounts = nullptr;
1891 acl_cache_initialized = false;
1892 }
1893 }
1894 free_root(&global_acl_memory, MYF(0));
1895 }
1896
check_engine_type_for_acl_table(THD * thd,bool mdl_locked)1897 bool check_engine_type_for_acl_table(THD *thd, bool mdl_locked) {
1898 TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
1899 uint flags = mdl_locked
1900 ? MYSQL_OPEN_HAS_MDL_LOCK | MYSQL_LOCK_IGNORE_TIMEOUT |
1901 MYSQL_OPEN_IGNORE_FLUSH
1902 : MYSQL_LOCK_IGNORE_TIMEOUT;
1903
1904 /*
1905 Open the following ACL tables to check their consistency.
1906 Although we don't read here from the tables being opened we still
1907 request a lock type MDL_SHARED_READ_ONLY for the sake of consistency
1908 with other code.
1909 */
1910
1911 grant_tables_setup_for_open(tables, TL_READ, MDL_SHARED_READ_ONLY);
1912
1913 bool result = open_and_lock_tables(thd, tables, flags);
1914 if (!result) {
1915 check_engine_type_for_acl_table(tables, false);
1916 if (!mdl_locked)
1917 commit_and_close_mysql_tables(thd);
1918 else
1919 close_thread_tables(thd);
1920 }
1921
1922 return result;
1923 }
1924
1925 /*
1926 This internal handler implements downgrade from SL_ERROR to SL_WARNING
1927 for acl_init()/handle_reload_request().
1928 */
1929 class Acl_ignore_error_handler : public Internal_error_handler {
1930 public:
handle_condition(THD *,uint sql_errno,const char *,Sql_condition::enum_severity_level * level,const char *)1931 virtual bool handle_condition(THD *, uint sql_errno, const char *,
1932 Sql_condition::enum_severity_level *level,
1933 const char *) {
1934 switch (sql_errno) {
1935 case ER_CANNOT_LOAD_FROM_TABLE_V2:
1936 case ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2:
1937 case ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE_V2:
1938 (*level) = Sql_condition::SL_WARNING;
1939 break;
1940 default:
1941 break;
1942 }
1943 return false;
1944 }
1945 };
1946
1947 /**
1948 Helper function that checks the sanity of tables object present in
1949 the TABLE_LIST object. it logs a warning message when a table is
1950 missing
1951
1952 @param thd Handle of current thread.
1953 @param tables A valid table list pointer
1954
1955 @retval
1956 false OK.
1957 true Error.
1958 */
check_acl_tables_intact(THD * thd,TABLE_LIST * tables)1959 bool check_acl_tables_intact(THD *thd, TABLE_LIST *tables) {
1960 Acl_table_intact table_intact(thd, WARNING_LEVEL);
1961 bool result_acl = false;
1962
1963 DBUG_ASSERT(tables);
1964 for (auto idx = 0; idx < ACL_TABLES::LAST_ENTRY; idx++) {
1965 if (tables[idx].table) {
1966 result_acl |= table_intact.check(tables[idx].table, (ACL_TABLES)idx);
1967 } else {
1968 LogErr(WARNING_LEVEL, ER_MISSING_ACL_SYSTEM_TABLE,
1969 tables[idx].table_name_length, tables[idx].table_name);
1970 result_acl |= true;
1971 }
1972 }
1973 /* say that we're still gonna give reading a try */
1974 if (result_acl)
1975 LogErr(INFORMATION_LEVEL, ER_ACL_WRONG_OR_MISSING_ACL_TABLES_LOG);
1976
1977 return result_acl;
1978 }
1979
1980 /**
1981 Opens the ACL tables and checks their sanity. This method reports error
1982 only if it is unable to open or lock tables. It is called in situations
1983 when server has to continue even if a corrupt table was found -
1984 For example - acl_init()
1985
1986 @param thd Handle of current thread.
1987 @param mdl_locked MDL is locked
1988
1989 @retval
1990 false OK.
1991 true Unable to open the table(s).
1992 */
check_acl_tables_intact(THD * thd,bool mdl_locked)1993 bool check_acl_tables_intact(THD *thd, bool mdl_locked) {
1994 TABLE_LIST tables[ACL_TABLES::LAST_ENTRY];
1995 Acl_ignore_error_handler acl_ignore_handler;
1996 uint flags = mdl_locked
1997 ? MYSQL_OPEN_HAS_MDL_LOCK | MYSQL_LOCK_IGNORE_TIMEOUT |
1998 MYSQL_OPEN_IGNORE_FLUSH
1999 : MYSQL_LOCK_IGNORE_TIMEOUT;
2000
2001 grant_tables_setup_for_open(tables, TL_READ, MDL_SHARED_READ_ONLY);
2002
2003 bool result_acl = open_and_lock_tables(thd, tables, flags);
2004
2005 thd->push_internal_handler(&acl_ignore_handler);
2006 if (!result_acl) {
2007 check_acl_tables_intact(thd, tables);
2008 if (!mdl_locked)
2009 commit_and_close_mysql_tables(thd);
2010 else
2011 close_thread_tables(thd);
2012 }
2013 thd->pop_internal_handler();
2014
2015 return result_acl;
2016 }
2017
2018 /**
2019 Small helper function which allows to determine if error which caused
2020 failure to open and lock privilege tables should not be reported to error
2021 log (because this is expected or temporary condition).
2022 */
2023
is_expected_or_transient_error(THD * thd)2024 bool is_expected_or_transient_error(THD *thd) {
2025 return !thd->get_stmt_da()->is_error() || // Interrupted/no error condition.
2026 thd->get_stmt_da()->mysql_errno() == ER_TABLE_NOT_LOCKED ||
2027 thd->get_stmt_da()->mysql_errno() == ER_LOCK_DEADLOCK;
2028 }
2029
2030 /*
2031 Forget current user/db-level privileges and read new privileges
2032 from the privilege tables.
2033
2034 SYNOPSIS
2035 acl_reload()
2036 thd Current thread
2037
2038 NOTE
2039 All tables of calling thread which were open and locked by LOCK TABLES
2040 statement will be unlocked and closed.
2041 This function is also used for initialization of structures responsible
2042 for user/db-level privilege checking.
2043
2044 RETURN VALUE
2045 false Success
2046 true Failure
2047 */
2048
acl_reload(THD * thd,bool mdl_locked)2049 bool acl_reload(THD *thd, bool mdl_locked) {
2050 MEM_ROOT old_mem;
2051 bool return_val = true;
2052 uint flags = mdl_locked
2053 ? MYSQL_OPEN_HAS_MDL_LOCK | MYSQL_LOCK_IGNORE_TIMEOUT |
2054 MYSQL_OPEN_IGNORE_FLUSH
2055 : MYSQL_LOCK_IGNORE_TIMEOUT;
2056 Prealloced_array<ACL_USER, ACL_PREALLOC_SIZE> *old_acl_users = nullptr;
2057 Prealloced_array<ACL_DB, ACL_PREALLOC_SIZE> *old_acl_dbs = nullptr;
2058 Prealloced_array<ACL_PROXY_USER, ACL_PREALLOC_SIZE> *old_acl_proxy_users =
2059 nullptr;
2060 Granted_roles_graph *old_granted_roles = nullptr;
2061 Default_roles *old_default_roles = nullptr;
2062 Role_index_map *old_authid_to_vertex = nullptr;
2063 Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
2064 User_to_dynamic_privileges_map *old_dyn_priv_map;
2065 unique_ptr<Acl_restrictions> old_acl_restrictions = nullptr;
2066 DBUG_TRACE;
2067
2068 // Interchange the global role cache ptrs with the local role cache ptrs.
2069 auto swap_role_cache = [&]() {
2070 std::swap(old_granted_roles, g_granted_roles);
2071 std::swap(old_default_roles, g_default_roles);
2072 std::swap(old_authid_to_vertex, g_authid_to_vertex);
2073 };
2074 // Delete the memory pointed by the local role cache ptrs.
2075 auto delete_old_role_cache = [&]() {
2076 delete old_granted_roles;
2077 delete old_default_roles;
2078 delete old_authid_to_vertex;
2079 };
2080
2081 /*
2082 To avoid deadlocks we should obtain table locks before obtaining
2083 acl_cache->lock mutex.
2084 */
2085 TABLE_LIST tables[6] = {
2086 TABLE_LIST("mysql", "user", TL_READ, MDL_SHARED_READ_ONLY),
2087 /*
2088 For a TABLE_LIST element that is inited with a lock type TL_READ
2089 the type MDL_SHARED_READ_ONLY of MDL is requested for.
2090 Acquiring strong MDL lock allows to avoid deadlock and timeout errors
2091 from SE level.
2092 */
2093 TABLE_LIST("mysql", "db", TL_READ, MDL_SHARED_READ_ONLY),
2094
2095 TABLE_LIST("mysql", "proxies_priv", TL_READ, MDL_SHARED_READ_ONLY),
2096
2097 TABLE_LIST("mysql", "global_grants", TL_READ, MDL_SHARED_READ_ONLY),
2098
2099 TABLE_LIST("mysql", "role_edges", TL_READ, MDL_SHARED_READ_ONLY),
2100
2101 TABLE_LIST("mysql", "default_roles", TL_READ, MDL_SHARED_READ_ONLY)};
2102
2103 tables[0].next_local = tables[0].next_global = tables + 1;
2104 tables[1].next_local = tables[1].next_global = tables + 2;
2105 tables[2].next_local = tables[2].next_global = tables + 3;
2106 tables[3].next_local = tables[3].next_global = tables + 4;
2107 tables[4].next_local = tables[4].next_global = tables + 5;
2108
2109 tables[0].open_type = tables[1].open_type = tables[2].open_type =
2110 tables[3].open_type = tables[4].open_type = tables[5].open_type =
2111 OT_BASE_ONLY;
2112 tables[3].open_strategy = tables[4].open_strategy = tables[5].open_strategy =
2113 TABLE_LIST::OPEN_IF_EXISTS;
2114
2115 if (open_and_lock_tables(thd, tables, flags)) {
2116 /*
2117 Execution might have been interrupted; only print the error message
2118 if a user error condition has been raised. Also do not print expected/
2119 transient errors about tables not being locked (occurs when user does
2120 FLUSH PRIVILEGES under LOCK TABLES) and MDL deadlocks. These errors
2121 can't occurr at start-up and will be reported to user anyway.
2122 */
2123 if (!is_expected_or_transient_error(thd)) {
2124 LogErr(ERROR_LEVEL, ER_AUTHCACHE_CANT_OPEN_AND_LOCK_PRIVILEGE_TABLES,
2125 thd->get_stmt_da()->message_text());
2126 }
2127 goto end;
2128 }
2129
2130 if (!acl_cache_lock.lock()) goto end;
2131
2132 old_acl_users = acl_users;
2133 old_acl_dbs = acl_dbs;
2134 old_acl_proxy_users = acl_proxy_users;
2135 old_acl_restrictions = move(acl_restrictions);
2136 swap_role_cache();
2137 roles_init();
2138
2139 acl_users =
2140 new Prealloced_array<ACL_USER, ACL_PREALLOC_SIZE>(key_memory_acl_mem);
2141 acl_dbs = new Prealloced_array<ACL_DB, ACL_PREALLOC_SIZE>(key_memory_acl_mem);
2142 acl_proxy_users = new Prealloced_array<ACL_PROXY_USER, ACL_PREALLOC_SIZE>(
2143 key_memory_acl_mem);
2144 acl_restrictions = make_unique<Acl_restrictions>();
2145
2146 // acl_load() overwrites global_acl_memory, so we need to free it.
2147 // However, we can't do that immediately, because acl_load() might fail,
2148 // and then we'd need to keep it.
2149 old_mem = move(global_acl_memory);
2150 delete acl_wild_hosts;
2151 acl_wild_hosts = nullptr;
2152 delete acl_check_hosts;
2153 acl_check_hosts = nullptr;
2154 old_dyn_priv_map =
2155 swap_dynamic_privileges_map(new User_to_dynamic_privileges_map());
2156
2157 /*
2158 Revert to the old acl caches, if either loading of acl cache or role
2159 cache failed. We do this because roles caches maintain the shallow
2160 copies of the ACL_USER(s).
2161 */
2162 if ((return_val = acl_load(thd, tables)) ||
2163 (return_val = reload_roles_cache(
2164 thd, (tables + 4)))) { // Error. Revert to old list
2165 DBUG_PRINT("error", ("Reverting to old privileges"));
2166 acl_free(); /* purecov: inspected */
2167 acl_users = old_acl_users;
2168 acl_dbs = old_acl_dbs;
2169 acl_proxy_users = old_acl_proxy_users;
2170 global_acl_memory = move(old_mem);
2171 acl_restrictions = move(old_acl_restrictions);
2172 // Revert to the old role caches
2173 swap_role_cache();
2174 // Old caches must be pointing to the global role caches right now
2175 delete_old_role_cache();
2176
2177 init_check_host();
2178 delete swap_dynamic_privileges_map(old_dyn_priv_map);
2179 if (!old_dyn_priv_map) dynamic_privileges_init();
2180 if (acl_users) rebuild_cached_acl_users_for_name();
2181 } else {
2182 delete old_acl_users;
2183 delete old_acl_dbs;
2184 delete old_acl_proxy_users;
2185 delete old_dyn_priv_map;
2186 // Delete the old role caches
2187 delete_old_role_cache();
2188 free_root(&old_mem, MYF(0));
2189 }
2190
2191 end:
2192 if (!mdl_locked)
2193 commit_and_close_mysql_tables(thd);
2194 else
2195 close_thread_tables(thd);
2196 get_global_acl_cache()->increase_version();
2197 DEBUG_SYNC(thd, "after_acl_reload");
2198 return return_val;
2199 }
2200
acl_insert_proxy_user(ACL_PROXY_USER * new_value)2201 void acl_insert_proxy_user(ACL_PROXY_USER *new_value) {
2202 DBUG_TRACE;
2203 DBUG_ASSERT(assert_acl_cache_write_lock(current_thd));
2204 acl_proxy_users->push_back(*new_value);
2205 std::sort(acl_proxy_users->begin(), acl_proxy_users->end(), ACL_compare());
2206 }
2207
2208 struct Free_grant_table {
operator ()Free_grant_table2209 void operator()(GRANT_TABLE *grant_table) const {
2210 grant_table->~GRANT_TABLE();
2211 }
2212 };
2213
2214 /* Free grant array if possible */
2215
grant_free(void)2216 void grant_free(void) {
2217 DBUG_TRACE;
2218 column_priv_hash.reset();
2219 proc_priv_hash.reset();
2220 func_priv_hash.reset();
2221 free_root(&memex, MYF(0));
2222 }
2223
2224 /**
2225 @brief Initialize structures responsible for table/column-level privilege
2226 checking and load information for them from tables in the 'mysql' database.
2227
2228 @param skip_grant_tables true if the command line option
2229 --skip-grant-tables is specified, else false.
2230
2231 @return Error status
2232 @retval false OK
2233 @retval true Could not initialize grant subsystem.
2234 */
2235
grant_init(bool skip_grant_tables)2236 bool grant_init(bool skip_grant_tables) {
2237 THD *thd;
2238 bool return_val;
2239 DBUG_TRACE;
2240
2241 if (skip_grant_tables) return false;
2242
2243 if (!(thd = new THD)) return true; /* purecov: deadcode */
2244 thd->thread_stack = (char *)&thd;
2245 thd->store_globals();
2246
2247 return_val = grant_reload(thd, false);
2248
2249 if (return_val && thd->get_stmt_da()->is_error())
2250 LogErr(ERROR_LEVEL, ER_AUTHCACHE_CANT_INIT_GRANT_SUBSYSTEM,
2251 thd->get_stmt_da()->message_text());
2252
2253 if (opt_mandatory_roles.length > 0) {
2254 return_val |= check_authorization_id_string(thd, opt_mandatory_roles);
2255 }
2256
2257 thd->release_resources();
2258 delete thd;
2259
2260 return return_val;
2261 }
2262
2263 /**
2264 @brief Helper function to grant_reload_procs_priv
2265
2266 Reads the procs_priv table into memory hash.
2267
2268 @param p_table A pointer to the procs_priv table structure.
2269
2270 @see grant_reload
2271 @see grant_reload_procs_priv
2272
2273 @return Error state
2274 @retval true An error occurred
2275 @retval false Success
2276 */
2277
grant_load_procs_priv(TABLE * p_table)2278 static bool grant_load_procs_priv(TABLE *p_table) {
2279 MEM_ROOT *memex_ptr;
2280 bool return_val = true;
2281 int error;
2282 bool check_no_resolve = specialflag & SPECIAL_NO_RESOLVE;
2283 MEM_ROOT **save_mem_root_ptr = THR_MALLOC;
2284 DBUG_TRACE;
2285 proc_priv_hash.reset(
2286 new malloc_unordered_multimap<string,
2287 unique_ptr_destroy_only<GRANT_NAME>>(
2288 key_memory_acl_memex));
2289 func_priv_hash.reset(
2290 new malloc_unordered_multimap<string,
2291 unique_ptr_destroy_only<GRANT_NAME>>(
2292 key_memory_acl_memex));
2293 error = p_table->file->ha_index_init(0, true);
2294 DBUG_EXECUTE_IF("wl7158_grant_load_proc_1", p_table->file->ha_index_end();
2295 error = HA_ERR_LOCK_DEADLOCK;);
2296 if (error) {
2297 acl_print_ha_error(error);
2298 return true;
2299 }
2300 p_table->use_all_columns();
2301
2302 error = p_table->file->ha_index_first(p_table->record[0]);
2303 DBUG_ASSERT(p_table->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
2304 error != HA_ERR_LOCK_DEADLOCK);
2305 DBUG_ASSERT(p_table->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
2306 error != HA_ERR_LOCK_WAIT_TIMEOUT);
2307 DBUG_EXECUTE_IF("wl7158_grant_load_proc_2", error = HA_ERR_LOCK_DEADLOCK;);
2308
2309 if (error) {
2310 if (error == HA_ERR_END_OF_FILE)
2311 return_val = false; // Return Ok.
2312 else
2313 acl_print_ha_error(error);
2314 } else {
2315 memex_ptr = &memex;
2316 THR_MALLOC = &memex_ptr;
2317 do {
2318 GRANT_NAME *mem_check;
2319 malloc_unordered_multimap<string, unique_ptr_destroy_only<GRANT_NAME>>
2320 *hash;
2321 if (!(mem_check = new (memex_ptr) GRANT_NAME(p_table, true))) {
2322 /* This could only happen if we are out memory */
2323 goto end_unlock;
2324 }
2325
2326 if (check_no_resolve) {
2327 if (hostname_requires_resolving(mem_check->host.get_host())) {
2328 LogErr(WARNING_LEVEL, ER_AUTHCACHE_PROCS_PRIV_SKIPPED_NEEDS_RESOLVE,
2329 mem_check->tname, mem_check->user,
2330 mem_check->host.get_host() ? mem_check->host.get_host() : "");
2331 }
2332 }
2333 const enum_sp_type sp_type = to_sp_type(p_table->field[4]->val_int());
2334 if (sp_type == enum_sp_type::PROCEDURE) {
2335 hash = proc_priv_hash.get();
2336 } else if (sp_type == enum_sp_type::FUNCTION) {
2337 hash = func_priv_hash.get();
2338 } else {
2339 LogErr(WARNING_LEVEL,
2340 ER_AUTHCACHE_PROCS_PRIV_ENTRY_IGNORED_BAD_ROUTINE_TYPE,
2341 mem_check->tname);
2342 continue;
2343 }
2344
2345 mem_check->privs = fix_rights_for_procedure(mem_check->privs);
2346 if (!mem_check->ok()) {
2347 destroy(mem_check);
2348 } else {
2349 hash->emplace(mem_check->hash_key,
2350 unique_ptr_destroy_only<GRANT_NAME>(mem_check));
2351 }
2352 error = p_table->file->ha_index_next(p_table->record[0]);
2353 DBUG_ASSERT(p_table->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
2354 error != HA_ERR_LOCK_DEADLOCK);
2355 DBUG_ASSERT(p_table->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
2356 error != HA_ERR_LOCK_WAIT_TIMEOUT);
2357 DBUG_EXECUTE_IF("wl7158_grant_load_proc_3",
2358 error = HA_ERR_LOCK_DEADLOCK;);
2359 if (error) {
2360 if (error == HA_ERR_END_OF_FILE)
2361 return_val = false;
2362 else
2363 acl_print_ha_error(error);
2364 goto end_unlock;
2365 }
2366 } while (true);
2367 }
2368
2369 end_unlock:
2370 p_table->file->ha_index_end();
2371 THR_MALLOC = save_mem_root_ptr;
2372 return return_val;
2373 }
2374
2375 /**
2376 @brief Initialize structures responsible for table/column-level privilege
2377 checking and load information about grants from open privilege tables.
2378
2379 @param thd Current thread
2380 @param tables List containing open "mysql.tables_priv" and
2381 "mysql.columns_priv" tables.
2382
2383 @see grant_reload
2384
2385 @return Error state
2386 @retval false Success
2387 @retval true Error
2388 */
2389
grant_load(THD * thd,TABLE_LIST * tables)2390 static bool grant_load(THD *thd, TABLE_LIST *tables) {
2391 bool return_val = true;
2392 int error;
2393 TABLE *t_table = nullptr, *c_table = nullptr;
2394 bool check_no_resolve = specialflag & SPECIAL_NO_RESOLVE;
2395 sql_mode_t old_sql_mode = thd->variables.sql_mode;
2396 DBUG_TRACE;
2397
2398 thd->variables.sql_mode &= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
2399
2400 column_priv_hash.reset(
2401 new malloc_unordered_multimap<string,
2402 unique_ptr_destroy_only<GRANT_TABLE>>(
2403 key_memory_acl_memex));
2404
2405 t_table = tables[0].table;
2406 c_table = tables[1].table;
2407 error = t_table->file->ha_index_init(0, true);
2408 DBUG_EXECUTE_IF("wl7158_grant_load_1", t_table->file->ha_index_end();
2409 error = HA_ERR_LOCK_DEADLOCK;);
2410 if (error) {
2411 acl_print_ha_error(error);
2412 goto end_index_init;
2413 }
2414 t_table->use_all_columns();
2415 c_table->use_all_columns();
2416
2417 error = t_table->file->ha_index_first(t_table->record[0]);
2418 DBUG_ASSERT(t_table->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
2419 error != HA_ERR_LOCK_DEADLOCK);
2420 DBUG_ASSERT(t_table->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
2421 error != HA_ERR_LOCK_WAIT_TIMEOUT);
2422 DBUG_EXECUTE_IF("wl7158_grant_load_2", error = HA_ERR_LOCK_DEADLOCK;);
2423 if (error) {
2424 if (error == HA_ERR_END_OF_FILE)
2425 return_val = false; // Return Ok.
2426 else
2427 acl_print_ha_error(error);
2428 } else {
2429 Swap_mem_root_guard guard(thd, &memex);
2430 do {
2431 GRANT_TABLE *mem_check = new (thd->mem_root) GRANT_TABLE(t_table);
2432
2433 if (!mem_check) {
2434 /* This could only happen if we are out memory */
2435 goto end_unlock;
2436 }
2437
2438 if (mem_check->init(c_table)) {
2439 destroy(mem_check);
2440 goto end_unlock;
2441 }
2442
2443 if (check_no_resolve) {
2444 if (hostname_requires_resolving(mem_check->host.get_host()) &&
2445 strcmp(mem_check->host.get_host(), "localhost") != 0) {
2446 LogErr(WARNING_LEVEL, ER_AUTHCACHE_TABLES_PRIV_SKIPPED_NEEDS_RESOLVE,
2447 mem_check->tname, mem_check->user ? mem_check->user : "",
2448 mem_check->host.get_host() ? mem_check->host.get_host() : "");
2449 }
2450 }
2451
2452 if (!mem_check->ok()) {
2453 destroy(mem_check);
2454 } else {
2455 column_priv_hash->emplace(
2456 mem_check->hash_key,
2457 unique_ptr_destroy_only<GRANT_TABLE>(mem_check));
2458 }
2459 error = t_table->file->ha_index_next(t_table->record[0]);
2460 DBUG_ASSERT(t_table->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
2461 error != HA_ERR_LOCK_DEADLOCK);
2462 DBUG_ASSERT(t_table->file->ht->db_type == DB_TYPE_NDBCLUSTER ||
2463 error != HA_ERR_LOCK_WAIT_TIMEOUT);
2464 DBUG_EXECUTE_IF("wl7158_grant_load_3", error = HA_ERR_LOCK_DEADLOCK;);
2465 if (error) {
2466 if (error != HA_ERR_END_OF_FILE)
2467 acl_print_ha_error(error);
2468 else
2469 return_val = false;
2470 goto end_unlock;
2471 }
2472
2473 } while (true);
2474 }
2475
2476 end_unlock:
2477 t_table->file->ha_index_end();
2478 end_index_init:
2479 thd->variables.sql_mode = old_sql_mode;
2480 return return_val;
2481 }
2482
2483 /**
2484 @brief Helper function to grant_reload. Reloads procs_priv table is it
2485 exists.
2486
2487 @param table A pointer to the table list.
2488
2489 @see grant_reload
2490
2491 @return Error state
2492 @retval false Success
2493 @retval true An error has occurred.
2494 */
2495
grant_reload_procs_priv(TABLE_LIST * table)2496 static bool grant_reload_procs_priv(TABLE_LIST *table) {
2497 DBUG_TRACE;
2498
2499 /* Save a copy of the current hash if we need to undo the grant load */
2500 unique_ptr<
2501 malloc_unordered_multimap<string, unique_ptr_destroy_only<GRANT_NAME>>>
2502 old_proc_priv_hash(move(proc_priv_hash));
2503 unique_ptr<
2504 malloc_unordered_multimap<string, unique_ptr_destroy_only<GRANT_NAME>>>
2505 old_func_priv_hash(move(func_priv_hash));
2506 bool return_val = false;
2507
2508 if ((return_val = grant_load_procs_priv(table->table))) {
2509 /* Error; Reverting to old hash */
2510 DBUG_PRINT("error", ("Reverting to old privileges"));
2511 proc_priv_hash = move(old_proc_priv_hash);
2512 func_priv_hash = move(old_func_priv_hash);
2513 }
2514
2515 return return_val;
2516 }
2517
2518 /**
2519 @brief Reload information about table and column level privileges if
2520 possible
2521
2522 @param thd Current thread
2523 @param mdl_locked MDL lock status - affects open/close table operations
2524
2525 Locked tables are checked by acl_reload() and doesn't have to be checked
2526 in this call.
2527 This function is also used for initialization of structures responsible
2528 for table/column-level privilege checking.
2529
2530 @return Error state
2531 @retval false Success
2532 @retval true Error
2533 */
2534
grant_reload(THD * thd,bool mdl_locked)2535 bool grant_reload(THD *thd, bool mdl_locked) {
2536 MEM_ROOT old_mem;
2537 bool return_val = true;
2538 uint flags = mdl_locked
2539 ? MYSQL_OPEN_HAS_MDL_LOCK | MYSQL_LOCK_IGNORE_TIMEOUT |
2540 MYSQL_OPEN_IGNORE_FLUSH
2541 : MYSQL_LOCK_IGNORE_TIMEOUT;
2542 Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::WRITE_MODE);
2543
2544 DBUG_TRACE;
2545
2546 /* Don't do anything if running with --skip-grant-tables */
2547 if (!initialized) return false;
2548
2549 TABLE_LIST tables[3] = {
2550
2551 /*
2552 Acquiring strong MDL lock allows to avoid deadlock and timeout errors
2553 from SE level.
2554 */
2555 TABLE_LIST("mysql", "tables_priv", TL_READ, MDL_SHARED_READ_ONLY),
2556
2557 TABLE_LIST("mysql", "columns_priv", TL_READ, MDL_SHARED_READ_ONLY),
2558
2559 TABLE_LIST("mysql", "procs_priv", TL_READ, MDL_SHARED_READ_ONLY)};
2560
2561 tables[0].next_local = tables[0].next_global = tables + 1;
2562 tables[1].next_local = tables[1].next_global = tables + 2;
2563 tables[0].open_type = tables[1].open_type = tables[2].open_type =
2564 OT_BASE_ONLY;
2565
2566 if (open_and_lock_tables(thd, tables, flags)) {
2567 if (!is_expected_or_transient_error(thd)) {
2568 LogErr(ERROR_LEVEL, ER_AUTHCACHE_CANT_OPEN_AND_LOCK_PRIVILEGE_TABLES,
2569 thd->get_stmt_da()->message_text());
2570 }
2571 goto end;
2572 }
2573
2574 if (!acl_cache_lock.lock()) goto end;
2575
2576 {
2577 unique_ptr<
2578 malloc_unordered_multimap<string, unique_ptr_destroy_only<GRANT_TABLE>>>
2579 old_column_priv_hash(move(column_priv_hash));
2580
2581 /*
2582 Create a new memory pool but save the current memory pool to make an
2583 undo opertion possible in case of failure.
2584 */
2585 old_mem = move(memex);
2586 init_sql_alloc(key_memory_acl_memex, &memex, ACL_ALLOC_BLOCK_SIZE, 0);
2587 /*
2588 tables[2].table i.e. procs_priv can be null if we are working with
2589 pre 4.1 privilage tables
2590 */
2591 if ((return_val = (grant_load(thd, tables) ||
2592 grant_reload_procs_priv(
2593 &tables[2])))) { // Error. Revert to old hash
2594 DBUG_PRINT("error", ("Reverting to old privileges"));
2595 column_priv_hash = move(old_column_priv_hash); /* purecov: deadcode */
2596 free_root(&memex, MYF(0));
2597 memex = move(old_mem); /* purecov: deadcode */
2598 } else { // Reload successful
2599 old_column_priv_hash.reset();
2600 free_root(&old_mem, MYF(0));
2601 grant_version++;
2602 get_global_acl_cache()->increase_version();
2603 }
2604 }
2605
2606 end:
2607 if (!mdl_locked)
2608 commit_and_close_mysql_tables(thd);
2609 else
2610 close_thread_tables(thd);
2611 return return_val;
2612 }
2613
acl_update_user(const char * user,const char * host,enum SSL_type ssl_type,const char * ssl_cipher,const char * x509_issuer,const char * x509_subject,USER_RESOURCES * mqh,ulong privileges,const LEX_CSTRING & plugin,const LEX_CSTRING & auth,const std::string & second_auth,const MYSQL_TIME & password_change_time,const LEX_ALTER & password_life,Restrictions & restrictions,acl_table::Pod_user_what_to_update & what_to_update,uint failed_login_attempts,int password_lock_time)2614 void acl_update_user(const char *user, const char *host, enum SSL_type ssl_type,
2615 const char *ssl_cipher, const char *x509_issuer,
2616 const char *x509_subject, USER_RESOURCES *mqh,
2617 ulong privileges, const LEX_CSTRING &plugin,
2618 const LEX_CSTRING &auth, const std::string &second_auth,
2619 const MYSQL_TIME &password_change_time,
2620 const LEX_ALTER &password_life, Restrictions &restrictions,
2621 acl_table::Pod_user_what_to_update &what_to_update,
2622 uint failed_login_attempts, int password_lock_time) {
2623 DBUG_TRACE;
2624 DBUG_ASSERT(assert_acl_cache_write_lock(current_thd));
2625 for (ACL_USER *acl_user = acl_users->begin(); acl_user != acl_users->end();
2626 ++acl_user) {
2627 if ((!acl_user->user && !user[0]) ||
2628 (acl_user->user && !strcmp(user, acl_user->user))) {
2629 if ((!acl_user->host.get_host() && !host[0]) ||
2630 (acl_user->host.get_host() &&
2631 !my_strcasecmp(system_charset_info, host,
2632 acl_user->host.get_host()))) {
2633 if (plugin.length > 0) {
2634 acl_user->plugin.str = plugin.str;
2635 acl_user->plugin.length = plugin.length;
2636 optimize_plugin_compare_by_pointer(&acl_user->plugin);
2637 if (!auth_plugin_is_built_in(acl_user->plugin.str))
2638 acl_user->plugin.str =
2639 strmake_root(&global_acl_memory, plugin.str, plugin.length);
2640 /* Update auth string only when specified in ALTER/GRANT */
2641 if (auth.str) {
2642 if (auth.length == 0)
2643 acl_user->credentials[PRIMARY_CRED].m_auth_string = EMPTY_CSTR;
2644 else
2645 acl_user->credentials[PRIMARY_CRED].m_auth_string.str =
2646 strmake_root(&global_acl_memory, auth.str, auth.length);
2647 acl_user->credentials[PRIMARY_CRED].m_auth_string.length =
2648 auth.length;
2649 set_user_salt(acl_user);
2650 if (password_change_time.time_type != MYSQL_TIMESTAMP_ERROR)
2651 acl_user->password_last_changed = password_change_time;
2652 }
2653
2654 if (what_to_update.m_what & USER_ATTRIBUTES) {
2655 if (what_to_update.m_user_attributes &
2656 acl_table::USER_ATTRIBUTE_RETAIN_PASSWORD) {
2657 acl_user->credentials[SECOND_CRED].m_auth_string.str =
2658 strmake_root(&global_acl_memory, second_auth.c_str(),
2659 second_auth.length());
2660 acl_user->credentials[SECOND_CRED].m_auth_string.length =
2661 second_auth.length();
2662 }
2663 if (what_to_update.m_user_attributes &
2664 acl_table::USER_ATTRIBUTE_DISCARD_PASSWORD) {
2665 acl_user->credentials[SECOND_CRED].m_auth_string = EMPTY_CSTR;
2666 }
2667 if (what_to_update.m_user_attributes &
2668 (acl_table::USER_ATTRIBUTE_FAILED_LOGIN_ATTEMPTS |
2669 acl_table::USER_ATTRIBUTE_PASSWORD_LOCK_TIME)) {
2670 acl_user->password_locked_state.set_parameters(
2671 password_lock_time, failed_login_attempts);
2672 }
2673 set_user_salt(acl_user);
2674 }
2675 }
2676 DBUG_PRINT("info",
2677 ("Updates global privilege for %s@%s to %lu", acl_user->user,
2678 acl_user->host.get_host(), privileges));
2679 acl_user->access = privileges;
2680 if (what_to_update.m_what & USER_ATTRIBUTES &&
2681 (what_to_update.m_user_attributes &
2682 acl_table::USER_ATTRIBUTE_RESTRICTIONS))
2683 acl_restrictions->upsert_restrictions(acl_user, restrictions);
2684
2685 if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
2686 acl_user->user_resource.questions = mqh->questions;
2687 if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
2688 acl_user->user_resource.updates = mqh->updates;
2689 if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
2690 acl_user->user_resource.conn_per_hour = mqh->conn_per_hour;
2691 if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
2692 acl_user->user_resource.user_conn = mqh->user_conn;
2693 if (ssl_type != SSL_TYPE_NOT_SPECIFIED) {
2694 acl_user->ssl_type = ssl_type;
2695 acl_user->ssl_cipher =
2696 (ssl_cipher ? strdup_root(&global_acl_memory, ssl_cipher)
2697 : nullptr);
2698 acl_user->x509_issuer =
2699 (x509_issuer ? strdup_root(&global_acl_memory, x509_issuer)
2700 : nullptr);
2701 acl_user->x509_subject =
2702 (x509_subject ? strdup_root(&global_acl_memory, x509_subject)
2703 : nullptr);
2704 }
2705 /* update details related to password lifetime, password expiry */
2706 if (password_life.update_password_expired_column ||
2707 what_to_update.m_what & PLUGIN_ATTR)
2708 acl_user->password_expired =
2709 password_life.update_password_expired_column;
2710 if (!password_life.update_password_expired_column &&
2711 password_life.update_password_expired_fields) {
2712 if (!password_life.use_default_password_lifetime) {
2713 acl_user->password_lifetime = password_life.expire_after_days;
2714 acl_user->use_default_password_lifetime = false;
2715 } else
2716 acl_user->use_default_password_lifetime = true;
2717 }
2718
2719 if (password_life.update_account_locked_column) {
2720 acl_user->account_locked = password_life.account_locked;
2721
2722 /* reset the runtime locked state if there is account locking */
2723 if (!acl_user->account_locked &&
2724 acl_user->password_locked_state.is_active())
2725 acl_user->password_locked_state.set_parameters(
2726 acl_user->password_locked_state.get_password_lock_time_days(),
2727 acl_user->password_locked_state.get_failed_login_attempts());
2728 }
2729
2730 /* Update role graph */
2731 string authid_role = create_authid_str_from(acl_user);
2732 Role_index_map::iterator it = g_authid_to_vertex->find(authid_role);
2733 if (it != g_authid_to_vertex->end()) {
2734 boost::property_map<Granted_roles_graph,
2735 boost::vertex_acl_user_t>::type user_pacl_user;
2736 user_pacl_user =
2737 boost::get(boost::vertex_acl_user_t(), *g_granted_roles);
2738 boost::put(user_pacl_user, it->second, *acl_user);
2739 }
2740
2741 /* update password history */
2742 if (password_life.update_password_history) {
2743 acl_user->use_default_password_history =
2744 password_life.use_default_password_history;
2745 acl_user->password_history_length =
2746 password_life.use_default_password_history
2747 ? 0
2748 : password_life.password_history_length;
2749 }
2750 /* update password history */
2751 if (password_life.update_password_reuse_interval) {
2752 acl_user->use_default_password_reuse_interval =
2753 password_life.use_default_password_reuse_interval;
2754 acl_user->password_reuse_interval =
2755 password_life.use_default_password_reuse_interval
2756 ? 0
2757 : password_life.password_reuse_interval;
2758 }
2759 /* update current password field value */
2760 if (password_life.update_password_require_current !=
2761 Lex_acl_attrib_udyn::UNCHANGED) {
2762 acl_user->password_require_current =
2763 password_life.update_password_require_current;
2764 }
2765
2766 /* search complete: */
2767 break;
2768 }
2769 }
2770 }
2771 }
2772
acl_users_add_one(const char * user,const char * host,enum SSL_type ssl_type,const char * ssl_cipher,const char * x509_issuer,const char * x509_subject,USER_RESOURCES * mqh,ulong privileges,const LEX_CSTRING & plugin,const LEX_CSTRING & auth,const LEX_CSTRING & second_auth,const MYSQL_TIME & password_change_time,const LEX_ALTER & password_life,bool add_role_vertex,Restrictions & restrictions,uint failed_login_attempts,int password_lock_time,THD * thd MY_ATTRIBUTE ((unused)))2773 void acl_users_add_one(const char *user, const char *host,
2774 enum SSL_type ssl_type, const char *ssl_cipher,
2775 const char *x509_issuer, const char *x509_subject,
2776 USER_RESOURCES *mqh, ulong privileges,
2777 const LEX_CSTRING &plugin, const LEX_CSTRING &auth,
2778 const LEX_CSTRING &second_auth,
2779 const MYSQL_TIME &password_change_time,
2780 const LEX_ALTER &password_life, bool add_role_vertex,
2781 Restrictions &restrictions, uint failed_login_attempts,
2782 int password_lock_time,
2783 THD *thd MY_ATTRIBUTE((unused))) {
2784 DBUG_TRACE;
2785 ACL_USER acl_user;
2786
2787 DBUG_ASSERT(assert_acl_cache_write_lock(thd));
2788 /*
2789 All accounts can authenticate per default. This will change when
2790 we add a new field to the user table.
2791
2792 Currently this flag is only set to false when authentication is attempted
2793 using an unknown user name.
2794 */
2795 acl_user.can_authenticate = true;
2796
2797 acl_user.set_user(&global_acl_memory, user);
2798 acl_user.set_host(&global_acl_memory, host);
2799 DBUG_ASSERT(plugin.str);
2800 if (plugin.str[0]) {
2801 acl_user.plugin = plugin;
2802 optimize_plugin_compare_by_pointer(&acl_user.plugin);
2803 if (!auth_plugin_is_built_in(acl_user.plugin.str))
2804 acl_user.plugin.str =
2805 strmake_root(&global_acl_memory, plugin.str, plugin.length);
2806 acl_user.credentials[PRIMARY_CRED].m_auth_string.str =
2807 auth.str ? strmake_root(&global_acl_memory, auth.str, auth.length) : "";
2808 acl_user.credentials[PRIMARY_CRED].m_auth_string.length =
2809 auth.str ? auth.length : 0;
2810 if (second_auth.length) {
2811 acl_user.credentials[SECOND_CRED].m_auth_string.str =
2812 strmake_root(&global_acl_memory, second_auth.str, second_auth.length);
2813 acl_user.credentials[SECOND_CRED].m_auth_string.length =
2814 second_auth.length;
2815 } else {
2816 acl_user.credentials[SECOND_CRED].m_auth_string = EMPTY_CSTR;
2817 }
2818 optimize_plugin_compare_by_pointer(&acl_user.plugin);
2819 }
2820
2821 acl_user.access = privileges;
2822 acl_user.user_resource = *mqh;
2823 acl_user.sort = get_sort(2, acl_user.host.get_host(), acl_user.user);
2824 // acl_user.hostname_length=(uint) strlen(host);
2825 acl_user.ssl_type =
2826 (ssl_type != SSL_TYPE_NOT_SPECIFIED ? ssl_type : SSL_TYPE_NONE);
2827 acl_user.ssl_cipher =
2828 ssl_cipher ? strdup_root(&global_acl_memory, ssl_cipher) : nullptr;
2829 acl_user.x509_issuer =
2830 x509_issuer ? strdup_root(&global_acl_memory, x509_issuer) : nullptr;
2831 acl_user.x509_subject =
2832 x509_subject ? strdup_root(&global_acl_memory, x509_subject) : nullptr;
2833 /* update details related to password lifetime, password expiry, history */
2834 acl_user.password_expired = password_life.update_password_expired_column;
2835 acl_user.password_lifetime = password_life.expire_after_days;
2836 acl_user.use_default_password_lifetime =
2837 password_life.use_default_password_lifetime;
2838 acl_user.password_last_changed = password_change_time;
2839 acl_user.account_locked = password_life.account_locked;
2840 acl_user.password_history_length =
2841 password_life.use_default_password_history
2842 ? 0
2843 : password_life.password_history_length;
2844 acl_user.use_default_password_history =
2845 password_life.use_default_password_history;
2846 acl_user.password_reuse_interval =
2847 password_life.use_default_password_reuse_interval
2848 ? 0
2849 : password_life.password_reuse_interval;
2850 acl_user.use_default_password_reuse_interval =
2851 password_life.use_default_password_reuse_interval;
2852
2853 /*
2854 Assign the password_require_current field value to the ACL USER.
2855 if it was not specified then assign the default value
2856 */
2857 if (password_life.update_password_require_current ==
2858 Lex_acl_attrib_udyn::UNCHANGED) {
2859 acl_user.password_require_current = Lex_acl_attrib_udyn::DEFAULT;
2860 } else {
2861 acl_user.password_require_current =
2862 password_life.update_password_require_current;
2863 }
2864 acl_restrictions->upsert_restrictions(&acl_user, restrictions);
2865 set_user_salt(&acl_user);
2866 /* New user is not a role by default. */
2867 acl_user.is_role = false;
2868
2869 acl_user.password_locked_state.set_parameters(password_lock_time,
2870 failed_login_attempts);
2871 acl_users->push_back(acl_user);
2872 if (acl_user.host.check_allow_all_hosts())
2873 allow_all_hosts = true; // Anyone can connect /* purecov: tested */
2874
2875 if (add_role_vertex) {
2876 /*
2877 Add vertex to role graph. ACL_USER object is copied with a shallow copy
2878 */
2879 create_role_vertex(&acl_user);
2880 }
2881 }
2882
acl_insert_user(THD * thd MY_ATTRIBUTE ((unused)),const char * user,const char * host,enum SSL_type ssl_type,const char * ssl_cipher,const char * x509_issuer,const char * x509_subject,USER_RESOURCES * mqh,ulong privileges,const LEX_CSTRING & plugin,const LEX_CSTRING & auth,const MYSQL_TIME & password_change_time,const LEX_ALTER & password_life,Restrictions & restrictions,uint failed_login_attempts,int password_lock_time)2883 void acl_insert_user(THD *thd MY_ATTRIBUTE((unused)), const char *user,
2884 const char *host, enum SSL_type ssl_type,
2885 const char *ssl_cipher, const char *x509_issuer,
2886 const char *x509_subject, USER_RESOURCES *mqh,
2887 ulong privileges, const LEX_CSTRING &plugin,
2888 const LEX_CSTRING &auth,
2889 const MYSQL_TIME &password_change_time,
2890 const LEX_ALTER &password_life, Restrictions &restrictions,
2891 uint failed_login_attempts, int password_lock_time) {
2892 DBUG_TRACE;
2893 acl_users_add_one(user, host, ssl_type, ssl_cipher, x509_issuer, x509_subject,
2894 mqh, privileges, plugin, auth, EMPTY_CSTR,
2895 password_change_time, password_life, true, restrictions,
2896 failed_login_attempts, password_lock_time, thd);
2897 std::sort(acl_users->begin(), acl_users->end(), ACL_compare());
2898 rebuild_cached_acl_users_for_name();
2899 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
2900 rebuild_check_host();
2901 /* reparse mandatory roles variable */
2902 opt_mandatory_roles_cache = false;
2903 }
2904
acl_update_proxy_user(ACL_PROXY_USER * new_value,bool is_revoke)2905 void acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke) {
2906 DBUG_TRACE;
2907 DBUG_ASSERT(assert_acl_cache_write_lock(current_thd));
2908 for (ACL_PROXY_USER *acl_user = acl_proxy_users->begin();
2909 acl_user != acl_proxy_users->end(); ++acl_user) {
2910 if (acl_user->pk_equals(new_value)) {
2911 if (is_revoke) {
2912 DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
2913 acl_proxy_users->erase(acl_user);
2914 } else {
2915 DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
2916 acl_user->set_data(new_value);
2917 }
2918 break;
2919 }
2920 }
2921 }
2922
acl_update_db(const char * user,const char * host,const char * db,ulong privileges)2923 void acl_update_db(const char *user, const char *host, const char *db,
2924 ulong privileges) {
2925 DBUG_ASSERT(assert_acl_cache_write_lock(current_thd));
2926
2927 for (ACL_DB *acl_db = acl_dbs->begin(); acl_db < acl_dbs->end();) {
2928 if ((!acl_db->user && !user[0]) ||
2929 (acl_db->user && !strcmp(user, acl_db->user))) {
2930 if ((!acl_db->host.get_host() && !host[0]) ||
2931 (acl_db->host.get_host() && !strcmp(host, acl_db->host.get_host()))) {
2932 if ((!acl_db->db && !db[0]) ||
2933 (acl_db->db && !strcmp(db, acl_db->db))) {
2934 if (privileges)
2935 acl_db->access = privileges;
2936 else {
2937 acl_db = acl_dbs->erase(acl_db);
2938 // Don't increment loop variable.
2939 continue;
2940 }
2941 }
2942 }
2943 }
2944 ++acl_db;
2945 }
2946 }
2947
2948 /*
2949 Insert a user/db/host combination into the global acl_cache
2950
2951 SYNOPSIS
2952 acl_insert_db()
2953 user User name
2954 host Host name
2955 db Database name
2956 privileges Bitmap of privileges
2957
2958 NOTES
2959 Acl caches must be locked
2960 */
2961
acl_insert_db(const char * user,const char * host,const char * db,ulong privileges)2962 void acl_insert_db(const char *user, const char *host, const char *db,
2963 ulong privileges) {
2964 ACL_DB acl_db;
2965 DBUG_ASSERT(assert_acl_cache_write_lock(current_thd));
2966 acl_db.set_user(&global_acl_memory, user);
2967 acl_db.set_host(&global_acl_memory, host);
2968 acl_db.db = strdup_root(&global_acl_memory, db);
2969 acl_db.access = privileges;
2970 acl_db.sort = get_sort(3, acl_db.host.get_host(), acl_db.db, acl_db.user);
2971 acl_dbs->push_back(acl_db);
2972 std::sort(acl_dbs->begin(), acl_dbs->end(), ACL_compare());
2973 }
2974
get_mqh(THD * thd,const char * user,const char * host,USER_CONN * uc)2975 void get_mqh(THD *thd, const char *user, const char *host, USER_CONN *uc) {
2976 ACL_USER *acl_user;
2977 Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
2978
2979 if (initialized && acl_cache_lock.lock(false) &&
2980 (acl_user = find_acl_user(host, user, false)))
2981 uc->user_resources = acl_user->user_resource;
2982 else
2983 memset(&uc->user_resources, 0, sizeof(uc->user_resources));
2984 }
2985
2986 /**
2987 Update the security context when updating the user
2988
2989 Helper function.
2990 Update only if the security context is pointing to the same user and
2991 the user is not a proxied user for a different proxy user.
2992 And return true if the update happens (i.e. we're operating on the
2993 user account of the current user).
2994 Normalize the names for a safe compare.
2995
2996 @param sctx The security context to update
2997 @param acl_user_ptr User account being updated
2998 @param expired new value of the expiration flag
2999 @return did the update happen ?
3000 */
update_sctx_cache(Security_context * sctx,ACL_USER * acl_user_ptr,bool expired)3001 bool update_sctx_cache(Security_context *sctx, ACL_USER *acl_user_ptr,
3002 bool expired) {
3003 const char *acl_host = acl_user_ptr->host.get_host();
3004 const char *acl_user = acl_user_ptr->user;
3005 const char *sctx_user = sctx->priv_user().str;
3006 const char *sctx_host = sctx->priv_host().str;
3007
3008 /* If the user is connected as a proxied user, verify against proxy user */
3009 if (sctx->proxy_user().str && *sctx->proxy_user().str != '\0') {
3010 sctx_user = sctx->user().str;
3011 }
3012
3013 if (!acl_host) acl_host = "";
3014 if (!acl_user) acl_user = "";
3015 if (!sctx_host) sctx_host = "";
3016 if (!sctx_user) sctx_user = "";
3017
3018 if (!strcmp(acl_user, sctx_user) && !strcmp(acl_host, sctx_host)) {
3019 sctx->set_password_expired(expired);
3020 return true;
3021 }
3022
3023 return false;
3024 }
3025
3026 struct Acl_hash_entry {
3027 uint64 version;
3028 uchar *key;
3029 unsigned key_length;
3030 Acl_map *map;
3031 };
3032
hash_key(const uchar * el,size_t * length)3033 const uchar *hash_key(const uchar *el, size_t *length) {
3034 const Acl_hash_entry *entry = reinterpret_cast<const Acl_hash_entry *>(el);
3035 *length = entry->key_length;
3036 return entry->key;
3037 }
3038
3039 /**
3040 Allocate a new cache key based on active roles, current user and
3041 global cache version
3042
3043 @param [out] out_key The resulting key
3044 @param [out] key_len Key length
3045 @param version Global Acl_cache version
3046 @param uid The authorization ID of the current user
3047 @param active_roles The active roles of the current user
3048
3049 @return Success state
3050 @retval true OK
3051 @retval false Fatal error occurred.
3052 */
3053
create_acl_cache_hash_key(uchar ** out_key,unsigned * key_len,uint64 version,const Auth_id_ref & uid,const List_of_auth_id_refs & active_roles)3054 bool create_acl_cache_hash_key(uchar **out_key, unsigned *key_len,
3055 uint64 version, const Auth_id_ref &uid,
3056 const List_of_auth_id_refs &active_roles) {
3057 List_of_auth_id_refs::const_iterator it = active_roles.begin();
3058 uint32 active_roles_size = 0;
3059 for (; it != active_roles.end(); ++it) {
3060 active_roles_size += it->first.length + it->second.length + 2;
3061 }
3062 auto auth_id(uid);
3063 *key_len = auth_id.first.length + auth_id.second.length + 2 + sizeof(uint64) +
3064 active_roles_size;
3065 *out_key =
3066 (uchar *)my_malloc(key_memory_acl_map_cache, *key_len, MYF(MY_WME));
3067 if (out_key == nullptr) return false;
3068 auth_id.first.str = auth_id.first.str ? auth_id.first.str : "";
3069 auth_id.second.str = auth_id.second.str ? auth_id.second.str : "";
3070 memcpy(*out_key, auth_id.first.str, auth_id.first.length);
3071 *(*out_key + uid.first.length) = '@';
3072 memcpy(*out_key + auth_id.first.length + 1, auth_id.second.str,
3073 auth_id.second.length);
3074 uint offset = auth_id.first.length + auth_id.second.length + 1;
3075 /* Separator between version and role */
3076 *(*out_key + offset) = '`';
3077 ++offset;
3078 memcpy(*out_key + offset, reinterpret_cast<void *>(&version), sizeof(uint64));
3079 it = active_roles.begin();
3080 offset += sizeof(uint64);
3081
3082 for (; it != active_roles.end(); ++it) {
3083 memcpy(*out_key + offset, it->first.str, it->first.length);
3084 *(*out_key + offset + it->first.length) = '@';
3085 memcpy(*out_key + offset + it->first.length + 1, it->second.str,
3086 it->second.length);
3087 offset += it->first.length + it->second.length + 1;
3088 /* Separator between roles */
3089 *(*out_key + offset) = '`';
3090 ++offset;
3091 }
3092 DBUG_ASSERT(((offset - *key_len) == 0));
3093 return true;
3094 }
3095
Acl_cache()3096 Acl_cache::Acl_cache() : m_role_graph_version(0L) {
3097 const char *category = "sql";
3098 int count;
3099 count = static_cast<int>(array_elements(all_acl_cache_mutexes));
3100 mysql_mutex_register(category, all_acl_cache_mutexes, count);
3101 lf_hash_init(&m_cache, sizeof(Acl_hash_entry), LF_HASH_UNIQUE,
3102 0, /* key offset */
3103 0, /* key length not used */
3104 hash_key, &my_charset_bin);
3105 mysql_mutex_init(key_LOCK_acl_cache_flush, &m_cache_flush_mutex,
3106 MY_MUTEX_INIT_SLOW);
3107 }
3108
~Acl_cache()3109 Acl_cache::~Acl_cache() {
3110 mysql_mutex_destroy(&m_cache_flush_mutex);
3111 lf_hash_destroy(&m_cache);
3112 }
3113
Acl_map(Security_context * sctx,uint64 ver)3114 Acl_map::Acl_map(Security_context *sctx, uint64 ver)
3115 : m_reference_count(0), m_version(ver), m_restrictions(nullptr) {
3116 DBUG_TRACE;
3117 Acl_cache_lock_guard acl_cache_lock(current_thd,
3118 Acl_cache_lock_mode::READ_MODE);
3119 if (!acl_cache_lock.lock(false)) {
3120 DBUG_PRINT("error", ("Acl_map could not be constructed for user %s@%s => "
3121 "Could not lock Acl caches.",
3122 sctx->priv_user().str, sctx->priv_host().str));
3123 return;
3124 }
3125 m_global_acl = 0;
3126 ACL_USER *acl_user =
3127 find_acl_user(sctx->priv_host().str, sctx->priv_user().str, true);
3128 if (acl_user == nullptr) {
3129 DBUG_PRINT("error", ("Acl_map could not be constructed for user %s@%s => "
3130 "No such user",
3131 sctx->priv_user().str, sctx->priv_host().str));
3132 return;
3133 }
3134 List_of_granted_roles granted_roles;
3135 get_privilege_access_maps(
3136 acl_user, sctx->get_active_roles(), &m_global_acl, &m_db_acls,
3137 &m_db_wild_acls, &m_table_acls, &m_sp_acls, &m_func_acls, &granted_roles,
3138 &m_with_admin_acls, &m_dynamic_privileges, m_restrictions);
3139 }
3140
~Acl_map()3141 Acl_map::~Acl_map() {
3142 // Db_access_map is automatically destroyed and cleaned up.
3143 }
3144
Acl_map(const Acl_map && map)3145 Acl_map::Acl_map(const Acl_map &&map) : m_restrictions(nullptr) {
3146 operator=(map);
3147 }
3148
operator =(Acl_map && map)3149 Acl_map &Acl_map::operator=(Acl_map &&map) {
3150 m_db_acls = move(map.m_db_acls);
3151 m_global_acl = map.m_global_acl;
3152 m_reference_count = map.m_reference_count.load();
3153 m_table_acls = move(map.m_table_acls);
3154 m_sp_acls = move(map.m_sp_acls);
3155 m_func_acls = move(map.m_func_acls);
3156 m_with_admin_acls = move(map.m_with_admin_acls);
3157 m_version = map.m_version;
3158 m_restrictions = map.m_restrictions;
3159 map.m_reference_count = 0;
3160 return *this;
3161 }
3162
operator =(const Acl_map &)3163 Acl_map &Acl_map::operator=(const Acl_map &) { return *this; }
3164
global_acl()3165 ulong Acl_map::global_acl() { return m_global_acl; }
3166
db_acls()3167 Db_access_map *Acl_map::db_acls() { return &m_db_acls; }
3168
db_wild_acls()3169 Db_access_map *Acl_map::db_wild_acls() { return &m_db_wild_acls; }
3170
table_acls()3171 Table_access_map *Acl_map::table_acls() { return &m_table_acls; }
3172
grant_acls()3173 Grant_acl_set *Acl_map::grant_acls() { return &m_with_admin_acls; }
3174
sp_acls()3175 SP_access_map *Acl_map::sp_acls() { return &m_sp_acls; }
3176
func_acls()3177 SP_access_map *Acl_map::func_acls() { return &m_func_acls; }
3178
dynamic_privileges()3179 Dynamic_privileges *Acl_map::dynamic_privileges() {
3180 return &m_dynamic_privileges;
3181 }
3182
restrictions()3183 Restrictions &Acl_map::restrictions() { return m_restrictions; }
3184
increase_reference_count()3185 void Acl_map::increase_reference_count() { ++m_reference_count; }
3186
decrease_reference_count()3187 void Acl_map::decrease_reference_count() { --m_reference_count; }
3188
increase_version()3189 void Acl_cache::increase_version() {
3190 DBUG_TRACE;
3191 ++m_role_graph_version;
3192 flush_cache();
3193 }
3194
version()3195 uint64 Acl_cache::version() { return m_role_graph_version.load(); }
3196
size()3197 int32 Acl_cache::size() { return m_cache.count.load(); }
3198
3199 /**
3200 Finds an Acl_map entry in the Acl_cache and increase its reference count.
3201 If no Acl_map is located, a new one is created with reference count one.
3202 The Acl_map is returned to the caller.
3203
3204 @param sctx The target Security_context
3205 @param uid The target authid
3206 @param active_roles A list of active roles
3207
3208 @return A pointer to an Acl_map
3209 @retval !NULL Success
3210 @retval NULL A fatal OOM error happened.
3211 */
3212
checkout_acl_map(Security_context * sctx,Auth_id_ref & uid,List_of_auth_id_refs & active_roles)3213 Acl_map *Acl_cache::checkout_acl_map(Security_context *sctx, Auth_id_ref &uid,
3214 List_of_auth_id_refs &active_roles) {
3215 DBUG_TRACE;
3216 // CREATE KEY
3217 uchar *key; // allocated by create_hash_key and released by
3218 // Acl_cache::flush_cache
3219 unsigned key_len;
3220 uint64 version = m_role_graph_version.load();
3221 if (!create_acl_cache_hash_key(&key, &key_len, version, uid, active_roles)) {
3222 /* OOM happened */
3223 active_roles.clear();
3224 return nullptr;
3225 }
3226 LF_PINS *pins = lf_hash_get_pins(&m_cache);
3227 Acl_hash_entry *entry =
3228 (Acl_hash_entry *)lf_hash_search(&m_cache, pins, key, key_len);
3229 if (entry == nullptr || entry == MY_LF_ERRPTR) {
3230 lf_hash_search_unpin(pins);
3231 Acl_map *map = create_acl_map(version, sctx); // deleted in cache_flusher
3232 Acl_hash_entry new_entry;
3233 new_entry.version = version;
3234 new_entry.map = map;
3235 new_entry.key = key;
3236 new_entry.key_length = key_len;
3237 int rc =
3238 lf_hash_insert(&m_cache, pins, &new_entry); // shallow copy of entry
3239 if (rc != 0) {
3240 /* There was a duplicate; throw away the allocated memory */
3241 lf_hash_put_pins(pins);
3242 my_free(key);
3243 delete map;
3244 DBUG_PRINT("info", ("Someone else checked out the cache key"));
3245 /* Potentially dangerous to dive here? */
3246 return checkout_acl_map(sctx, uid, active_roles);
3247 }
3248 map->increase_reference_count();
3249 lf_hash_put_pins(pins);
3250 DBUG_PRINT("info", ("Checked out new privilege map. Key= %s", key));
3251 return map;
3252 }
3253 Acl_map *map = entry->map;
3254 map->increase_reference_count();
3255 lf_hash_search_unpin(pins);
3256 lf_hash_put_pins(pins);
3257 my_free(key);
3258 DBUG_PRINT("info", ("Checked out old privilege map. Key= %s", key));
3259 return map;
3260 }
3261
return_acl_map(Acl_map * map)3262 void Acl_cache::return_acl_map(Acl_map *map) {
3263 map->decrease_reference_count();
3264 }
3265
3266 /**
3267 This global is protected by the Acl_cache::m_cache_flush_mutex and used when
3268 iterating the Acl_map hash in Acl_cache::flush_cache
3269 @see Acl_cache::flush_cache
3270 */
3271 uint64 l_cache_flusher_global_version;
3272
3273 /**
3274 Utility function for removing all items from the hash.
3275 @param ptr A pointer to a Acl_hash_entry
3276 @return Always 0 with the intention that this causes the hash_search
3277 function to iterate every single element in the hash.
3278 */
cache_flusher(const uchar * ptr)3279 static int cache_flusher(const uchar *ptr) {
3280 DBUG_TRACE;
3281 const Acl_hash_entry *entry = reinterpret_cast<const Acl_hash_entry *>(ptr);
3282 if (entry != nullptr) {
3283 if (entry->map->reference_count() == 0 &&
3284 entry->map->version() < l_cache_flusher_global_version)
3285 return 1;
3286 }
3287 return 0;
3288 }
3289
flush_cache()3290 void Acl_cache::flush_cache() {
3291 DBUG_TRACE;
3292 LF_PINS *pins = lf_hash_get_pins(&m_cache);
3293 Acl_hash_entry *entry = nullptr;
3294 mysql_mutex_lock(&m_cache_flush_mutex);
3295 l_cache_flusher_global_version = version();
3296 do {
3297 entry = static_cast<Acl_hash_entry *>(
3298 lf_hash_random_match(&m_cache, pins, &cache_flusher, 0));
3299 if (entry &&
3300 !lf_hash_delete(&m_cache, pins, entry->key, entry->key_length)) {
3301 // Hash element is removed from cache; safe to delete
3302 my_free(entry->key);
3303 delete entry->map;
3304 }
3305 lf_hash_search_unpin(pins);
3306 } while (entry != nullptr);
3307 lf_hash_put_pins(pins);
3308 mysql_mutex_unlock(&m_cache_flush_mutex);
3309 }
3310
create_acl_map(uint64 version,Security_context * sctx)3311 Acl_map *Acl_cache::create_acl_map(uint64 version, Security_context *sctx) {
3312 Acl_map *map = new Acl_map(sctx, version);
3313 return map;
3314 }
3315
operator new(size_t size)3316 void *Acl_map::operator new(size_t size) {
3317 return my_malloc(key_memory_acl_map_cache, size, MYF(0));
3318 }
3319
operator delete(void * p)3320 void Acl_map::operator delete(void *p) { my_free(p); }
3321
init_acl_cache()3322 void init_acl_cache() {
3323 g_acl_cache = new Acl_cache();
3324 g_mandatory_roles = new std::vector<Role_id>;
3325 opt_mandatory_roles_cache = false;
3326 }
3327
3328 /**
3329 Shutdown the global Acl_cache system which was only initialized if the
3330 rwlocks were initialized.
3331 @see acl_init()
3332 */
3333
shutdown_acl_cache()3334 void shutdown_acl_cache() {
3335 if (!acl_cache_initialized) return;
3336
3337 /* This should clean up all remaining Acl_cache items */
3338 g_acl_cache->increase_version();
3339 DBUG_ASSERT(g_acl_cache->size() == 0);
3340 delete g_acl_cache;
3341 g_acl_cache = nullptr;
3342 roles_delete();
3343 dynamic_privileges_delete();
3344 delete g_mandatory_roles;
3345 }
3346
3347 /* Constants used by Acl_cache_lock_guard */
3348 static const ulong ACL_CACHE_LOCK_TIMEOUT = 3600UL;
3349 static const MDL_key ACL_CACHE_KEY(MDL_key::ACL_CACHE, "", "");
3350
3351 /**
3352 Internal_error_handler subclass to suppress ER_LOCK_DEADLOCK,
3353 ER_LOCK_WAIT_TIMEOUT, ER_QUERY_INTERRUPTED and ER_QUERY_TIMEOUT.
3354 Instead, we will use Acl_cache_lock_guard::lock()
3355 to raise ER_CANNOT_LOCK_USER_MANAGEMENT_CACHES error.
3356 */
3357 class Acl_cache_error_handler : public Internal_error_handler {
3358 public:
3359 /**
3360 Handle an error condition
3361
3362 @param [in] thd THD handle
3363 @param [in] sql_errno Error raised by MDL subsystem
3364 @param [in] sqlstate SQL state. Unused.
3365 @param [in] level Severity level. Unused.
3366 @param [in] msg Message string. Unused.
3367 */
3368
handle_condition(THD * thd MY_ATTRIBUTE ((unused)),uint sql_errno,const char * sqlstate MY_ATTRIBUTE ((unused)),Sql_condition::enum_severity_level * level MY_ATTRIBUTE ((unused)),const char * msg MY_ATTRIBUTE ((unused)))3369 virtual bool handle_condition(THD *thd MY_ATTRIBUTE((unused)), uint sql_errno,
3370 const char *sqlstate MY_ATTRIBUTE((unused)),
3371 Sql_condition::enum_severity_level *level
3372 MY_ATTRIBUTE((unused)),
3373 const char *msg MY_ATTRIBUTE((unused))) {
3374 return (sql_errno == ER_LOCK_DEADLOCK ||
3375 sql_errno == ER_LOCK_WAIT_TIMEOUT ||
3376 sql_errno == ER_QUERY_INTERRUPTED || sql_errno == ER_QUERY_TIMEOUT);
3377 }
3378 };
3379
3380 /*
3381 MDL_release_locks_visitor subclass to release MDL for ACL_CACHE.
3382 */
3383 class Release_acl_cache_locks : public MDL_release_locks_visitor {
3384 public:
3385 /**
3386 Lock releaser.
3387 Check details of given key and see it is of type ACL_CACHE
3388 and if key name it matches with m_partition. If so, release it.
3389
3390 @param [in] ticket MDL Ticket returned by MDL subsystem
3391
3392 @returns Whether ticket matches our criteria or not
3393 @retval true Ticket matches
3394 @retval false Ticket does not match
3395 */
3396
release(MDL_ticket * ticket)3397 virtual bool release(MDL_ticket *ticket) {
3398 return ticket->get_key()->mdl_namespace() == MDL_key::ACL_CACHE;
3399 }
3400 };
3401
3402 /**
3403 Acl_cache_lock_guard constructor.
3404
3405 @param [in] thd THD Handle.
3406 @param [in] mode Lock mode
3407
3408 */
3409
Acl_cache_lock_guard(THD * thd,Acl_cache_lock_mode mode)3410 Acl_cache_lock_guard::Acl_cache_lock_guard(THD *thd, Acl_cache_lock_mode mode)
3411 : m_thd(thd), m_mode(mode), m_locked(false) {
3412 DBUG_ASSERT(thd);
3413 }
3414
3415 /**
3416 Explicitly take lock on Acl_cache_lock_cache object.
3417 If cache was already locked, just return.
3418
3419 @param [in] raise_error Whether to raise error if we fail to acquire lock
3420
3421 @returns status of lock
3422 @retval true Lock was acquired/already acquired.
3423 @retval false There was some problem acquiring lock.
3424 */
3425
lock(bool raise_error)3426 bool Acl_cache_lock_guard::lock(bool raise_error) /* = true */
3427 {
3428 DBUG_ASSERT(!m_locked);
3429
3430 if (already_locked()) return true;
3431
3432 /* If we do not have MDL, we should not be holding LOCK_open */
3433 mysql_mutex_assert_not_owner(&LOCK_open);
3434
3435 MDL_request lock_request;
3436 MDL_REQUEST_INIT_BY_KEY(
3437 &lock_request, &ACL_CACHE_KEY,
3438 m_mode == Acl_cache_lock_mode::READ_MODE ? MDL_SHARED : MDL_EXCLUSIVE,
3439 MDL_EXPLICIT);
3440 Acl_cache_error_handler handler;
3441 m_thd->push_internal_handler(&handler);
3442 m_locked =
3443 !m_thd->mdl_context.acquire_lock(&lock_request, ACL_CACHE_LOCK_TIMEOUT);
3444 m_thd->pop_internal_handler();
3445
3446 if (!m_locked && raise_error)
3447 my_error(ER_CANNOT_LOCK_USER_MANAGEMENT_CACHES, MYF(0));
3448
3449 return m_locked;
3450 }
3451
3452 /**
3453 Explicitly unlock all acquired locks.
3454 */
3455
unlock()3456 void Acl_cache_lock_guard::unlock() {
3457 /*
3458 It is possible that we did not take any lock. E.g.
3459 1. Function 1 : Take shared lock on ACL caches
3460 2. Function 1 : Take Lock_open
3461 3. Call Function 2
3462 4. Function 2 : Try to acquire shared lock on ACL
3463 caches again
3464
3465 In such cases, at 4, we do not actually take
3466 MDL because it will be in conflict with LOCK_open
3467
3468 If unlock() is called as part of destructor or
3469 directly in Function 2 above, we should
3470 not release any locks because we never acquired
3471 any in the first place.
3472 */
3473 if (m_locked) {
3474 Release_acl_cache_locks lock_visitor;
3475 m_thd->mdl_context.release_locks(&lock_visitor);
3476 m_locked = false;
3477 }
3478 }
3479
3480 /**
3481 Check whether lock is already obtained or not.
3482
3483 @returns lock status
3484 @retval true Lock has already been taken.
3485 @retval false No lock is taken with this Acl_cache_lock_guard object
3486 */
3487
already_locked()3488 bool Acl_cache_lock_guard::already_locked() {
3489 return m_thd->mdl_context.owns_equal_or_stronger_lock(
3490 MDL_key::ACL_CACHE, "", "",
3491 m_mode == Acl_cache_lock_mode::READ_MODE ? MDL_SHARED : MDL_EXCLUSIVE);
3492 }
3493
3494 /**
3495 Assert that thread owns MDL_SHARED on partition specific to the thread
3496
3497 @param [in] thd Thread for which lock is to be checked
3498
3499 @returns thread owns required lock or not
3500 @retval true Thread owns lock
3501 @retval false Thread does not own lock
3502 */
3503
assert_acl_cache_read_lock(THD * thd)3504 bool assert_acl_cache_read_lock(THD *thd) {
3505 return thd->mdl_context.owns_equal_or_stronger_lock(MDL_key::ACL_CACHE, "",
3506 "", MDL_SHARED);
3507 }
3508
3509 /**
3510 Assert that thread owns MDL_EXCLUSIVE on all partitions
3511
3512 @param [in] thd Thread for which lock is to be checked
3513
3514 @returns thread owns required lock or not
3515 @retval true Thread owns lock
3516 @retval false Thread does not own lock
3517 */
3518
assert_acl_cache_write_lock(THD * thd)3519 bool assert_acl_cache_write_lock(THD *thd) {
3520 return thd->mdl_context.owns_equal_or_stronger_lock(MDL_key::ACL_CACHE, "",
3521 "", MDL_EXCLUSIVE);
3522 }
3523
3524 /** Global sysvar: the number of old passwords to check in the history. */
3525 uint32 global_password_history = 0;
3526 /** Global sysvar: the number of days before a password can be reused. */
3527 uint32 global_password_reuse_interval = 0;
3528
3529 /**
3530 Reload all ACL caches
3531
3532 @param [in] thd THD handle
3533 @param [in] mdl_locked MDL locks are taken
3534 @returns Status of reloading ACL caches
3535 @retval false Success
3536 @retval true Error
3537 */
3538
reload_acl_caches(THD * thd,bool mdl_locked)3539 bool reload_acl_caches(THD *thd, bool mdl_locked) {
3540 bool retval = true;
3541 DBUG_TRACE;
3542
3543 if (check_engine_type_for_acl_table(thd, mdl_locked) ||
3544 check_acl_tables_intact(thd, mdl_locked) || acl_reload(thd, mdl_locked) ||
3545 grant_reload(thd, mdl_locked)) {
3546 goto end;
3547 }
3548 retval = false;
3549
3550 end:
3551 return retval;
3552 }
3553
3554 /**
3555 Determine sort order for two user accounts
3556
3557 @param [in] a First user account's sort value
3558 @param [in] b Secound user account's sort value
3559
3560 @returns Whether a comes before b or not
3561 */
operator ()(const ACL_ACCESS & a,const ACL_ACCESS & b)3562 bool ACL_compare::operator()(const ACL_ACCESS &a, const ACL_ACCESS &b) {
3563 return a.sort > b.sort;
3564 }
3565
3566 /**
3567 Determine sort order for two user accounts
3568
3569 @param [in] a First user account's sort value
3570 @param [in] b Secound user account's sort value
3571
3572 @returns Whether a comes before b or not
3573 */
operator ()(const ACL_ACCESS * a,const ACL_ACCESS * b)3574 bool ACL_compare::operator()(const ACL_ACCESS *a, const ACL_ACCESS *b) {
3575 return a->sort > b->sort;
3576 }
3577
3578 /**
3579 Construstor
3580 */
Acl_restrictions()3581 Acl_restrictions::Acl_restrictions() : m_restrictions_map(key_memory_acl_mem) {}
3582
3583 /**
3584 Remove the Restrictions of the ACL_USER.
3585
3586 @param [in] acl_user The ACL_USER for whom to remove the Restrictions
3587 */
remove_restrictions(const ACL_USER * acl_user)3588 void Acl_restrictions::remove_restrictions(const ACL_USER *acl_user) {
3589 DBUG_ASSERT(assert_acl_cache_write_lock(current_thd));
3590 const Auth_id auth_id(acl_user);
3591 auto itr = m_restrictions_map.find(auth_id.auth_str());
3592 if (itr != m_restrictions_map.end()) m_restrictions_map.erase(itr);
3593 }
3594
3595 /**
3596 Update, insert or remove the Restrictions for the ACL_USER.
3597
3598 If ACL_USER has a Restrictions
3599 - If specified Restrictions is not empty then update ACL_USER's
3600 Restrictions
3601 - Otherwise clear the ACL_USER's restriction
3602 Else if there no Restrictions for the ACL_USER then insert the specified
3603 Restrictions.
3604
3605 @param [in] acl_user The ACL_USER for whom to alter the Restrictions
3606 @param [in] restrictions Restrictions to be inserted for the ACL_USER
3607 */
upsert_restrictions(const ACL_USER * acl_user,const Restrictions & restrictions)3608 void Acl_restrictions::upsert_restrictions(const ACL_USER *acl_user,
3609 const Restrictions &restrictions) {
3610 DBUG_ASSERT(assert_acl_cache_write_lock(current_thd));
3611 const Auth_id auth_id(acl_user);
3612 const std::string auth_str = auth_id.auth_str();
3613 auto restrictions_itr = m_restrictions_map.find(auth_str);
3614 if (restrictions_itr != m_restrictions_map.end()) {
3615 if (restrictions.is_empty()) {
3616 /* Empty restrictions means we want to remove that from global cache */
3617 m_restrictions_map.erase(restrictions_itr);
3618 } else {
3619 /* If there exists restrictions then update that in the global cache */
3620 restrictions_itr->second = restrictions;
3621 }
3622 } else if (!restrictions.is_empty()) {
3623 /* Insert non-empty restrictions object in the global cache */
3624 m_restrictions_map.emplace(auth_str, restrictions);
3625 }
3626 }
3627
3628 /**
3629 Find the Restrictions of the ACL_USER.
3630
3631 @param [in] acl_user The ACL_USER for whom to find the Restrictions
3632
3633 @returns valid Restrictions if found otherwise empty Restrictions
3634 */
find_restrictions(const ACL_USER * acl_user) const3635 Restrictions Acl_restrictions::find_restrictions(
3636 const ACL_USER *acl_user) const {
3637 DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));
3638 const Auth_id auth_id(acl_user);
3639 auto restrictions_itr = m_restrictions_map.find(auth_id.auth_str());
3640 if (restrictions_itr != m_restrictions_map.end())
3641 return restrictions_itr->second;
3642 else
3643 return Restrictions(nullptr);
3644 }
3645
3646 /**
3647 @returns the number of Restrictions present. It is not thread safe method.
3648 */
size() const3649 size_t Acl_restrictions::size() const { return m_restrictions_map.size(); }
3650
3651 /**
3652 Method to check if there exists at least one partial revokes in the cache.
3653 If the cache is not initialized at the time of the method call then it
3654 returns no partial revokes exists.
3655
3656 @param [in] thd THD handle
3657
3658 @retval true Partial revokes exists
3659 @retval false Otherwise
3660 */
is_partial_revoke_exists(THD * thd)3661 bool is_partial_revoke_exists(THD *thd) {
3662 bool partial_revoke = false;
3663 if (thd) {
3664 Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
3665 if (!acl_cache_lock.lock(false)) {
3666 return true;
3667 }
3668 /*
3669 Check the restrictions only if server has initialized the acl caches
3670 (i.e. Server is not started with --skip-grant-tables=1 option).
3671 */
3672 if (acl_restrictions) partial_revoke = (acl_restrictions->size() > 0);
3673 } else {
3674 /*
3675 We need to determine the number of partial revokes at the time of server
3676 start. In that case thd(s) is not be available so it is safe to
3677 determine the number of partial revokes without lock.
3678 */
3679 if (acl_restrictions) partial_revoke = (acl_restrictions->size() > 0);
3680 }
3681 return partial_revoke;
3682 }
3683
is_acl_inited()3684 bool is_acl_inited() { return acl_cache_initialized; }
3685