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