1 /* Copyright (c) 2000, 2021, 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_show.h" /* append_identifier */
24 #include "log.h" /* sql_print_warning */
25 #include "sql_base.h" /* MYSQL_LOCK_IGNORE_TIMEOUT */
26 #include "key.h" /* key_copy, key_cmp_if_same */
27 /* key_restore */
28
29 #include "auth_internal.h"
30 #include "sql_auth_cache.h"
31 #include "sql_authentication.h"
32 #include "sql_time.h"
33 #include "my_user.h" /* parse_user */
34 #include "password.h" /* my_make_scrambled_password_sha1 */
35 #include "sql_plugin.h" // lock_plugin_data etc.
36 #include "debug_sync.h"
37 #include "sql_user_table.h"
38
39 #define INVALID_DATE "0000-00-00 00:00:00"
40
41 #include <algorithm>
42 #include <functional>
43 using std::min;
44
45 struct ACL_internal_schema_registry_entry
46 {
47 const LEX_STRING *m_name;
48 const ACL_internal_schema_access *m_access;
49 };
50 /**
51 Internal schema registered.
52 Currently, this is only:
53 - performance_schema
54 - information_schema,
55 This can be reused later for:
56 - mysql
57 */
58 static ACL_internal_schema_registry_entry registry_array[2];
59 static uint m_registry_array_size= 0;
60
61 #ifndef NO_EMBEDDED_ACCESS_CHECKS
62 MEM_ROOT global_acl_memory;
63 MEM_ROOT memex;
64 Prealloced_array<ACL_USER, ACL_PREALLOC_SIZE> *acl_users= NULL;
65 Prealloced_array<ACL_PROXY_USER, ACL_PREALLOC_SIZE> *acl_proxy_users= NULL;
66 Prealloced_array<ACL_DB, ACL_PREALLOC_SIZE> *acl_dbs= NULL;
67 Prealloced_array<ACL_HOST_AND_IP, ACL_PREALLOC_SIZE> *acl_wild_hosts= NULL;
68
69 HASH column_priv_hash, proc_priv_hash, func_priv_hash;
70 hash_filo *acl_cache;
71 HASH acl_check_hosts;
72
73 bool initialized=0;
74 bool allow_all_hosts=1;
75 uint grant_version=0; /* Version of priv tables */
76 my_bool validate_user_plugins= TRUE;
77 /**
78 Flag to track if rwlocks in ACL subsystem were initialized.
79 Necessary because acl_free() can be called in some error scenarios
80 without prior call to acl_init().
81 */
82 bool rwlocks_initialized= false;
83
84 const uint LOCK_GRANT_PARTITIONS= 32;
85 Partitioned_rwlock LOCK_grant;
86
87 #define FIRST_NON_YN_FIELD 26
88
89 #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
90 #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
91 1 + USERNAME_LENGTH + 1)
92
93 ACL_USER acl_utility_user;
94 static LEX_STRING acl_utility_user_name, acl_utility_user_host_name;
95 static bool acl_utility_user_initialized= false;
96 static std::vector<std::string> acl_utility_user_schema_access;
97
98 static void acl_free_utility_user();
99
100 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
101
102 /**
103 Check if the given host name is equal to "localhost".
104
105 @return a flag telling if the given host name is equal to "localhost".
106 @retval TRUE the given host name is equal to "localhost".
107 @retval FALSE the given host name is not equal to "localhost".
108 }
109 */
is_localhost_string(const char * hostname)110 static bool is_localhost_string(const char *hostname)
111 {
112 if (!hostname) return false;
113
114 return !strcmp(hostname, "localhost");
115 }
116
117 /**
118 Add an internal schema to the registry.
119 @param name the schema name
120 @param access the schema ACL specific rules
121 */
register_schema(const LEX_STRING & name,const ACL_internal_schema_access * access)122 void ACL_internal_schema_registry::register_schema
123 (const LEX_STRING &name, const ACL_internal_schema_access *access)
124 {
125 assert(m_registry_array_size < array_elements(registry_array));
126
127 /* Not thread safe, and does not need to be. */
128 registry_array[m_registry_array_size].m_name= &name;
129 registry_array[m_registry_array_size].m_access= access;
130 m_registry_array_size++;
131 }
132
133
134 /**
135 Search per internal schema ACL by name.
136 @param name a schema name
137 @return per schema rules, or NULL
138 */
139 const ACL_internal_schema_access *
lookup(const char * name)140 ACL_internal_schema_registry::lookup(const char *name)
141 {
142 assert(name != NULL);
143
144 uint i;
145
146 for (i= 0; i<m_registry_array_size; i++)
147 {
148 if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
149 name) == 0)
150 return registry_array[i].m_access;
151 }
152 return NULL;
153 }
154
155
156 const char *
calc_ip(const char * ip_arg,long * val,char end)157 ACL_HOST_AND_IP::calc_ip(const char *ip_arg, long *val, char end)
158 {
159 long ip_val,tmp;
160 if (!(ip_arg=str2int(ip_arg,10,0,255,&ip_val)) || *ip_arg != '.')
161 return 0;
162 ip_val<<=24;
163 if (!(ip_arg=str2int(ip_arg+1,10,0,255,&tmp)) || *ip_arg != '.')
164 return 0;
165 ip_val+=tmp<<16;
166 if (!(ip_arg=str2int(ip_arg+1,10,0,255,&tmp)) || *ip_arg != '.')
167 return 0;
168 ip_val+=tmp<<8;
169 if (!(ip_arg=str2int(ip_arg+1,10,0,255,&tmp)) || *ip_arg != end)
170 return 0;
171 *val=ip_val+tmp;
172 return ip_arg;
173 }
174
175 /**
176 @brief Update the hostname. Updates ip and ip_mask accordingly.
177
178 @param host_arg Value to be stored
179 */
180 void
update_hostname(const char * host_arg)181 ACL_HOST_AND_IP::update_hostname(const char *host_arg)
182 {
183 hostname=(char*) host_arg; // This will not be modified!
184 hostname_length= hostname ? strlen( hostname ) : 0;
185 if (!host_arg ||
186 (!(host_arg=(char*) calc_ip(host_arg,&ip,'/')) ||
187 !(host_arg=(char*) calc_ip(host_arg+1,&ip_mask,'\0'))))
188 {
189 ip= ip_mask=0; // Not a masked ip
190 }
191 }
192
193 /*
194 @brief Comparing of hostnames
195
196 @param host_arg Hostname to be compared with
197 @param ip_arg IP address to be compared with
198
199 @notes
200 A hostname may be of type:
201 1) hostname (May include wildcards); monty.pp.sci.fi
202 2) ip (May include wildcards); 192.168.0.0
203 3) ip/netmask 192.168.0.0/255.255.255.0
204 A net mask of 0.0.0.0 is not allowed.
205
206 @return
207 true if matched
208 false if not matched
209 */
210
211 bool
compare_hostname(const char * host_arg,const char * ip_arg)212 ACL_HOST_AND_IP::compare_hostname(const char *host_arg, const char *ip_arg)
213 {
214 long tmp;
215 if (ip_mask && ip_arg && calc_ip(ip_arg,&tmp,'\0'))
216 {
217 return (tmp & ip_mask) == ip;
218 }
219 return (!hostname ||
220 (host_arg &&
221 !wild_case_compare(system_charset_info, host_arg, hostname)) ||
222 (ip_arg && !wild_compare(ip_arg, hostname, 0)));
223 }
224
225 ACL_USER *
copy(MEM_ROOT * root)226 ACL_USER::copy(MEM_ROOT *root)
227 {
228 ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
229 if (!dst)
230 return 0;
231 *dst= *this;
232 dst->user= safe_strdup_root(root, user);
233 dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
234 dst->x509_issuer= safe_strdup_root(root, x509_issuer);
235 dst->x509_subject= safe_strdup_root(root, x509_subject);
236 /*
237 If the plugin is built in we don't need to reallocate the name of the
238 plugin.
239 */
240 if (auth_plugin_is_built_in(dst->plugin.str))
241 dst->plugin= plugin;
242 else
243 {
244 dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
245 dst->plugin.length= plugin.length;
246 }
247 dst->auth_string.str= safe_strdup_root(root, auth_string.str);
248 dst->host.update_hostname(host.is_null() ? NULL : strdup_root(root, host.get_host()));
249 return dst;
250 }
251
252 void
init(const char * host_arg,const char * user_arg,const char * proxied_host_arg,const char * proxied_user_arg,bool with_grant_arg)253 ACL_PROXY_USER::init(const char *host_arg, const char *user_arg,
254 const char *proxied_host_arg,
255 const char *proxied_user_arg, bool with_grant_arg)
256 {
257 user= (user_arg && *user_arg) ? user_arg : NULL;
258 host.update_hostname ((host_arg && *host_arg) ? host_arg : NULL);
259 proxied_user= (proxied_user_arg && *proxied_user_arg) ?
260 proxied_user_arg : NULL;
261 proxied_host.update_hostname ((proxied_host_arg && *proxied_host_arg) ?
262 proxied_host_arg : NULL);
263 with_grant= with_grant_arg;
264 sort= get_sort(4, host.get_host(), user,
265 proxied_host.get_host(), proxied_user);
266 }
267
268 void
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)269 ACL_PROXY_USER::init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
270 const char *proxied_host_arg,
271 const char *proxied_user_arg, bool with_grant_arg)
272 {
273 init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
274 (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
275 (proxied_host_arg && *proxied_host_arg) ?
276 strdup_root (mem, proxied_host_arg) : NULL,
277 (proxied_user_arg && *proxied_user_arg) ?
278 strdup_root (mem, proxied_user_arg) : NULL,
279 with_grant_arg);
280 }
281
282 void
init(TABLE * table,MEM_ROOT * mem)283 ACL_PROXY_USER::init(TABLE *table, MEM_ROOT *mem)
284 {
285 init (get_field(mem, table->field[MYSQL_PROXIES_PRIV_HOST]),
286 get_field(mem, table->field[MYSQL_PROXIES_PRIV_USER]),
287 get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]),
288 get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]),
289 table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->val_int() != 0);
290 }
291
292 void
check_validity(bool check_no_resolve)293 ACL_PROXY_USER::check_validity(bool check_no_resolve)
294 {
295 if (check_no_resolve &&
296 (hostname_requires_resolving(host.get_host()) ||
297 hostname_requires_resolving(proxied_host.get_host())) &&
298 !is_localhost_string(host.get_host())) {
299 sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
300 "ignored in --skip-name-resolve mode.",
301 proxied_user ? proxied_user : "",
302 proxied_host.get_host(),
303 user ? user : "",
304 host.get_host());
305 }
306 }
307
308 bool
matches(const char * host_arg,const char * user_arg,const char * ip_arg,const char * proxied_user_arg,bool any_proxy_user)309 ACL_PROXY_USER::matches(const char *host_arg, const char *user_arg,
310 const char *ip_arg, const char *proxied_user_arg,
311 bool any_proxy_user)
312 {
313 DBUG_ENTER("ACL_PROXY_USER::matches");
314 DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
315 "compare_hostname(%s,%s,%s) &&"
316 "wild_compare (%s,%s) &&"
317 "wild_compare (%s,%s)",
318 host.get_host(),
319 host_arg ? host_arg : "<NULL>",
320 ip_arg ? ip_arg : "<NULL>",
321 proxied_host.get_host(),
322 host_arg ? host_arg : "<NULL>",
323 ip_arg ? ip_arg : "<NULL>",
324 user_arg ? user_arg : "<NULL>",
325 user ? user : "<NULL>",
326 proxied_user_arg ? proxied_user_arg : "<NULL>",
327 proxied_user ? proxied_user : "<NULL>"));
328 DBUG_RETURN(host.compare_hostname(host_arg, ip_arg) &&
329 proxied_host.compare_hostname(host_arg, ip_arg) &&
330 (!user ||
331 (user_arg && !wild_compare(user_arg, user, TRUE))) &&
332 (any_proxy_user || !proxied_user ||
333 (proxied_user && !wild_compare(proxied_user_arg, proxied_user,
334 TRUE))));
335 }
336
337 bool
pk_equals(ACL_PROXY_USER * grant)338 ACL_PROXY_USER::pk_equals(ACL_PROXY_USER *grant)
339 {
340 DBUG_ENTER("pk_equals");
341 DBUG_PRINT("info", ("strcmp(%s,%s) &&"
342 "strcmp(%s,%s) &&"
343 "wild_compare (%s,%s) &&"
344 "wild_compare (%s,%s)",
345 user ? user : "<NULL>",
346 grant->user ? grant->user : "<NULL>",
347 proxied_user ? proxied_user : "<NULL>",
348 grant->proxied_user ? grant->proxied_user : "<NULL>",
349 host.get_host(),
350 grant->host.get_host(),
351 proxied_host.get_host(),
352 grant->proxied_host.get_host()));
353
354 DBUG_RETURN(auth_element_equals(user, grant->user) &&
355 auth_element_equals(proxied_user, grant->proxied_user) &&
356 auth_element_equals(host.get_host(), grant->host.get_host()) &&
357 auth_element_equals(proxied_host.get_host(),
358 grant->proxied_host.get_host()));
359 }
360
361 void
print_grant(THD * thd,String * str)362 ACL_PROXY_USER::print_grant(THD *thd, String *str)
363 {
364 str->append(STRING_WITH_LEN("GRANT PROXY ON "));
365 String proxied_user_str(proxied_user, get_proxied_user_length(),
366 system_charset_info);
367 append_query_string(thd, system_charset_info, &proxied_user_str, str);
368 str->append(STRING_WITH_LEN("@"));
369 String proxied_host_str(proxied_host.get_host(), proxied_host.get_host_len(),
370 system_charset_info);
371 append_query_string(thd, system_charset_info, &proxied_host_str, str);
372 str->append(STRING_WITH_LEN(" TO "));
373 String user_str(user, get_user_length(), system_charset_info);
374 append_query_string(thd, system_charset_info, &user_str, str);
375 str->append(STRING_WITH_LEN("@"));
376 String host_str(host.get_host(), host.get_host_len(), system_charset_info);
377 append_query_string(thd, system_charset_info, &host_str, str);
378 if (with_grant)
379 str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
380 }
381
382 int
store_pk(TABLE * table,const LEX_CSTRING & host,const LEX_CSTRING & user,const LEX_CSTRING & proxied_host,const LEX_CSTRING & proxied_user)383 ACL_PROXY_USER::store_pk(TABLE *table,
384 const LEX_CSTRING &host,
385 const LEX_CSTRING &user,
386 const LEX_CSTRING &proxied_host,
387 const LEX_CSTRING &proxied_user)
388 {
389 DBUG_ENTER("ACL_PROXY_USER::store_pk");
390 DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
391 host.str ? host.str : "<NULL>",
392 user.str ? user.str : "<NULL>",
393 proxied_host.str ? proxied_host.str : "<NULL>",
394 proxied_user.str ? proxied_user.str : "<NULL>"));
395 if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host.str,
396 host.length,
397 system_charset_info))
398 DBUG_RETURN(TRUE);
399 if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user.str,
400 user.length,
401 system_charset_info))
402 DBUG_RETURN(TRUE);
403 if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host.str,
404 proxied_host.length,
405 system_charset_info))
406 DBUG_RETURN(TRUE);
407 if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user.str,
408 proxied_user.length,
409 system_charset_info))
410 DBUG_RETURN(TRUE);
411
412 DBUG_RETURN(FALSE);
413 }
414
415 int
store_with_grant(TABLE * table,bool with_grant)416 ACL_PROXY_USER::store_with_grant(TABLE * table,
417 bool with_grant)
418 {
419 DBUG_ENTER("ACL_PROXY_USER::store_with_grant");
420 DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
421 if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
422 TRUE))
423 DBUG_RETURN(TRUE);
424
425 DBUG_RETURN(FALSE);
426 }
427
428 int
store_data_record(TABLE * table,const LEX_CSTRING & host,const LEX_CSTRING & user,const LEX_CSTRING & proxied_host,const LEX_CSTRING & proxied_user,bool with_grant,const char * grantor)429 ACL_PROXY_USER::store_data_record(TABLE *table,
430 const LEX_CSTRING &host,
431 const LEX_CSTRING &user,
432 const LEX_CSTRING &proxied_host,
433 const LEX_CSTRING &proxied_user,
434 bool with_grant,
435 const char *grantor)
436 {
437 DBUG_ENTER("ACL_PROXY_USER::store_pk");
438 if (store_pk(table, host, user, proxied_host, proxied_user))
439 DBUG_RETURN(TRUE);
440 if (store_with_grant(table, with_grant))
441 DBUG_RETURN(TRUE);
442 if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
443 strlen(grantor),
444 system_charset_info))
445 DBUG_RETURN(TRUE);
446
447 DBUG_RETURN(FALSE);
448 }
449
450
wild_case_compare(CHARSET_INFO * cs,const char * str,const char * wildstr)451 int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
452 {
453 int flag;
454 DBUG_ENTER("wild_case_compare");
455 DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr));
456 while (*wildstr)
457 {
458 while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
459 {
460 if (*wildstr == wild_prefix && wildstr[1])
461 wildstr++;
462 if (my_toupper(cs, *wildstr++) !=
463 my_toupper(cs, *str++)) DBUG_RETURN(1);
464 }
465 if (! *wildstr ) DBUG_RETURN (*str != 0);
466 if (*wildstr++ == wild_one)
467 {
468 if (! *str++) DBUG_RETURN (1); /* One char; skip */
469 }
470 else
471 { /* Found '*' */
472 if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */
473 flag=(*wildstr != wild_many && *wildstr != wild_one);
474 do
475 {
476 if (flag)
477 {
478 char cmp;
479 if ((cmp= *wildstr) == wild_prefix && wildstr[1])
480 cmp=wildstr[1];
481 cmp=my_toupper(cs, cmp);
482 while (*str && my_toupper(cs, *str) != cmp)
483 str++;
484 if (!*str) DBUG_RETURN (1);
485 }
486 if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
487 } while (*str++);
488 DBUG_RETURN(1);
489 }
490 }
491 DBUG_RETURN (*str != '\0');
492 }
493
494
495 /*
496 Return a number which, if sorted 'desc', puts strings in this order:
497 no wildcards
498 strings containg wildcards and non-wildcard characters
499 single muilt-wildcard character('%')
500 empty string
501 */
502
get_sort(uint count,...)503 ulong get_sort(uint count,...)
504 {
505 va_list args;
506 va_start(args,count);
507 ulong sort=0;
508
509 /* Should not use this function with more than 4 arguments for compare. */
510 assert(count <= 4);
511
512 while (count--)
513 {
514 char *start, *str= va_arg(args,char*);
515 uint chars= 0;
516 uint wild_pos= 0;
517
518 /*
519 wild_pos
520 0 if string is empty
521 1 if string is a single muilt-wildcard
522 character('%')
523 first wildcard position + 1 if string containg wildcards and
524 non-wildcard characters
525 */
526
527 if ((start= str))
528 {
529 for (; *str ; str++)
530 {
531 if (*str == wild_prefix && str[1])
532 str++;
533 else if (*str == wild_many || *str == wild_one)
534 {
535 wild_pos= (uint) (str - start) + 1;
536 if (!(wild_pos == 1 && *str == wild_many && *(++str) == '\0'))
537 wild_pos++;
538 break;
539 }
540 chars= 128; // Marker that chars existed
541 }
542 }
543 sort= (sort << 8) + (wild_pos ? min(wild_pos, 127U) : chars);
544 }
545 va_end(args);
546 return sort;
547 }
548
549
550 /**
551 Check if the given host name needs to be resolved or not.
552 Host name has to be resolved if it actually contains *name*.
553
554 For example:
555 192.168.1.1 --> FALSE
556 192.168.1.0/255.255.255.0 --> FALSE
557 % --> FALSE
558 192.168.1.% --> FALSE
559 AB% --> FALSE
560
561 AAAAFFFF --> TRUE (Hostname)
562 AAAA:FFFF:1234:5678 --> FALSE
563 ::1 --> FALSE
564
565 This function does not check if the given string is a valid host name or
566 not. It assumes that the argument is a valid host name.
567
568 @param hostname the string to check.
569
570 @return a flag telling if the argument needs to be resolved or not.
571 @retval TRUE the argument is a host name and needs to be resolved.
572 @retval FALSE the argument is either an IP address, or a patter and
573 should not be resolved.
574 */
575
hostname_requires_resolving(const char * hostname)576 bool hostname_requires_resolving(const char *hostname)
577 {
578
579 /* called only for --skip-name-resolve */
580 assert(specialflag & SPECIAL_NO_RESOLVE);
581
582 if (!hostname)
583 return FALSE;
584
585 /*
586 If the string contains any of {':', '%', '_', '/'}, it is definitely
587 not a host name:
588 - ':' means that the string is an IPv6 address;
589 - '%' or '_' means that the string is a pattern;
590 - '/' means that the string is an IPv4 network address;
591 */
592
593 for (const char *p= hostname; *p; ++p)
594 {
595 switch (*p) {
596 case ':':
597 case '%':
598 case '_':
599 case '/':
600 return FALSE;
601 }
602 }
603
604 /*
605 Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
606 (12.34.56.78). The assumption is that if the string contains only
607 digits and dots, it is an IPv4 address. Otherwise -- a host name.
608 */
609
610 for (const char *p= hostname; *p; ++p)
611 {
612 if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
613 return TRUE; /* a "letter" has been found. */
614 }
615
616 return FALSE; /* all characters are either dots or digits. */
617 }
618
619
620 #ifndef NO_EMBEDDED_ACCESS_CHECKS
621
622
get_key_column(GRANT_COLUMN * buff,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))623 static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
624 my_bool not_used MY_ATTRIBUTE((unused)))
625 {
626 *length=buff->key_length;
627 return (uchar*) buff->column;
628 }
629
630
get_grant_table(GRANT_NAME * buff,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))631 uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
632 my_bool not_used MY_ATTRIBUTE((unused)))
633 {
634 *length=buff->key_length;
635 return (uchar*) buff->hash_key;
636 }
637
638
GRANT_COLUMN(String & c,ulong y)639 GRANT_COLUMN::GRANT_COLUMN(String &c, ulong y) :rights (y)
640 {
641 column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length());
642 }
643
644
set_user_details(const char * h,const char * d,const char * u,const char * t,bool is_routine)645 void GRANT_NAME::set_user_details(const char *h, const char *d,
646 const char *u, const char *t,
647 bool is_routine)
648 {
649 /* Host given by user */
650 host.update_hostname(strdup_root(&memex, h));
651 if (db != d)
652 {
653 db= strdup_root(&memex, d);
654 if (lower_case_table_names)
655 my_casedn_str(files_charset_info, db);
656 }
657 user = strdup_root(&memex,u);
658 sort= get_sort(3,host.get_host(),db,user);
659 if (tname != t)
660 {
661 tname= strdup_root(&memex, t);
662 if (lower_case_table_names || is_routine)
663 my_casedn_str(files_charset_info, tname);
664 }
665 key_length= strlen(d) + strlen(u)+ strlen(t)+3;
666 hash_key= (char*) alloc_root(&memex,key_length);
667 my_stpcpy(my_stpcpy(my_stpcpy(hash_key,user)+1,db)+1,tname);
668 }
669
GRANT_NAME(const char * h,const char * d,const char * u,const char * t,ulong p,bool is_routine)670 GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
671 const char *t, ulong p, bool is_routine)
672 :db(0), tname(0), privs(p)
673 {
674 set_user_details(h, d, u, t, is_routine);
675 }
676
GRANT_TABLE(const char * h,const char * d,const char * u,const char * t,ulong p,ulong c)677 GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
678 const char *t, ulong p, ulong c)
679 :GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
680 {
681 (void) my_hash_init2(&hash_columns,4,system_charset_info,
682 0,0,0, (my_hash_get_key) get_key_column,0,0,
683 key_memory_acl_memex);
684 }
685
686
GRANT_NAME(TABLE * form,bool is_routine)687 GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
688 {
689 host.update_hostname(get_field(&memex, form->field[0]));
690 db= get_field(&memex,form->field[1]);
691 user= get_field(&memex,form->field[2]);
692 if (!user)
693 user= (char*) "";
694 sort= get_sort(3, host.get_host(), db, user);
695 tname= get_field(&memex,form->field[3]);
696 if (!db || !tname) {
697 /* Wrong table row; Ignore it */
698 privs= 0;
699 return; /* purecov: inspected */
700 }
701 if (lower_case_table_names)
702 {
703 my_casedn_str(files_charset_info, db);
704 }
705 if (lower_case_table_names || is_routine)
706 {
707 my_casedn_str(files_charset_info, tname);
708 }
709 key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
710 hash_key= (char*) alloc_root(&memex, key_length);
711 my_stpcpy(my_stpcpy(my_stpcpy(hash_key,user)+1,db)+1,tname);
712
713 if (form->field[MYSQL_TABLES_PRIV_FIELD_TABLE_PRIV])
714 {
715 privs = (ulong) form->field[MYSQL_TABLES_PRIV_FIELD_TABLE_PRIV]->val_int();
716 privs = fix_rights_for_table(privs);
717 }
718 }
719
720
GRANT_TABLE(TABLE * form)721 GRANT_TABLE::GRANT_TABLE(TABLE *form)
722 :GRANT_NAME(form, false)
723 {
724 if (!db || !tname)
725 {
726 /* Wrong table row; Ignore it */
727 my_hash_clear(&hash_columns); /* allow for destruction */
728 cols= 0;
729 return;
730 }
731
732 if (form->field[MYSQL_TABLES_PRIV_FIELD_COLUMN_PRIV])
733 {
734 cols= (ulong) form->field[MYSQL_TABLES_PRIV_FIELD_COLUMN_PRIV]->val_int();
735 cols = fix_rights_for_column(cols);
736 }
737 else
738 cols= 0;
739
740 (void) my_hash_init2(&hash_columns,4,system_charset_info,
741 0,0,0, (my_hash_get_key) get_key_column,0,0,
742 key_memory_acl_memex);
743 }
744
745
~GRANT_TABLE()746 GRANT_TABLE::~GRANT_TABLE()
747 {
748 my_hash_free(&hash_columns);
749 }
750
751
init(TABLE * col_privs)752 bool GRANT_TABLE::init(TABLE *col_privs)
753 {
754 int error;
755
756 if (cols)
757 {
758 uchar key[MAX_KEY_LENGTH];
759 uint key_prefix_len;
760
761 if (!col_privs->key_info)
762 {
763 my_error(ER_MISSING_KEY, MYF(0), col_privs->s->db.str,
764 col_privs->s->table_name.str);
765 return true;
766 }
767
768 KEY_PART_INFO *key_part= col_privs->key_info->key_part;
769 col_privs->field[0]->store(host.get_host(),
770 host.get_host_len(),
771 system_charset_info);
772 col_privs->field[1]->store(db, strlen(db), system_charset_info);
773 col_privs->field[2]->store(user, strlen(user), system_charset_info);
774 col_privs->field[3]->store(tname, strlen(tname), system_charset_info);
775
776 key_prefix_len= (key_part[0].store_length +
777 key_part[1].store_length +
778 key_part[2].store_length +
779 key_part[3].store_length);
780 key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
781 col_privs->field[4]->store("", 0, &my_charset_latin1);
782
783 error= col_privs->file->ha_index_init(0, 1);
784 if (error)
785 {
786 acl_print_ha_error(col_privs, error);
787 return true;
788 }
789
790 error=
791 col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
792 (key_part_map)15, HA_READ_KEY_EXACT);
793 DBUG_EXECUTE_IF("se_error_grant_table_init_read",
794 error= HA_ERR_LOCK_WAIT_TIMEOUT;);
795 if (error)
796 {
797 bool ret= false;
798 cols= 0;
799 if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
800 {
801 acl_print_ha_error(col_privs, error);
802 ret= true;
803 }
804 col_privs->file->ha_index_end();
805 return ret;
806 }
807
808 do
809 {
810 String *res,column_name;
811 GRANT_COLUMN *mem_check;
812 /* As column name is a string, we don't have to supply a buffer */
813 res= col_privs->field[4]->val_str(&column_name);
814 ulong priv= (ulong) col_privs->field[6]->val_int();
815 if (!(mem_check= new GRANT_COLUMN(*res,
816 fix_rights_for_column(priv))) ||
817 my_hash_insert(&hash_columns, (uchar *) mem_check))
818 {
819 /* Don't use this entry */
820 col_privs->file->ha_index_end();
821 return true;
822 }
823
824 error= col_privs->file->ha_index_next(col_privs->record[0]);
825 DBUG_EXECUTE_IF("se_error_grant_table_init_read_next",
826 error= HA_ERR_LOCK_WAIT_TIMEOUT;);
827 if (error && error != HA_ERR_END_OF_FILE)
828 {
829 acl_print_ha_error(col_privs, error);
830 col_privs->file->ha_index_end();
831 return true;
832 }
833 }
834 while (!error && !key_cmp_if_same(col_privs,key,0,key_prefix_len));
835 col_privs->file->ha_index_end();
836 }
837
838 return false;
839 }
840
841 /*
842 Find first entry that matches the current user
843 */
844
845 ACL_USER *
find_acl_user(const char * host,const char * user,my_bool exact)846 find_acl_user(const char *host, const char *user, my_bool exact)
847 {
848 DBUG_ENTER("find_acl_user");
849 DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user));
850
851 mysql_mutex_assert_owner(&acl_cache->lock);
852
853 if (likely(acl_users))
854 {
855 for (ACL_USER *acl_user= acl_users->begin();
856 acl_user != acl_users->end(); ++acl_user)
857 {
858 DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
859 user, acl_user->user ? acl_user->user : "",
860 host,
861 acl_user->host.get_host()));
862 if ((!acl_user->user && !user[0]) ||
863 (acl_user->user && !strcmp(user,acl_user->user)))
864 {
865 if (exact ? !my_strcasecmp(system_charset_info, host,
866 acl_user->host.get_host()) :
867 acl_user->host.compare_hostname(host,host))
868 {
869 DBUG_RETURN(acl_user);
870 }
871 }
872 }
873 }
874 DBUG_RETURN(0);
875 }
876
877
878 /*
879 Find user in ACL
880
881 SYNOPSIS
882 is_acl_user()
883 host host name
884 user user name
885
886 RETURN
887 FALSE user not fond
888 TRUE there are such user
889 */
890
is_acl_user(const char * host,const char * user)891 bool is_acl_user(const char *host, const char *user)
892 {
893 bool res;
894
895 /* --skip-grants */
896 if (!initialized)
897 return TRUE;
898
899 mysql_mutex_lock(&acl_cache->lock);
900 res= find_acl_user(host, user, TRUE) != NULL;
901 mysql_mutex_unlock(&acl_cache->lock);
902 return res;
903 }
904
905
906 /**
907 Validate if a user can proxy as another user
908
909 @thd current thread
910 @param user the logged in user (proxy user)
911 @param authenticated_as the effective user a plugin is trying to
912 impersonate as (proxied user)
913 @return proxy user definition
914 @retval NULL proxy user definition not found or not applicable
915 @retval non-null the proxy user data
916 */
917
918 ACL_PROXY_USER *
acl_find_proxy_user(const char * user,const char * host,const char * ip,char * authenticated_as,bool * proxy_used)919 acl_find_proxy_user(const char *user, const char *host, const char *ip,
920 char *authenticated_as, bool *proxy_used)
921 {
922 /* if the proxied and proxy user are the same return OK */
923 DBUG_ENTER("acl_find_proxy_user");
924 DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
925 user, host, ip, authenticated_as));
926
927 if (!strcmp(authenticated_as, user))
928 {
929 DBUG_PRINT ("info", ("user is the same as authenticated_as"));
930 DBUG_RETURN (NULL);
931 }
932
933 bool find_any = check_proxy_users && !*authenticated_as;
934
935 if(!find_any)
936 *proxy_used= TRUE;
937 for (ACL_PROXY_USER *proxy= acl_proxy_users->begin();
938 proxy != acl_proxy_users->end(); ++proxy)
939 {
940 if (proxy->matches(host, user, ip, authenticated_as, find_any))
941 {
942 DBUG_PRINT("info", ("proxy matched=%s@%s",
943 proxy->get_proxied_user(),
944 proxy->get_proxied_host()));
945 if (!find_any)
946 {
947 DBUG_PRINT("info", ("returning specific match as authenticated_as was specified"));
948 *proxy_used = TRUE;
949 DBUG_RETURN(proxy);
950 }
951 else
952 {
953 // we never use anonymous users when mapping
954 // proxy users for internal plugins:
955 if (strcmp(proxy->get_proxied_user() ?
956 proxy->get_proxied_user() : "", ""))
957 {
958 if (find_acl_user(
959 proxy->get_proxied_host(),
960 proxy->get_proxied_user(),
961 TRUE))
962 {
963 DBUG_PRINT("info", ("setting proxy_used to true, as \
964 find_all search matched real user=%s host=%s",
965 proxy->get_proxied_user(),
966 proxy->get_proxied_host()));
967 *proxy_used = TRUE;
968 strcpy(authenticated_as, proxy->get_proxied_user());
969 }
970 else
971 {
972 DBUG_PRINT("info", ("skipping match because ACL user \
973 does not exist, looking for next match to map"));
974 }
975 if (*proxy_used)
976 {
977 DBUG_PRINT("info", ("returning matching user"));
978 DBUG_RETURN(proxy);
979 }
980 }
981 }
982 }
983 }
984 DBUG_PRINT("info", ("No matching users found, returning null"));
985 DBUG_RETURN(NULL);
986 }
987
988
acl_entry_get_key(acl_entry * entry,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))989 static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
990 my_bool not_used MY_ATTRIBUTE((unused)))
991 {
992 *length=(uint) entry->length;
993 return (uchar*) entry->key;
994 }
995
996
check_get_key(ACL_USER * buff,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))997 static uchar* check_get_key(ACL_USER *buff, size_t *length,
998 my_bool not_used MY_ATTRIBUTE((unused)))
999 {
1000 *length=buff->host.get_host_len();
1001 return (uchar*) buff->host.get_host();
1002 }
1003
1004
1005 /*
1006 Get privilege for a host, user and db combination
1007
1008 as db_is_pattern changes the semantics of comparison,
1009 acl_cache is not used if db_is_pattern is set.
1010 */
1011
acl_get(const char * host,const char * ip,const char * user,const char * db,my_bool db_is_pattern)1012 ulong acl_get(const char *host, const char *ip,
1013 const char *user, const char *db, my_bool db_is_pattern)
1014 {
1015 ulong host_access= ~(ulong)0, db_access= 0;
1016 size_t key_length, copy_length;
1017 char key[ACL_KEY_LENGTH],*tmp_db,*end;
1018 acl_entry *entry;
1019 DBUG_ENTER("acl_get");
1020
1021 copy_length= (strlen(ip ? ip : "") +
1022 strlen(user ? user : "") +
1023 strlen(db ? db : "")) + 2; /* Added 2 at the end to avoid
1024 buffer overflow at strmov()*/
1025 /*
1026 Make sure that my_stpcpy() operations do not result in buffer overflow.
1027 */
1028 if (copy_length >= ACL_KEY_LENGTH)
1029 DBUG_RETURN(0);
1030
1031 mysql_mutex_lock(&acl_cache->lock);
1032 end=my_stpcpy((tmp_db=my_stpcpy(my_stpcpy(key, ip ? ip : "")+1,user)+1),db);
1033 if (lower_case_table_names)
1034 {
1035 my_casedn_str(files_charset_info, tmp_db);
1036 db=tmp_db;
1037 }
1038 key_length= (size_t) (end-key);
1039 if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key,
1040 key_length)))
1041 {
1042 db_access=entry->access;
1043 mysql_mutex_unlock(&acl_cache->lock);
1044 DBUG_PRINT("exit", ("access: 0x%lx", db_access));
1045 DBUG_RETURN(db_access);
1046 }
1047
1048 /* Check to see if the inquiry is for the utility_user */
1049 if (acl_is_utility_user(user, host, ip))
1050 {
1051 /* Check to see if database is within the schema access list */
1052 std::vector<std::string>::const_iterator it=
1053 std::find(acl_utility_user_schema_access.begin(),
1054 acl_utility_user_schema_access.end(), db);
1055 if (it != acl_utility_user_schema_access.end())
1056 db_access= host_access= GLOBAL_ACLS;
1057 goto exit;
1058 }
1059
1060 /*
1061 Check if there are some access rights for database and user
1062 */
1063 for (ACL_DB *acl_db= acl_dbs->begin(); acl_db != acl_dbs->end(); ++acl_db)
1064 {
1065 if (!acl_db->user || !strcmp(user,acl_db->user))
1066 {
1067 if (acl_db->host.compare_hostname(host,ip))
1068 {
1069 if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
1070 {
1071 db_access=acl_db->access;
1072 if (!acl_db->host.is_null())
1073 goto exit; // Fully specified. Take it
1074 break; /* purecov: tested */
1075 }
1076 }
1077 }
1078 }
1079 if (!db_access)
1080 goto exit; // Can't be better
1081
1082 exit:
1083 /* Save entry in cache for quick retrieval */
1084 if (!db_is_pattern &&
1085 (entry= (acl_entry*) my_malloc(key_memory_acl_cache,
1086 sizeof(acl_entry)+key_length,
1087 MYF(0))))
1088 {
1089 entry->access=(db_access & host_access);
1090 entry->length=key_length;
1091 memcpy((uchar*) entry->key,key,key_length);
1092 acl_cache->add(entry);
1093 }
1094 mysql_mutex_unlock(&acl_cache->lock);
1095 DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
1096 DBUG_RETURN(db_access & host_access);
1097 }
1098
1099
1100 /**
1101 Check if the user is allowed to change password
1102
1103 @param thd THD
1104 @param host Hostname for the user
1105 @param user User name
1106 @param new_password new password
1107
1108 new_password cannot be NULL
1109
1110 @return Error status
1111 @retval 0 OK
1112 @retval 1 ERROR; In this case the error is sent to the client.
1113 */
1114
1115 /*
1116 Check if there are any possible matching entries for this host
1117
1118 NOTES
1119 All host names without wild cards are stored in a hash table,
1120 entries with wildcards are stored in a dynamic array
1121 */
1122
init_check_host(void)1123 static void init_check_host(void)
1124 {
1125 DBUG_ENTER("init_check_host");
1126 if (acl_wild_hosts != NULL)
1127 acl_wild_hosts->clear();
1128 else
1129 acl_wild_hosts=
1130 new Prealloced_array<ACL_HOST_AND_IP, ACL_PREALLOC_SIZE>(key_memory_acl_mem);
1131
1132 size_t acl_users_size= acl_users ? acl_users->size() : 0;
1133
1134 (void) my_hash_init(&acl_check_hosts,system_charset_info,
1135 acl_users_size, 0, 0,
1136 (my_hash_get_key) check_get_key, 0, 0,
1137 key_memory_acl_mem);
1138 if (acl_users_size && !allow_all_hosts)
1139 {
1140 for (ACL_USER *acl_user= acl_users->begin();
1141 acl_user != acl_users->end(); ++acl_user)
1142 {
1143 if (acl_user->host.has_wildcard())
1144 { // Has wildcard
1145 ACL_HOST_AND_IP *acl= NULL;
1146 for (acl= acl_wild_hosts->begin(); acl != acl_wild_hosts->end(); ++acl)
1147 { // Check if host already exists
1148 if (!my_strcasecmp(system_charset_info,
1149 acl_user->host.get_host(), acl->get_host()))
1150 break; // already stored
1151 }
1152 if (acl == acl_wild_hosts->end()) // If new
1153 acl_wild_hosts->push_back(acl_user->host);
1154 }
1155 else if (!my_hash_search(&acl_check_hosts,(uchar*)
1156 acl_user->host.get_host(),
1157 acl_user->host.get_host_len()))
1158 {
1159 if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
1160 { // End of memory
1161 allow_all_hosts=1; // Should never happen
1162 DBUG_VOID_RETURN;
1163 }
1164 }
1165 }
1166 }
1167 acl_wild_hosts->shrink_to_fit();
1168 freeze_size(&acl_check_hosts.array);
1169 DBUG_VOID_RETURN;
1170 }
1171
1172
1173 /*
1174 Rebuild lists used for checking of allowed hosts
1175
1176 We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
1177 dropping or renaming user, since they contain pointers to elements of
1178 'acl_user' array, which are invalidated by drop operation, and use
1179 ACL_USER::host::hostname as a key, which is changed by rename.
1180 */
rebuild_check_host(void)1181 void rebuild_check_host(void)
1182 {
1183 delete acl_wild_hosts;
1184 acl_wild_hosts= NULL;
1185 my_hash_free(&acl_check_hosts);
1186 init_check_host();
1187 }
1188
1189
1190 /*
1191 Gets user credentials without authentication and resource limit checks.
1192
1193 SYNOPSIS
1194 acl_getroot()
1195 sctx Context which should be initialized
1196 user user name
1197 host host name
1198 ip IP
1199 db current data base name
1200
1201 RETURN
1202 FALSE OK
1203 TRUE Error
1204 */
1205
acl_getroot(Security_context * sctx,char * user,char * host,char * ip,const char * db)1206 bool acl_getroot(Security_context *sctx, char *user, char *host,
1207 char *ip, const char *db)
1208 {
1209 int res= 1;
1210 ACL_USER *acl_user= 0;
1211 DBUG_ENTER("acl_getroot");
1212
1213 DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
1214 (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
1215 user, (db ? db : "(NULL)")));
1216 sctx->set_user_ptr(user, user ? strlen(user) : 0);
1217 sctx->set_host_ptr(host, host ? strlen(host) : 0);
1218 sctx->set_ip_ptr(ip, ip? strlen(ip) : 0);
1219 sctx->set_host_or_ip_ptr();
1220
1221 if (!initialized)
1222 {
1223 /*
1224 here if mysqld's been started with --skip-grant-tables option.
1225 */
1226 sctx->skip_grants();
1227 DBUG_RETURN(FALSE);
1228 }
1229
1230 mysql_mutex_lock(&acl_cache->lock);
1231
1232 sctx->set_master_access(0);
1233 sctx->set_db_access(0);
1234 sctx->assign_priv_user("", 0);
1235 sctx->assign_priv_host("", 0);
1236
1237 /*
1238 Find acl entry in user database.
1239 This is specially tailored to suit the check we do for CALL of
1240 a stored procedure; user is set to what is actually a
1241 priv_user, which can be ''.
1242 */
1243 for (ACL_USER *acl_user_tmp= acl_users->begin();
1244 acl_user_tmp != acl_users->end(); ++acl_user_tmp)
1245 {
1246 if ((!acl_user_tmp->user && !user[0]) ||
1247 (acl_user_tmp->user && strcmp(user, acl_user_tmp->user) == 0))
1248 {
1249 if (acl_user_tmp->host.compare_hostname(host, ip))
1250 {
1251 acl_user= acl_user_tmp;
1252 res= 0;
1253 break;
1254 }
1255 }
1256 }
1257
1258 if (acl_user)
1259 {
1260 for (ACL_DB *acl_db= acl_dbs->begin(); acl_db != acl_dbs->end(); ++acl_db)
1261 {
1262 if (!acl_db->user ||
1263 (user && user[0] && !strcmp(user, acl_db->user)))
1264 {
1265 if (acl_db->host.compare_hostname(host, ip))
1266 {
1267 if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
1268 {
1269 sctx->set_db_access(acl_db->access);
1270 break;
1271 }
1272 }
1273 }
1274 }
1275 sctx->set_master_access(acl_user->access);
1276 sctx->assign_priv_user(user, user ? strlen(user) : 0);
1277
1278 sctx->assign_priv_host(acl_user->host.get_host(),
1279 acl_user->host.get_host_len());
1280
1281 sctx->set_password_expired(acl_user->password_expired);
1282 }
1283 mysql_mutex_unlock(&acl_cache->lock);
1284 DBUG_RETURN(res);
1285 }
1286
1287
1288 namespace {
1289
1290 class ACL_compare :
1291 public std::binary_function<ACL_ACCESS, ACL_ACCESS, bool>
1292 {
1293 public:
operator ()(const ACL_ACCESS & a,const ACL_ACCESS & b)1294 bool operator()(const ACL_ACCESS &a, const ACL_ACCESS &b)
1295 {
1296 return a.sort > b.sort;
1297 }
1298 };
1299
1300 } // namespace
1301
1302
1303 /**
1304 Convert scrambled password to binary form, according to scramble type,
1305 Binary form is stored in user.salt.
1306
1307 @param acl_user The object where to store the salt
1308
1309 Despite the name of the function it is used when loading ACLs from disk
1310 to store the password hash in the ACL_USER object.
1311 Note that it works only for native and "old" mysql authentication built-in
1312 plugins.
1313
1314 Assumption : user's authentication plugin information is available.
1315
1316 @return Password hash validation
1317 @retval false Hash is of suitable length
1318 @retval true Hash is of wrong length or format
1319 */
1320
set_user_salt(ACL_USER * acl_user)1321 bool set_user_salt(ACL_USER *acl_user)
1322 {
1323 bool result= false;
1324 plugin_ref plugin= NULL;
1325
1326 plugin= my_plugin_lock_by_name(0, acl_user->plugin,
1327 MYSQL_AUTHENTICATION_PLUGIN);
1328 if (plugin)
1329 {
1330 st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
1331 result= auth->set_salt(acl_user->auth_string.str,
1332 acl_user->auth_string.length,
1333 acl_user->salt,
1334 &acl_user->salt_len);
1335 plugin_unlock(0, plugin);
1336 }
1337 return result;
1338 }
1339
1340 /**
1341 Iterate over the user records and check for irregularities.
1342 Currently this includes :
1343 - checking if the plugin referenced is present.
1344 - if there's sha256 users and there's neither SSL nor RSA configured
1345 */
1346 static void
validate_user_plugin_records()1347 validate_user_plugin_records()
1348 {
1349 DBUG_ENTER("validate_user_plugin_records");
1350 if (!validate_user_plugins)
1351 DBUG_VOID_RETURN;
1352
1353 lock_plugin_data();
1354 for (ACL_USER *acl_user= acl_users->begin();
1355 acl_user != acl_users->end(); ++acl_user)
1356 {
1357 struct st_plugin_int *plugin;
1358
1359 if (acl_user->plugin.length)
1360 {
1361 /* rule 1 : plugin does exit */
1362 if (!auth_plugin_is_built_in(acl_user->plugin.str))
1363 {
1364 plugin= plugin_find_by_type(acl_user->plugin,
1365 MYSQL_AUTHENTICATION_PLUGIN);
1366
1367 if (!plugin)
1368 {
1369 sql_print_warning("The plugin '%.*s' used to authenticate "
1370 "user '%s'@'%.*s' is not loaded."
1371 " Nobody can currently login using this account.",
1372 (int) acl_user->plugin.length, acl_user->plugin.str,
1373 acl_user->user,
1374 static_cast<int>(acl_user->host.get_host_len()),
1375 acl_user->host.get_host());
1376 }
1377 }
1378 if (acl_user->plugin.str == sha256_password_plugin_name.str &&
1379 rsa_auth_status() && !ssl_acceptor_fd)
1380 {
1381 sql_print_warning("The plugin '%s' is used to authenticate "
1382 "user '%s'@'%.*s', "
1383 "but neither SSL nor RSA keys are "
1384 "configured. "
1385 "Nobody can currently login using this account.",
1386 sha256_password_plugin_name.str,
1387 acl_user->user,
1388 static_cast<int>(acl_user->host.get_host_len()),
1389 acl_user->host.get_host());
1390 }
1391 }
1392 }
1393 unlock_plugin_data();
1394 DBUG_VOID_RETURN;
1395 }
1396
1397
1398 /*
1399 Initialize structures responsible for user/db-level privilege checking and
1400 load privilege information for them from tables in the 'mysql' database.
1401
1402 SYNOPSIS
1403 acl_init()
1404 dont_read_acl_tables TRUE if we want to skip loading data from
1405 privilege tables and disable privilege checking.
1406
1407 NOTES
1408 This function is mostly responsible for preparatory steps, main work
1409 on initialization and grants loading is done in acl_reload().
1410
1411 RETURN VALUES
1412 0 ok
1413 1 Could not initialize grant's
1414 */
1415
acl_init(bool dont_read_acl_tables)1416 my_bool acl_init(bool dont_read_acl_tables)
1417 {
1418 THD *thd;
1419 my_bool return_val;
1420 DBUG_ENTER("acl_init");
1421
1422 acl_cache= new hash_filo(key_memory_acl_cache,
1423 ACL_CACHE_SIZE, 0, 0,
1424 (my_hash_get_key) acl_entry_get_key,
1425 (my_hash_free_key) my_free,
1426 &my_charset_utf8_bin);
1427
1428 LOCK_grant.init(LOCK_GRANT_PARTITIONS
1429 #ifdef HAVE_PSI_INTERFACE
1430 , key_rwlock_LOCK_grant
1431 #endif
1432 );
1433 rwlocks_initialized= true;
1434
1435 /*
1436 cache built-in native authentication plugins,
1437 to avoid hash searches and a global mutex lock on every connect
1438 */
1439 native_password_plugin= my_plugin_lock_by_name(0,
1440 native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
1441 if (!native_password_plugin)
1442 DBUG_RETURN(1);
1443
1444 if (dont_read_acl_tables)
1445 {
1446 DBUG_RETURN(0); /* purecov: tested */
1447 }
1448
1449 /*
1450 To be able to run this from boot, we allocate a temporary THD
1451 */
1452 if (!(thd=new THD))
1453 DBUG_RETURN(1); /* purecov: inspected */
1454 thd->thread_stack= (char*) &thd;
1455 thd->store_globals();
1456 /*
1457 It is safe to call acl_reload() since acl_* arrays and hashes which
1458 will be freed there are global static objects and thus are initialized
1459 by zeros at startup.
1460 */
1461 return_val= acl_reload(thd);
1462
1463 thd->release_resources();
1464 delete thd;
1465
1466 DBUG_RETURN(return_val);
1467 }
1468
1469 /*
1470 Set up the acl_utility_user and add it to the acl_user list.
1471 */
1472 static
1473 bool
acl_init_utility_user(bool check_no_resolve)1474 acl_init_utility_user(bool check_no_resolve)
1475 {
1476 bool ret= true;
1477
1478 if (!utility_user)
1479 goto end;
1480
1481 acl_free_utility_user();
1482
1483 /* Allocate all initial resources necessary */
1484 acl_utility_user_name.str= (char *) my_malloc(key_memory_acl_mem,
1485 USERNAME_LENGTH + 1,
1486 MY_ZEROFILL);
1487 acl_utility_user_host_name.str= (char *) my_malloc(key_memory_acl_mem,
1488 HOSTNAME_LENGTH+1,
1489 MY_ZEROFILL);
1490
1491 /* Buffer sizing here assumes that the utility user is using the
1492 mysql_native_password-style authentication. */
1493 acl_utility_user.auth_string.str=
1494 static_cast<char *>(my_malloc(key_memory_acl_mem,
1495 SCRAMBLED_PASSWORD_CHAR_LENGTH+1,
1496 MYF(0)));
1497
1498 acl_utility_user_initialized= true;
1499
1500 /* parse out the option to its component user and host name parts */
1501 parse_user(utility_user, strlen(utility_user),
1502 acl_utility_user_name.str, &acl_utility_user_name.length,
1503 acl_utility_user_host_name.str,
1504 &acl_utility_user_host_name.length);
1505
1506 /* Check to see if the username is anonymous */
1507 if (!acl_utility_user_name.str || acl_utility_user_name.str[0] == '\0')
1508 {
1509 sql_print_error("'utility user' specified as '%s' is anonymous"
1510 " and not allowed.",
1511 utility_user);
1512 ret= false;
1513 goto cleanup;
1514 }
1515
1516 /* Check to see that a password was supplied */
1517 if (!utility_user_password || utility_user_password[0] == '\0')
1518 {
1519 sql_print_error("'utility user' specified as '%s' but has no "
1520 "password. Please see --utility_user_password.",
1521 utility_user);
1522 ret= false;
1523 goto cleanup;
1524 }
1525
1526 /* set up some of the static utility user struct fields */
1527 acl_utility_user.user= acl_utility_user_name.str;
1528
1529 acl_utility_user.host.update_hostname(acl_utility_user_host_name.str);
1530
1531 acl_utility_user.sort= get_sort(2, acl_utility_user.host.get_host(),
1532 acl_utility_user.user);
1533
1534 /* Check to see if the utility user matches any existing user */
1535 for (ACL_USER *user= acl_users->begin(); user != acl_users->end(); ++user)
1536 {
1537 if (user->user
1538 && strcmp(acl_utility_user_name.str, user->user) == 0)
1539 {
1540 if (user->sort == acl_utility_user.sort)
1541 {
1542 sql_print_error("'utility user' specification '%s' exactly"
1543 " matches existing user in mysql.user table.",
1544 utility_user);
1545 ret= false;
1546 goto cleanup;
1547 }
1548 else if (user->sort < acl_utility_user.sort)
1549 {
1550 sql_print_warning("'utility user' specification '%s' closely"
1551 " matches more specific existing user '%s@%s' in"
1552 " mysql.user table which may render the utility_user"
1553 " inaccessable from certain hosts.", utility_user,
1554 user->user ? user->user : "",
1555 user->host.get_host());
1556 }
1557 }
1558 }
1559
1560 if (check_no_resolve
1561 && hostname_requires_resolving(acl_utility_user.host.get_host()))
1562 {
1563 sql_print_warning("'utility user' entry '%s@%s' "
1564 "ignored in --skip-name-resolve mode.",
1565 acl_utility_user.user ? acl_utility_user.user : "",
1566 acl_utility_user.host.get_host() ?
1567 acl_utility_user.host.get_host() : "");
1568 ret= false;
1569 goto cleanup;
1570 }
1571
1572 /* Fill out the rest of the static utility user struct, and add it into the
1573 acl_users list, then resort */
1574 my_make_scrambled_password_sha1(acl_utility_user.auth_string.str,
1575 utility_user_password,
1576 strlen(utility_user_password));
1577
1578 acl_utility_user.auth_string.length=
1579 strlen(acl_utility_user.auth_string.str);
1580
1581 acl_utility_user.plugin= native_password_plugin_name;
1582
1583 if (set_user_salt(&acl_utility_user))
1584 {
1585 goto cleanup;
1586 }
1587
1588 assert(utility_user_privileges <= UINT_MAX32);
1589 acl_utility_user.access= utility_user_privileges & UINT_MAX32;
1590 if (acl_utility_user.access)
1591 {
1592 char privilege_desc[512];
1593 get_privilege_desc(privilege_desc, array_elements(privilege_desc), acl_utility_user.access);
1594 sql_print_information("Utility user '%s'@'%s' in use with access rights "
1595 "'%s'.",
1596 acl_utility_user.user,
1597 acl_utility_user.host.get_host(),
1598 privilege_desc);
1599 }
1600 else
1601 {
1602 sql_print_information("Utility user '%s'@'%s' in use with basic "
1603 "access rights.",
1604 acl_utility_user.user,
1605 acl_utility_user.host.get_host());
1606 }
1607
1608 acl_utility_user.ssl_type= SSL_TYPE_NONE;
1609
1610 acl_utility_user.can_authenticate= true;
1611
1612 acl_users->push_back(acl_utility_user);
1613
1614 /* initialize the schema access list if specified */
1615 if (utility_user_schema_access)
1616 {
1617 char *cur_pos= utility_user_schema_access;
1618 char *cur_db= cur_pos;
1619 do
1620 {
1621 if (*cur_pos == ',' || *cur_pos == '\0')
1622 {
1623 if (cur_pos - cur_db > 0) {
1624 acl_utility_user_schema_access.push_back(std::string(cur_db,
1625 cur_pos
1626 - cur_db));
1627 }
1628 cur_db= cur_pos+1;
1629 if (*cur_pos == '\0')
1630 break;
1631 }
1632 cur_pos++;
1633 } while(1);
1634
1635 sql_print_information("Utility user '%s'@'%s' in use with full access to"
1636 " schemas '%s'.",
1637 acl_utility_user.user,
1638 acl_utility_user.host.get_host(),
1639 utility_user_schema_access);
1640 }
1641 else
1642 {
1643 sql_print_information("Utility user '%s'@'%s' in use with"
1644 " no schema access",
1645 acl_utility_user.user,
1646 acl_utility_user.host.get_host());
1647 }
1648 goto end;
1649
1650 cleanup:
1651 acl_free_utility_user();
1652
1653 end:
1654 return ret;
1655 }
1656
1657 /*
1658 Free up any resources allocated during acl_init_utility_user.
1659 */
1660 static
1661 void
acl_free_utility_user()1662 acl_free_utility_user()
1663 {
1664 if (acl_utility_user_initialized)
1665 {
1666 acl_utility_user_schema_access.clear();
1667 my_free(acl_utility_user_name.str);
1668 my_free(acl_utility_user_host_name.str);
1669 my_free(acl_utility_user.auth_string.str);
1670 memset(&acl_utility_user, 0, sizeof(acl_utility_user));
1671 acl_utility_user_initialized= false;
1672 }
1673 }
1674
1675 /*
1676 Determines if the user specified by user, host, ip matches the utility user
1677 */
1678 bool
acl_is_utility_user(const char * user,const char * host,const char * ip)1679 acl_is_utility_user(const char *user, const char *host, const char *ip)
1680 {
1681 return (user && acl_utility_user.user
1682 && strcmp(user, acl_utility_user.user) == 0
1683 && acl_utility_user.host.compare_hostname(host, ip));
1684 }
1685
1686 /*
1687 Initialize structures responsible for user/db-level privilege checking
1688 and load information about grants from open privilege tables.
1689
1690 SYNOPSIS
1691 acl_load()
1692 thd Current thread
1693 tables List containing open "mysql.host", "mysql.user",
1694 "mysql.db" and "mysql.proxies_priv" tables in that order.
1695
1696 RETURN VALUES
1697 FALSE Success
1698 TRUE Error
1699 */
1700
acl_load(THD * thd,TABLE_LIST * tables)1701 static my_bool acl_load(THD *thd, TABLE_LIST *tables)
1702 {
1703 TABLE *table;
1704 READ_RECORD read_record_info;
1705 my_bool return_val= TRUE;
1706 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
1707 char tmp_name[NAME_LEN+1];
1708 sql_mode_t old_sql_mode= thd->variables.sql_mode;
1709 bool password_expired= false;
1710 bool super_users_with_empty_plugin= false;
1711 Acl_load_user_table_schema_factory user_table_schema_factory;
1712 Acl_load_user_table_schema *table_schema = NULL;
1713 bool is_old_db_layout= false;
1714 DBUG_ENTER("acl_load");
1715
1716 DBUG_EXECUTE_IF("wl_9262_set_max_length_hostname",
1717 thd->security_context()->assign_priv_host(
1718 "oh_my_gosh_this_is_a_long_"
1719 "hostname_look_at_it_it_has_60"
1720 "_char", 60);
1721 thd->security_context()->assign_host(
1722 "oh_my_gosh_this_is_a_long_"
1723 "hostname_look_at_it_it_has_60"
1724 "_char", 60);
1725 thd->security_context()->set_host_or_ip_ptr();
1726 );
1727
1728 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
1729
1730 grant_version++; /* Privileges updated */
1731
1732
1733 acl_cache->clear(1); // Clear locked hostname cache
1734
1735 init_sql_alloc(key_memory_acl_mem,
1736 &global_acl_memory, ACL_ALLOC_BLOCK_SIZE, 0);
1737 /*
1738 Prepare reading from the mysql.user table
1739 */
1740 if (init_read_record(&read_record_info, thd, table=tables[0].table,
1741 NULL, 1, 1, FALSE))
1742 goto end;
1743 table->use_all_columns();
1744 acl_users->clear();
1745 /*
1746 We need to check whether we are working with old database layout. This
1747 might be the case for instance when we are running mysql_upgrade.
1748 */
1749 if (user_table_schema_factory.user_table_schema_check(table))
1750 {
1751 table_schema= user_table_schema_factory.get_user_table_schema(table);
1752 is_old_db_layout= user_table_schema_factory.is_old_user_table_schema(table);
1753 }
1754 else
1755 {
1756 sql_print_error("[FATAL] mysql.user table is damaged. "
1757 "Please run mysql_upgrade.");
1758 end_read_record(&read_record_info);
1759 goto end;
1760 }
1761
1762 allow_all_hosts=0;
1763 int read_rec_errcode;
1764 while (!(read_rec_errcode= read_record_info.read_record(&read_record_info)))
1765 {
1766 password_expired= false;
1767 /* Reading record from mysql.user */
1768 ACL_USER user;
1769 memset(&user, 0, sizeof(user));
1770
1771 /*
1772 All accounts can authenticate per default. This will change when
1773 we add a new field to the user table.
1774
1775 Currently this flag is only set to false when authentication is attempted
1776 using an unknown user name.
1777 */
1778 user.can_authenticate= true;
1779
1780 /*
1781 Account is unlocked by default.
1782 */
1783 user.account_locked= false;
1784
1785 user.host.update_hostname(get_field(&global_acl_memory,
1786 table->field[table_schema->host_idx()]));
1787 user.user= get_field(&global_acl_memory,
1788 table->field[table_schema->user_idx()]);
1789 if (check_no_resolve && hostname_requires_resolving(user.host.get_host()) &&
1790 !is_localhost_string(user.host.get_host())) {
1791 sql_print_warning("'user' entry '%s@%s' "
1792 "ignored in --skip-name-resolve mode.",
1793 user.user ? user.user : "",
1794 user.host.get_host());
1795 }
1796
1797 /* Read password from authentication_string field */
1798 if (table->s->fields > table_schema->authentication_string_idx())
1799 user.auth_string.str=
1800 get_field(&global_acl_memory,
1801 table->field[table_schema->authentication_string_idx()]);
1802 else
1803 {
1804 sql_print_error("Fatal error: mysql.user table is damaged. "
1805 "Please run mysql_upgrade.");
1806
1807 end_read_record(&read_record_info);
1808 goto end;
1809 }
1810 if(user.auth_string.str)
1811 user.auth_string.length= strlen(user.auth_string.str);
1812 else
1813 user.auth_string= EMPTY_STR;
1814
1815 {
1816 uint next_field;
1817 user.access= get_access(table, table_schema->select_priv_idx(),
1818 &next_field) & GLOBAL_ACLS;
1819 /*
1820 if it is pre 5.0.1 privilege table then map CREATE privilege on
1821 CREATE VIEW & SHOW VIEW privileges
1822 */
1823 if (table->s->fields <= 31 && (user.access & CREATE_ACL))
1824 user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
1825
1826 /*
1827 if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
1828 CREATE PROCEDURE & ALTER PROCEDURE privileges
1829 */
1830 if (table->s->fields <= 33 && (user.access & CREATE_ACL))
1831 user.access|= CREATE_PROC_ACL;
1832 if (table->s->fields <= 33 && (user.access & ALTER_ACL))
1833 user.access|= ALTER_PROC_ACL;
1834
1835 /*
1836 pre 5.0.3 did not have CREATE_USER_ACL
1837 */
1838 if (table->s->fields <= 36 && (user.access & GRANT_ACL))
1839 user.access|= CREATE_USER_ACL;
1840
1841
1842 /*
1843 if it is pre 5.1.6 privilege table then map CREATE privilege on
1844 CREATE|ALTER|DROP|EXECUTE EVENT
1845 */
1846 if (table->s->fields <= 37 && (user.access & SUPER_ACL))
1847 user.access|= EVENT_ACL;
1848
1849 /*
1850 if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
1851 */
1852 if (table->s->fields <= 38 && (user.access & SUPER_ACL))
1853 user.access|= TRIGGER_ACL;
1854
1855 user.sort= get_sort(2,user.host.get_host(),user.user);
1856
1857 /* Starting from 4.0.2 we have more fields */
1858 if (table->s->fields >= 31)
1859 {
1860 char *ssl_type=
1861 get_field(thd->mem_root, table->field[table_schema->ssl_type_idx()]);
1862 if (!ssl_type)
1863 user.ssl_type=SSL_TYPE_NONE;
1864 else if (!strcmp(ssl_type, "ANY"))
1865 user.ssl_type=SSL_TYPE_ANY;
1866 else if (!strcmp(ssl_type, "X509"))
1867 user.ssl_type=SSL_TYPE_X509;
1868 else /* !strcmp(ssl_type, "SPECIFIED") */
1869 user.ssl_type=SSL_TYPE_SPECIFIED;
1870
1871 user.ssl_cipher=
1872 get_field(&global_acl_memory,
1873 table->field[table_schema->ssl_cipher_idx()]);
1874 user.x509_issuer=
1875 get_field(&global_acl_memory,
1876 table->field[table_schema->x509_issuer_idx()]);
1877 user.x509_subject=
1878 get_field(&global_acl_memory,
1879 table->field[table_schema->x509_subject_idx()]);
1880
1881 char *ptr= get_field(thd->mem_root,
1882 table->field[table_schema->max_questions_idx()]);
1883 user.user_resource.questions=ptr ? atoi(ptr) : 0;
1884 ptr= get_field(thd->mem_root,
1885 table->field[table_schema->max_updates_idx()]);
1886 user.user_resource.updates=ptr ? atoi(ptr) : 0;
1887 ptr= get_field(thd->mem_root,
1888 table->field[table_schema->max_connections_idx()]);
1889 user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
1890 if (user.user_resource.questions || user.user_resource.updates ||
1891 user.user_resource.conn_per_hour)
1892 mqh_used=1;
1893
1894 if (table->s->fields > table_schema->max_user_connections_idx())
1895 {
1896 /* Starting from 5.0.3 we have max_user_connections field */
1897 ptr= get_field(thd->mem_root,
1898 table->field[table_schema->max_user_connections_idx()]);
1899 user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
1900 }
1901
1902 if (table->s->fields >= 41)
1903 {
1904 /* We may have plugin & auth_String fields */
1905 const char *tmpstr=
1906 get_field(&global_acl_memory,
1907 table->field[table_schema->plugin_idx()]);
1908
1909 /* In case we are working with 5.6 db layout we need to make server
1910 aware of Password field and that the plugin column can be null.
1911 In case when plugin column is null we use native password plugin
1912 if we can.
1913 */
1914 if (is_old_db_layout && (tmpstr == NULL || strlen(tmpstr) == 0 ||
1915 my_strcasecmp(system_charset_info, tmpstr,
1916 native_password_plugin_name.str) == 0))
1917 {
1918 char *password= get_field(&global_acl_memory,
1919 table->field[table_schema->password_idx()]);
1920
1921 //We only support native hash, we do not support pre 4.1 hashes
1922 plugin_ref native_plugin= NULL;
1923 native_plugin= my_plugin_lock_by_name(0,
1924 native_password_plugin_name,
1925 MYSQL_AUTHENTICATION_PLUGIN);
1926 if (native_plugin)
1927 {
1928 uint password_len= password ? strlen(password) : 0;
1929 st_mysql_auth *auth=
1930 (st_mysql_auth *) plugin_decl(native_plugin)->info;
1931 if (auth->validate_authentication_string(password,
1932 password_len) == 0)
1933 {
1934 //auth_string takes precedence over password
1935 if (user.auth_string.length == 0)
1936 {
1937 user.auth_string.str= password;
1938 user.auth_string.length= password_len;
1939 }
1940 if (tmpstr == NULL || strlen(tmpstr) == 0)
1941 tmpstr= native_password_plugin_name.str;
1942 }
1943 else
1944 {
1945 if ((user.access & SUPER_ACL) && !super_users_with_empty_plugin
1946 && (tmpstr == NULL || strlen(tmpstr) == 0))
1947 super_users_with_empty_plugin= true;
1948
1949 sql_print_warning("User entry '%s'@'%s' has a deprecated "
1950 "pre-4.1 password. The user will be ignored and no one can "
1951 "login with this user anymore.",
1952 user.user ? user.user : "",
1953 user.host.get_host());
1954 plugin_unlock(0, native_plugin);
1955 continue;
1956 }
1957 plugin_unlock(0, native_plugin);
1958 }
1959 }
1960
1961 /*
1962 Check if the plugin string is blank or null.
1963 If it is, the user will be skipped.
1964 */
1965 if(tmpstr == NULL || strlen(tmpstr) == 0)
1966 {
1967 if ((user.access & SUPER_ACL) && !super_users_with_empty_plugin)
1968 super_users_with_empty_plugin= true;
1969 sql_print_warning("User entry '%s'@'%s' has an empty plugin "
1970 "value. The user will be ignored and no one can login "
1971 "with this user anymore.",
1972 user.user ? user.user : "",
1973 user.host.get_host());
1974 continue;
1975 }
1976 /*
1977 By comparing the plugin with the built in plugins it is possible
1978 to optimize the string allocation and comparision.
1979 */
1980 if (my_strcasecmp(system_charset_info, tmpstr,
1981 native_password_plugin_name.str) == 0)
1982 user.plugin= native_password_plugin_name;
1983 #if defined(HAVE_OPENSSL)
1984 else
1985 if (my_strcasecmp(system_charset_info, tmpstr,
1986 sha256_password_plugin_name.str) == 0)
1987 user.plugin= sha256_password_plugin_name;
1988 #endif
1989 else
1990 {
1991 user.plugin.str= tmpstr;
1992 user.plugin.length= strlen(tmpstr);
1993 }
1994 }
1995
1996 /* Validate the hash string. */
1997 plugin_ref plugin= NULL;
1998 plugin= my_plugin_lock_by_name(0, user.plugin,
1999 MYSQL_AUTHENTICATION_PLUGIN);
2000 if (plugin)
2001 {
2002 st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
2003 if (auth->validate_authentication_string(user.auth_string.str,
2004 user.auth_string.length))
2005 {
2006 sql_print_warning("Found invalid password for user: '%s@%s'; "
2007 "Ignoring user", user.user ? user.user : "",
2008 user.host.get_host());
2009 plugin_unlock(0, plugin);
2010 continue;
2011 }
2012 plugin_unlock(0, plugin);
2013 }
2014
2015 if (table->s->fields > table_schema->password_expired_idx())
2016 {
2017 char *tmpstr= get_field(&global_acl_memory,
2018 table->field[table_schema->password_expired_idx()]);
2019 if (tmpstr && (*tmpstr == 'Y' || *tmpstr == 'y'))
2020 {
2021 user.password_expired= true;
2022
2023 if (!auth_plugin_supports_expiration(user.plugin.str))
2024 {
2025 sql_print_warning("'user' entry '%s@%s' has the password ignore "
2026 "flag raised, but its authentication plugin "
2027 "doesn't support password expiration. "
2028 "The user id will be ignored.",
2029 user.user ? user.user : "",
2030 user.host.get_host());
2031 continue;
2032 }
2033 password_expired= true;
2034 }
2035 }
2036
2037 if (table->s->fields > table_schema->account_locked_idx())
2038 {
2039 char *locked = get_field(&global_acl_memory,
2040 table->field[table_schema->account_locked_idx()]);
2041
2042 if (locked && (*locked == 'Y' || *locked == 'y'))
2043 {
2044 user.account_locked= true;
2045 }
2046 }
2047
2048 /*
2049 Initalize the values of timestamp and expire after day
2050 to error and true respectively.
2051 */
2052 user.password_last_changed.time_type= MYSQL_TIMESTAMP_ERROR;
2053 user.use_default_password_lifetime= true;
2054 user.password_lifetime= 0;
2055
2056 if (table->s->fields > table_schema->password_last_changed_idx())
2057 {
2058 if (!table->field[table_schema->password_last_changed_idx()]->is_null())
2059 {
2060 char *password_last_changed= get_field(&global_acl_memory,
2061 table->field[table_schema->password_last_changed_idx()]);
2062
2063 if (password_last_changed &&
2064 memcmp(password_last_changed, INVALID_DATE, sizeof(INVALID_DATE)))
2065 {
2066 String str(password_last_changed, &my_charset_bin);
2067 str_to_time_with_warn(&str,&(user.password_last_changed));
2068 }
2069 }
2070 }
2071
2072 if (table->s->fields > table_schema->password_lifetime_idx())
2073 {
2074 if (!table->
2075 field[table_schema->password_lifetime_idx()]->is_null())
2076 {
2077 char *ptr= get_field(&global_acl_memory,
2078 table->field[table_schema->password_lifetime_idx()]);
2079 user.password_lifetime= ptr ? atoi(ptr) : 0;
2080 user.use_default_password_lifetime= false;
2081 }
2082 }
2083
2084 } // end if (table->s->fields >= 31)
2085 else
2086 {
2087 user.ssl_type=SSL_TYPE_NONE;
2088 if (table->s->fields <= 13)
2089 { // Without grant
2090 if (user.access & CREATE_ACL)
2091 user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
2092 }
2093 /* Convert old privileges */
2094 user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
2095 if (user.access & FILE_ACL)
2096 user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
2097 if (user.access & PROCESS_ACL)
2098 user.access|= SUPER_ACL | EXECUTE_ACL;
2099 }
2100
2101 set_user_salt(&user);
2102 user.password_expired= password_expired;
2103
2104 acl_users->push_back(user);
2105 if (user.host.check_allow_all_hosts())
2106 allow_all_hosts=1; // Anyone can connect
2107 }
2108 } // END while reading records from the mysql.user table
2109
2110 end_read_record(&read_record_info);
2111
2112 DBUG_EXECUTE_IF("simulate_acl_init_failure",
2113 read_rec_errcode= HA_ERR_WRONG_IN_RECORD;);
2114
2115 if (read_rec_errcode > 0)
2116 {
2117 table->file->print_error(read_rec_errcode, MYF(ME_ERRORLOG));
2118 goto end;
2119 }
2120
2121 if (!acl_init_utility_user(check_no_resolve))
2122 goto end;
2123
2124 std::sort(acl_users->begin(), acl_users->end(), ACL_compare());
2125 acl_users->shrink_to_fit();
2126
2127 if (super_users_with_empty_plugin)
2128 {
2129 sql_print_warning("Some of the user accounts with SUPER privileges were "
2130 "disabled because of empty mysql.user.plugin value. "
2131 "If you are upgrading from MySQL 5.6 to MySQL 5.7 it "
2132 "means we were not able to substitute for empty plugin "
2133 "column. Probably because of pre 4.1 password hash. "
2134 "If your account is disabled you will need to:");
2135 sql_print_warning("1. Stop the server and restart it with "
2136 "--skip-grant-tables.");
2137 sql_print_warning("2. Run mysql_upgrade.");
2138 sql_print_warning("3. Restart the server with the parameters you "
2139 "normally use.");
2140 sql_print_warning("For complete instructions on how to upgrade MySQL "
2141 "to a new version please see the 'Upgrading MySQL' "
2142 "section from the MySQL manual");
2143 }
2144
2145 /*
2146 Prepare reading from the mysql.db table
2147 */
2148 if (init_read_record(&read_record_info, thd, table=tables[1].table,
2149 NULL, 1, 1, FALSE))
2150 goto end;
2151 table->use_all_columns();
2152 acl_dbs->clear();
2153
2154 while (!(read_rec_errcode= read_record_info.read_record(&read_record_info)))
2155 {
2156 /* Reading record in mysql.db */
2157 ACL_DB db;
2158 db.host.update_hostname(get_field(&global_acl_memory,
2159 table->field[MYSQL_DB_FIELD_HOST]));
2160 db.db=get_field(&global_acl_memory, table->field[MYSQL_DB_FIELD_DB]);
2161 if (!db.db)
2162 {
2163 sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
2164 continue;
2165 }
2166 db.user=get_field(&global_acl_memory, table->field[MYSQL_DB_FIELD_USER]);
2167 if (check_no_resolve && hostname_requires_resolving(db.host.get_host()) &&
2168 !is_localhost_string(db.host.get_host())) {
2169 sql_print_warning("'db' entry '%s %s@%s' "
2170 "ignored in --skip-name-resolve mode.",
2171 db.db,
2172 db.user ? db.user : "",
2173 db.host.get_host());
2174 }
2175 db.access=get_access(table,3,0);
2176 db.access=fix_rights_for_db(db.access);
2177 if (lower_case_table_names)
2178 {
2179 /*
2180 convert db to lower case and give a warning if the db wasn't
2181 already in lower case
2182 */
2183 (void)my_stpcpy(tmp_name, db.db);
2184 my_casedn_str(files_charset_info, db.db);
2185 if (strcmp(db.db, tmp_name) != 0)
2186 {
2187 sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
2188 "case that has been forced to lowercase because "
2189 "lower_case_table_names is set. It will not be "
2190 "possible to remove this privilege using REVOKE.",
2191 db.db,
2192 db.user ? db.user : "",
2193 db.host.get_host());
2194 }
2195 }
2196 db.sort=get_sort(3,db.host.get_host(),db.db,db.user);
2197 if (table->s->fields <= 9)
2198 { // Without grant
2199 if (db.access & CREATE_ACL)
2200 db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
2201 }
2202 acl_dbs->push_back(db);
2203 } // END reading records from mysql.db tables
2204
2205 end_read_record(&read_record_info);
2206 if (read_rec_errcode > 0)
2207 {
2208 table->file->print_error(read_rec_errcode, MYF(ME_ERRORLOG));
2209 goto end;
2210 }
2211
2212 std::sort(acl_dbs->begin(), acl_dbs->end(), ACL_compare());
2213 acl_dbs->shrink_to_fit();
2214
2215 /* Prepare to read records from the mysql.proxies_priv table */
2216 acl_proxy_users->clear();
2217
2218 if (tables[2].table)
2219 {
2220 if (init_read_record(&read_record_info, thd, table= tables[2].table,
2221 NULL, 1, 1, FALSE))
2222 goto end;
2223 table->use_all_columns();
2224 while (!(read_rec_errcode= read_record_info.read_record(&read_record_info)))
2225 {
2226 /* Reading record in mysql.proxies_priv */
2227 ACL_PROXY_USER proxy;
2228 proxy.init(table, &global_acl_memory);
2229 proxy.check_validity(check_no_resolve);
2230 if (acl_proxy_users->push_back(proxy))
2231 {
2232 end_read_record(&read_record_info);
2233 goto end;
2234 }
2235 } // END reading records from the mysql.proxies_priv table
2236
2237 end_read_record(&read_record_info);
2238 if (read_rec_errcode > 0)
2239 {
2240 table->file->print_error(read_rec_errcode, MYF(ME_ERRORLOG));
2241 goto end;
2242 }
2243
2244 std::sort(acl_proxy_users->begin(), acl_proxy_users->end(), ACL_compare());
2245 }
2246 else
2247 {
2248 sql_print_error("Missing system table mysql.proxies_priv; "
2249 "please run mysql_upgrade to create it");
2250 }
2251 acl_proxy_users->shrink_to_fit();
2252 validate_user_plugin_records();
2253 init_check_host();
2254
2255 initialized=1;
2256 return_val= FALSE;
2257
2258 end:
2259 thd->variables.sql_mode= old_sql_mode;
2260 if (table_schema)
2261 delete table_schema;
2262 DBUG_RETURN(return_val);
2263 }
2264
2265
2266
2267
acl_free(bool end)2268 void acl_free(bool end)
2269 {
2270 acl_free_utility_user();
2271 free_root(&global_acl_memory,MYF(0));
2272 delete acl_users;
2273 acl_users= NULL;
2274 delete acl_dbs;
2275 acl_dbs= NULL;
2276 delete acl_wild_hosts;
2277 acl_wild_hosts= NULL;
2278 delete acl_proxy_users;
2279 acl_proxy_users= NULL;
2280 my_hash_free(&acl_check_hosts);
2281 if (!end)
2282 acl_cache->clear(1); /* purecov: inspected */
2283 else
2284 {
2285 plugin_unlock(0, native_password_plugin);
2286 delete acl_cache;
2287 acl_cache=0;
2288
2289 if (rwlocks_initialized)
2290 {
2291 LOCK_grant.destroy();
2292 rwlocks_initialized= false;
2293 }
2294 }
2295 }
2296
2297
2298 /*
2299 Forget current user/db-level privileges and read new privileges
2300 from the privilege tables.
2301
2302 SYNOPSIS
2303 acl_reload()
2304 thd Current thread
2305
2306 NOTE
2307 All tables of calling thread which were open and locked by LOCK TABLES
2308 statement will be unlocked and closed.
2309 This function is also used for initialization of structures responsible
2310 for user/db-level privilege checking.
2311
2312 RETURN VALUE
2313 FALSE Success
2314 TRUE Failure
2315 */
2316
acl_reload(THD * thd)2317 my_bool acl_reload(THD *thd)
2318 {
2319 TABLE_LIST tables[3];
2320
2321 MEM_ROOT old_mem;
2322 bool old_initialized;
2323 my_bool return_val= TRUE;
2324 DBUG_ENTER("acl_reload");
2325
2326 /*
2327 To avoid deadlocks we should obtain table locks before
2328 obtaining acl_cache->lock mutex.
2329 */
2330 tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
2331 C_STRING_WITH_LEN("user"), "user", TL_READ);
2332 tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
2333 C_STRING_WITH_LEN("db"), "db", TL_READ);
2334 tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
2335 C_STRING_WITH_LEN("proxies_priv"),
2336 "proxies_priv", TL_READ);
2337 tables[0].next_local= tables[0].next_global= tables + 1;
2338 tables[1].next_local= tables[1].next_global= tables + 2;
2339 tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
2340 tables[2].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
2341
2342 if (open_and_lock_tables(thd, tables, MYSQL_LOCK_IGNORE_TIMEOUT))
2343 {
2344 /*
2345 Execution might have been interrupted; only print the error message
2346 if a user error condition has been raised.
2347 */
2348 if (thd->get_stmt_da()->is_error())
2349 {
2350 sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
2351 thd->get_stmt_da()->message_text());
2352 }
2353 close_acl_tables(thd);
2354 DBUG_RETURN(true);
2355 }
2356
2357 if ((old_initialized=initialized))
2358 mysql_mutex_lock(&acl_cache->lock);
2359
2360 Prealloced_array<ACL_USER, ACL_PREALLOC_SIZE> *old_acl_users= acl_users;
2361 Prealloced_array<ACL_DB, ACL_PREALLOC_SIZE> *old_acl_dbs= acl_dbs;
2362 Prealloced_array<ACL_PROXY_USER,
2363 ACL_PREALLOC_SIZE> *old_acl_proxy_users = acl_proxy_users;
2364
2365 acl_users= new Prealloced_array<ACL_USER,
2366 ACL_PREALLOC_SIZE>(key_memory_acl_mem);
2367 acl_dbs= new Prealloced_array<ACL_DB,
2368 ACL_PREALLOC_SIZE>(key_memory_acl_mem);
2369 acl_proxy_users=
2370 new Prealloced_array<ACL_PROXY_USER,
2371 ACL_PREALLOC_SIZE>(key_memory_acl_mem);
2372
2373 old_mem= global_acl_memory;
2374 delete acl_wild_hosts;
2375 acl_wild_hosts= NULL;
2376 my_hash_free(&acl_check_hosts);
2377
2378 if ((return_val= acl_load(thd, tables)))
2379 { // Error. Revert to old list
2380 DBUG_PRINT("error",("Reverting to old privileges"));
2381 acl_free(); /* purecov: inspected */
2382 acl_users= old_acl_users;
2383 acl_dbs= old_acl_dbs;
2384 acl_proxy_users= old_acl_proxy_users;
2385
2386 global_acl_memory= old_mem;
2387 init_check_host();
2388 }
2389 else
2390 {
2391 free_root(&old_mem,MYF(0));
2392 delete old_acl_users;
2393 delete old_acl_dbs;
2394 delete old_acl_proxy_users;
2395 }
2396 if (old_initialized)
2397 mysql_mutex_unlock(&acl_cache->lock);
2398
2399 close_acl_tables(thd);
2400
2401 DEBUG_SYNC(thd, "after_acl_reload");
2402 DBUG_RETURN(return_val);
2403 }
2404
2405
acl_insert_proxy_user(ACL_PROXY_USER * new_value)2406 void acl_insert_proxy_user(ACL_PROXY_USER *new_value)
2407 {
2408 DBUG_ENTER("acl_insert_proxy_user");
2409 mysql_mutex_assert_owner(&acl_cache->lock);
2410 acl_proxy_users->push_back(*new_value);
2411 std::sort(acl_proxy_users->begin(), acl_proxy_users->end(), ACL_compare());
2412 DBUG_VOID_RETURN;
2413 }
2414
2415
free_grant_table(GRANT_TABLE * grant_table)2416 void free_grant_table(GRANT_TABLE *grant_table)
2417 {
2418 my_hash_free(&grant_table->hash_columns);
2419 }
2420
2421
2422 /* Search after a matching grant. Prefer exact grants before not exact ones */
2423
name_hash_search(HASH * name_hash,const char * host,const char * ip,const char * db,const char * user,const char * tname,bool exact,bool name_tolower)2424 GRANT_NAME *name_hash_search(HASH *name_hash,
2425 const char *host,const char* ip,
2426 const char *db,
2427 const char *user, const char *tname,
2428 bool exact, bool name_tolower)
2429 {
2430 char helping [NAME_LEN*2+USERNAME_LENGTH+3], *name_ptr;
2431 uint len;
2432 GRANT_NAME *grant_name,*found=0;
2433 HASH_SEARCH_STATE state;
2434
2435 name_ptr= my_stpcpy(my_stpcpy(helping, user) + 1, db) + 1;
2436 len = (uint) (my_stpcpy(name_ptr, tname) - helping) + 1;
2437 if (name_tolower)
2438 my_casedn_str(files_charset_info, name_ptr);
2439 for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
2440 len, &state);
2441 grant_name ;
2442 grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
2443 len, &state))
2444 {
2445 if (exact)
2446 {
2447 if (grant_name->host.is_null() ||
2448 (host &&
2449 !my_strcasecmp(system_charset_info, host,
2450 grant_name->host.get_host())) ||
2451 (ip && !strcmp(ip, grant_name->host.get_host())))
2452 return grant_name;
2453 }
2454 else
2455 {
2456 if (grant_name->host.compare_hostname(host, ip) &&
2457 (!found || found->sort < grant_name->sort))
2458 found=grant_name; // Host ok
2459 }
2460 }
2461 return found;
2462 }
2463
2464
2465 /* Free grant array if possible */
2466
grant_free(void)2467 void grant_free(void)
2468 {
2469 DBUG_ENTER("grant_free");
2470 my_hash_free(&column_priv_hash);
2471 my_hash_free(&proc_priv_hash);
2472 my_hash_free(&func_priv_hash);
2473 free_root(&memex,MYF(0));
2474 DBUG_VOID_RETURN;
2475 }
2476
2477
2478 /**
2479 @brief Initialize structures responsible for table/column-level privilege
2480 checking and load information for them from tables in the 'mysql' database.
2481
2482 @param skip_grant_tables true if the command line option
2483 --skip-grant-tables is specified, else false.
2484
2485 @return Error status
2486 @retval false OK
2487 @retval true Could not initialize grant subsystem.
2488 */
2489
grant_init(bool skip_grant_tables)2490 bool grant_init(bool skip_grant_tables)
2491 {
2492 THD *thd;
2493 my_bool return_val;
2494 DBUG_ENTER("grant_init");
2495
2496 if (skip_grant_tables)
2497 DBUG_RETURN(false);
2498
2499 if (!(thd= new THD))
2500 DBUG_RETURN(1); /* purecov: deadcode */
2501 thd->thread_stack= (char*) &thd;
2502 thd->store_globals();
2503
2504 return_val= grant_reload(thd);
2505
2506 if (return_val && thd->get_stmt_da()->is_error())
2507 sql_print_error("Fatal: can't initialize grant subsystem - '%s'",
2508 thd->get_stmt_da()->message_text());
2509
2510 thd->release_resources();
2511 delete thd;
2512
2513 DBUG_RETURN(return_val);
2514 }
2515
2516
2517 /**
2518 @brief Helper function to grant_reload
2519
2520 Reads the procs_priv table into memory hash.
2521
2522 @param table A pointer to the procs_priv table structure.
2523
2524 @see grant_reload
2525
2526 @return Error state
2527 @retval TRUE An error occurred
2528 @retval FALSE Success
2529 */
2530
grant_load_procs_priv(TABLE * p_table)2531 static my_bool grant_load_procs_priv(TABLE *p_table)
2532 {
2533 MEM_ROOT *memex_ptr;
2534 my_bool return_val= 1;
2535 int error;
2536 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
2537 MEM_ROOT **save_mem_root_ptr= my_thread_get_THR_MALLOC();
2538 DBUG_ENTER("grant_load_procs_priv");
2539 (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
2540 0,0,0, (my_hash_get_key) get_grant_table,
2541 0, 0, key_memory_acl_memex);
2542 (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
2543 0,0,0, (my_hash_get_key) get_grant_table,
2544 0, 0, key_memory_acl_memex);
2545 error= p_table->file->ha_index_init(0, 1);
2546 if (error)
2547 {
2548 acl_print_ha_error(p_table, error);
2549 DBUG_RETURN(true);
2550 }
2551 p_table->use_all_columns();
2552
2553 error= p_table->file->ha_index_first(p_table->record[0]);
2554 DBUG_EXECUTE_IF("se_error_grant_load_procs_read",
2555 error= HA_ERR_LOCK_WAIT_TIMEOUT;);
2556 if (error)
2557 {
2558 if (error == HA_ERR_END_OF_FILE)
2559 return_val= 0; // Return Ok.
2560 else
2561 acl_print_ha_error(p_table, error);
2562 }
2563 else
2564 {
2565 memex_ptr= &memex;
2566 my_thread_set_THR_MALLOC(&memex_ptr);
2567 do
2568 {
2569 GRANT_NAME *mem_check;
2570 HASH *hash;
2571 if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE)))
2572 {
2573 /* This could only happen if we are out memory */
2574 goto end_unlock;
2575 }
2576
2577 if (check_no_resolve)
2578 {
2579 if (hostname_requires_resolving(mem_check->host.get_host()))
2580 {
2581 sql_print_warning("'procs_priv' entry '%s %s@%s' "
2582 "ignored in --skip-name-resolve mode.",
2583 mem_check->tname, mem_check->user,
2584 mem_check->host.get_host());
2585 }
2586 }
2587 if (p_table->field[4]->val_int() == SP_TYPE_PROCEDURE)
2588 {
2589 hash= &proc_priv_hash;
2590 }
2591 else
2592 if (p_table->field[4]->val_int() == SP_TYPE_FUNCTION)
2593 {
2594 hash= &func_priv_hash;
2595 }
2596 else
2597 {
2598 sql_print_warning("'procs_priv' entry '%s' "
2599 "ignored, bad routine type",
2600 mem_check->tname);
2601 continue;
2602 }
2603
2604 mem_check->privs= fix_rights_for_procedure(mem_check->privs);
2605 if (! mem_check->ok())
2606 delete mem_check;
2607 else if (my_hash_insert(hash, (uchar*) mem_check))
2608 {
2609 delete mem_check;
2610 goto end_unlock;
2611 }
2612 error= p_table->file->ha_index_next(p_table->record[0]);
2613 DBUG_EXECUTE_IF("se_error_grant_load_procs_read_next",
2614 error= HA_ERR_LOCK_WAIT_TIMEOUT;);
2615 if (error)
2616 {
2617 if (error == HA_ERR_END_OF_FILE)
2618 return_val= 0;
2619 else
2620 acl_print_ha_error(p_table, error);
2621 goto end_unlock;
2622 }
2623 }
2624 while (true);
2625 }
2626
2627 end_unlock:
2628 p_table->file->ha_index_end();
2629 my_thread_set_THR_MALLOC(save_mem_root_ptr);
2630 DBUG_RETURN(return_val);
2631 }
2632
2633
2634 /**
2635 @brief Initialize structures responsible for table/column-level privilege
2636 checking and load information about grants from open privilege tables.
2637
2638 @param thd Current thread
2639 @param tables List containing open "mysql.tables_priv" and
2640 "mysql.columns_priv" tables.
2641
2642 @see grant_reload
2643
2644 @return Error state
2645 @retval FALSE Success
2646 @retval TRUE Error
2647 */
2648
grant_load(THD * thd,TABLE_LIST * tables)2649 static my_bool grant_load(THD *thd, TABLE_LIST *tables)
2650 {
2651 MEM_ROOT *memex_ptr;
2652 my_bool return_val= 1;
2653 int error;
2654 TABLE *t_table= 0, *c_table= 0;
2655 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
2656 MEM_ROOT **save_mem_root_ptr= my_thread_get_THR_MALLOC();
2657 sql_mode_t old_sql_mode= thd->variables.sql_mode;
2658 DBUG_ENTER("grant_load");
2659
2660 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
2661
2662 (void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
2663 0,0,0, (my_hash_get_key) get_grant_table,
2664 (my_hash_free_key) free_grant_table,0,
2665 key_memory_acl_memex);
2666
2667 t_table = tables[0].table;
2668 c_table = tables[1].table;
2669 error= t_table->file->ha_index_init(0, 1);
2670 if (error)
2671 {
2672 acl_print_ha_error(t_table, error);
2673 goto end_index_init;
2674 }
2675 t_table->use_all_columns();
2676 c_table->use_all_columns();
2677
2678 error= t_table->file->ha_index_first(t_table->record[0]);
2679 DBUG_EXECUTE_IF("se_error_grant_load_read",
2680 error= HA_ERR_LOCK_WAIT_TIMEOUT;);
2681 if (error)
2682 {
2683 if (error == HA_ERR_END_OF_FILE)
2684 return_val= 0; // Return Ok.
2685 else
2686 acl_print_ha_error(t_table, error);
2687 }
2688 else
2689 {
2690 memex_ptr= &memex;
2691 my_thread_set_THR_MALLOC(&memex_ptr);
2692 do
2693 {
2694 GRANT_TABLE *mem_check;
2695 mem_check= new (memex_ptr) GRANT_TABLE(t_table);
2696
2697 if (!mem_check)
2698 {
2699 /* This could only happen if we are out memory */
2700 goto end_unlock;
2701 }
2702
2703 if (mem_check->init(c_table))
2704 {
2705 delete mem_check;
2706 goto end_unlock;
2707 }
2708
2709 if (check_no_resolve)
2710 {
2711 if (hostname_requires_resolving(mem_check->host.get_host()) &&
2712 !is_localhost_string(mem_check->host.get_host())) {
2713 sql_print_warning("'tables_priv' entry '%s %s@%s' "
2714 "ignored in --skip-name-resolve mode.",
2715 mem_check->tname,
2716 mem_check->user ? mem_check->user : "",
2717 mem_check->host.get_host());
2718 }
2719 }
2720
2721 if (! mem_check->ok())
2722 delete mem_check;
2723 else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
2724 {
2725 delete mem_check;
2726 goto end_unlock;
2727 }
2728 error= t_table->file->ha_index_next(t_table->record[0]);
2729 DBUG_EXECUTE_IF("se_error_grant_load_read_next",
2730 error= HA_ERR_LOCK_WAIT_TIMEOUT;);
2731 if (error)
2732 {
2733 if (error != HA_ERR_END_OF_FILE)
2734 acl_print_ha_error(t_table, error);
2735 else
2736 return_val= 0;
2737 goto end_unlock;
2738 }
2739
2740 }
2741 while (true);
2742 }
2743
2744 end_unlock:
2745 t_table->file->ha_index_end();
2746 my_thread_set_THR_MALLOC(save_mem_root_ptr);
2747 end_index_init:
2748 thd->variables.sql_mode= old_sql_mode;
2749 DBUG_RETURN(return_val);
2750 }
2751
2752
2753 /**
2754 @brief Helper function to grant_reload. Reloads procs_priv table is it
2755 exists.
2756
2757 @param thd A pointer to the thread handler object.
2758 @param table A pointer to the table list.
2759
2760 @see grant_reload
2761
2762 @return Error state
2763 @retval FALSE Success
2764 @retval TRUE An error has occurred.
2765 */
2766
grant_reload_procs_priv(THD * thd,TABLE_LIST * table)2767 static my_bool grant_reload_procs_priv(THD *thd, TABLE_LIST *table)
2768 {
2769 HASH old_proc_priv_hash, old_func_priv_hash;
2770 my_bool return_val= FALSE;
2771 DBUG_ENTER("grant_reload_procs_priv");
2772
2773 /* Save a copy of the current hash if we need to undo the grant load */
2774 old_proc_priv_hash= proc_priv_hash;
2775 old_func_priv_hash= func_priv_hash;
2776
2777 if ((return_val= grant_load_procs_priv(table->table)))
2778 {
2779 /* Error; Reverting to old hash */
2780 DBUG_PRINT("error",("Reverting to old privileges"));
2781 my_hash_free(&proc_priv_hash);
2782 my_hash_free(&func_priv_hash);
2783 proc_priv_hash= old_proc_priv_hash;
2784 func_priv_hash= old_func_priv_hash;
2785 }
2786 else
2787 {
2788 my_hash_free(&old_proc_priv_hash);
2789 my_hash_free(&old_func_priv_hash);
2790 }
2791
2792 DBUG_RETURN(return_val);
2793 }
2794
2795
2796 /**
2797 @brief Reload information about table and column level privileges if possible
2798
2799 @param thd Current thread
2800
2801 Locked tables are checked by acl_reload() and doesn't have to be checked
2802 in this call.
2803 This function is also used for initialization of structures responsible
2804 for table/column-level privilege checking.
2805
2806 @return Error state
2807 @retval FALSE Success
2808 @retval TRUE Error
2809 */
2810
grant_reload(THD * thd)2811 my_bool grant_reload(THD *thd)
2812 {
2813 TABLE_LIST tables[3];
2814 HASH old_column_priv_hash;
2815 MEM_ROOT old_mem;
2816 my_bool return_val= 1;
2817 DBUG_ENTER("grant_reload");
2818
2819 /* Don't do anything if running with --skip-grant-tables */
2820 if (!initialized)
2821 DBUG_RETURN(0);
2822
2823 tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
2824 C_STRING_WITH_LEN("tables_priv"),
2825 "tables_priv", TL_READ);
2826 tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
2827 C_STRING_WITH_LEN("columns_priv"),
2828 "columns_priv", TL_READ);
2829 tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
2830 C_STRING_WITH_LEN("procs_priv"),
2831 "procs_priv", TL_READ);
2832
2833 tables[0].next_local= tables[0].next_global= tables+1;
2834 tables[1].next_local= tables[1].next_global= tables+2;
2835 tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
2836
2837 /*
2838 Reload will work in the following manner:-
2839
2840 proc_priv_hash structure
2841 / \
2842 not initialized initialized
2843 / \ |
2844 mysql.procs_priv table Server Startup |
2845 is missing \ |
2846 | open_and_lock_tables()
2847 Assume we are working on /success \failure
2848 pre 4.1 system tables. Normal Scenario. An error is thrown.
2849 A warning is printed Reload column privilege. Retain the old hash.
2850 and continue with Reload function and
2851 reloading the column procedure privileges,
2852 privileges. if available.
2853 */
2854
2855 if (!(my_hash_inited(&proc_priv_hash)))
2856 tables[2].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
2857
2858 /*
2859 To avoid deadlocks we should obtain table locks before
2860 obtaining LOCK_grant rwlock.
2861 */
2862 if (open_and_lock_tables(thd, tables, MYSQL_LOCK_IGNORE_TIMEOUT))
2863 {
2864 if (thd->get_stmt_da()->is_error())
2865 {
2866 sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
2867 thd->get_stmt_da()->message_text());
2868 }
2869 goto end;
2870 }
2871
2872 if (tables[2].table == NULL)
2873 {
2874 sql_print_warning("Table 'mysql.procs_priv' does not exist. "
2875 "Please run mysql_upgrade.");
2876 push_warning_printf(thd, Sql_condition::SL_WARNING, ER_NO_SUCH_TABLE,
2877 ER(ER_NO_SUCH_TABLE), tables[2].db,
2878 tables[2].table_name);
2879 }
2880
2881 LOCK_grant.wrlock();
2882
2883 /* Save a copy of the current hash if we need to undo the grant load */
2884 old_column_priv_hash= column_priv_hash;
2885
2886 /*
2887 Create a new memory pool but save the current memory pool to make an undo
2888 opertion possible in case of failure.
2889 */
2890 old_mem= memex;
2891 init_sql_alloc(key_memory_acl_memex,
2892 &memex, ACL_ALLOC_BLOCK_SIZE, 0);
2893 /*
2894 tables[2].table i.e. procs_priv can be null if we are working with
2895 pre 4.1 privilage tables
2896 */
2897 if ((return_val= (grant_load(thd, tables) ||
2898 (tables[2].table != NULL &&
2899 grant_reload_procs_priv(thd, &tables[2])))
2900 ))
2901 { // Error. Revert to old hash
2902 DBUG_PRINT("error",("Reverting to old privileges"));
2903 my_hash_free(&column_priv_hash);
2904 free_root(&memex,MYF(0));
2905 column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
2906 memex= old_mem; /* purecov: deadcode */
2907 }
2908 else
2909 { //Reload successful
2910 my_hash_free(&old_column_priv_hash);
2911 free_root(&old_mem,MYF(0));
2912 grant_version++;
2913 }
2914 LOCK_grant.wrunlock();
2915
2916 end:
2917 close_acl_tables(thd);
2918 DBUG_RETURN(return_val);
2919 }
2920
2921
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,MYSQL_TIME password_change_time,LEX_ALTER password_life,ulong what_is_set)2922 void acl_update_user(const char *user, const char *host,
2923 enum SSL_type ssl_type,
2924 const char *ssl_cipher,
2925 const char *x509_issuer,
2926 const char *x509_subject,
2927 USER_RESOURCES *mqh,
2928 ulong privileges,
2929 const LEX_CSTRING &plugin,
2930 const LEX_CSTRING &auth,
2931 MYSQL_TIME password_change_time,
2932 LEX_ALTER password_life,
2933 ulong what_is_set)
2934 {
2935 DBUG_ENTER("acl_update_user");
2936 mysql_mutex_assert_owner(&acl_cache->lock);
2937 for (ACL_USER *acl_user= acl_users->begin();
2938 acl_user != acl_users->end(); ++acl_user)
2939 {
2940 if ((!acl_user->user && !user[0]) ||
2941 (acl_user->user && !strcmp(user,acl_user->user)))
2942 {
2943 if ((acl_user->host.is_null() && !host[0]) ||
2944 (!acl_user->host.is_null() &&
2945 !my_strcasecmp(system_charset_info, host, acl_user->host.get_host())))
2946 {
2947 if (plugin.length > 0)
2948 {
2949 acl_user->plugin.str= plugin.str;
2950 acl_user->plugin.length = plugin.length;
2951 optimize_plugin_compare_by_pointer(&acl_user->plugin);
2952 if (!auth_plugin_is_built_in(acl_user->plugin.str))
2953 acl_user->plugin.str= strmake_root(&global_acl_memory,
2954 plugin.str, plugin.length);
2955 /* Update auth string only when specified in ALTER/GRANT */
2956 if (auth.str)
2957 {
2958 if (auth.length == 0)
2959 acl_user->auth_string.str= const_cast<char*>("");
2960 else
2961 acl_user->auth_string.str= strmake_root(&global_acl_memory,
2962 auth.str, auth.length);
2963 acl_user->auth_string.length= auth.length;
2964 set_user_salt(acl_user);
2965 if (password_change_time.time_type != MYSQL_TIMESTAMP_ERROR)
2966 acl_user->password_last_changed= password_change_time;
2967 }
2968 }
2969 acl_user->access=privileges;
2970 if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
2971 acl_user->user_resource.questions=mqh->questions;
2972 if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
2973 acl_user->user_resource.updates=mqh->updates;
2974 if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
2975 acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
2976 if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
2977 acl_user->user_resource.user_conn= mqh->user_conn;
2978 if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
2979 {
2980 acl_user->ssl_type= ssl_type;
2981 acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&global_acl_memory,
2982 ssl_cipher) : 0);
2983 acl_user->x509_issuer= (x509_issuer ? strdup_root(&global_acl_memory,
2984 x509_issuer) : 0);
2985 acl_user->x509_subject= (x509_subject ?
2986 strdup_root(&global_acl_memory, x509_subject) : 0);
2987 }
2988 /* update details related to password lifetime, password expiry */
2989 if (password_life.update_password_expired_column ||
2990 what_is_set & PLUGIN_ATTR)
2991 acl_user->password_expired= password_life.update_password_expired_column;
2992 if (!password_life.update_password_expired_column &&
2993 password_life.update_password_expired_fields)
2994 {
2995 if (!password_life.use_default_password_lifetime)
2996 {
2997 acl_user->password_lifetime= password_life.expire_after_days;
2998 acl_user->use_default_password_lifetime= false;
2999 }
3000 else
3001 acl_user->use_default_password_lifetime= true;
3002 }
3003
3004 if (password_life.update_account_locked_column)
3005 {
3006 acl_user->account_locked = password_life.account_locked;
3007 }
3008
3009 /* search complete: */
3010 break;
3011 }
3012 }
3013 }
3014 DBUG_VOID_RETURN;
3015 }
3016
3017
acl_insert_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,MYSQL_TIME password_change_time,LEX_ALTER password_life)3018 void acl_insert_user(const char *user, const char *host,
3019 enum SSL_type ssl_type,
3020 const char *ssl_cipher,
3021 const char *x509_issuer,
3022 const char *x509_subject,
3023 USER_RESOURCES *mqh,
3024 ulong privileges,
3025 const LEX_CSTRING &plugin,
3026 const LEX_CSTRING &auth,
3027 MYSQL_TIME password_change_time,
3028 LEX_ALTER password_life)
3029 {
3030 DBUG_ENTER("acl_insert_user");
3031 ACL_USER acl_user;
3032
3033 mysql_mutex_assert_owner(&acl_cache->lock);
3034 /*
3035 All accounts can authenticate per default. This will change when
3036 we add a new field to the user table.
3037
3038 Currently this flag is only set to false when authentication is attempted
3039 using an unknown user name.
3040 */
3041 acl_user.can_authenticate= true;
3042
3043 acl_user.user= *user ? strdup_root(&global_acl_memory,user) : 0;
3044 acl_user.host.update_hostname(*host ? strdup_root(&global_acl_memory, host) : 0);
3045 if (plugin.str[0])
3046 {
3047 acl_user.plugin= plugin;
3048 optimize_plugin_compare_by_pointer(&acl_user.plugin);
3049 if (!auth_plugin_is_built_in(acl_user.plugin.str))
3050 acl_user.plugin.str= strmake_root(&global_acl_memory,
3051 plugin.str, plugin.length);
3052 acl_user.auth_string.str= auth.str ?
3053 strmake_root(&global_acl_memory, auth.str,
3054 auth.length) : const_cast<char*>("");
3055 acl_user.auth_string.length= auth.length;
3056
3057 optimize_plugin_compare_by_pointer(&acl_user.plugin);
3058 }
3059 else
3060 {
3061 acl_user.plugin= native_password_plugin_name;
3062 acl_user.auth_string.str= const_cast<char*>("");
3063 acl_user.auth_string.length= 0;
3064 }
3065
3066 acl_user.access= privileges;
3067 acl_user.user_resource= *mqh;
3068 acl_user.sort= get_sort(2,acl_user.host.get_host(), acl_user.user);
3069 //acl_user.hostname_length=(uint) strlen(host);
3070 acl_user.ssl_type=
3071 (ssl_type != SSL_TYPE_NOT_SPECIFIED ? ssl_type : SSL_TYPE_NONE);
3072 acl_user.ssl_cipher=
3073 ssl_cipher ? strdup_root(&global_acl_memory, ssl_cipher) : 0;
3074 acl_user.x509_issuer=
3075 x509_issuer ? strdup_root(&global_acl_memory, x509_issuer) : 0;
3076 acl_user.x509_subject=
3077 x509_subject ? strdup_root(&global_acl_memory, x509_subject) : 0;
3078 /* update details related to password lifetime, password expiry */
3079 acl_user.password_expired= password_life.update_password_expired_column;
3080 acl_user.password_lifetime= password_life.expire_after_days;
3081 acl_user.use_default_password_lifetime= password_life.use_default_password_lifetime;
3082 acl_user.password_last_changed= password_change_time;
3083 acl_user.account_locked= password_life.account_locked;
3084
3085 set_user_salt(&acl_user);
3086
3087 acl_users->push_back(acl_user);
3088 if (acl_user.host.check_allow_all_hosts())
3089 allow_all_hosts=1; // Anyone can connect /* purecov: tested */
3090 std::sort(acl_users->begin(), acl_users->end(), ACL_compare());
3091
3092 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
3093 rebuild_check_host();
3094 DBUG_VOID_RETURN;
3095 }
3096
3097
acl_update_proxy_user(ACL_PROXY_USER * new_value,bool is_revoke)3098 void acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
3099 {
3100 mysql_mutex_assert_owner(&acl_cache->lock);
3101
3102 DBUG_ENTER("acl_update_proxy_user");
3103 for (ACL_PROXY_USER *acl_user= acl_proxy_users->begin();
3104 acl_user != acl_proxy_users->end(); ++acl_user)
3105 {
3106 if (acl_user->pk_equals(new_value))
3107 {
3108 if (is_revoke)
3109 {
3110 DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
3111 acl_proxy_users->erase(acl_user);
3112 }
3113 else
3114 {
3115 DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
3116 acl_user->set_data(new_value);
3117 }
3118 break;
3119 }
3120 }
3121 DBUG_VOID_RETURN;
3122 }
3123
3124
acl_update_db(const char * user,const char * host,const char * db,ulong privileges)3125 void acl_update_db(const char *user, const char *host, const char *db,
3126 ulong privileges)
3127 {
3128 mysql_mutex_assert_owner(&acl_cache->lock);
3129
3130 for (ACL_DB *acl_db= acl_dbs->begin(); acl_db < acl_dbs->end(); )
3131 {
3132 if ((!acl_db->user && !user[0]) ||
3133 (acl_db->user &&
3134 !strcmp(user,acl_db->user)))
3135 {
3136 if ((acl_db->host.is_null() && !host[0]) ||
3137 (acl_db->host.get_host_len() &&
3138 !strcmp(host, acl_db->host.get_host())))
3139 {
3140 if ((!acl_db->db && !db[0]) ||
3141 (acl_db->db && !strcmp(db,acl_db->db)))
3142 {
3143 if (privileges)
3144 acl_db->access=privileges;
3145 else
3146 {
3147 acl_db= acl_dbs->erase(acl_db);
3148 // Don't increment loop variable.
3149 continue;
3150 }
3151 }
3152 }
3153 }
3154 ++acl_db;
3155 }
3156 }
3157
3158
3159 /*
3160 Insert a user/db/host combination into the global acl_cache
3161
3162 SYNOPSIS
3163 acl_insert_db()
3164 user User name
3165 host Host name
3166 db Database name
3167 privileges Bitmap of privileges
3168
3169 NOTES
3170 acl_cache->lock must be locked when calling this
3171 */
3172
acl_insert_db(const char * user,const char * host,const char * db,ulong privileges)3173 void acl_insert_db(const char *user, const char *host, const char *db,
3174 ulong privileges)
3175 {
3176 ACL_DB acl_db;
3177 mysql_mutex_assert_owner(&acl_cache->lock);
3178 acl_db.user= strdup_root(&global_acl_memory,user);
3179 acl_db.host.update_hostname(*host ? strdup_root(&global_acl_memory, host) : 0);
3180 acl_db.db= strdup_root(&global_acl_memory, db);
3181 acl_db.access= privileges;
3182 acl_db.sort= get_sort(3,acl_db.host.get_host(), acl_db.db, acl_db.user);
3183 acl_dbs->push_back(acl_db);
3184 std::sort(acl_dbs->begin(), acl_dbs->end(), ACL_compare());
3185 }
3186
3187
get_mqh(const char * user,const char * host,USER_CONN * uc)3188 void get_mqh(const char *user, const char *host, USER_CONN *uc)
3189 {
3190 ACL_USER *acl_user;
3191
3192 mysql_mutex_lock(&acl_cache->lock);
3193
3194 if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
3195 uc->user_resources= acl_user->user_resource;
3196 else
3197 memset(&uc->user_resources, 0, sizeof(uc->user_resources));
3198
3199 mysql_mutex_unlock(&acl_cache->lock);
3200 }
3201
3202
3203
3204 /**
3205 Update the security context when updating the user
3206
3207 Helper function.
3208 Update only if the security context is pointing to the same user and
3209 the user is not a proxied user for a different proxy user.
3210 And return true if the update happens (i.e. we're operating on the
3211 user account of the current user).
3212 Normalize the names for a safe compare.
3213
3214 @param sctx The security context to update
3215 @param acl_user_ptr User account being updated
3216 @param expired new value of the expiration flag
3217 @return did the update happen ?
3218 */
3219 bool
update_sctx_cache(Security_context * sctx,ACL_USER * acl_user_ptr,bool expired)3220 update_sctx_cache(Security_context *sctx, ACL_USER *acl_user_ptr, bool expired)
3221 {
3222 const char *acl_host= acl_user_ptr->host.get_host();
3223 const char *acl_user= acl_user_ptr->user;
3224 const char *sctx_user= sctx->priv_user().str;
3225 const char *sctx_host= sctx->priv_host().str;
3226
3227 /* If the user is connected as a proxied user, verify against proxy user */
3228 if (sctx->proxy_user().str && *sctx->proxy_user().str != '\0')
3229 {
3230 sctx_user = sctx->user().str;
3231 }
3232
3233 if(!acl_user)
3234 acl_user= "";
3235 if (!sctx_host)
3236 sctx_host= "";
3237 if (!sctx_user)
3238 sctx_user= "";
3239
3240 if (!strcmp(acl_user, sctx_user) && !strcmp(acl_host, sctx_host))
3241 {
3242 sctx->set_password_expired(expired);
3243 return true;
3244 }
3245
3246 return false;
3247 }
3248
3249 #else
3250
acl_is_utility_user(const char * user,const char * host,const char * ip)3251 bool acl_is_utility_user(const char *user, const char *host,
3252 const char *ip)
3253 {
3254 return false;
3255 }
3256
3257 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
3258
3259