1 /* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
2 Copyright (c) 2009, 2021, MariaDB
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
16
17
18 /*
19 The privileges are saved in the following tables:
20 mysql/user ; super user who are allowed to do almost anything
21 mysql/host ; host privileges. This is used if host is empty in mysql/db.
22 mysql/db ; database privileges / user
23
24 data in tables is sorted according to how many not-wild-cards there is
25 in the relevant fields. Empty strings comes last.
26 */
27
28 #include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */
29 #include "sql_priv.h"
30 #include "sql_acl.h" // MYSQL_DB_FIELD_COUNT, ACL_ACCESS
31 #include "sql_base.h" // close_mysql_tables
32 #include "key.h" // key_copy, key_cmp_if_same, key_restore
33 #include "sql_show.h" // append_identifier
34 #include "sql_table.h" // write_bin_log
35 #include "hash_filo.h"
36 #include "sql_parse.h" // check_access
37 #include "sql_view.h" // VIEW_ANY_ACL
38 #include "records.h" // READ_RECORD, read_record_info,
39 // init_read_record, end_read_record
40 #include "rpl_filter.h" // rpl_filter
41 #include "rpl_rli.h"
42 #include <m_ctype.h>
43 #include <stdarg.h>
44 #include "sp_head.h"
45 #include "sp.h"
46 #include "transaction.h"
47 #include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
48 #include <sql_common.h>
49 #include <mysql/plugin_auth.h>
50 #include <mysql/plugin_password_validation.h>
51 #include "sql_connect.h"
52 #include "hostname.h"
53 #include "sql_db.h"
54 #include "sql_array.h"
55 #include "sql_hset.h"
56 #include "password.h"
57
58 #include "sql_plugin_compat.h"
59
60 #define MAX_SCRAMBLE_LENGTH 1024
61
62 bool mysql_user_table_is_in_short_password_format= false;
63 bool using_global_priv_table= true;
64
65 // set that from field length in acl_load?
66 #ifndef NO_EMBEDDED_ACCESS_CHECKS
67 const uint max_hostname_length= 60;
68 const uint max_dbname_length= 64;
69 #endif
70
safe_vio_type_name(Vio * vio)71 const char *safe_vio_type_name(Vio *vio)
72 {
73 size_t unused;
74 #ifdef EMBEDDED_LIBRARY
75 if (!vio) return "Internal";
76 #endif
77 return vio_type_name(vio_type(vio), &unused);
78 }
79
80 #include "sql_acl_getsort.ic"
81
82 static LEX_CSTRING native_password_plugin_name= {
83 STRING_WITH_LEN("mysql_native_password")
84 };
85
86 static LEX_CSTRING old_password_plugin_name= {
87 STRING_WITH_LEN("mysql_old_password")
88 };
89
90 /// @todo make it configurable
91 LEX_CSTRING *default_auth_plugin_name= &native_password_plugin_name;
92
93 /*
94 Wildcard host, matches any hostname
95 */
96 LEX_CSTRING host_not_specified= { STRING_WITH_LEN("%") };
97
98 /*
99 Constants, used in the SHOW GRANTS command.
100 Their actual string values are irrelevant, they're always compared
101 as pointers to these string constants.
102 */
103 LEX_CSTRING current_user= { STRING_WITH_LEN("*current_user") };
104 LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role") };
105 LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role") };
106
107
108 static plugin_ref old_password_plugin;
109 static plugin_ref native_password_plugin;
110
get_auth_plugin(THD * thd,const LEX_CSTRING & name,bool * locked)111 static plugin_ref get_auth_plugin(THD *thd, const LEX_CSTRING &name, bool *locked)
112 {
113 if (name.str == native_password_plugin_name.str)
114 return native_password_plugin;
115 else if (name.str == old_password_plugin_name.str)
116 return old_password_plugin;
117 *locked=true;
118 return my_plugin_lock_by_name(thd, &name, MYSQL_AUTHENTICATION_PLUGIN);
119 }
120
121 /* Classes */
122
123 struct acl_host_and_ip
124 {
125 char *hostname;
126 long ip, ip_mask; // Used with masked ip:s
127 };
128
129 #ifndef NO_EMBEDDED_ACCESS_CHECKS
130 static bool compare_hostname(const acl_host_and_ip *, const char *, const char *);
131 #else
132 #define compare_hostname(X,Y,Z) 0
133 #endif
134
135 class ACL_ACCESS {
136 public:
137 ulonglong sort;
138 privilege_t access;
ACL_ACCESS()139 ACL_ACCESS()
140 :sort(0), access(NO_ACL)
141 { }
142 };
143
144 /* ACL_HOST is used if no host is specified */
145
146 class ACL_HOST :public ACL_ACCESS
147 {
148 public:
149 acl_host_and_ip host;
150 char *db;
151 };
152
153 class ACL_USER_BASE :public ACL_ACCESS, public Sql_alloc
154 {
155
156 public:
ACL_USER_BASE()157 ACL_USER_BASE()
158 :flags(0), user(null_clex_str)
159 {
160 bzero(&role_grants, sizeof(role_grants));
161 }
162 uchar flags; // field used to store various state information
163 LEX_CSTRING user;
164 /* list to hold references to granted roles (ACL_ROLE instances) */
165 DYNAMIC_ARRAY role_grants;
get_username()166 const char *get_username() { return user.str; }
167 };
168
169 class ACL_USER_PARAM
170 {
171 public:
ACL_USER_PARAM()172 ACL_USER_PARAM()
173 {
174 bzero(this, sizeof(*this));
175 }
176 acl_host_and_ip host;
177 size_t hostname_length;
178 USER_RESOURCES user_resource;
179 enum SSL_type ssl_type;
180 uint password_errors;
181 const char *ssl_cipher, *x509_issuer, *x509_subject;
182 LEX_CSTRING default_rolename;
183 struct AUTH { LEX_CSTRING plugin, auth_string, salt; } *auth;
184 uint nauth;
185 bool account_locked;
186 bool password_expired;
187 my_time_t password_last_changed;
188 longlong password_lifetime;
189
alloc_auth(MEM_ROOT * root,uint n)190 bool alloc_auth(MEM_ROOT *root, uint n)
191 {
192 return !(auth= (AUTH*) alloc_root(root, (nauth= n)*sizeof(AUTH)));
193 }
194 };
195
196
197 class ACL_USER :public ACL_USER_BASE,
198 public ACL_USER_PARAM
199 {
200 public:
201
ACL_USER()202 ACL_USER() { }
203 ACL_USER(THD *thd, const LEX_USER &combo,
204 const Account_options &options,
205 const privilege_t privileges);
206
copy(MEM_ROOT * root)207 ACL_USER *copy(MEM_ROOT *root)
208 {
209 ACL_USER *dst;
210 AUTH *dauth;
211 if (!multi_alloc_root(root, &dst, sizeof(ACL_USER),
212 &dauth, sizeof(AUTH)*nauth, NULL))
213 return 0;
214 *dst= *this;
215 dst->user= safe_lexcstrdup_root(root, user);
216 dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
217 dst->x509_issuer= safe_strdup_root(root, x509_issuer);
218 dst->x509_subject= safe_strdup_root(root, x509_subject);
219 dst->auth= dauth;
220 for (uint i=0; i < nauth; i++, dauth++)
221 {
222 if (auth[i].plugin.str == native_password_plugin_name.str ||
223 auth[i].plugin.str == old_password_plugin_name.str)
224 dauth->plugin= auth[i].plugin;
225 else
226 dauth->plugin= safe_lexcstrdup_root(root, auth[i].plugin);
227 dauth->auth_string= safe_lexcstrdup_root(root, auth[i].auth_string);
228 if (auth[i].salt.length == 0)
229 dauth->salt= auth[i].salt;
230 else
231 dauth->salt= safe_lexcstrdup_root(root, auth[i].salt);
232 }
233 dst->host.hostname= safe_strdup_root(root, host.hostname);
234 dst->default_rolename= safe_lexcstrdup_root(root, default_rolename);
235 bzero(&dst->role_grants, sizeof(role_grants));
236 return dst;
237 }
238
cmp(const char * user2,const char * host2)239 int cmp(const char *user2, const char *host2)
240 {
241 CHARSET_INFO *cs= system_charset_info;
242 int res;
243 res= strcmp(user.str, user2);
244 if (!res)
245 res= my_strcasecmp(cs, host.hostname, host2);
246 return res;
247 }
248
eq(const char * user2,const char * host2)249 bool eq(const char *user2, const char *host2) { return !cmp(user2, host2); }
250
wild_eq(const char * user2,const char * host2,const char * ip2)251 bool wild_eq(const char *user2, const char *host2, const char *ip2)
252 {
253 if (strcmp(user.str, user2))
254 return false;
255
256 return compare_hostname(&host, host2, ip2 ? ip2 : host2);
257 }
258 };
259
260 class ACL_ROLE :public ACL_USER_BASE
261 {
262 public:
263 /*
264 In case of granting a role to a role, the access bits are merged together
265 via a bit OR operation and placed in the ACL_USER::access field.
266
267 When rebuilding role_grants via the rebuild_role_grant function,
268 the ACL_USER::access field needs to be reset first. The field
269 initial_role_access holds initial grants, as granted directly to the role
270 */
271 privilege_t initial_role_access;
272 /*
273 In subgraph traversal, when we need to traverse only a part of the graph
274 (e.g. all direct and indirect grantees of a role X), the counter holds the
275 number of affected neighbour nodes.
276 See also propagate_role_grants()
277 */
278 uint counter;
279 DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted
280
281 ACL_ROLE(ACL_USER * user, MEM_ROOT *mem);
282 ACL_ROLE(const char * rolename, privilege_t privileges, MEM_ROOT *mem);
283
284 };
285
286 class ACL_DB :public ACL_ACCESS
287 {
288 public:
ACL_DB()289 ACL_DB() :initial_access(NO_ACL) { }
290 acl_host_and_ip host;
291 const char *user,*db;
292 privilege_t initial_access; /* access bits present in the table */
293
get_username()294 const char *get_username() { return user; }
295 };
296
297 #ifndef DBUG_OFF
298 /* status variables, only visible in SHOW STATUS after -#d,role_merge_stats */
299 ulong role_global_merges= 0, role_db_merges= 0, role_table_merges= 0,
300 role_column_merges= 0, role_routine_merges= 0;
301 #endif
302
303 #ifndef NO_EMBEDDED_ACCESS_CHECKS
304 static void update_hostname(acl_host_and_ip *host, const char *hostname);
305 static bool show_proxy_grants (THD *, const char *, const char *,
306 char *, size_t);
307 static bool show_role_grants(THD *, const char *,
308 ACL_USER_BASE *, char *, size_t);
309 static bool show_default_role(THD *, ACL_USER *, char *, size_t);
310 static bool show_global_privileges(THD *, ACL_USER_BASE *,
311 bool, char *, size_t);
312 static bool show_database_privileges(THD *, const char *, const char *,
313 char *, size_t);
314 static bool show_table_and_column_privileges(THD *, const char *, const char *,
315 char *, size_t);
316 static int show_routine_grants(THD *, const char *, const char *,
317 const Sp_handler *sph, char *, int);
318
319 class Grant_tables;
320 class User_table;
321 class Proxies_priv_table;
322
323 class ACL_PROXY_USER :public ACL_ACCESS
324 {
325 acl_host_and_ip host;
326 const char *user;
327 acl_host_and_ip proxied_host;
328 const char *proxied_user;
329 bool with_grant;
330
331 typedef enum {
332 MYSQL_PROXIES_PRIV_HOST,
333 MYSQL_PROXIES_PRIV_USER,
334 MYSQL_PROXIES_PRIV_PROXIED_HOST,
335 MYSQL_PROXIES_PRIV_PROXIED_USER,
336 MYSQL_PROXIES_PRIV_WITH_GRANT,
337 MYSQL_PROXIES_PRIV_GRANTOR,
338 MYSQL_PROXIES_PRIV_TIMESTAMP } proxy_table_fields;
339 public:
ACL_PROXY_USER()340 ACL_PROXY_USER () {};
341
init(const char * host_arg,const char * user_arg,const char * proxied_host_arg,const char * proxied_user_arg,bool with_grant_arg)342 void init(const char *host_arg, const char *user_arg,
343 const char *proxied_host_arg, const char *proxied_user_arg,
344 bool with_grant_arg)
345 {
346 user= user_arg;
347 update_hostname (&host, (host_arg && *host_arg) ? host_arg : NULL);
348 proxied_user= proxied_user_arg;
349 update_hostname (&proxied_host,
350 (proxied_host_arg && *proxied_host_arg) ?
351 proxied_host_arg : NULL);
352 with_grant= with_grant_arg;
353 sort= get_magic_sort("huhu", host.hostname, user, proxied_host.hostname,
354 proxied_user);
355 }
356
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)357 void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
358 const char *proxied_host_arg, const char *proxied_user_arg,
359 bool with_grant_arg)
360 {
361 init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
362 strdup_root (mem, user_arg),
363 (proxied_host_arg && *proxied_host_arg) ?
364 strdup_root (mem, proxied_host_arg) : NULL,
365 strdup_root (mem, proxied_user_arg),
366 with_grant_arg);
367 }
368
369 void init(const Proxies_priv_table& proxies_priv_table, MEM_ROOT *mem);
370
get_with_grant()371 bool get_with_grant() { return with_grant; }
get_user()372 const char *get_user() { return user; }
get_host()373 const char *get_host() { return host.hostname; }
get_proxied_user()374 const char *get_proxied_user() { return proxied_user; }
get_proxied_host()375 const char *get_proxied_host() { return proxied_host.hostname; }
set_user(MEM_ROOT * mem,const char * user_arg)376 void set_user(MEM_ROOT *mem, const char *user_arg)
377 {
378 user= *user_arg ? strdup_root(mem, user_arg) : "";
379 }
set_host(MEM_ROOT * mem,const char * host_arg)380 void set_host(MEM_ROOT *mem, const char *host_arg)
381 {
382 update_hostname(&host, safe_strdup_root(mem, host_arg));
383 }
384
check_validity(bool check_no_resolve)385 bool check_validity(bool check_no_resolve)
386 {
387 if (check_no_resolve &&
388 (hostname_requires_resolving(host.hostname) ||
389 hostname_requires_resolving(proxied_host.hostname)))
390 {
391 sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
392 "ignored in --skip-name-resolve mode.",
393 proxied_user,
394 safe_str(proxied_host.hostname), user,
395 safe_str(host.hostname));
396 return TRUE;
397 }
398 return FALSE;
399 }
400
matches(const char * host_arg,const char * user_arg,const char * ip_arg,const char * proxied_user_arg)401 bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
402 const char *proxied_user_arg)
403 {
404 DBUG_ENTER("ACL_PROXY_USER::matches");
405 DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
406 "compare_hostname(%s,%s,%s) &&"
407 "wild_compare (%s,%s) &&"
408 "wild_compare (%s,%s)",
409 host.hostname, host_arg, ip_arg, proxied_host.hostname,
410 host_arg, ip_arg, user_arg, user,
411 proxied_user_arg, proxied_user));
412 DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
413 compare_hostname(&proxied_host, host_arg, ip_arg) &&
414 (!*user || !strcmp(user_arg, user)) &&
415 (!*proxied_user || !strcmp(proxied_user_arg, proxied_user)));
416 }
417
418
auth_element_equals(const char * a,const char * b)419 inline static bool auth_element_equals(const char *a, const char *b)
420 {
421 return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
422 }
423
424
pk_equals(ACL_PROXY_USER * grant)425 bool pk_equals(ACL_PROXY_USER *grant)
426 {
427 DBUG_ENTER("pk_equals");
428 DBUG_PRINT("info", ("strcmp(%s,%s) &&"
429 "strcmp(%s,%s) &&"
430 "wild_compare (%s,%s) &&"
431 "wild_compare (%s,%s)",
432 user, grant->user, proxied_user, grant->proxied_user,
433 host.hostname, grant->host.hostname,
434 proxied_host.hostname, grant->proxied_host.hostname));
435
436 bool res= auth_element_equals(user, grant->user) &&
437 auth_element_equals(proxied_user, grant->proxied_user) &&
438 auth_element_equals(host.hostname, grant->host.hostname) &&
439 auth_element_equals(proxied_host.hostname,
440 grant->proxied_host.hostname);
441 DBUG_RETURN(res);
442 }
443
444
granted_on(const char * host_arg,const char * user_arg)445 bool granted_on(const char *host_arg, const char *user_arg)
446 {
447 return (!strcmp(user, user_arg) &&
448 ((!host.hostname && (!host_arg || !host_arg[0])) ||
449 (host.hostname && host_arg && !strcmp(host.hostname, host_arg))));
450 }
451
452
print_grant(String * str)453 void print_grant(String *str)
454 {
455 str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
456 str->append(proxied_user);
457 str->append(STRING_WITH_LEN("'@'"));
458 if (proxied_host.hostname)
459 str->append(proxied_host.hostname, strlen(proxied_host.hostname));
460 str->append(STRING_WITH_LEN("' TO '"));
461 str->append(user);
462 str->append(STRING_WITH_LEN("'@'"));
463 if (host.hostname)
464 str->append(host.hostname);
465 str->append(STRING_WITH_LEN("'"));
466 if (with_grant)
467 str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
468 }
469
set_data(ACL_PROXY_USER * grant)470 void set_data(ACL_PROXY_USER *grant)
471 {
472 with_grant= grant->with_grant;
473 }
474
store_pk(TABLE * table,const LEX_CSTRING * host,const LEX_CSTRING * user,const LEX_CSTRING * proxied_host,const LEX_CSTRING * proxied_user)475 static int store_pk(TABLE *table,
476 const LEX_CSTRING *host,
477 const LEX_CSTRING *user,
478 const LEX_CSTRING *proxied_host,
479 const LEX_CSTRING *proxied_user)
480 {
481 DBUG_ENTER("ACL_PROXY_USER::store_pk");
482 DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
483 host->str, user->str,
484 proxied_host->str, proxied_user->str));
485 if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
486 host->length,
487 system_charset_info))
488 DBUG_RETURN(TRUE);
489 if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
490 user->length,
491 system_charset_info))
492 DBUG_RETURN(TRUE);
493 if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str,
494 proxied_host->length,
495 system_charset_info))
496 DBUG_RETURN(TRUE);
497 if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str,
498 proxied_user->length,
499 system_charset_info))
500 DBUG_RETURN(TRUE);
501
502 DBUG_RETURN(FALSE);
503 }
504
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)505 static int store_data_record(TABLE *table,
506 const LEX_CSTRING *host,
507 const LEX_CSTRING *user,
508 const LEX_CSTRING *proxied_host,
509 const LEX_CSTRING *proxied_user,
510 bool with_grant,
511 const char *grantor)
512 {
513 DBUG_ENTER("ACL_PROXY_USER::store_pk");
514 if (store_pk(table, host, user, proxied_host, proxied_user))
515 DBUG_RETURN(TRUE);
516 DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
517 if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
518 TRUE))
519 DBUG_RETURN(TRUE);
520 if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
521 strlen(grantor),
522 system_charset_info))
523 DBUG_RETURN(TRUE);
524
525 DBUG_RETURN(FALSE);
526 }
527 };
528
529 #define FIRST_NON_YN_FIELD 26
530
531 class acl_entry :public hash_filo_element
532 {
533 public:
534 privilege_t access;
535 uint16 length;
536 char key[1]; // Key will be stored here
537 };
538
539
acl_entry_get_key(acl_entry * entry,size_t * length,my_bool not_used)540 static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
541 my_bool not_used __attribute__((unused)))
542 {
543 *length=(uint) entry->length;
544 return (uchar*) entry->key;
545 }
546
acl_role_get_key(ACL_ROLE * entry,size_t * length,my_bool not_used)547 static uchar* acl_role_get_key(ACL_ROLE *entry, size_t *length,
548 my_bool not_used __attribute__((unused)))
549 {
550 *length=(uint) entry->user.length;
551 return (uchar*) entry->user.str;
552 }
553
554 struct ROLE_GRANT_PAIR : public Sql_alloc
555 {
556 char *u_uname;
557 char *u_hname;
558 char *r_uname;
559 LEX_STRING hashkey;
560 bool with_admin;
561
562 bool init(MEM_ROOT *mem, const char *username, const char *hostname,
563 const char *rolename, bool with_admin_option);
564 };
565
acl_role_map_get_key(ROLE_GRANT_PAIR * entry,size_t * length,my_bool not_used)566 static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length,
567 my_bool not_used __attribute__((unused)))
568 {
569 *length=(uint) entry->hashkey.length;
570 return (uchar*) entry->hashkey.str;
571 }
572
init(MEM_ROOT * mem,const char * username,const char * hostname,const char * rolename,bool with_admin_option)573 bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, const char *username,
574 const char *hostname, const char *rolename,
575 bool with_admin_option)
576 {
577 size_t uname_l = safe_strlen(username);
578 size_t hname_l = safe_strlen(hostname);
579 size_t rname_l = safe_strlen(rolename);
580 /*
581 Create a buffer that holds all 3 NULL terminated strings in succession
582 To save memory space, the same buffer is used as the hashkey
583 */
584 size_t bufflen = uname_l + hname_l + rname_l + 3; //add the '\0' aswell
585 char *buff= (char *)alloc_root(mem, bufflen);
586 if (!buff)
587 return true;
588
589 /*
590 Offsets in the buffer for all 3 strings
591 */
592 char *username_pos= buff;
593 char *hostname_pos= buff + uname_l + 1;
594 char *rolename_pos= buff + uname_l + hname_l + 2;
595
596 if (username) //prevent undefined behaviour
597 memcpy(username_pos, username, uname_l);
598 username_pos[uname_l]= '\0'; //#1 string terminator
599 u_uname= username_pos;
600
601 if (hostname) //prevent undefined behaviour
602 memcpy(hostname_pos, hostname, hname_l);
603 hostname_pos[hname_l]= '\0'; //#2 string terminator
604 u_hname= hostname_pos;
605
606 if (rolename) //prevent undefined behaviour
607 memcpy(rolename_pos, rolename, rname_l);
608 rolename_pos[rname_l]= '\0'; //#3 string terminator
609 r_uname= rolename_pos;
610
611 hashkey.str = buff;
612 hashkey.length = bufflen;
613
614 with_admin= with_admin_option;
615
616 return false;
617 }
618
619 #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
620 #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
621 1 + USERNAME_LENGTH + 1)
622
623 #if defined(HAVE_OPENSSL)
624 /*
625 Without SSL the handshake consists of one packet. This packet
626 has both client capabilities and scrambled password.
627 With SSL the handshake might consist of two packets. If the first
628 packet (client capabilities) has CLIENT_SSL flag set, we have to
629 switch to SSL and read the second packet. The scrambled password
630 is in the second packet and client_capabilities field will be ignored.
631 Maybe it is better to accept flags other than CLIENT_SSL from the
632 second packet?
633 */
634 #define SSL_HANDSHAKE_SIZE 2
635 #define MIN_HANDSHAKE_SIZE 2
636 #else
637 #define MIN_HANDSHAKE_SIZE 6
638 #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
639 #define NORMAL_HANDSHAKE_SIZE 6
640
641 #define ROLE_ASSIGN_COLUMN_IDX 44
642 #define DEFAULT_ROLE_COLUMN_IDX 45
643 #define MAX_STATEMENT_TIME_COLUMN_IDX 46
644
645 /* various flags valid for ACL_USER */
646 #define IS_ROLE (1L << 0)
647 /* Flag to mark that a ROLE is on the recursive DEPTH_FIRST_SEARCH stack */
648 #define ROLE_ON_STACK (1L << 1)
649 /*
650 Flag to mark that a ROLE and all it's neighbours have
651 been visited
652 */
653 #define ROLE_EXPLORED (1L << 2)
654 /* Flag to mark that on_node was already called for this role */
655 #define ROLE_OPENED (1L << 3)
656
657 static DYNAMIC_ARRAY acl_hosts, acl_users, acl_proxy_users;
658 static Dynamic_array<ACL_DB> acl_dbs(PSI_INSTRUMENT_MEM, 0U, 50U);
659 typedef Dynamic_array<ACL_DB>::CMP_FUNC acl_dbs_cmp;
660 static HASH acl_roles;
661 /*
662 An hash containing mappings user <--> role
663
664 A hash is used so as to make updates quickly
665 The hashkey used represents all the entries combined
666 */
667 static HASH acl_roles_mappings;
668 static MEM_ROOT acl_memroot, grant_memroot;
669 static bool initialized=0;
670 static bool allow_all_hosts=1;
671 static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
672 static HASH package_spec_priv_hash, package_body_priv_hash;
673 static DYNAMIC_ARRAY acl_wild_hosts;
674 static Hash_filo<acl_entry> *acl_cache;
675 static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
676 static privilege_t get_access(TABLE *form, uint fieldnr, uint *next_field=0);
677 static int acl_compare(const ACL_ACCESS *a, const ACL_ACCESS *b);
678 static int acl_user_compare(const ACL_USER *a, const ACL_USER *b);
679 static void rebuild_acl_users();
680 static int acl_db_compare(const ACL_DB *a, const ACL_DB *b);
681 static void rebuild_acl_dbs();
682 static void init_check_host(void);
683 static void rebuild_check_host(void);
684 static void rebuild_role_grants(void);
685 static ACL_USER *find_user_exact(const char *host, const char *user);
686 static ACL_USER *find_user_wild(const char *host, const char *user, const char *ip= 0);
687 static ACL_ROLE *find_acl_role(const char *user);
688 static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, const LEX_CSTRING *h, const LEX_CSTRING *r);
689 static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host);
690 static bool update_user_table_password(THD *, const User_table&, const ACL_USER&);
691 static bool acl_load(THD *thd, const Grant_tables& grant_tables);
692 static inline void get_grantor(THD *thd, char* grantor);
693 static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname);
694 static bool get_YN_as_bool(Field *field);
695
696 #define ROLE_CYCLE_FOUND 2
697 static int traverse_role_graph_up(ACL_ROLE *, void *,
698 int (*) (ACL_ROLE *, void *),
699 int (*) (ACL_ROLE *, ACL_ROLE *, void *));
700
701 static int traverse_role_graph_down(ACL_USER_BASE *, void *,
702 int (*) (ACL_USER_BASE *, void *),
703 int (*) (ACL_USER_BASE *, ACL_ROLE *, void *));
704
705
get_priv_hash() const706 HASH *Sp_handler_procedure::get_priv_hash() const
707 {
708 return &proc_priv_hash;
709 }
710
711
get_priv_hash() const712 HASH *Sp_handler_function::get_priv_hash() const
713 {
714 return &func_priv_hash;
715 }
716
717
get_priv_hash() const718 HASH *Sp_handler_package_spec::get_priv_hash() const
719 {
720 return &package_spec_priv_hash;
721 }
722
723
get_priv_hash() const724 HASH *Sp_handler_package_body::get_priv_hash() const
725 {
726 return &package_body_priv_hash;
727 }
728
729
730 /*
731 Enumeration of ACL/GRANT tables in the mysql database
732 */
733 enum enum_acl_tables
734 {
735 DB_TABLE,
736 TABLES_PRIV_TABLE,
737 COLUMNS_PRIV_TABLE,
738 #define FIRST_OPTIONAL_TABLE HOST_TABLE
739 HOST_TABLE,
740 PROCS_PRIV_TABLE,
741 PROXIES_PRIV_TABLE,
742 ROLES_MAPPING_TABLE,
743 USER_TABLE // <== always the last
744 };
745
746 static const int Table_user= 1 << USER_TABLE;
747 static const int Table_db= 1 << DB_TABLE;
748 static const int Table_tables_priv= 1 << TABLES_PRIV_TABLE;
749 static const int Table_columns_priv= 1 << COLUMNS_PRIV_TABLE;
750 static const int Table_host= 1 << HOST_TABLE;
751 static const int Table_procs_priv= 1 << PROCS_PRIV_TABLE;
752 static const int Table_proxies_priv= 1 << PROXIES_PRIV_TABLE;
753 static const int Table_roles_mapping= 1 << ROLES_MAPPING_TABLE;
754
755 static LEX_CSTRING MYSQL_TABLE_NAME[USER_TABLE+1]= {
756 {STRING_WITH_LEN("db")},
757 {STRING_WITH_LEN("tables_priv")},
758 {STRING_WITH_LEN("columns_priv")},
759 {STRING_WITH_LEN("host")},
760 {STRING_WITH_LEN("procs_priv")},
761 {STRING_WITH_LEN("proxies_priv")},
762 {STRING_WITH_LEN("roles_mapping")},
763 {STRING_WITH_LEN("global_priv")}
764 };
765 static LEX_CSTRING MYSQL_TABLE_NAME_USER={STRING_WITH_LEN("user")};
766
767 /**
768 Choose from either native or old password plugins when assigning a password
769 */
770
guess_auth_plugin(THD * thd,size_t password_len)771 static LEX_CSTRING &guess_auth_plugin(THD *thd, size_t password_len)
772 {
773 if (thd->variables.old_passwords == 1 ||
774 password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
775 return old_password_plugin_name;
776 else
777 return native_password_plugin_name;
778 }
779
780 /**
781 Base class representing a generic grant table from the mysql database.
782
783 The potential tables that this class can represent are:
784 user, db, columns_priv, tables_priv, host, procs_priv, proxies_priv,
785 roles_mapping
786
787 Objects belonging to this parent class can only be constructed by the
788 Grants_table class. This ensures the correct initialization of the objects.
789 */
790 class Grant_table_base
791 {
792 public:
793 /* Number of fields for this Grant Table. */
num_fields() const794 uint num_fields() const { return m_table->s->fields; }
795 /* Check if the table exists after an attempt to open it was made.
796 Some tables, such as the host table in MySQL 5.6.7+ are missing. */
table_exists() const797 bool table_exists() const { return m_table; };
798 /* Initializes the READ_RECORD structure provided as a parameter
799 to read through the whole table, with all columns available. Cleaning up
800 is the caller's job. */
init_read_record(READ_RECORD * info) const801 bool init_read_record(READ_RECORD* info) const
802 {
803 DBUG_ASSERT(m_table);
804
805 if (num_fields() < min_columns)
806 {
807 my_printf_error(ER_UNKNOWN_ERROR, "Fatal error: mysql.%s table is "
808 "damaged or in unsupported 3.20 format",
809 MYF(ME_ERROR_LOG), m_table->s->table_name.str);
810 return 1;
811 }
812
813 bool result= ::init_read_record(info, m_table->in_use, m_table,
814 NULL, NULL, 1, true, false);
815 if (!result)
816 m_table->use_all_columns();
817 return result;
818 }
819
820 /* Return the underlying TABLE handle. */
table() const821 TABLE* table() const { return m_table; }
822
get_access() const823 privilege_t get_access() const
824 {
825 ulonglong access_bits= 0, bit= 1;
826 for (uint i = start_priv_columns; i < end_priv_columns; i++, bit<<=1)
827 {
828 if (get_YN_as_bool(m_table->field[i]))
829 access_bits|= bit;
830 }
831 return ALL_KNOWN_ACL & access_bits;
832 }
833
834 protected:
835 friend class Grant_tables;
836
Grant_table_base()837 Grant_table_base() : min_columns(3), start_priv_columns(0), end_priv_columns(0), m_table(0)
838 { }
839
840 /* Compute how many privilege columns this table has. This method
841 can only be called after the table has been opened.
842
843 IMPLEMENTATION
844 A privilege column is of type enum('Y', 'N'). Privilege columns are
845 expected to be one after another.
846 */
set_table(TABLE * table)847 void set_table(TABLE *table)
848 {
849 if (!(m_table= table)) // Table does not exist or not opened.
850 return;
851
852 for (end_priv_columns= 0; end_priv_columns < num_fields(); end_priv_columns++)
853 {
854 Field *field= m_table->field[end_priv_columns];
855 if (field->real_type() == MYSQL_TYPE_ENUM &&
856 static_cast<Field_enum*>(field)->typelib->count == 2)
857 {
858 if (!start_priv_columns)
859 start_priv_columns= end_priv_columns;
860 }
861 else if (start_priv_columns)
862 break;
863 }
864 }
865
866
867 /* the min number of columns a table should have */
868 uint min_columns;
869 /* The index at which privilege columns start. */
870 uint start_priv_columns;
871 /* The index after the last privilege column */
872 uint end_priv_columns;
873
874 TABLE *m_table;
875 };
876
877 class User_table: public Grant_table_base
878 {
879 public:
init_read_record(READ_RECORD * info) const880 bool init_read_record(READ_RECORD* info) const
881 {
882 return Grant_table_base::init_read_record(info) || setup_sysvars();
883 }
884
885 virtual LEX_CSTRING& name() const = 0;
886 virtual int get_auth(THD *, MEM_ROOT *, ACL_USER *u) const= 0;
887 virtual bool set_auth(const ACL_USER &u) const = 0;
888 virtual privilege_t get_access() const = 0;
889 virtual void set_access(const privilege_t rights, bool revoke) const = 0;
890
get_host(MEM_ROOT * root) const891 char *get_host(MEM_ROOT *root) const
892 { return ::get_field(root, m_table->field[0]); }
set_host(const char * s,size_t l) const893 int set_host(const char *s, size_t l) const
894 { return m_table->field[0]->store(s, l, system_charset_info); };
get_user(MEM_ROOT * root) const895 char *get_user(MEM_ROOT *root) const
896 { return ::get_field(root, m_table->field[1]); }
set_user(const char * s,size_t l) const897 int set_user(const char *s, size_t l) const
898 { return m_table->field[1]->store(s, l, system_charset_info); };
899
900 virtual SSL_type get_ssl_type () const = 0;
901 virtual int set_ssl_type (SSL_type x) const = 0;
902 virtual const char* get_ssl_cipher (MEM_ROOT *root) const = 0;
903 virtual int set_ssl_cipher (const char *s, size_t l) const = 0;
904 virtual const char* get_x509_issuer (MEM_ROOT *root) const = 0;
905 virtual int set_x509_issuer (const char *s, size_t l) const = 0;
906 virtual const char* get_x509_subject (MEM_ROOT *root) const = 0;
907 virtual int set_x509_subject (const char *s, size_t l) const = 0;
908 virtual longlong get_max_questions () const = 0;
909 virtual int set_max_questions (longlong x) const = 0;
910 virtual longlong get_max_updates () const = 0;
911 virtual int set_max_updates (longlong x) const = 0;
912 virtual longlong get_max_connections () const = 0;
913 virtual int set_max_connections (longlong x) const = 0;
914 virtual longlong get_max_user_connections () const = 0;
915 virtual int set_max_user_connections (longlong x) const = 0;
916 virtual double get_max_statement_time () const = 0;
917 virtual int set_max_statement_time (double x) const = 0;
918 virtual bool get_is_role () const = 0;
919 virtual int set_is_role (bool x) const = 0;
920 virtual const char* get_default_role (MEM_ROOT *root) const = 0;
921 virtual int set_default_role (const char *s, size_t l) const = 0;
922 virtual bool get_account_locked () const = 0;
923 virtual int set_account_locked (bool x) const = 0;
924 virtual bool get_password_expired () const = 0;
925 virtual int set_password_expired (bool x) const = 0;
926 virtual my_time_t get_password_last_changed () const = 0;
927 virtual int set_password_last_changed (my_time_t x) const = 0;
928 virtual longlong get_password_lifetime () const = 0;
929 virtual int set_password_lifetime (longlong x) const = 0;
930
~User_table()931 virtual ~User_table() {}
932 private:
933 friend class Grant_tables;
934 virtual int setup_sysvars() const = 0;
935 };
936
937 /* MySQL-3.23 to MariaDB 10.3 `user` table */
938 class User_table_tabular: public User_table
939 {
940 public:
941
name() const942 LEX_CSTRING& name() const { return MYSQL_TABLE_NAME_USER; }
943
get_auth(THD * thd,MEM_ROOT * root,ACL_USER * u) const944 int get_auth(THD *thd, MEM_ROOT *root, ACL_USER *u) const
945 {
946 u->alloc_auth(root, 1);
947 if (have_password())
948 {
949 const char *as= safe_str(::get_field(&acl_memroot, password()));
950 u->auth->auth_string.str= as;
951 u->auth->auth_string.length= strlen(as);
952 u->auth->plugin= guess_auth_plugin(thd, u->auth->auth_string.length);
953 }
954 else
955 {
956 u->auth->plugin= native_password_plugin_name;
957 u->auth->auth_string= empty_clex_str;
958 }
959 if (plugin() && authstr())
960 {
961 char *tmpstr= ::get_field(&acl_memroot, plugin());
962 if (tmpstr)
963 {
964 const char *pw= u->auth->auth_string.str;
965 const char *as= safe_str(::get_field(&acl_memroot, authstr()));
966 if (*pw)
967 {
968 if (*as && strcmp(as, pw))
969 {
970 sql_print_warning("'user' entry '%s@%s' has both a password and an "
971 "authentication plugin specified. The password will be ignored.",
972 safe_str(get_user(thd->mem_root)), safe_str(get_host(thd->mem_root)));
973 }
974 else
975 as= pw;
976 }
977 u->auth->plugin.str= tmpstr;
978 u->auth->plugin.length= strlen(tmpstr);
979 u->auth->auth_string.str= as;
980 u->auth->auth_string.length= strlen(as);
981 }
982 }
983 return 0;
984 }
985
set_auth(const ACL_USER & u) const986 bool set_auth(const ACL_USER &u) const
987 {
988 if (u.nauth != 1)
989 return 1;
990 if (plugin())
991 {
992 if (have_password())
993 password()->reset();
994 plugin()->store(u.auth->plugin.str, u.auth->plugin.length, system_charset_info);
995 authstr()->store(u.auth->auth_string.str, u.auth->auth_string.length, system_charset_info);
996 }
997 else
998 {
999 if (u.auth->plugin.str != native_password_plugin_name.str &&
1000 u.auth->plugin.str != old_password_plugin_name.str)
1001 return 1;
1002 password()->store(u.auth->auth_string.str, u.auth->auth_string.length, system_charset_info);
1003 }
1004 return 0;
1005 }
1006
get_access() const1007 privilege_t get_access() const
1008 {
1009 privilege_t access(Grant_table_base::get_access());
1010 if ((num_fields() <= 13) && (access & CREATE_ACL))
1011 access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
1012
1013 if (num_fields() <= 18)
1014 {
1015 access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
1016 if (access & FILE_ACL)
1017 access|= BINLOG_MONITOR_ACL | REPL_SLAVE_ACL | BINLOG_ADMIN_ACL |
1018 BINLOG_REPLAY_ACL;
1019 if (access & PROCESS_ACL)
1020 access|= SUPER_ACL | EXECUTE_ACL;
1021 }
1022
1023 if (num_fields() <= 31 && (access & CREATE_ACL))
1024 access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
1025
1026 if (num_fields() <= 33)
1027 {
1028 if (access & CREATE_ACL)
1029 access|= CREATE_PROC_ACL;
1030 if (access & ALTER_ACL)
1031 access|= ALTER_PROC_ACL;
1032 }
1033
1034 if (num_fields() <= 36 && (access & GRANT_ACL))
1035 access|= CREATE_USER_ACL;
1036
1037 if (num_fields() <= 37 && (access & SUPER_ACL))
1038 access|= EVENT_ACL;
1039
1040 if (num_fields() <= 38 && (access & SUPER_ACL))
1041 access|= TRIGGER_ACL;
1042
1043 if (num_fields() <= 46 && (access & DELETE_ACL))
1044 access|= DELETE_HISTORY_ACL;
1045
1046 if (access & SUPER_ACL)
1047 access|= GLOBAL_SUPER_ADDED_SINCE_USER_TABLE_ACLS;
1048
1049 /*
1050 The SHOW SLAVE HOSTS statement :
1051 - required REPLICATION SLAVE privilege prior to 10.5.2
1052 - requires REPLICATION MASTER ADMIN privilege since 10.5.2
1053 There is no a way to GRANT MASTER ADMIN with User_table_tabular.
1054 So let's automatically add REPLICATION MASTER ADMIN for all users
1055 that had REPLICATION SLAVE. This will allow to do SHOW SLAVE HOSTS.
1056 */
1057 if (access & REPL_SLAVE_ACL)
1058 access|= REPL_MASTER_ADMIN_ACL;
1059
1060 if (access & REPL_SLAVE_ACL)
1061 access|= SLAVE_MONITOR_ACL;
1062
1063 return access & GLOBAL_ACLS;
1064 }
1065
set_access(const privilege_t rights,bool revoke) const1066 void set_access(const privilege_t rights, bool revoke) const
1067 {
1068 ulonglong priv(SELECT_ACL);
1069 for (uint i= start_priv_columns; i < end_priv_columns; i++, priv <<= 1)
1070 {
1071 if (priv & rights)
1072 m_table->field[i]->store(1 + !revoke, 0);
1073 }
1074 }
1075
get_ssl_type() const1076 SSL_type get_ssl_type () const
1077 {
1078 Field *f= get_field(end_priv_columns, MYSQL_TYPE_ENUM);
1079 return (SSL_type)(f ? f->val_int()-1 : 0);
1080 }
set_ssl_type(SSL_type x) const1081 int set_ssl_type (SSL_type x) const
1082 {
1083 if (Field *f= get_field(end_priv_columns, MYSQL_TYPE_ENUM))
1084 return f->store(x+1, 0);
1085 else
1086 return 1;
1087 }
get_ssl_cipher(MEM_ROOT * root) const1088 const char* get_ssl_cipher (MEM_ROOT *root) const
1089 {
1090 Field *f= get_field(end_priv_columns + 1, MYSQL_TYPE_BLOB);
1091 return f ? ::get_field(root,f) : 0;
1092 }
set_ssl_cipher(const char * s,size_t l) const1093 int set_ssl_cipher (const char *s, size_t l) const
1094 {
1095 if (Field *f= get_field(end_priv_columns + 1, MYSQL_TYPE_BLOB))
1096 return f->store(s, l, &my_charset_latin1);
1097 else
1098 return 1;
1099 }
get_x509_issuer(MEM_ROOT * root) const1100 const char* get_x509_issuer (MEM_ROOT *root) const
1101 {
1102 Field *f= get_field(end_priv_columns + 2, MYSQL_TYPE_BLOB);
1103 return f ? ::get_field(root,f) : 0;
1104 }
set_x509_issuer(const char * s,size_t l) const1105 int set_x509_issuer (const char *s, size_t l) const
1106 {
1107 if (Field *f= get_field(end_priv_columns + 2, MYSQL_TYPE_BLOB))
1108 return f->store(s, l, &my_charset_latin1);
1109 else
1110 return 1;
1111 }
get_x509_subject(MEM_ROOT * root) const1112 const char* get_x509_subject (MEM_ROOT *root) const
1113 {
1114 Field *f= get_field(end_priv_columns + 3, MYSQL_TYPE_BLOB);
1115 return f ? ::get_field(root,f) : 0;
1116 }
set_x509_subject(const char * s,size_t l) const1117 int set_x509_subject (const char *s, size_t l) const
1118 {
1119 if (Field *f= get_field(end_priv_columns + 3, MYSQL_TYPE_BLOB))
1120 return f->store(s, l, &my_charset_latin1);
1121 else
1122 return 1;
1123 }
get_max_questions() const1124 longlong get_max_questions () const
1125 {
1126 Field *f= get_field(end_priv_columns + 4, MYSQL_TYPE_LONG);
1127 return f ? f->val_int() : 0;
1128 }
set_max_questions(longlong x) const1129 int set_max_questions (longlong x) const
1130 {
1131 if (Field *f= get_field(end_priv_columns + 4, MYSQL_TYPE_LONG))
1132 return f->store(x, 0);
1133 else
1134 return 1;
1135 }
get_max_updates() const1136 longlong get_max_updates () const
1137 {
1138 Field *f= get_field(end_priv_columns + 5, MYSQL_TYPE_LONG);
1139 return f ? f->val_int() : 0;
1140 }
set_max_updates(longlong x) const1141 int set_max_updates (longlong x) const
1142 {
1143 if (Field *f= get_field(end_priv_columns + 5, MYSQL_TYPE_LONG))
1144 return f->store(x, 0);
1145 else
1146 return 1;
1147 }
get_max_connections() const1148 longlong get_max_connections () const
1149 {
1150 Field *f= get_field(end_priv_columns + 6, MYSQL_TYPE_LONG);
1151 return f ? f->val_int() : 0;
1152 }
set_max_connections(longlong x) const1153 int set_max_connections (longlong x) const
1154 {
1155 if (Field *f= get_field(end_priv_columns + 6, MYSQL_TYPE_LONG))
1156 return f->store(x, 0);
1157 else
1158 return 1;
1159 }
get_max_user_connections() const1160 longlong get_max_user_connections () const
1161 {
1162 Field *f= get_field(end_priv_columns + 7, MYSQL_TYPE_LONG);
1163 return f ? f->val_int() : 0;
1164 }
set_max_user_connections(longlong x) const1165 int set_max_user_connections (longlong x) const
1166 {
1167 if (Field *f= get_field(end_priv_columns + 7, MYSQL_TYPE_LONG))
1168 return f->store(x, 0);
1169 else
1170 return 1;
1171 }
get_max_statement_time() const1172 double get_max_statement_time () const
1173 {
1174 Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_NEWDECIMAL);
1175 return f ? f->val_real() : 0;
1176 }
set_max_statement_time(double x) const1177 int set_max_statement_time (double x) const
1178 {
1179 if (Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_NEWDECIMAL))
1180 return f->store(x);
1181 else
1182 return 1;
1183 }
get_is_role() const1184 bool get_is_role () const
1185 {
1186 Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_ENUM);
1187 return f ? f->val_int()-1 : 0;
1188 }
set_is_role(bool x) const1189 int set_is_role (bool x) const
1190 {
1191 if (Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_ENUM))
1192 return f->store(x+1, 0);
1193 else
1194 return 1;
1195 }
get_default_role(MEM_ROOT * root) const1196 const char* get_default_role (MEM_ROOT *root) const
1197 {
1198 Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_STRING);
1199 return f ? ::get_field(root,f) : 0;
1200 }
set_default_role(const char * s,size_t l) const1201 int set_default_role (const char *s, size_t l) const
1202 {
1203 if (Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_STRING))
1204 return f->store(s, l, system_charset_info);
1205 else
1206 return 1;
1207 }
1208 /* On a MariaDB 10.3 user table, the account locking accessors will try to
1209 get the content of the max_statement_time column, but they will fail due
1210 to the typecheck in get_field. */
get_account_locked() const1211 bool get_account_locked () const
1212 {
1213 Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_ENUM);
1214 return f ? f->val_int()-1 : 0;
1215 }
set_account_locked(bool x) const1216 int set_account_locked (bool x) const
1217 {
1218 if (Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_ENUM))
1219 return f->store(x+1, 0);
1220
1221 return 1;
1222 }
1223
get_password_expired() const1224 bool get_password_expired () const
1225 {
1226 uint field_num= end_priv_columns + 10;
1227
1228 Field *f= get_field(field_num, MYSQL_TYPE_ENUM);
1229 return f ? f->val_int()-1 : 0;
1230 }
set_password_expired(bool x) const1231 int set_password_expired (bool x) const
1232 {
1233 uint field_num= end_priv_columns + 10;
1234
1235 if (Field *f= get_field(field_num, MYSQL_TYPE_ENUM))
1236 return f->store(x+1, 0);
1237 return 1;
1238 }
get_password_last_changed() const1239 my_time_t get_password_last_changed () const
1240 {
1241 ulong unused_dec;
1242 if (Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_TIMESTAMP2))
1243 return f->get_timestamp(&unused_dec);
1244 return 0;
1245 }
set_password_last_changed(my_time_t x) const1246 int set_password_last_changed (my_time_t x) const
1247 {
1248 if (Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_TIMESTAMP2))
1249 {
1250 f->set_notnull();
1251 return f->store_timestamp(x, 0);
1252 }
1253 return 1;
1254 }
get_password_lifetime() const1255 longlong get_password_lifetime () const
1256 {
1257 if (Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_SHORT))
1258 {
1259 if (f->is_null())
1260 return -1;
1261 return f->val_int();
1262 }
1263 return 0;
1264 }
set_password_lifetime(longlong x) const1265 int set_password_lifetime (longlong x) const
1266 {
1267 if (Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_SHORT))
1268 {
1269 if (x < 0)
1270 {
1271 f->set_null();
1272 return 0;
1273 }
1274 f->set_notnull();
1275 return f->store(x, 0);
1276 }
1277 return 1;
1278 }
1279
~User_table_tabular()1280 virtual ~User_table_tabular() {}
1281 private:
1282 friend class Grant_tables;
1283
1284 /* Only Grant_tables can instantiate this class. */
User_table_tabular()1285 User_table_tabular() { min_columns= 13; /* As in 3.20.13 */ }
1286
1287 /* The user table is a bit different compared to the other Grant tables.
1288 Usually, we only add columns to the grant tables when adding functionality.
1289 This makes it easy to test which version of the table we are using, by
1290 just looking at the number of fields present in the table.
1291
1292 In MySQL 5.7.6 the Password column was removed. We need to guard for that.
1293 The field-fetching methods for the User table return NULL if the field
1294 doesn't exist. This simplifies checking of table "version", as we don't
1295 have to make use of num_fields() any more.
1296 */
get_field(uint field_num,enum enum_field_types type) const1297 inline Field* get_field(uint field_num, enum enum_field_types type) const
1298 {
1299 if (field_num >= num_fields())
1300 return NULL;
1301 Field *f= m_table->field[field_num];
1302 return f->real_type() == type ? f : NULL;
1303 }
1304
setup_sysvars() const1305 int setup_sysvars() const
1306 {
1307 username_char_length= MY_MIN(m_table->field[1]->char_length(),
1308 USERNAME_CHAR_LENGTH);
1309 using_global_priv_table= false;
1310
1311 if (have_password()) // Password column might be missing. (MySQL 5.7.6+)
1312 {
1313 int password_length= password()->field_length /
1314 password()->charset()->mbmaxlen;
1315 if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1316 {
1317 sql_print_error("Fatal error: mysql.user table is damaged or in "
1318 "unsupported 3.20 format.");
1319 return 1;
1320 }
1321
1322 mysql_mutex_lock(&LOCK_global_system_variables);
1323 if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
1324 {
1325 if (opt_secure_auth)
1326 {
1327 mysql_mutex_unlock(&LOCK_global_system_variables);
1328 sql_print_error("Fatal error: mysql.user table is in old format, "
1329 "but server started with --secure-auth option.");
1330 return 1;
1331 }
1332 mysql_user_table_is_in_short_password_format= true;
1333 if (global_system_variables.old_passwords)
1334 mysql_mutex_unlock(&LOCK_global_system_variables);
1335 else
1336 {
1337 extern sys_var *Sys_old_passwords_ptr;
1338 Sys_old_passwords_ptr->value_origin= sys_var::AUTO;
1339 global_system_variables.old_passwords= 1;
1340 mysql_mutex_unlock(&LOCK_global_system_variables);
1341 sql_print_warning("mysql.user table is not updated to new password format; "
1342 "Disabling new password usage until "
1343 "mysql_fix_privilege_tables is run");
1344 }
1345 m_table->in_use->variables.old_passwords= 1;
1346 }
1347 else
1348 {
1349 mysql_user_table_is_in_short_password_format= false;
1350 mysql_mutex_unlock(&LOCK_global_system_variables);
1351 }
1352 }
1353 return 0;
1354 }
1355
1356 /* Normally password column is the third column in the table. If privileges
1357 start on the third column instead, we are missing the password column.
1358 This means we are using a MySQL 5.7.6+ data directory. */
have_password() const1359 bool have_password() const { return start_priv_columns == 3; }
1360
password() const1361 Field* password() const { return m_table->field[2]; }
plugin() const1362 Field* plugin() const { return get_field(end_priv_columns + 8, MYSQL_TYPE_STRING); }
authstr() const1363 Field* authstr() const { return get_field(end_priv_columns + 9, MYSQL_TYPE_BLOB); }
1364 };
1365
1366 /*
1367 MariaDB 10.4 and up `global_priv` table
1368
1369 TODO possible optimizations:
1370 * update json in-place if the new value can fit
1371 * don't repeat get_value for every key, but use a streaming parser
1372 to convert json into in-memory object (ACL_USER?) in one json scan.
1373 - this makes sense for acl_load(), but hardly for GRANT
1374 * similarly, pack ACL_USER (?) into json in one go.
1375 - doesn't make sense? GRANT rarely updates more than one field.
1376 */
1377 class User_table_json: public User_table
1378 {
name() const1379 LEX_CSTRING& name() const { return MYSQL_TABLE_NAME[USER_TABLE]; }
1380
get_auth(THD * thd,MEM_ROOT * root,ACL_USER * u) const1381 int get_auth(THD *thd, MEM_ROOT *root, ACL_USER *u) const
1382 {
1383 size_t array_len;
1384 const char *array;
1385 int vl;
1386 const char *v;
1387
1388 if (get_value("auth_or", JSV_ARRAY, &array, &array_len))
1389 {
1390 u->alloc_auth(root, 1);
1391 return get_auth1(thd, root, u, 0);
1392 }
1393
1394 if (json_get_array_item(array, array + array_len, (int)array_len,
1395 &v, &vl) != JSV_NOTHING)
1396 return 1;
1397 u->alloc_auth(root, vl);
1398 for (uint i=0; i < u->nauth; i++)
1399 {
1400 if (json_get_array_item(array, array + array_len, i, &v, &vl) != JSV_OBJECT)
1401 return 1;
1402
1403 const char *p, *a;
1404 int pl, al;
1405 switch (json_get_object_key(v, v + vl, "plugin", &p, &pl)) {
1406 case JSV_STRING: u->auth[i].plugin.str= strmake_root(root, p, pl);
1407 u->auth[i].plugin.length= pl;
1408 break;
1409 case JSV_NOTHING: if (get_auth1(thd, root, u, i))
1410 return 1;
1411 else
1412 continue;
1413 default: return 1;
1414 }
1415 switch (json_get_object_key(v, v + vl, "authentication_string", &a, &al)) {
1416 case JSV_NOTHING: u->auth[i].auth_string= empty_clex_str;
1417 break;
1418 case JSV_STRING: u->auth[i].auth_string.str= strmake_root(root, a, al);
1419 u->auth[i].auth_string.length= al;
1420 break;
1421 default: return 1;
1422 }
1423 }
1424 return 0;
1425 }
1426
get_auth1(THD * thd,MEM_ROOT * root,ACL_USER * u,uint n) const1427 int get_auth1(THD *thd, MEM_ROOT *root, ACL_USER *u, uint n) const
1428 {
1429 const char *authstr= get_str_value(root, "authentication_string");
1430 const char *plugin= get_str_value(root, "plugin");
1431 if (plugin && authstr)
1432 {
1433 if (plugin && *plugin)
1434 {
1435 u->auth[n].plugin.str= plugin;
1436 u->auth[n].plugin.length= strlen(plugin);
1437 }
1438 else
1439 u->auth[n].plugin= native_password_plugin_name;
1440 u->auth[n].auth_string.str= authstr;
1441 u->auth[n].auth_string.length= strlen(authstr);
1442 return 0;
1443 }
1444 return 1;
1445 }
1446
append_str_value(String * to,const LEX_CSTRING & str) const1447 bool append_str_value(String *to, const LEX_CSTRING &str) const
1448 {
1449 to->append('"');
1450 to->reserve(str.length*2);
1451 int len= json_escape(system_charset_info, (uchar*)str.str, (uchar*)str.str + str.length,
1452 to->charset(), (uchar*)to->end(), (uchar*)to->end() + str.length*2);
1453 if (len < 0)
1454 return 1;
1455 to->length(to->length() + len);
1456 to->append('"');
1457 return 0;
1458 }
1459
set_auth(const ACL_USER & u) const1460 bool set_auth(const ACL_USER &u) const
1461 {
1462 size_t array_len;
1463 const char *array;
1464 if (u.nauth == 1 && get_value("auth_or", JSV_ARRAY, &array, &array_len))
1465 return set_auth1(u, 0);
1466
1467 StringBuffer<JSON_SIZE> json(m_table->field[2]->charset());
1468 bool top_done = false;
1469 json.append('[');
1470 for (uint i=0; i < u.nauth; i++)
1471 {
1472 ACL_USER::AUTH * const auth= u.auth + i;
1473 if (i)
1474 json.append(',');
1475 json.append('{');
1476 if (!top_done &&
1477 (auth->plugin.str == native_password_plugin_name.str ||
1478 auth->plugin.str == old_password_plugin_name.str ||
1479 i == u.nauth - 1))
1480 {
1481 if (set_auth1(u, i))
1482 return 1;
1483 top_done= true;
1484 }
1485 else
1486 {
1487 json.append(STRING_WITH_LEN("\"plugin\":"));
1488 if (append_str_value(&json, auth->plugin))
1489 return 1;
1490 if (auth->auth_string.length)
1491 {
1492 json.append(STRING_WITH_LEN(",\"authentication_string\":"));
1493 if (append_str_value(&json, auth->auth_string))
1494 return 1;
1495 }
1496 }
1497 json.append('}');
1498 }
1499 json.append(']');
1500 return set_value("auth_or", json.ptr(), json.length(), false) == JSV_BAD_JSON;
1501 }
set_auth1(const ACL_USER & u,uint i) const1502 bool set_auth1(const ACL_USER &u, uint i) const
1503 {
1504 return set_str_value("plugin",
1505 u.auth[i].plugin.str, u.auth[i].plugin.length) ||
1506 set_str_value("authentication_string",
1507 u.auth[i].auth_string.str, u.auth[i].auth_string.length);
1508 }
1509
print_warning_bad_version_id(ulonglong version_id) const1510 void print_warning_bad_version_id(ulonglong version_id) const
1511 {
1512 sql_print_warning("'user' entry '%s@%s' has a wrong 'version_id' value %lld",
1513 safe_str(get_user(current_thd->mem_root)),
1514 safe_str(get_host(current_thd->mem_root)),
1515 version_id);
1516 }
1517
print_warning_bad_access(ulonglong version_id,privilege_t mask,ulonglong access) const1518 void print_warning_bad_access(ulonglong version_id,
1519 privilege_t mask,
1520 ulonglong access) const
1521 {
1522 sql_print_warning("'user' entry '%s@%s' "
1523 "has a wrong 'access' value 0x%llx "
1524 "(allowed mask is 0x%llx, version_id=%lld)",
1525 safe_str(get_user(current_thd->mem_root)),
1526 safe_str(get_host(current_thd->mem_root)),
1527 access, mask, version_id);
1528 }
1529
adjust_access(ulonglong version_id,ulonglong access) const1530 privilege_t adjust_access(ulonglong version_id, ulonglong access) const
1531 {
1532 privilege_t mask= ALL_KNOWN_ACL_100304;
1533 ulonglong orig_access= access;
1534 if (version_id >= 100509)
1535 {
1536 mask= ALL_KNOWN_ACL_100509;
1537 }
1538 else if (version_id >= 100502)
1539 {
1540 if (version_id >= 100508)
1541 mask= ALL_KNOWN_ACL_100508;
1542 else
1543 mask= ALL_KNOWN_ACL_100502;
1544 if (access & REPL_SLAVE_ADMIN_ACL)
1545 access|= SLAVE_MONITOR_ACL;
1546 }
1547 else // 100501 or earlier
1548 {
1549 /*
1550 Address changes in SUPER and REPLICATION SLAVE made in 10.5.2.
1551 This also covers a special case: if the user had ALL PRIVILEGES before
1552 the upgrade, it gets ALL PRIVILEGES after the upgrade.
1553 */
1554 if (access & SUPER_ACL)
1555 {
1556 if (access & REPL_SLAVE_ACL)
1557 {
1558 /*
1559 The user could do both before the upgrade:
1560 - set global variables (because of SUPER_ACL)
1561 - execute "SHOW SLAVE HOSTS" (because of REPL_SLAVE_ACL)
1562 Grant all new privileges that were splitted from SUPER (in 10.5.2),
1563 and REPLICATION MASTER ADMIN, so it still can do "SHOW SLAVE HOSTS".
1564 */
1565 access|= REPL_MASTER_ADMIN_ACL;
1566 }
1567 access|= GLOBAL_SUPER_ADDED_SINCE_USER_TABLE_ACLS;
1568 }
1569 /*
1570 REPLICATION_CLIENT(BINLOG_MONITOR_ACL) should allow SHOW SLAVE STATUS
1571 REPLICATION SLAVE should allow SHOW RELAYLOG EVENTS
1572 */
1573 if (access & BINLOG_MONITOR_ACL || access & REPL_SLAVE_ACL)
1574 access|= SLAVE_MONITOR_ACL;
1575 }
1576
1577 if (orig_access & ~mask)
1578 {
1579 print_warning_bad_access(version_id, mask, orig_access);
1580 return NO_ACL;
1581 }
1582 return access & ALL_KNOWN_ACL;
1583 }
1584
get_access() const1585 privilege_t get_access() const
1586 {
1587 ulonglong version_id= (ulonglong) get_int_value("version_id");
1588 ulonglong access= (ulonglong) get_int_value("access");
1589
1590 /*
1591 Special case:
1592 mysql_system_tables_data.sql populates "ALL PRIVILEGES"
1593 for the super user this way:
1594 {"access":18446744073709551615}
1595 */
1596 if (access == (ulonglong) ~0)
1597 return GLOBAL_ACLS;
1598
1599 /*
1600 Reject obviously bad (negative and too large) version_id values.
1601 Also reject versions before 10.4.0 (when JSON table was added).
1602 */
1603 if ((longlong) version_id < 0 || version_id > 999999 ||
1604 (version_id > 0 && version_id < 100400))
1605 {
1606 print_warning_bad_version_id(version_id);
1607 return NO_ACL;
1608 }
1609 return adjust_access(version_id, access) & GLOBAL_ACLS;
1610 }
1611
set_access(const privilege_t rights,bool revoke) const1612 void set_access(const privilege_t rights, bool revoke) const
1613 {
1614 privilege_t access= get_access();
1615 if (revoke)
1616 access&= ~rights;
1617 else
1618 access|= rights;
1619 set_int_value("access", (longlong) (access & GLOBAL_ACLS));
1620 set_int_value("version_id", (longlong) MYSQL_VERSION_ID);
1621 }
unsafe_str(const char * s) const1622 const char *unsafe_str(const char *s) const
1623 { return s[0] ? s : NULL; }
1624
get_ssl_type() const1625 SSL_type get_ssl_type () const
1626 { return (SSL_type)get_int_value("ssl_type"); }
set_ssl_type(SSL_type x) const1627 int set_ssl_type (SSL_type x) const
1628 { return set_int_value("ssl_type", x); }
get_ssl_cipher(MEM_ROOT * root) const1629 const char* get_ssl_cipher (MEM_ROOT *root) const
1630 { return unsafe_str(get_str_value(root, "ssl_cipher")); }
set_ssl_cipher(const char * s,size_t l) const1631 int set_ssl_cipher (const char *s, size_t l) const
1632 { return set_str_value("ssl_cipher", s, l); }
get_x509_issuer(MEM_ROOT * root) const1633 const char* get_x509_issuer (MEM_ROOT *root) const
1634 { return unsafe_str(get_str_value(root, "x509_issuer")); }
set_x509_issuer(const char * s,size_t l) const1635 int set_x509_issuer (const char *s, size_t l) const
1636 { return set_str_value("x509_issuer", s, l); }
get_x509_subject(MEM_ROOT * root) const1637 const char* get_x509_subject (MEM_ROOT *root) const
1638 { return unsafe_str(get_str_value(root, "x509_subject")); }
set_x509_subject(const char * s,size_t l) const1639 int set_x509_subject (const char *s, size_t l) const
1640 { return set_str_value("x509_subject", s, l); }
get_max_questions() const1641 longlong get_max_questions () const
1642 { return get_int_value("max_questions"); }
set_max_questions(longlong x) const1643 int set_max_questions (longlong x) const
1644 { return set_int_value("max_questions", x); }
get_max_updates() const1645 longlong get_max_updates () const
1646 { return get_int_value("max_updates"); }
set_max_updates(longlong x) const1647 int set_max_updates (longlong x) const
1648 { return set_int_value("max_updates", x); }
get_max_connections() const1649 longlong get_max_connections () const
1650 { return get_int_value("max_connections"); }
set_max_connections(longlong x) const1651 int set_max_connections (longlong x) const
1652 { return set_int_value("max_connections", x); }
get_max_user_connections() const1653 longlong get_max_user_connections () const
1654 { return get_int_value("max_user_connections"); }
set_max_user_connections(longlong x) const1655 int set_max_user_connections (longlong x) const
1656 { return set_int_value("max_user_connections", x); }
get_max_statement_time() const1657 double get_max_statement_time () const
1658 { return get_double_value("max_statement_time"); }
set_max_statement_time(double x) const1659 int set_max_statement_time (double x) const
1660 { return set_double_value("max_statement_time", x); }
get_is_role() const1661 bool get_is_role () const
1662 { return get_bool_value("is_role"); }
set_is_role(bool x) const1663 int set_is_role (bool x) const
1664 { return set_bool_value("is_role", x); }
get_default_role(MEM_ROOT * root) const1665 const char* get_default_role (MEM_ROOT *root) const
1666 { return get_str_value(root, "default_role"); }
set_default_role(const char * s,size_t l) const1667 int set_default_role (const char *s, size_t l) const
1668 { return set_str_value("default_role", s, l); }
get_account_locked() const1669 bool get_account_locked () const
1670 { return get_bool_value("account_locked"); }
set_account_locked(bool x) const1671 int set_account_locked (bool x) const
1672 { return set_bool_value("account_locked", x); }
get_password_last_changed() const1673 my_time_t get_password_last_changed () const
1674 { return static_cast<my_time_t>(get_int_value("password_last_changed")); }
set_password_last_changed(my_time_t x) const1675 int set_password_last_changed (my_time_t x) const
1676 { return set_int_value("password_last_changed", static_cast<longlong>(x)); }
set_password_lifetime(longlong x) const1677 int set_password_lifetime (longlong x) const
1678 { return set_int_value("password_lifetime", x); }
get_password_lifetime() const1679 longlong get_password_lifetime () const
1680 { return get_int_value("password_lifetime", -1); }
1681 /*
1682 password_last_changed=0 means the password is manually expired.
1683 In MySQL 5.7+ this state is described using the password_expired column
1684 in mysql.user
1685 */
get_password_expired() const1686 bool get_password_expired () const
1687 { return get_int_value("password_last_changed", -1) == 0; }
set_password_expired(bool x) const1688 int set_password_expired (bool x) const
1689 { return x ? set_password_last_changed(0) : 0; }
1690
~User_table_json()1691 ~User_table_json() {}
1692 private:
1693 friend class Grant_tables;
1694 static const uint JSON_SIZE=1024;
setup_sysvars() const1695 int setup_sysvars() const
1696 {
1697 using_global_priv_table= true;
1698 username_char_length= MY_MIN(m_table->field[1]->char_length(),
1699 USERNAME_CHAR_LENGTH);
1700 return 0;
1701 }
get_value(const char * key,enum json_types vt,const char ** v,size_t * vl) const1702 bool get_value(const char *key,
1703 enum json_types vt, const char **v, size_t *vl) const
1704 {
1705 enum json_types value_type;
1706 int int_vl;
1707 String str, *res= m_table->field[2]->val_str(&str);
1708 if (!res ||
1709 (value_type= json_get_object_key(res->ptr(), res->end(), key,
1710 v, &int_vl)) == JSV_BAD_JSON)
1711 return 1; // invalid
1712 *vl= int_vl;
1713 return value_type != vt;
1714 }
get_str_value(MEM_ROOT * root,const char * key) const1715 const char *get_str_value(MEM_ROOT *root, const char *key) const
1716 {
1717 size_t value_len;
1718 const char *value_start;
1719 if (get_value(key, JSV_STRING, &value_start, &value_len))
1720 return "";
1721 char *ptr= (char*)alloca(value_len);
1722 int len= json_unescape(m_table->field[2]->charset(),
1723 (const uchar*)value_start,
1724 (const uchar*)value_start + value_len,
1725 system_charset_info,
1726 (uchar*)ptr, (uchar*)ptr + value_len);
1727 if (len < 0)
1728 return NULL;
1729 return strmake_root(root, ptr, len);
1730 }
get_int_value(const char * key,longlong def_val=0) const1731 longlong get_int_value(const char *key, longlong def_val= 0) const
1732 {
1733 int err;
1734 size_t value_len;
1735 const char *value_start;
1736 if (get_value(key, JSV_NUMBER, &value_start, &value_len))
1737 return def_val;
1738 const char *value_end= value_start + value_len;
1739 return my_strtoll10(value_start, (char**)&value_end, &err);
1740 }
get_double_value(const char * key) const1741 double get_double_value(const char *key) const
1742 {
1743 int err;
1744 size_t value_len;
1745 const char *value_start;
1746 if (get_value(key, JSV_NUMBER, &value_start, &value_len))
1747 return 0;
1748 const char *value_end= value_start + value_len;
1749 return my_strtod(value_start, (char**)&value_end, &err);
1750 }
get_bool_value(const char * key) const1751 bool get_bool_value(const char *key) const
1752 {
1753 size_t value_len;
1754 const char *value_start;
1755 if (get_value(key, JSV_TRUE, &value_start, &value_len))
1756 return false;
1757 return true;
1758 }
set_value(const char * key,const char * val,size_t vlen,bool string) const1759 enum json_types set_value(const char *key,
1760 const char *val, size_t vlen, bool string) const
1761 {
1762 int value_len;
1763 const char *value_start;
1764 enum json_types value_type;
1765 String str, *res= m_table->field[2]->val_str(&str);
1766 if (!res || !res->length())
1767 (res= &str)->set(STRING_WITH_LEN("{}"), m_table->field[2]->charset());
1768 value_type= json_get_object_key(res->ptr(), res->end(), key,
1769 &value_start, &value_len);
1770 if (value_type == JSV_BAD_JSON)
1771 return value_type; // invalid
1772 StringBuffer<JSON_SIZE> json(res->charset());
1773 json.copy(res->ptr(), value_start - res->ptr(), res->charset());
1774 if (value_type == JSV_NOTHING)
1775 {
1776 if (value_len)
1777 json.append(',');
1778 json.append('"');
1779 json.append(key);
1780 json.append(STRING_WITH_LEN("\":"));
1781 if (string)
1782 json.append('"');
1783 }
1784 else
1785 value_start+= value_len;
1786 json.append(val, vlen);
1787 if (!value_type && string)
1788 json.append('"');
1789 json.append(value_start, res->end() - value_start);
1790 DBUG_ASSERT(json_valid(json.ptr(), json.length(), json.charset()));
1791 m_table->field[2]->store(json.ptr(), json.length(), json.charset());
1792 return value_type;
1793 }
set_str_value(const char * key,const char * val,size_t vlen) const1794 bool set_str_value(const char *key, const char *val, size_t vlen) const
1795 {
1796 char buf[JSON_SIZE];
1797 int blen= json_escape(system_charset_info,
1798 (const uchar*)val, (const uchar*)val + vlen,
1799 m_table->field[2]->charset(),
1800 (uchar*)buf, (uchar*)buf+sizeof(buf));
1801 if (blen < 0)
1802 return 1;
1803 return set_value(key, buf, blen, true) == JSV_BAD_JSON;
1804 }
set_int_value(const char * key,longlong val) const1805 bool set_int_value(const char *key, longlong val) const
1806 {
1807 char v[MY_INT64_NUM_DECIMAL_DIGITS+1];
1808 size_t vlen= longlong10_to_str(val, v, -10) - v;
1809 return set_value(key, v, vlen, false) == JSV_BAD_JSON;
1810 }
set_double_value(const char * key,double val) const1811 bool set_double_value(const char *key, double val) const
1812 {
1813 char v[FLOATING_POINT_BUFFER+1];
1814 size_t vlen= my_fcvt(val, TIME_SECOND_PART_DIGITS, v, NULL);
1815 return set_value(key, v, vlen, false) == JSV_BAD_JSON;
1816 }
set_bool_value(const char * key,bool val) const1817 bool set_bool_value(const char *key, bool val) const
1818 {
1819 return set_value(key, val ? "true" : "false", val ? 4 : 5, false) == JSV_BAD_JSON;
1820 }
1821 };
1822
1823 class Db_table: public Grant_table_base
1824 {
1825 public:
host() const1826 Field* host() const { return m_table->field[0]; }
db() const1827 Field* db() const { return m_table->field[1]; }
user() const1828 Field* user() const { return m_table->field[2]; }
1829
1830 private:
1831 friend class Grant_tables;
1832
Db_table()1833 Db_table() { min_columns= 9; /* as in 3.20.13 */ }
1834 };
1835
1836 class Tables_priv_table: public Grant_table_base
1837 {
1838 public:
host() const1839 Field* host() const { return m_table->field[0]; }
db() const1840 Field* db() const { return m_table->field[1]; }
user() const1841 Field* user() const { return m_table->field[2]; }
table_name() const1842 Field* table_name() const { return m_table->field[3]; }
grantor() const1843 Field* grantor() const { return m_table->field[4]; }
timestamp() const1844 Field* timestamp() const { return m_table->field[5]; }
table_priv() const1845 Field* table_priv() const { return m_table->field[6]; }
column_priv() const1846 Field* column_priv() const { return m_table->field[7]; }
1847
1848 private:
1849 friend class Grant_tables;
1850
Tables_priv_table()1851 Tables_priv_table() { min_columns= 8; /* as in 3.22.26a */ }
1852 };
1853
1854 class Columns_priv_table: public Grant_table_base
1855 {
1856 public:
host() const1857 Field* host() const { return m_table->field[0]; }
db() const1858 Field* db() const { return m_table->field[1]; }
user() const1859 Field* user() const { return m_table->field[2]; }
table_name() const1860 Field* table_name() const { return m_table->field[3]; }
column_name() const1861 Field* column_name() const { return m_table->field[4]; }
timestamp() const1862 Field* timestamp() const { return m_table->field[5]; }
column_priv() const1863 Field* column_priv() const { return m_table->field[6]; }
1864
1865 private:
1866 friend class Grant_tables;
1867
Columns_priv_table()1868 Columns_priv_table() { min_columns= 7; /* as in 3.22.26a */ }
1869 };
1870
1871 class Host_table: public Grant_table_base
1872 {
1873 public:
host() const1874 Field* host() const { return m_table->field[0]; }
db() const1875 Field* db() const { return m_table->field[1]; }
1876
1877 private:
1878 friend class Grant_tables;
1879
Host_table()1880 Host_table() { min_columns= 8; /* as in 3.20.13 */ }
1881 };
1882
1883 class Procs_priv_table: public Grant_table_base
1884 {
1885 public:
host() const1886 Field* host() const { return m_table->field[0]; }
db() const1887 Field* db() const { return m_table->field[1]; }
user() const1888 Field* user() const { return m_table->field[2]; }
routine_name() const1889 Field* routine_name() const { return m_table->field[3]; }
routine_type() const1890 Field* routine_type() const { return m_table->field[4]; }
grantor() const1891 Field* grantor() const { return m_table->field[5]; }
proc_priv() const1892 Field* proc_priv() const { return m_table->field[6]; }
timestamp() const1893 Field* timestamp() const { return m_table->field[7]; }
1894
1895 private:
1896 friend class Grant_tables;
1897
Procs_priv_table()1898 Procs_priv_table() { min_columns=8; }
1899 };
1900
1901 class Proxies_priv_table: public Grant_table_base
1902 {
1903 public:
host() const1904 Field* host() const { return m_table->field[0]; }
user() const1905 Field* user() const { return m_table->field[1]; }
proxied_host() const1906 Field* proxied_host() const { return m_table->field[2]; }
proxied_user() const1907 Field* proxied_user() const { return m_table->field[3]; }
with_grant() const1908 Field* with_grant() const { return m_table->field[4]; }
grantor() const1909 Field* grantor() const { return m_table->field[5]; }
timestamp() const1910 Field* timestamp() const { return m_table->field[6]; }
1911
1912 private:
1913 friend class Grant_tables;
1914
Proxies_priv_table()1915 Proxies_priv_table() { min_columns= 7; }
1916 };
1917
1918 class Roles_mapping_table: public Grant_table_base
1919 {
1920 public:
host() const1921 Field* host() const { return m_table->field[0]; }
user() const1922 Field* user() const { return m_table->field[1]; }
role() const1923 Field* role() const { return m_table->field[2]; }
admin_option() const1924 Field* admin_option() const { return m_table->field[3]; }
1925
1926 private:
1927 friend class Grant_tables;
1928
Roles_mapping_table()1929 Roles_mapping_table() { min_columns= 4; }
1930 };
1931
1932 /**
1933 Class that represents a collection of grant tables.
1934 */
1935 class Grant_tables
1936 {
1937 public:
Grant_tables()1938 Grant_tables() : p_user_table(&m_user_table_json) { }
1939
open_and_lock(THD * thd,int which_tables,enum thr_lock_type lock_type)1940 int open_and_lock(THD *thd, int which_tables, enum thr_lock_type lock_type)
1941 {
1942 DBUG_ENTER("Grant_tables::open_and_lock");
1943 TABLE_LIST tables[USER_TABLE+1], *first= NULL;
1944
1945 DBUG_ASSERT(which_tables); /* At least one table must be opened. */
1946 /*
1947 We can read privilege tables even when !initialized.
1948 This can be acl_load() - server startup or FLUSH PRIVILEGES
1949 */
1950 if (lock_type >= TL_WRITE_ALLOW_WRITE && !initialized)
1951 {
1952 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1953 DBUG_RETURN(-1);
1954 }
1955
1956 for (int i=USER_TABLE; i >=0; i--)
1957 {
1958 TABLE_LIST *tl= tables + i;
1959 if (which_tables & (1 << i))
1960 {
1961 tl->init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLE_NAME[i],
1962 NULL, lock_type);
1963 tl->open_type= OT_BASE_ONLY;
1964 tl->i_s_requested_object= OPEN_TABLE_ONLY;
1965 tl->updating= lock_type >= TL_WRITE_ALLOW_WRITE;
1966 if (i >= FIRST_OPTIONAL_TABLE)
1967 tl->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
1968 tl->next_global= tl->next_local= first;
1969 first= tl;
1970 }
1971 else
1972 tl->table= NULL;
1973 }
1974
1975 uint counter;
1976 int res= really_open(thd, first, &counter);
1977
1978 /* if User_table_json wasn't found, let's try User_table_tabular */
1979 if (!res && (which_tables & Table_user) && !tables[USER_TABLE].table)
1980 {
1981 uint unused;
1982 TABLE_LIST *tl= tables + USER_TABLE;
1983 TABLE *backup_open_tables= thd->open_tables;
1984 thd->set_open_tables(NULL);
1985
1986 tl->init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLE_NAME_USER,
1987 NULL, lock_type);
1988 tl->open_type= OT_BASE_ONLY;
1989 tl->i_s_requested_object= OPEN_TABLE_ONLY;
1990 tl->updating= lock_type >= TL_WRITE_ALLOW_WRITE;
1991 p_user_table= &m_user_table_tabular;
1992 counter++;
1993 res= really_open(thd, tl, &unused);
1994 thd->set_open_tables(backup_open_tables);
1995 if (tables[USER_TABLE].table)
1996 {
1997 tables[USER_TABLE].table->next= backup_open_tables;
1998 thd->set_open_tables(tables[USER_TABLE].table);
1999 }
2000 }
2001 if (res)
2002 DBUG_RETURN(res);
2003
2004 if (lock_tables(thd, first, counter,
2005 MYSQL_LOCK_IGNORE_TIMEOUT |
2006 MYSQL_OPEN_IGNORE_LOGGING_FORMAT))
2007 DBUG_RETURN(-1);
2008
2009 p_user_table->set_table(tables[USER_TABLE].table);
2010 m_db_table.set_table(tables[DB_TABLE].table);
2011 m_tables_priv_table.set_table(tables[TABLES_PRIV_TABLE].table);
2012 m_columns_priv_table.set_table(tables[COLUMNS_PRIV_TABLE].table);
2013 m_host_table.set_table(tables[HOST_TABLE].table);
2014 m_procs_priv_table.set_table(tables[PROCS_PRIV_TABLE].table);
2015 m_proxies_priv_table.set_table(tables[PROXIES_PRIV_TABLE].table);
2016 m_roles_mapping_table.set_table(tables[ROLES_MAPPING_TABLE].table);
2017 DBUG_RETURN(0);
2018 }
2019
user_table() const2020 inline const User_table& user_table() const
2021 { return *p_user_table; }
2022
db_table() const2023 inline const Db_table& db_table() const
2024 { return m_db_table; }
2025
tables_priv_table() const2026 inline const Tables_priv_table& tables_priv_table() const
2027 { return m_tables_priv_table; }
2028
columns_priv_table() const2029 inline const Columns_priv_table& columns_priv_table() const
2030 { return m_columns_priv_table; }
2031
host_table() const2032 inline const Host_table& host_table() const
2033 { return m_host_table; }
2034
procs_priv_table() const2035 inline const Procs_priv_table& procs_priv_table() const
2036 { return m_procs_priv_table; }
2037
proxies_priv_table() const2038 inline const Proxies_priv_table& proxies_priv_table() const
2039 { return m_proxies_priv_table; }
2040
roles_mapping_table() const2041 inline const Roles_mapping_table& roles_mapping_table() const
2042 { return m_roles_mapping_table; }
2043
2044 private:
2045
2046 /* Before any operation is possible on grant tables, they must be opened.
2047
2048 @retval 1 replication filters matched. Abort the operation,
2049 but return OK (!)
2050 @retval 0 tables were opened successfully
2051 @retval -1 error, tables could not be opened
2052 */
really_open(THD * thd,TABLE_LIST * tables,uint * counter)2053 int really_open(THD *thd, TABLE_LIST* tables, uint *counter)
2054 {
2055 DBUG_ENTER("Grant_tables::really_open:");
2056 #ifdef HAVE_REPLICATION
2057 if (tables->lock_type >= TL_WRITE_ALLOW_WRITE &&
2058 thd->slave_thread && !thd->spcont)
2059 {
2060 /*
2061 GRANT and REVOKE are applied the slave in/exclusion rules as they are
2062 some kind of updates to the mysql.% tables.
2063 */
2064 Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
2065 if (rpl_filter->is_on() && !rpl_filter->tables_ok(0, tables))
2066 DBUG_RETURN(1);
2067 }
2068 #endif
2069 if (open_tables(thd, &tables, counter, MYSQL_LOCK_IGNORE_TIMEOUT))
2070 DBUG_RETURN(-1);
2071 DBUG_RETURN(0);
2072 }
2073
2074 User_table *p_user_table;
2075 User_table_json m_user_table_json;
2076 User_table_tabular m_user_table_tabular;
2077 Db_table m_db_table;
2078 Tables_priv_table m_tables_priv_table;
2079 Columns_priv_table m_columns_priv_table;
2080 Host_table m_host_table;
2081 Procs_priv_table m_procs_priv_table;
2082 Proxies_priv_table m_proxies_priv_table;
2083 Roles_mapping_table m_roles_mapping_table;
2084 };
2085
2086
init(const Proxies_priv_table & proxies_priv_table,MEM_ROOT * mem)2087 void ACL_PROXY_USER::init(const Proxies_priv_table& proxies_priv_table,
2088 MEM_ROOT *mem)
2089 {
2090 init(get_field(mem, proxies_priv_table.host()),
2091 safe_str(get_field(mem, proxies_priv_table.user())),
2092 get_field(mem, proxies_priv_table.proxied_host()),
2093 safe_str(get_field(mem, proxies_priv_table.proxied_user())),
2094 proxies_priv_table.with_grant()->val_int() != 0);
2095 }
2096
2097
2098 /*
2099 Enumeration of various ACL's and Hashes used in handle_grant_struct()
2100 */
2101 enum enum_acl_lists
2102 {
2103 USER_ACL= 0,
2104 ROLE_ACL,
2105 DB_ACL,
2106 COLUMN_PRIVILEGES_HASH,
2107 PROC_PRIVILEGES_HASH,
2108 FUNC_PRIVILEGES_HASH,
2109 PACKAGE_SPEC_PRIVILEGES_HASH,
2110 PACKAGE_BODY_PRIVILEGES_HASH,
2111 PROXY_USERS_ACL,
2112 ROLES_MAPPINGS_HASH
2113 };
2114
ACL_ROLE(ACL_USER * user,MEM_ROOT * root)2115 ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root)
2116 :
2117 /* set initial role access the same as the table row privileges */
2118 initial_role_access(user->access),
2119 counter(0)
2120 {
2121 access= user->access;
2122 this->user= user->user;
2123 bzero(&parent_grantee, sizeof(parent_grantee));
2124 flags= IS_ROLE;
2125 }
2126
ACL_ROLE(const char * rolename,privilege_t privileges,MEM_ROOT * root)2127 ACL_ROLE::ACL_ROLE(const char * rolename, privilege_t privileges,
2128 MEM_ROOT *root) :
2129 initial_role_access(privileges), counter(0)
2130 {
2131 this->access= initial_role_access;
2132 this->user.str= safe_strdup_root(root, rolename);
2133 this->user.length= strlen(rolename);
2134 bzero(&parent_grantee, sizeof(parent_grantee));
2135 flags= IS_ROLE;
2136 }
2137
2138
is_invalid_role_name(const char * str)2139 static bool is_invalid_role_name(const char *str)
2140 {
2141 if (*str && strcasecmp(str, "PUBLIC") && strcasecmp(str, "NONE"))
2142 return false;
2143
2144 my_error(ER_INVALID_ROLE, MYF(0), str);
2145 return true;
2146 }
2147
2148
free_acl_user(ACL_USER * user)2149 static void free_acl_user(ACL_USER *user)
2150 {
2151 delete_dynamic(&(user->role_grants));
2152 }
2153
free_acl_role(ACL_ROLE * role)2154 static void free_acl_role(ACL_ROLE *role)
2155 {
2156 delete_dynamic(&(role->role_grants));
2157 delete_dynamic(&(role->parent_grantee));
2158 }
2159
check_if_exists(THD *,plugin_ref,void *)2160 static my_bool check_if_exists(THD *, plugin_ref, void *)
2161 {
2162 return TRUE;
2163 }
2164
has_validation_plugins()2165 static bool has_validation_plugins()
2166 {
2167 return plugin_foreach(NULL, check_if_exists,
2168 MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL);
2169 }
2170
2171 struct validation_data { const LEX_CSTRING *user, *password; };
2172
do_validate(THD *,plugin_ref plugin,void * arg)2173 static my_bool do_validate(THD *, plugin_ref plugin, void *arg)
2174 {
2175 struct validation_data *data= (struct validation_data *)arg;
2176 struct st_mariadb_password_validation *handler=
2177 (st_mariadb_password_validation *)plugin_decl(plugin)->info;
2178 return handler->validate_password(data->user, data->password);
2179 }
2180
2181
validate_password(THD * thd,const LEX_CSTRING & user,const LEX_CSTRING & pwtext,bool has_hash)2182 static bool validate_password(THD *thd, const LEX_CSTRING &user,
2183 const LEX_CSTRING &pwtext, bool has_hash)
2184 {
2185 if (pwtext.length || !has_hash)
2186 {
2187 struct validation_data data= { &user,
2188 pwtext.str ? &pwtext : &empty_clex_str };
2189 if (plugin_foreach(NULL, do_validate,
2190 MariaDB_PASSWORD_VALIDATION_PLUGIN, &data))
2191 {
2192 my_error(ER_NOT_VALID_PASSWORD, MYF(0));
2193 return true;
2194 }
2195 }
2196 else
2197 {
2198 if (!thd->slave_thread &&
2199 strict_password_validation && has_validation_plugins()
2200 #ifdef WITH_WSREP
2201 && !thd->wsrep_applier
2202 #endif
2203 )
2204 {
2205 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--strict-password-validation");
2206 return true;
2207 }
2208 }
2209 return false;
2210 }
2211
set_user_salt(ACL_USER::AUTH * auth,plugin_ref plugin)2212 static int set_user_salt(ACL_USER::AUTH *auth, plugin_ref plugin)
2213 {
2214 st_mysql_auth *info= (st_mysql_auth *) plugin_decl(plugin)->info;
2215 if (info->interface_version >= 0x0202 && info->preprocess_hash &&
2216 auth->auth_string.length)
2217 {
2218 uchar buf[MAX_SCRAMBLE_LENGTH];
2219 size_t len= sizeof(buf);
2220 if (info->preprocess_hash(auth->auth_string.str,
2221 auth->auth_string.length, buf, &len))
2222 return 1;
2223 auth->salt.str= (char*)memdup_root(&acl_memroot, buf, len);
2224 auth->salt.length= len;
2225 }
2226 else
2227 auth->salt= safe_lexcstrdup_root(&acl_memroot, auth->auth_string);
2228
2229 return 0;
2230 }
2231
2232 /**
2233 Fills in ACL_USER::auth_string and ACL_USER::salt fields, as needed
2234
2235 hashes the plain-text password (if provided) to auth_string,
2236 converts auth_string to salt.
2237
2238 Fails if the plain-text password fails validation, if the plugin is
2239 not loaded, if the auth_string is invalid, if the password is not applicable
2240 */
set_user_auth(THD * thd,const LEX_CSTRING & user,ACL_USER::AUTH * auth,const LEX_CSTRING & pwtext)2241 static int set_user_auth(THD *thd, const LEX_CSTRING &user,
2242 ACL_USER::AUTH *auth, const LEX_CSTRING &pwtext)
2243 {
2244 const char *plugin_name= auth->plugin.str;
2245 bool unlock_plugin= false;
2246 plugin_ref plugin= get_auth_plugin(thd, auth->plugin, &unlock_plugin);
2247 int res= 1;
2248
2249 if (!plugin)
2250 {
2251 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
2252 ER_PLUGIN_IS_NOT_LOADED,
2253 ER_THD(thd, ER_PLUGIN_IS_NOT_LOADED), plugin_name);
2254 return ER_PLUGIN_IS_NOT_LOADED;
2255 }
2256
2257 auth->salt= auth->auth_string;
2258
2259 st_mysql_auth *info= (st_mysql_auth *) plugin_decl(plugin)->info;
2260 if (info->interface_version < 0x0202)
2261 {
2262 res= pwtext.length ? ER_SET_PASSWORD_AUTH_PLUGIN : 0;
2263 goto end;
2264 }
2265
2266 if (thd->lex->sql_command == SQLCOM_SET_OPTION && !info->hash_password)
2267 {
2268 res= ER_SET_PASSWORD_AUTH_PLUGIN;
2269 goto end;
2270 }
2271
2272 if (info->hash_password &&
2273 validate_password(thd, user, pwtext, auth->auth_string.length))
2274 {
2275 res= ER_NOT_VALID_PASSWORD;
2276 goto end;
2277 }
2278 if (pwtext.length)
2279 {
2280 if (info->hash_password)
2281 {
2282 char buf[MAX_SCRAMBLE_LENGTH];
2283 size_t len= sizeof(buf) - 1;
2284 if (info->hash_password(pwtext.str, pwtext.length, buf, &len))
2285 {
2286 res= ER_OUTOFMEMORY;
2287 goto end;
2288 }
2289 buf[len] = 0;
2290 auth->auth_string.str= (char*)memdup_root(&acl_memroot, buf, len+1);
2291 auth->auth_string.length= len;
2292 }
2293 else
2294 {
2295 res= ER_SET_PASSWORD_AUTH_PLUGIN;
2296 goto end;
2297 }
2298 }
2299 if (set_user_salt(auth, plugin))
2300 {
2301 res= ER_PASSWD_LENGTH;
2302 goto end;
2303 }
2304
2305 res= 0;
2306 end:
2307 if (unlock_plugin)
2308 plugin_unlock(thd, plugin);
2309 return res;
2310 }
2311
2312
2313 /**
2314 Lazily computes user's salt from the password hash
2315 */
set_user_salt_if_needed(ACL_USER * user_copy,int curr_auth,plugin_ref plugin)2316 static bool set_user_salt_if_needed(ACL_USER *user_copy, int curr_auth,
2317 plugin_ref plugin)
2318 {
2319 ACL_USER::AUTH *auth_copy= user_copy->auth + curr_auth;
2320 DBUG_ASSERT(!strcasecmp(auth_copy->plugin.str, plugin_name(plugin)->str));
2321
2322 if (auth_copy->salt.str)
2323 return 0; // already done
2324
2325 if (set_user_salt(auth_copy, plugin))
2326 return 1;
2327
2328 mysql_mutex_lock(&acl_cache->lock);
2329 ACL_USER *user= find_user_exact(user_copy->host.hostname, user_copy->user.str);
2330 // make sure the user wasn't altered or dropped meanwhile
2331 if (user)
2332 {
2333 ACL_USER::AUTH *auth= user->auth + curr_auth;
2334 if (!auth->salt.str && auth->plugin.length == auth_copy->plugin.length &&
2335 auth->auth_string.length == auth_copy->auth_string.length &&
2336 !memcmp(auth->plugin.str, auth_copy->plugin.str, auth->plugin.length) &&
2337 !memcmp(auth->auth_string.str, auth_copy->auth_string.str, auth->auth_string.length))
2338 auth->salt= auth_copy->salt;
2339 }
2340 mysql_mutex_unlock(&acl_cache->lock);
2341 return 0;
2342 }
2343
2344
2345 /**
2346 Fix ACL::plugin pointer to point to a hard-coded string, if appropriate
2347
2348 Make sure that if ACL_USER's plugin is a built-in, then it points
2349 to a hard coded string, not to an allocated copy. Run-time, for
2350 authentication, we want to be able to detect built-ins by comparing
2351 pointers, not strings.
2352
2353 @retval 0 the pointers were fixed
2354 @retval 1 this ACL_USER uses a not built-in plugin
2355 */
fix_user_plugin_ptr(ACL_USER::AUTH * auth)2356 static bool fix_user_plugin_ptr(ACL_USER::AUTH *auth)
2357 {
2358 if (lex_string_eq(&auth->plugin, &native_password_plugin_name))
2359 auth->plugin= native_password_plugin_name;
2360 else
2361 if (lex_string_eq(&auth->plugin, &old_password_plugin_name))
2362 auth->plugin= old_password_plugin_name;
2363 else
2364 return true;
2365 return false;
2366 }
2367
2368
get_YN_as_bool(Field * field)2369 static bool get_YN_as_bool(Field *field)
2370 {
2371 char buff[2];
2372 String res(buff,sizeof(buff),&my_charset_latin1);
2373 field->val_str(&res);
2374 return res[0] == 'Y' || res[0] == 'y';
2375 }
2376
2377
2378 /*
2379 Initialize structures responsible for user/db-level privilege checking and
2380 load privilege information for them from tables in the 'mysql' database.
2381
2382 SYNOPSIS
2383 acl_init()
2384 dont_read_acl_tables TRUE if we want to skip loading data from
2385 privilege tables and disable privilege checking.
2386
2387 NOTES
2388 This function is mostly responsible for preparatory steps, main work
2389 on initialization and grants loading is done in acl_reload().
2390
2391 RETURN VALUES
2392 0 ok
2393 1 Could not initialize grant's
2394 */
2395
acl_init(bool dont_read_acl_tables)2396 bool acl_init(bool dont_read_acl_tables)
2397 {
2398 THD *thd;
2399 bool return_val;
2400 DBUG_ENTER("acl_init");
2401
2402 acl_cache= new Hash_filo<acl_entry>(key_memory_acl_cache, ACL_CACHE_SIZE, 0, 0,
2403 (my_hash_get_key) acl_entry_get_key,
2404 (my_hash_free_key) my_free,
2405 &my_charset_utf8mb3_bin);
2406
2407 /*
2408 cache built-in native authentication plugins,
2409 to avoid hash searches and a global mutex lock on every connect
2410 */
2411 native_password_plugin= my_plugin_lock_by_name(0,
2412 &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
2413 old_password_plugin= my_plugin_lock_by_name(0,
2414 &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
2415
2416 if (!native_password_plugin || !old_password_plugin)
2417 DBUG_RETURN(1);
2418
2419 if (dont_read_acl_tables)
2420 {
2421 DBUG_RETURN(0); /* purecov: tested */
2422 }
2423
2424 /*
2425 To be able to run this from boot, we allocate a temporary THD
2426 */
2427 if (!(thd=new THD(0)))
2428 DBUG_RETURN(1); /* purecov: inspected */
2429 thd->thread_stack= (char*) &thd;
2430 thd->store_globals();
2431 /*
2432 It is safe to call acl_reload() since acl_* arrays and hashes which
2433 will be freed there are global static objects and thus are initialized
2434 by zeros at startup.
2435 */
2436 return_val= acl_reload(thd);
2437 delete thd;
2438 DBUG_RETURN(return_val);
2439 }
2440
push_new_user(const ACL_USER & user)2441 static void push_new_user(const ACL_USER &user)
2442 {
2443 push_dynamic(&acl_users, &user);
2444 if (!user.host.hostname ||
2445 (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
2446 allow_all_hosts=1; // Anyone can connect
2447 }
2448
2449
2450 /*
2451 Initialize structures responsible for user/db-level privilege checking
2452 and load information about grants from open privilege tables.
2453
2454 SYNOPSIS
2455 acl_load()
2456 thd Current thread
2457 tables List containing open "mysql.host", "mysql.user",
2458 "mysql.db", "mysql.proxies_priv" and "mysql.roles_mapping"
2459 tables.
2460
2461 RETURN VALUES
2462 FALSE Success
2463 TRUE Error
2464 */
2465
acl_load(THD * thd,const Grant_tables & tables)2466 static bool acl_load(THD *thd, const Grant_tables& tables)
2467 {
2468 READ_RECORD read_record_info;
2469 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
2470 char tmp_name[SAFE_NAME_LEN+1];
2471 Sql_mode_save old_mode_save(thd);
2472 DBUG_ENTER("acl_load");
2473
2474 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
2475
2476 grant_version++; /* Privileges updated */
2477
2478 const Host_table& host_table= tables.host_table();
2479 init_sql_alloc(key_memory_acl_mem, &acl_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
2480 if (host_table.table_exists()) // "host" table may not exist (e.g. in MySQL 5.6.7+)
2481 {
2482 if (host_table.init_read_record(&read_record_info))
2483 DBUG_RETURN(true);
2484 while (!(read_record_info.read_record()))
2485 {
2486 ACL_HOST host;
2487 update_hostname(&host.host, get_field(&acl_memroot, host_table.host()));
2488 host.db= get_field(&acl_memroot, host_table.db());
2489 if (lower_case_table_names && host.db)
2490 {
2491 /*
2492 convert db to lower case and give a warning if the db wasn't
2493 already in lower case
2494 */
2495 char *end = strnmov(tmp_name, host.db, sizeof(tmp_name));
2496 if (end >= tmp_name + sizeof(tmp_name))
2497 {
2498 sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), host.db);
2499 continue;
2500 }
2501 my_casedn_str(files_charset_info, host.db);
2502 if (strcmp(host.db, tmp_name) != 0)
2503 sql_print_warning("'host' entry '%s|%s' had database in mixed "
2504 "case that has been forced to lowercase because "
2505 "lower_case_table_names is set. It will not be "
2506 "possible to remove this privilege using REVOKE.",
2507 host.host.hostname, host.db);
2508 }
2509 host.access= host_table.get_access();
2510 host.access= fix_rights_for_db(host.access);
2511 host.sort= get_magic_sort("hd", host.host.hostname, host.db);
2512 if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
2513 {
2514 sql_print_warning("'host' entry '%s|%s' "
2515 "ignored in --skip-name-resolve mode.",
2516 safe_str(host.host.hostname),
2517 safe_str(host.db));
2518 continue;
2519 }
2520 #ifndef TO_BE_REMOVED
2521 if (host_table.num_fields() == 8)
2522 { // Without grant
2523 if (host.access & CREATE_ACL)
2524 host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
2525 }
2526 #endif
2527 (void) push_dynamic(&acl_hosts,(uchar*) &host);
2528 }
2529 my_qsort((uchar*) dynamic_element(&acl_hosts, 0, ACL_HOST*),
2530 acl_hosts.elements, sizeof(ACL_HOST),(qsort_cmp) acl_compare);
2531 end_read_record(&read_record_info);
2532 }
2533 freeze_size(&acl_hosts);
2534
2535 const User_table& user_table= tables.user_table();
2536 if (user_table.init_read_record(&read_record_info))
2537 DBUG_RETURN(true);
2538
2539 allow_all_hosts=0;
2540 while (!(read_record_info.read_record()))
2541 {
2542 ACL_USER user;
2543 bool is_role= FALSE;
2544 update_hostname(&user.host, user_table.get_host(&acl_memroot));
2545 char *username= safe_str(user_table.get_user(&acl_memroot));
2546 user.user.str= username;
2547 user.user.length= strlen(username);
2548
2549 is_role= user_table.get_is_role();
2550
2551 user.access= user_table.get_access();
2552
2553 user.sort= get_magic_sort("hu", user.host.hostname, user.user.str);
2554 user.hostname_length= safe_strlen(user.host.hostname);
2555
2556 my_init_dynamic_array(key_memory_acl_mem, &user.role_grants,
2557 sizeof(ACL_ROLE *), 0, 8, MYF(0));
2558
2559 user.account_locked= user_table.get_account_locked();
2560
2561 user.password_expired= user_table.get_password_expired();
2562 user.password_last_changed= user_table.get_password_last_changed();
2563 user.password_lifetime= user_table.get_password_lifetime();
2564
2565 if (is_role)
2566 {
2567 if (is_invalid_role_name(username))
2568 {
2569 thd->clear_error(); // the warning is still issued
2570 continue;
2571 }
2572
2573 ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot);
2574 entry->role_grants = user.role_grants;
2575 my_init_dynamic_array(key_memory_acl_mem, &entry->parent_grantee,
2576 sizeof(ACL_USER_BASE *), 0, 8, MYF(0));
2577 my_hash_insert(&acl_roles, (uchar *)entry);
2578
2579 continue;
2580 }
2581 else
2582 {
2583 if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
2584 {
2585 sql_print_warning("'user' entry '%s@%s' "
2586 "ignored in --skip-name-resolve mode.", user.user.str,
2587 safe_str(user.host.hostname));
2588 continue;
2589 }
2590
2591 if (user_table.get_auth(thd, &acl_memroot, &user))
2592 continue;
2593 for (uint i= 0; i < user.nauth; i++)
2594 {
2595 ACL_USER::AUTH *auth= user.auth + i;
2596 auth->salt= null_clex_str;
2597 fix_user_plugin_ptr(auth);
2598 }
2599
2600 user.ssl_type= user_table.get_ssl_type();
2601 user.ssl_cipher= user_table.get_ssl_cipher(&acl_memroot);
2602 user.x509_issuer= safe_str(user_table.get_x509_issuer(&acl_memroot));
2603 user.x509_subject= safe_str(user_table.get_x509_subject(&acl_memroot));
2604 user.user_resource.questions= (uint)user_table.get_max_questions();
2605 user.user_resource.updates= (uint)user_table.get_max_updates();
2606 user.user_resource.conn_per_hour= (uint)user_table.get_max_connections();
2607 if (user.user_resource.questions || user.user_resource.updates ||
2608 user.user_resource.conn_per_hour)
2609 mqh_used=1;
2610
2611 user.user_resource.user_conn= (int)user_table.get_max_user_connections();
2612 user.user_resource.max_statement_time= user_table.get_max_statement_time();
2613
2614 user.default_rolename.str= user_table.get_default_role(&acl_memroot);
2615 user.default_rolename.length= safe_strlen(user.default_rolename.str);
2616 }
2617 push_new_user(user);
2618 }
2619 rebuild_acl_users();
2620 end_read_record(&read_record_info);
2621 freeze_size(&acl_users);
2622
2623 const Db_table& db_table= tables.db_table();
2624 if (db_table.init_read_record(&read_record_info))
2625 DBUG_RETURN(TRUE);
2626 while (!(read_record_info.read_record()))
2627 {
2628 ACL_DB db;
2629 char *db_name;
2630 db.user=safe_str(get_field(&acl_memroot, db_table.user()));
2631 const char *hostname= get_field(&acl_memroot, db_table.host());
2632 if (!hostname && find_acl_role(db.user))
2633 hostname= "";
2634 update_hostname(&db.host, hostname);
2635 db.db= db_name= get_field(&acl_memroot, db_table.db());
2636 if (!db.db)
2637 {
2638 sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
2639 continue;
2640 }
2641 if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
2642 {
2643 sql_print_warning("'db' entry '%s %s@%s' "
2644 "ignored in --skip-name-resolve mode.",
2645 db.db, db.user, safe_str(db.host.hostname));
2646 continue;
2647 }
2648 db.access= db_table.get_access();
2649 db.access=fix_rights_for_db(db.access);
2650 db.initial_access= db.access;
2651 if (lower_case_table_names)
2652 {
2653 /*
2654 convert db to lower case and give a warning if the db wasn't
2655 already in lower case
2656 */
2657 char *end = strnmov(tmp_name, db.db, sizeof(tmp_name));
2658 if (end >= tmp_name + sizeof(tmp_name))
2659 {
2660 sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), db.db);
2661 continue;
2662 }
2663 my_casedn_str(files_charset_info, db_name);
2664 if (strcmp(db_name, tmp_name) != 0)
2665 {
2666 sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
2667 "case that has been forced to lowercase because "
2668 "lower_case_table_names is set. It will not be "
2669 "possible to remove this privilege using REVOKE.",
2670 db.db, db.user, safe_str(db.host.hostname));
2671 }
2672 }
2673 db.sort=get_magic_sort("hdu", db.host.hostname, db.db, db.user);
2674 #ifndef TO_BE_REMOVED
2675 if (db_table.num_fields() <= 9)
2676 { // Without grant
2677 if (db.access & CREATE_ACL)
2678 db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
2679 }
2680 #endif
2681 acl_dbs.push(db);
2682 }
2683 end_read_record(&read_record_info);
2684 rebuild_acl_dbs();
2685 acl_dbs.freeze();
2686
2687 const Proxies_priv_table& proxies_priv_table= tables.proxies_priv_table();
2688 if (proxies_priv_table.table_exists())
2689 {
2690 if (proxies_priv_table.init_read_record(&read_record_info))
2691 DBUG_RETURN(TRUE);
2692 while (!(read_record_info.read_record()))
2693 {
2694 ACL_PROXY_USER proxy;
2695 proxy.init(proxies_priv_table, &acl_memroot);
2696 if (proxy.check_validity(check_no_resolve))
2697 continue;
2698 if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
2699 DBUG_RETURN(TRUE);
2700 }
2701 my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
2702 acl_proxy_users.elements,
2703 sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
2704 end_read_record(&read_record_info);
2705 }
2706 else
2707 {
2708 sql_print_error("Missing system table mysql.proxies_priv; "
2709 "please run mysql_upgrade to create it");
2710 }
2711 freeze_size(&acl_proxy_users);
2712
2713 const Roles_mapping_table& roles_mapping_table= tables.roles_mapping_table();
2714 if (roles_mapping_table.table_exists())
2715 {
2716 if (roles_mapping_table.init_read_record(&read_record_info))
2717 DBUG_RETURN(TRUE);
2718
2719 MEM_ROOT temp_root;
2720 init_alloc_root(key_memory_acl_mem, &temp_root, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
2721 while (!(read_record_info.read_record()))
2722 {
2723 char *hostname= safe_str(get_field(&temp_root, roles_mapping_table.host()));
2724 char *username= safe_str(get_field(&temp_root, roles_mapping_table.user()));
2725 char *rolename= safe_str(get_field(&temp_root, roles_mapping_table.role()));
2726 bool with_grant_option= get_YN_as_bool(roles_mapping_table.admin_option());
2727
2728 if (add_role_user_mapping(username, hostname, rolename)) {
2729 sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'",
2730 username, hostname, rolename);
2731 continue;
2732 }
2733
2734 ROLE_GRANT_PAIR *mapping= new (&acl_memroot) ROLE_GRANT_PAIR;
2735
2736 if (mapping->init(&acl_memroot, username, hostname, rolename, with_grant_option))
2737 continue;
2738
2739 my_hash_insert(&acl_roles_mappings, (uchar*) mapping);
2740 }
2741
2742 free_root(&temp_root, MYF(0));
2743 end_read_record(&read_record_info);
2744 }
2745 else
2746 {
2747 sql_print_error("Missing system table mysql.roles_mapping; "
2748 "please run mysql_upgrade to create it");
2749 }
2750
2751 init_check_host();
2752
2753 thd->bootstrap= !initialized; // keep FLUSH PRIVILEGES connection special
2754 initialized=1;
2755 DBUG_RETURN(FALSE);
2756 }
2757
2758
acl_free(bool end)2759 void acl_free(bool end)
2760 {
2761 my_hash_free(&acl_roles);
2762 free_root(&acl_memroot,MYF(0));
2763 delete_dynamic(&acl_hosts);
2764 delete_dynamic_with_callback(&acl_users, (FREE_FUNC) free_acl_user);
2765 acl_dbs.free_memory();
2766 delete_dynamic(&acl_wild_hosts);
2767 delete_dynamic(&acl_proxy_users);
2768 my_hash_free(&acl_check_hosts);
2769 my_hash_free(&acl_roles_mappings);
2770 if (!end)
2771 acl_cache->clear(1); /* purecov: inspected */
2772 else
2773 {
2774 plugin_unlock(0, native_password_plugin);
2775 plugin_unlock(0, old_password_plugin);
2776 delete acl_cache;
2777 acl_cache=0;
2778 }
2779 }
2780
2781
2782 /*
2783 Forget current user/db-level privileges and read new privileges
2784 from the privilege tables.
2785
2786 SYNOPSIS
2787 acl_reload()
2788 thd Current thread
2789
2790 NOTE
2791 All tables of calling thread which were open and locked by LOCK TABLES
2792 statement will be unlocked and closed.
2793 This function is also used for initialization of structures responsible
2794 for user/db-level privilege checking.
2795
2796 RETURN VALUE
2797 FALSE Success
2798 TRUE Failure
2799 */
2800
acl_reload(THD * thd)2801 bool acl_reload(THD *thd)
2802 {
2803 DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_proxy_users;
2804 Dynamic_array<ACL_DB> old_acl_dbs(0U,0U);
2805 HASH old_acl_roles, old_acl_roles_mappings;
2806 MEM_ROOT old_mem;
2807 int result;
2808 DBUG_ENTER("acl_reload");
2809
2810 Grant_tables tables;
2811 /*
2812 To avoid deadlocks we should obtain table locks before
2813 obtaining acl_cache->lock mutex.
2814 */
2815 const uint tables_to_open= Table_host | Table_user | Table_db |
2816 Table_proxies_priv | Table_roles_mapping;
2817 if ((result= tables.open_and_lock(thd, tables_to_open, TL_READ)))
2818 {
2819 DBUG_ASSERT(result <= 0);
2820 /*
2821 Execution might have been interrupted; only print the error message
2822 if an error condition has been raised.
2823 */
2824 if (thd->get_stmt_da()->is_error())
2825 sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
2826 thd->get_stmt_da()->message());
2827 goto end;
2828 }
2829
2830 acl_cache->clear(0);
2831 mysql_mutex_lock(&acl_cache->lock);
2832
2833 old_acl_hosts= acl_hosts;
2834 old_acl_users= acl_users;
2835 old_acl_roles= acl_roles;
2836 old_acl_roles_mappings= acl_roles_mappings;
2837 old_acl_proxy_users= acl_proxy_users;
2838 old_acl_dbs= acl_dbs;
2839 my_init_dynamic_array(key_memory_acl_mem, &acl_hosts, sizeof(ACL_HOST), 20, 50, MYF(0));
2840 my_init_dynamic_array(key_memory_acl_mem, &acl_users, sizeof(ACL_USER), 50, 100, MYF(0));
2841 acl_dbs.init(key_memory_acl_mem, 50, 100);
2842 my_init_dynamic_array(key_memory_acl_mem, &acl_proxy_users, sizeof(ACL_PROXY_USER), 50, 100, MYF(0));
2843 my_hash_init2(key_memory_acl_mem, &acl_roles,50, &my_charset_utf8mb3_bin,
2844 0, 0, 0, (my_hash_get_key) acl_role_get_key, 0,
2845 (void (*)(void *))free_acl_role, 0);
2846 my_hash_init2(key_memory_acl_mem, &acl_roles_mappings, 50,
2847 &my_charset_utf8mb3_bin, 0, 0, 0, (my_hash_get_key)
2848 acl_role_map_get_key, 0, 0, 0);
2849 old_mem= acl_memroot;
2850 delete_dynamic(&acl_wild_hosts);
2851 my_hash_free(&acl_check_hosts);
2852
2853 if ((result= acl_load(thd, tables)))
2854 { // Error. Revert to old list
2855 DBUG_PRINT("error",("Reverting to old privileges"));
2856 acl_free(); /* purecov: inspected */
2857 acl_hosts= old_acl_hosts;
2858 acl_users= old_acl_users;
2859 acl_roles= old_acl_roles;
2860 acl_roles_mappings= old_acl_roles_mappings;
2861 acl_proxy_users= old_acl_proxy_users;
2862 acl_dbs= old_acl_dbs;
2863 old_acl_dbs.init(0,0);
2864 acl_memroot= old_mem;
2865 init_check_host();
2866 }
2867 else
2868 {
2869 my_hash_free(&old_acl_roles);
2870 free_root(&old_mem,MYF(0));
2871 delete_dynamic(&old_acl_hosts);
2872 delete_dynamic_with_callback(&old_acl_users, (FREE_FUNC) free_acl_user);
2873 delete_dynamic(&old_acl_proxy_users);
2874 my_hash_free(&old_acl_roles_mappings);
2875 }
2876 mysql_mutex_unlock(&acl_cache->lock);
2877 end:
2878 close_mysql_tables(thd);
2879 DBUG_RETURN(result);
2880 }
2881
2882 /*
2883 Get all access bits from table after fieldnr
2884
2885 IMPLEMENTATION
2886 We know that the access privileges ends when there is no more fields
2887 or the field is not an enum with two elements.
2888
2889 SYNOPSIS
2890 get_access()
2891 form an open table to read privileges from.
2892 The record should be already read in table->record[0]
2893 fieldnr number of the first privilege (that is ENUM('N','Y') field
2894 next_field on return - number of the field next to the last ENUM
2895 (unless next_field == 0)
2896
2897 RETURN VALUE
2898 privilege mask
2899 */
2900
get_access(TABLE * form,uint fieldnr,uint * next_field)2901 static privilege_t get_access(TABLE *form, uint fieldnr, uint *next_field)
2902 {
2903 ulonglong access_bits=0,bit;
2904 char buff[2];
2905 String res(buff,sizeof(buff),&my_charset_latin1);
2906 Field **pos;
2907
2908 for (pos=form->field+fieldnr, bit=1;
2909 *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
2910 ((Field_enum*) (*pos))->typelib->count == 2 ;
2911 pos++, fieldnr++, bit<<=1)
2912 {
2913 if (get_YN_as_bool(*pos))
2914 access_bits|= bit;
2915 }
2916 if (next_field)
2917 *next_field=fieldnr;
2918 return ALL_KNOWN_ACL & access_bits;
2919 }
2920
2921
acl_compare(const ACL_ACCESS * a,const ACL_ACCESS * b)2922 static int acl_compare(const ACL_ACCESS *a, const ACL_ACCESS *b)
2923 {
2924 if (a->sort > b->sort)
2925 return -1;
2926 if (a->sort < b->sort)
2927 return 1;
2928 return 0;
2929 }
2930
acl_user_compare(const ACL_USER * a,const ACL_USER * b)2931 static int acl_user_compare(const ACL_USER *a, const ACL_USER *b)
2932 {
2933 int res= strcmp(a->user.str, b->user.str);
2934 if (res)
2935 return res;
2936
2937 res= acl_compare(a, b);
2938 if (res)
2939 return res;
2940
2941 /*
2942 For more deterministic results, resolve ambiguity between
2943 "localhost" and "127.0.0.1"/"::1" by sorting "localhost" before
2944 loopback addresses.
2945 Test suite (on Windows) expects "root@localhost", even if
2946 root@::1 would also match.
2947 */
2948 return -strcmp(a->host.hostname, b->host.hostname);
2949 }
2950
acl_db_compare(const ACL_DB * a,const ACL_DB * b)2951 static int acl_db_compare(const ACL_DB *a, const ACL_DB *b)
2952 {
2953 int res= strcmp(a->user, b->user);
2954 if (res)
2955 return res;
2956
2957 return acl_compare(a, b);
2958 }
2959
rebuild_acl_users()2960 static void rebuild_acl_users()
2961 {
2962 my_qsort((uchar*)dynamic_element(&acl_users, 0, ACL_USER*), acl_users.elements,
2963 sizeof(ACL_USER), (qsort_cmp)acl_user_compare);
2964 }
2965
rebuild_acl_dbs()2966 static void rebuild_acl_dbs()
2967 {
2968 acl_dbs.sort(acl_db_compare);
2969 }
2970
2971
2972 /*
2973 Return index of the first entry with given user in the array,
2974 or SIZE_T_MAX if not found.
2975
2976 Assumes the array is sorted by get_username
2977 */
find_first_user(T * arr,size_t len,const char * user)2978 template<typename T> size_t find_first_user(T* arr, size_t len, const char *user)
2979 {
2980 size_t low= 0;
2981 size_t high= len;
2982 size_t mid;
2983
2984 bool found= false;
2985 if(!len)
2986 return SIZE_T_MAX;
2987
2988 #ifndef DBUG_OFF
2989 for (uint i = 0; i < len - 1; i++)
2990 DBUG_ASSERT(strcmp(arr[i].get_username(), arr[i + 1].get_username()) <= 0);
2991 #endif
2992 while (low < high)
2993 {
2994 mid= low + (high - low) / 2;
2995 int cmp= strcmp(arr[mid].get_username(),user);
2996 if (cmp == 0)
2997 found= true;
2998
2999 if (cmp >= 0 )
3000 high= mid;
3001 else
3002 low= mid + 1;
3003 }
3004 return (!found || low == len || strcmp(arr[low].get_username(), user)!=0 )?SIZE_T_MAX:low;
3005 }
3006
acl_find_user_by_name(const char * user)3007 static size_t acl_find_user_by_name(const char *user)
3008 {
3009 return find_first_user<ACL_USER>((ACL_USER *)acl_users.buffer,acl_users.elements,user);
3010 }
3011
acl_find_db_by_username(const char * user)3012 static size_t acl_find_db_by_username(const char *user)
3013 {
3014 return find_first_user<ACL_DB>(acl_dbs.front(), acl_dbs.elements(), user);
3015 }
3016
match_db(ACL_DB * acl_db,const char * db,my_bool db_is_pattern)3017 static bool match_db(ACL_DB *acl_db, const char *db, my_bool db_is_pattern)
3018 {
3019 return !acl_db->db || (db && !wild_compare(db, acl_db->db, db_is_pattern));
3020 }
3021
3022
3023 /*
3024 Lookup in the acl_users or acl_dbs for the best matching entry corresponding to
3025 given user, host and ip parameters (also db, in case of ACL_DB)
3026
3027 Historical note:
3028
3029 In the past, both arrays were sorted just by ACL_ENTRY::sort field and were
3030 searched linearly, until the first match of (username,host) pair was found.
3031
3032 This function uses optimizations (binary search by username), yet preserves the
3033 historical behavior, i.e the returns a match with highest ACL_ENTRY::sort.
3034 */
find_by_username_or_anon(T * arr,size_t len,const char * user,const char * host,const char * ip,const char * db,my_bool db_is_pattern,bool (* match_db_func)(T *,const char *,my_bool))3035 template <typename T> T* find_by_username_or_anon(T* arr, size_t len, const char *user,
3036 const char *host, const char *ip,
3037 const char *db, my_bool db_is_pattern, bool (*match_db_func)(T*,const char *,my_bool))
3038 {
3039 size_t i;
3040 T *ret = NULL;
3041
3042 // Check entries matching user name.
3043 size_t start = find_first_user(arr, len, user);
3044 for (i= start; i < len; i++)
3045 {
3046 T *entry= &arr[i];
3047 if (i > start && strcmp(user, entry->get_username()))
3048 break;
3049
3050 if (compare_hostname(&entry->host, host, ip) && (!match_db_func || match_db_func(entry, db, db_is_pattern)))
3051 {
3052 ret= entry;
3053 break;
3054 }
3055 }
3056
3057 // Look also for anonymous user (username is empty string)
3058 // Due to sort by name, entries for anonymous user start at the start of array.
3059 for (i= 0; i < len; i++)
3060 {
3061 T *entry = &arr[i];
3062 if (*entry->get_username() || (ret && acl_compare(entry, ret) >= 0))
3063 break;
3064 if (compare_hostname(&entry->host, host, ip) && (!match_db_func || match_db_func(entry, db, db_is_pattern)))
3065 {
3066 ret= entry;
3067 break;
3068 }
3069 }
3070 return ret;
3071 }
3072
acl_db_find(const char * db,const char * user,const char * host,const char * ip,my_bool db_is_pattern)3073 static ACL_DB *acl_db_find(const char *db, const char *user, const char *host, const char *ip, my_bool db_is_pattern)
3074 {
3075 return find_by_username_or_anon(acl_dbs.front(), acl_dbs.elements(),
3076 user, host, ip, db, db_is_pattern, match_db);
3077 }
3078
3079
3080 /*
3081 Gets user credentials without authentication and resource limit checks.
3082
3083 SYNOPSIS
3084 acl_getroot()
3085 sctx Context which should be initialized
3086 user user name
3087 host host name
3088 ip IP
3089 db current data base name
3090
3091 RETURN
3092 FALSE OK
3093 TRUE Error
3094 */
3095
acl_getroot(Security_context * sctx,const char * user,const char * host,const char * ip,const char * db)3096 bool acl_getroot(Security_context *sctx, const char *user, const char *host,
3097 const char *ip, const char *db)
3098 {
3099 int res= 1;
3100 ACL_USER *acl_user= 0;
3101 DBUG_ENTER("acl_getroot");
3102
3103 DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
3104 host, ip, user, db));
3105 sctx->init();
3106 sctx->user= *user ? user : NULL;
3107 sctx->host= host;
3108 sctx->ip= ip;
3109 sctx->host_or_ip= host ? host : (safe_str(ip));
3110
3111 if (!initialized)
3112 {
3113 /*
3114 here if mysqld's been started with --skip-grant-tables option.
3115 */
3116 sctx->skip_grants();
3117 DBUG_RETURN(FALSE);
3118 }
3119
3120 mysql_mutex_lock(&acl_cache->lock);
3121
3122 sctx->db_access= NO_ACL;
3123
3124 if (host[0]) // User, not Role
3125 {
3126 acl_user= find_user_wild(host, user, ip);
3127
3128 if (acl_user)
3129 {
3130 res= 0;
3131 if (ACL_DB *acl_db= acl_db_find(db, user, host, ip, FALSE))
3132 sctx->db_access= acl_db->access;
3133
3134 sctx->master_access= acl_user->access;
3135
3136 strmake_buf(sctx->priv_user, user);
3137
3138 if (acl_user->host.hostname)
3139 strmake_buf(sctx->priv_host, acl_user->host.hostname);
3140 }
3141 }
3142 else // Role, not User
3143 {
3144 ACL_ROLE *acl_role= find_acl_role(user);
3145 if (acl_role)
3146 {
3147 res= 0;
3148 if (ACL_DB *acl_db= acl_db_find(db, user, "", "", FALSE))
3149 sctx->db_access = acl_db->access;
3150
3151 sctx->master_access= acl_role->access;
3152
3153 strmake_buf(sctx->priv_role, user);
3154 }
3155 }
3156
3157 mysql_mutex_unlock(&acl_cache->lock);
3158 DBUG_RETURN(res);
3159 }
3160
check_role_is_granted_callback(ACL_USER_BASE * grantee,void * data)3161 static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data)
3162 {
3163 LEX_CSTRING *rolename= static_cast<LEX_CSTRING *>(data);
3164 if (rolename->length == grantee->user.length &&
3165 !strcmp(rolename->str, grantee->user.str))
3166 return -1; // End search, we've found our role.
3167
3168 /* Keep looking, we haven't found our role yet. */
3169 return 0;
3170 }
3171
3172 /*
3173 unlike find_user_exact and find_user_wild,
3174 this function finds anonymous users too, it's when a
3175 user is not empty, but priv_user (acl_user->user) is empty.
3176 */
find_user_or_anon(const char * host,const char * user,const char * ip)3177 static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip)
3178 {
3179 return find_by_username_or_anon<ACL_USER>
3180 (reinterpret_cast<ACL_USER*>(acl_users.buffer), acl_users.elements,
3181 user, host, ip, NULL, FALSE, NULL);
3182 }
3183
3184
check_user_can_set_role(THD * thd,const char * user,const char * host,const char * ip,const char * rolename,privilege_t * access)3185 static int check_user_can_set_role(THD *thd, const char *user,
3186 const char *host, const char *ip,
3187 const char *rolename, privilege_t *access)
3188 {
3189 ACL_ROLE *role;
3190 ACL_USER_BASE *acl_user_base;
3191 ACL_USER *UNINIT_VAR(acl_user);
3192 bool is_granted= FALSE;
3193 int result= 0;
3194
3195 /* clear role privileges */
3196 mysql_mutex_lock(&acl_cache->lock);
3197
3198 if (!strcasecmp(rolename, "NONE"))
3199 {
3200 /* have to clear the privileges */
3201 /* get the current user */
3202 acl_user= find_user_wild(host, user, ip);
3203 if (acl_user == NULL)
3204 result= ER_INVALID_CURRENT_USER;
3205 else if (access)
3206 *access= acl_user->access;
3207
3208 goto end;
3209 }
3210
3211 role= find_acl_role(rolename);
3212
3213 /* According to SQL standard, the same error message must be presented */
3214 if (role == NULL)
3215 {
3216 result= ER_INVALID_ROLE;
3217 goto end;
3218 }
3219
3220 for (uint i=0 ; i < role->parent_grantee.elements ; i++)
3221 {
3222 acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**));
3223 if (acl_user_base->flags & IS_ROLE)
3224 continue;
3225
3226 acl_user= (ACL_USER *)acl_user_base;
3227 if (acl_user->wild_eq(user, host, ip))
3228 {
3229 is_granted= TRUE;
3230 break;
3231 }
3232 }
3233
3234 /* According to SQL standard, the same error message must be presented */
3235 if (!is_granted)
3236 {
3237 result= 1;
3238 goto end;
3239 }
3240
3241 if (access)
3242 {
3243 *access = acl_user->access | role->access;
3244 }
3245
3246 end:
3247 mysql_mutex_unlock(&acl_cache->lock);
3248
3249 /* We present different error messages depending if the user has sufficient
3250 privileges to know if the INVALID_ROLE exists. */
3251 switch (result)
3252 {
3253 case ER_INVALID_CURRENT_USER:
3254 my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename);
3255 break;
3256 case ER_INVALID_ROLE:
3257 /* Role doesn't exist at all */
3258 my_error(ER_INVALID_ROLE, MYF(0), rolename);
3259 break;
3260 case 1:
3261 LEX_CSTRING role_lex;
3262 /* First, check if current user can see mysql database. */
3263 bool read_access= !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 1);
3264
3265 role_lex.str= rolename;
3266 role_lex.length= strlen(rolename);
3267 mysql_mutex_lock(&acl_cache->lock);
3268 ACL_USER *cur_user= find_user_or_anon(thd->security_ctx->priv_host,
3269 thd->security_ctx->priv_user,
3270 thd->security_ctx->ip);
3271
3272 /* If the current user does not have select priv to mysql database,
3273 see if the current user can discover the role if it was granted to him.
3274 */
3275 if (cur_user && (read_access ||
3276 traverse_role_graph_down(cur_user, &role_lex,
3277 check_role_is_granted_callback,
3278 NULL) == -1))
3279 {
3280 /* Role is not granted but current user can see the role */
3281 my_printf_error(ER_INVALID_ROLE, "User %`s@%`s has not been granted role %`s",
3282 MYF(0), thd->security_ctx->priv_user,
3283 thd->security_ctx->priv_host, rolename);
3284 }
3285 else
3286 {
3287 /* Role is not granted and current user cannot see the role */
3288 my_error(ER_INVALID_ROLE, MYF(0), rolename);
3289 }
3290 mysql_mutex_unlock(&acl_cache->lock);
3291 break;
3292 }
3293
3294 return result;
3295 }
3296
3297
acl_check_setrole(THD * thd,const char * rolename,privilege_t * access)3298 int acl_check_setrole(THD *thd, const char *rolename, privilege_t *access)
3299 {
3300 if (!initialized)
3301 {
3302 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3303 return 1;
3304 }
3305
3306 return check_user_can_set_role(thd, thd->security_ctx->priv_user,
3307 thd->security_ctx->host, thd->security_ctx->ip, rolename, access);
3308 }
3309
3310
acl_setrole(THD * thd,const char * rolename,privilege_t access)3311 int acl_setrole(THD *thd, const char *rolename, privilege_t access)
3312 {
3313 /* merge the privileges */
3314 Security_context *sctx= thd->security_ctx;
3315 sctx->master_access= access;
3316 if (thd->db.str)
3317 sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db.str, FALSE);
3318
3319 if (!strcasecmp(rolename, "NONE"))
3320 {
3321 thd->security_ctx->priv_role[0]= 0;
3322 }
3323 else
3324 {
3325 if (thd->db.str)
3326 sctx->db_access|= acl_get("", "", rolename, thd->db.str, FALSE);
3327 /* mark the current role */
3328 strmake_buf(thd->security_ctx->priv_role, rolename);
3329 }
3330 return 0;
3331 }
3332
check_get_key(ACL_USER * buff,size_t * length,my_bool not_used)3333 static uchar* check_get_key(ACL_USER *buff, size_t *length,
3334 my_bool not_used __attribute__((unused)))
3335 {
3336 *length=buff->hostname_length;
3337 return (uchar*) buff->host.hostname;
3338 }
3339
3340
acl_update_role(const char * rolename,const privilege_t privileges)3341 static void acl_update_role(const char *rolename, const privilege_t privileges)
3342 {
3343 ACL_ROLE *role= find_acl_role(rolename);
3344 if (role)
3345 role->initial_role_access= role->access= privileges;
3346 }
3347
3348
ACL_USER(THD * thd,const LEX_USER & combo,const Account_options & options,const privilege_t privileges)3349 ACL_USER::ACL_USER(THD *thd, const LEX_USER &combo,
3350 const Account_options &options,
3351 const privilege_t privileges)
3352 {
3353 user= safe_lexcstrdup_root(&acl_memroot, combo.user);
3354 update_hostname(&host, safe_strdup_root(&acl_memroot, combo.host.str));
3355 hostname_length= combo.host.length;
3356 sort= get_magic_sort("hu", host.hostname, user.str);
3357 password_last_changed= thd->query_start();
3358 password_lifetime= -1;
3359 my_init_dynamic_array(PSI_INSTRUMENT_ME, &role_grants, sizeof(ACL_USER *), 0, 8, MYF(0));
3360 }
3361
3362
acl_user_update(THD * thd,ACL_USER * acl_user,uint nauth,const LEX_USER & combo,const Account_options & options,const privilege_t privileges)3363 static int acl_user_update(THD *thd, ACL_USER *acl_user, uint nauth,
3364 const LEX_USER &combo,
3365 const Account_options &options,
3366 const privilege_t privileges)
3367 {
3368 ACL_USER_PARAM::AUTH *work_copy= NULL;
3369 if (nauth)
3370 {
3371 if (!(work_copy= (ACL_USER_PARAM::AUTH*)
3372 alloc_root(thd->mem_root, nauth * sizeof(ACL_USER_PARAM::AUTH))))
3373 return 1;
3374
3375 USER_AUTH *auth= combo.auth;
3376 for (uint i= 0; i < nauth; i++, auth= auth->next)
3377 {
3378 work_copy[i].plugin= auth->plugin;
3379 work_copy[i].auth_string= safe_lexcstrdup_root(&acl_memroot,
3380 auth->auth_str);
3381 if (fix_user_plugin_ptr(work_copy + i))
3382 work_copy[i].plugin= safe_lexcstrdup_root(&acl_memroot, auth->plugin);
3383 if (set_user_auth(thd, acl_user->user, work_copy + i, auth->pwtext))
3384 return 1;
3385 }
3386 }
3387
3388 acl_user->access= privileges;
3389 if (options.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
3390 acl_user->user_resource.questions= options.questions;
3391 if (options.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
3392 acl_user->user_resource.updates= options.updates;
3393 if (options.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
3394 acl_user->user_resource.conn_per_hour= options.conn_per_hour;
3395 if (options.specified_limits & USER_RESOURCES::USER_CONNECTIONS)
3396 acl_user->user_resource.user_conn= options.user_conn;
3397 if (options.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
3398 acl_user->user_resource.max_statement_time= options.max_statement_time;
3399 if (options.ssl_type != SSL_TYPE_NOT_SPECIFIED)
3400 {
3401 acl_user->ssl_type= options.ssl_type;
3402 acl_user->ssl_cipher= safe_strdup_root(&acl_memroot, options.ssl_cipher.str);
3403 acl_user->x509_issuer= safe_strdup_root(&acl_memroot,
3404 safe_str(options.x509_issuer.str));
3405 acl_user->x509_subject= safe_strdup_root(&acl_memroot,
3406 safe_str(options.x509_subject.str));
3407 }
3408 if (options.account_locked != ACCOUNTLOCK_UNSPECIFIED)
3409 acl_user->account_locked= options.account_locked == ACCOUNTLOCK_LOCKED;
3410
3411 if (thd->is_error())
3412 {
3413 // If something went wrong (including OOM) we will not spoil acl cache
3414 return 1;
3415 }
3416 /* Unexpire the user password and copy AUTH (when no more errors possible)*/
3417 if (nauth)
3418 {
3419 acl_user->password_expired= false;
3420 acl_user->password_last_changed= thd->query_start();
3421
3422 if (acl_user->nauth >= nauth)
3423 {
3424 acl_user->nauth= nauth;
3425 }
3426 else
3427 {
3428 if (acl_user->alloc_auth(&acl_memroot, nauth))
3429 {
3430 /*
3431 acl_user is a copy, so NULL assigned in case of an error do not
3432 change the acl cache
3433 */
3434 return 1;
3435 }
3436 }
3437 DBUG_ASSERT(work_copy); // allocated under the same condinition
3438 memcpy(acl_user->auth, work_copy, nauth * sizeof(ACL_USER_PARAM::AUTH));
3439 }
3440
3441 switch (options.password_expire) {
3442 case PASSWORD_EXPIRE_UNSPECIFIED:
3443 break;
3444 case PASSWORD_EXPIRE_NOW:
3445 acl_user->password_expired= true;
3446 break;
3447 case PASSWORD_EXPIRE_NEVER:
3448 acl_user->password_lifetime= 0;
3449 break;
3450 case PASSWORD_EXPIRE_DEFAULT:
3451 acl_user->password_lifetime= -1;
3452 break;
3453 case PASSWORD_EXPIRE_INTERVAL:
3454 acl_user->password_lifetime= options.num_expiration_days;
3455 break;
3456 }
3457
3458 return 0;
3459 }
3460
3461
acl_insert_role(const char * rolename,privilege_t privileges)3462 static void acl_insert_role(const char *rolename, privilege_t privileges)
3463 {
3464 ACL_ROLE *entry;
3465
3466 mysql_mutex_assert_owner(&acl_cache->lock);
3467 entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot);
3468 my_init_dynamic_array(key_memory_acl_mem, &entry->parent_grantee,
3469 sizeof(ACL_USER_BASE *), 0, 8, MYF(0));
3470 my_init_dynamic_array(key_memory_acl_mem, &entry->role_grants,
3471 sizeof(ACL_ROLE *), 0, 8, MYF(0));
3472
3473 my_hash_insert(&acl_roles, (uchar *)entry);
3474 }
3475
3476
acl_update_db(const char * user,const char * host,const char * db,privilege_t privileges)3477 static bool acl_update_db(const char *user, const char *host, const char *db,
3478 privilege_t privileges)
3479 {
3480 mysql_mutex_assert_owner(&acl_cache->lock);
3481
3482 bool updated= false;
3483
3484 for (size_t i= acl_find_db_by_username(user); i < acl_dbs.elements(); i++)
3485 {
3486 ACL_DB *acl_db= &acl_dbs.at(i);
3487 if (!strcmp(user,acl_db->user))
3488 {
3489 if ((!acl_db->host.hostname && !host[0]) ||
3490 (acl_db->host.hostname && !strcmp(host, acl_db->host.hostname)))
3491 {
3492 if ((!acl_db->db && !db[0]) ||
3493 (acl_db->db && !strcmp(db,acl_db->db)))
3494
3495 {
3496 if (privileges)
3497 {
3498 acl_db->access= privileges;
3499 acl_db->initial_access= acl_db->access;
3500 }
3501 else
3502 acl_dbs.del(i);
3503 updated= true;
3504 }
3505 }
3506 }
3507 else
3508 break;
3509 }
3510
3511 return updated;
3512 }
3513
3514
3515 /*
3516 Insert a user/db/host combination into the global acl_cache
3517
3518 SYNOPSIS
3519 acl_insert_db()
3520 user User name
3521 host Host name
3522 db Database name
3523 privileges Bitmap of privileges
3524
3525 NOTES
3526 acl_cache->lock must be locked when calling this
3527 */
3528
acl_insert_db(const char * user,const char * host,const char * db,const privilege_t privileges)3529 static void acl_insert_db(const char *user, const char *host, const char *db,
3530 const privilege_t privileges)
3531 {
3532 ACL_DB acl_db;
3533 mysql_mutex_assert_owner(&acl_cache->lock);
3534 acl_db.user=strdup_root(&acl_memroot,user);
3535 update_hostname(&acl_db.host, safe_strdup_root(&acl_memroot, host));
3536 acl_db.db=strdup_root(&acl_memroot,db);
3537 acl_db.initial_access= acl_db.access= privileges;
3538 acl_db.sort=get_magic_sort("hdu", acl_db.host.hostname, acl_db.db, acl_db.user);
3539 acl_dbs.push(acl_db);
3540 rebuild_acl_dbs();
3541 }
3542
3543
3544 /*
3545 Get privilege for a host, user and db combination
3546
3547 as db_is_pattern changes the semantics of comparison,
3548 acl_cache is not used if db_is_pattern is set.
3549 */
3550
acl_get(const char * host,const char * ip,const char * user,const char * db,my_bool db_is_pattern)3551 privilege_t acl_get(const char *host, const char *ip,
3552 const char *user, const char *db, my_bool db_is_pattern)
3553 {
3554 privilege_t host_access(ALL_KNOWN_ACL), db_access(NO_ACL);
3555 uint i;
3556 size_t key_length;
3557 char key[ACL_KEY_LENGTH],*tmp_db,*end;
3558 acl_entry *entry;
3559 DBUG_ENTER("acl_get");
3560
3561 tmp_db= strmov(strmov(key, safe_str(ip)) + 1, user) + 1;
3562 end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db);
3563
3564 if (end >= key + sizeof(key)) // db name was truncated
3565 DBUG_RETURN(NO_ACL); // no privileges for an invalid db name
3566
3567 if (lower_case_table_names)
3568 {
3569 my_casedn_str(files_charset_info, tmp_db);
3570 db=tmp_db;
3571 }
3572 key_length= (size_t) (end-key);
3573
3574 mysql_mutex_lock(&acl_cache->lock);
3575 if (!db_is_pattern && (entry=acl_cache->search((uchar*) key, key_length)))
3576 {
3577 db_access=entry->access;
3578 mysql_mutex_unlock(&acl_cache->lock);
3579 DBUG_PRINT("exit", ("access: 0x%llx", (longlong) db_access));
3580 DBUG_RETURN(db_access);
3581 }
3582
3583 /*
3584 Check if there are some access rights for database and user
3585 */
3586 if (ACL_DB *acl_db= acl_db_find(db,user, host, ip, db_is_pattern))
3587 {
3588 db_access= acl_db->access;
3589 if (acl_db->host.hostname)
3590 goto exit; // Fully specified. Take it
3591 /* the host table is not used for roles */
3592 if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user))
3593 goto exit;
3594 }
3595
3596 if (!db_access)
3597 goto exit; // Can't be better
3598
3599 /*
3600 No host specified for user. Get hostdata from host table
3601 */
3602 host_access= NO_ACL; // Host must be found
3603 for (i=0 ; i < acl_hosts.elements ; i++)
3604 {
3605 ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
3606 if (compare_hostname(&acl_host->host,host,ip))
3607 {
3608 if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
3609 {
3610 host_access=acl_host->access; // Fully specified. Take it
3611 break;
3612 }
3613 }
3614 }
3615 exit:
3616 /* Save entry in cache for quick retrieval */
3617 if (!db_is_pattern &&
3618 (entry= (acl_entry*) my_malloc(key_memory_acl_cache,
3619 sizeof(acl_entry)+key_length, MYF(MY_WME))))
3620 {
3621 entry->access=(db_access & host_access);
3622 DBUG_ASSERT(key_length < 0xffff);
3623 entry->length=(uint16)key_length;
3624 memcpy((uchar*) entry->key,key,key_length);
3625 acl_cache->add(entry);
3626 }
3627 mysql_mutex_unlock(&acl_cache->lock);
3628 DBUG_PRINT("exit", ("access: 0x%llx", (longlong) (db_access & host_access)));
3629 DBUG_RETURN(db_access & host_access);
3630 }
3631
3632 /*
3633 Check if there are any possible matching entries for this host
3634
3635 NOTES
3636 All host names without wild cards are stored in a hash table,
3637 entries with wildcards are stored in a dynamic array
3638 */
3639
init_check_host(void)3640 static void init_check_host(void)
3641 {
3642 DBUG_ENTER("init_check_host");
3643 (void) my_init_dynamic_array(key_memory_acl_mem, &acl_wild_hosts,
3644 sizeof(struct acl_host_and_ip),
3645 acl_users.elements, 1, MYF(0));
3646 (void) my_hash_init(key_memory_acl_mem, &acl_check_hosts,system_charset_info,
3647 acl_users.elements, 0, 0,
3648 (my_hash_get_key) check_get_key, 0, 0);
3649 if (!allow_all_hosts)
3650 {
3651 for (uint i=0 ; i < acl_users.elements ; i++)
3652 {
3653 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
3654 if (strchr(acl_user->host.hostname,wild_many) ||
3655 strchr(acl_user->host.hostname,wild_one) ||
3656 acl_user->host.ip_mask)
3657 { // Has wildcard
3658 uint j;
3659 for (j=0 ; j < acl_wild_hosts.elements ; j++)
3660 { // Check if host already exists
3661 acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j,
3662 acl_host_and_ip *);
3663 if (!my_strcasecmp(system_charset_info,
3664 acl_user->host.hostname, acl->hostname))
3665 break; // already stored
3666 }
3667 if (j == acl_wild_hosts.elements) // If new
3668 (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
3669 }
3670 else if (!my_hash_search(&acl_check_hosts,(uchar*)
3671 acl_user->host.hostname,
3672 strlen(acl_user->host.hostname)))
3673 {
3674 if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
3675 { // End of memory
3676 allow_all_hosts=1; // Should never happen
3677 DBUG_VOID_RETURN;
3678 }
3679 }
3680 }
3681 }
3682 freeze_size(&acl_wild_hosts);
3683 freeze_size(&acl_check_hosts.array);
3684 DBUG_VOID_RETURN;
3685 }
3686
3687
3688 /*
3689 Rebuild lists used for checking of allowed hosts
3690
3691 We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
3692 dropping or renaming user, since they contain pointers to elements of
3693 'acl_user' array, which are invalidated by drop operation, and use
3694 ACL_USER::host::hostname as a key, which is changed by rename.
3695 */
rebuild_check_host(void)3696 static void rebuild_check_host(void)
3697 {
3698 delete_dynamic(&acl_wild_hosts);
3699 my_hash_free(&acl_check_hosts);
3700 init_check_host();
3701 }
3702
3703 /*
3704 Reset a role role_grants dynamic array.
3705 Also, the role's access bits are reset to the ones present in the table.
3706 */
acl_role_reset_role_arrays(void * ptr,void * not_used)3707 static my_bool acl_role_reset_role_arrays(void *ptr,
3708 void * not_used __attribute__((unused)))
3709 {
3710 ACL_ROLE *role= (ACL_ROLE *)ptr;
3711 reset_dynamic(&role->role_grants);
3712 reset_dynamic(&role->parent_grantee);
3713 role->counter= 0;
3714 return 0;
3715 }
3716
3717 /*
3718 Add a the coresponding pointers present in the mapping to the entries in
3719 acl_users and acl_roles
3720 */
add_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role)3721 static bool add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
3722 {
3723 return push_dynamic(&grantee->role_grants, (uchar*) &role)
3724 || push_dynamic(&role->parent_grantee, (uchar*) &grantee);
3725
3726 }
3727
3728 /*
3729 Revert the last add_role_user_mapping() action
3730 */
undo_add_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role)3731 static void undo_add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
3732 {
3733 void *pop __attribute__((unused));
3734
3735 pop= pop_dynamic(&grantee->role_grants);
3736 DBUG_ASSERT(role == *(ACL_ROLE**)pop);
3737
3738 pop= pop_dynamic(&role->parent_grantee);
3739 DBUG_ASSERT(grantee == *(ACL_USER_BASE**)pop);
3740 }
3741
3742 /*
3743 this helper is used when building role_grants and parent_grantee arrays
3744 from scratch.
3745
3746 this happens either on initial loading of data from tables, in acl_load().
3747 or in rebuild_role_grants after acl_role_reset_role_arrays().
3748 */
add_role_user_mapping(const char * uname,const char * hname,const char * rname)3749 static bool add_role_user_mapping(const char *uname, const char *hname,
3750 const char *rname)
3751 {
3752 ACL_USER_BASE *grantee= find_acl_user_base(uname, hname);
3753 ACL_ROLE *role= find_acl_role(rname);
3754
3755 if (grantee == NULL || role == NULL)
3756 return 1;
3757
3758 /*
3759 because all arrays are rebuilt completely, and counters were also reset,
3760 we can increment them here, and after the rebuild all counters will
3761 have correct values (equal to the number of roles granted).
3762 */
3763 if (grantee->flags & IS_ROLE)
3764 ((ACL_ROLE*)grantee)->counter++;
3765 return add_role_user_mapping(grantee, role);
3766 }
3767
3768 /*
3769 This helper function is used to removes roles and grantees
3770 from the corresponding cross-reference arrays. see remove_role_user_mapping().
3771 as such, it asserts that an element to delete is present in the array,
3772 and is present only once.
3773 */
remove_ptr_from_dynarray(DYNAMIC_ARRAY * array,void * ptr)3774 static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr)
3775 {
3776 bool found __attribute__((unused))= false;
3777 for (uint i= 0; i < array->elements; i++)
3778 {
3779 if (ptr == *dynamic_element(array, i, void**))
3780 {
3781 DBUG_ASSERT(!found);
3782 delete_dynamic_element(array, i);
3783 IF_DBUG_ASSERT(found= true, break);
3784 }
3785 }
3786 DBUG_ASSERT(found);
3787 }
3788
remove_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role,int grantee_idx=-1,int role_idx=-1)3789 static void remove_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role,
3790 int grantee_idx=-1, int role_idx=-1)
3791 {
3792 remove_ptr_from_dynarray(&grantee->role_grants, role);
3793 remove_ptr_from_dynarray(&role->parent_grantee, grantee);
3794 }
3795
3796
add_role_user_mapping_action(void * ptr,void * unused)3797 static my_bool add_role_user_mapping_action(void *ptr, void *unused __attribute__((unused)))
3798 {
3799 ROLE_GRANT_PAIR *pair= (ROLE_GRANT_PAIR*)ptr;
3800 bool status __attribute__((unused));
3801 status= add_role_user_mapping(pair->u_uname, pair->u_hname, pair->r_uname);
3802 /*
3803 The invariant chosen is that acl_roles_mappings should _always_
3804 only contain valid entries, referencing correct user and role grants.
3805 If add_role_user_mapping detects an invalid entry, it will not add
3806 the mapping into the ACL_USER::role_grants array.
3807 */
3808 DBUG_ASSERT(status == 0);
3809 return 0;
3810 }
3811
3812
3813 /*
3814 Rebuild the role grants every time the acl_users is modified
3815
3816 The role grants in the ACL_USER class need to be rebuilt, as they contain
3817 pointers to elements of the acl_users array.
3818 */
3819
rebuild_role_grants(void)3820 static void rebuild_role_grants(void)
3821 {
3822 DBUG_ENTER("rebuild_role_grants");
3823 /*
3824 Reset every user's and role's role_grants array
3825 */
3826 for (uint i=0; i < acl_users.elements; i++) {
3827 ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *);
3828 reset_dynamic(&user->role_grants);
3829 }
3830 my_hash_iterate(&acl_roles, acl_role_reset_role_arrays, NULL);
3831
3832 /* Rebuild the direct links between users and roles in ACL_USER::role_grants */
3833 my_hash_iterate(&acl_roles_mappings, add_role_user_mapping_action, NULL);
3834
3835 DBUG_VOID_RETURN;
3836 }
3837
3838
3839 /* Return true if there is no users that can match the given host */
acl_check_host(const char * host,const char * ip)3840 bool acl_check_host(const char *host, const char *ip)
3841 {
3842 if (allow_all_hosts)
3843 return 0;
3844 mysql_mutex_lock(&acl_cache->lock);
3845
3846 if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
3847 (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
3848 {
3849 mysql_mutex_unlock(&acl_cache->lock);
3850 return 0; // Found host
3851 }
3852 for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
3853 {
3854 acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
3855 if (compare_hostname(acl, host, ip))
3856 {
3857 mysql_mutex_unlock(&acl_cache->lock);
3858 return 0; // Host ok
3859 }
3860 }
3861 mysql_mutex_unlock(&acl_cache->lock);
3862 if (ip != NULL)
3863 {
3864 /* Increment HOST_CACHE.COUNT_HOST_ACL_ERRORS. */
3865 Host_errors errors;
3866 errors.m_host_acl= 1;
3867 inc_host_errors(ip, &errors);
3868 }
3869 return 1; // Host is not allowed
3870 }
3871
3872 /**
3873 Check if the user is allowed to alter the mysql.user table
3874
3875 @param thd THD
3876 @param host Hostname for the user
3877 @param user User name
3878
3879 @return Error status
3880 @retval 0 OK
3881 @retval 1 Error
3882 */
3883
check_alter_user(THD * thd,const char * host,const char * user)3884 static int check_alter_user(THD *thd, const char *host, const char *user)
3885 {
3886 int error = 1;
3887 if (!initialized)
3888 {
3889 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3890 goto end;
3891 }
3892
3893 if (IF_WSREP((!WSREP(thd) || !thd->wsrep_applier), 1) &&
3894 !thd->slave_thread && !thd->security_ctx->priv_user[0] &&
3895 !thd->bootstrap)
3896 {
3897 my_message(ER_PASSWORD_ANONYMOUS_USER,
3898 ER_THD(thd, ER_PASSWORD_ANONYMOUS_USER),
3899 MYF(0));
3900 goto end;
3901 }
3902 if (!host) // Role
3903 {
3904 my_error(ER_PASSWORD_NO_MATCH, MYF(0));
3905 goto end;
3906 }
3907
3908 if (!thd->slave_thread &&
3909 IF_WSREP((!WSREP(thd) || !thd->wsrep_applier),1) &&
3910 !thd->security_ctx->is_priv_user(user, host))
3911 {
3912 if (thd->security_ctx->password_expired)
3913 {
3914 my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
3915 goto end;
3916 }
3917 if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
3918 goto end;
3919 }
3920
3921 error = 0;
3922
3923 end:
3924 return error;
3925 }
3926 /**
3927 Check if the user is allowed to change password
3928
3929 @param thd THD
3930 @param user User, hostname, new password or password hash
3931
3932 @return Error status
3933 @retval 0 OK
3934 @retval 1 ERROR; In this case the error is sent to the client.
3935 */
3936
check_change_password(THD * thd,LEX_USER * user)3937 bool check_change_password(THD *thd, LEX_USER *user)
3938 {
3939 LEX_USER *real_user= get_current_user(thd, user);
3940 user->user= real_user->user;
3941 user->host= real_user->host;
3942 return check_alter_user(thd, user->host.str, user->user.str);
3943 }
3944
3945
3946 /**
3947 Change a password for a user.
3948
3949 @param thd THD
3950 @param user User, hostname, new password hash
3951
3952 @return Error code
3953 @retval 0 ok
3954 @retval 1 ERROR; In this case the error is sent to the client.
3955 */
change_password(THD * thd,LEX_USER * user)3956 bool change_password(THD *thd, LEX_USER *user)
3957 {
3958 Grant_tables tables;
3959 /* Buffer should be extended when password length is extended. */
3960 char buff[512];
3961 ulong query_length= 0;
3962 enum_binlog_format save_binlog_format;
3963 bool result= false, acl_cache_is_locked= false;
3964 ACL_USER *acl_user;
3965 ACL_USER::AUTH auth;
3966 const char *password_plugin= 0;
3967 const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
3968 DBUG_ENTER("change_password");
3969 DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
3970 user->host.str, user->user.str, user->auth->auth_str.str));
3971 DBUG_ASSERT(user->host.str != 0); // Ensured by caller
3972
3973 /*
3974 This statement will be replicated as a statement, even when using
3975 row-based replication. The flag will be reset at the end of the
3976 statement.
3977 This has to be handled here as it's called by set_var.cc, which is
3978 not automaticly handled by sql_parse.cc
3979 */
3980 save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
3981
3982 if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
3983 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
3984
3985 if ((result= tables.open_and_lock(thd, Table_user, TL_WRITE)))
3986 DBUG_RETURN(result != 1);
3987
3988 acl_cache_is_locked= 1;
3989 mysql_mutex_lock(&acl_cache->lock);
3990
3991 if (!(acl_user= find_user_exact(user->host.str, user->user.str)))
3992 {
3993 my_error(ER_PASSWORD_NO_MATCH, MYF(0));
3994 goto end;
3995 }
3996
3997 if (acl_user->nauth == 1 &&
3998 (acl_user->auth[0].plugin.str == native_password_plugin_name.str ||
3999 acl_user->auth[0].plugin.str == old_password_plugin_name.str))
4000 {
4001 /* historical hack of auto-changing the plugin */
4002 acl_user->auth[0].plugin= guess_auth_plugin(thd, user->auth->auth_str.length);
4003 }
4004
4005 for (uint i=0; i < acl_user->nauth; i++)
4006 {
4007 auth= acl_user->auth[i];
4008 auth.auth_string= safe_lexcstrdup_root(&acl_memroot, user->auth->auth_str);
4009 int r= set_user_auth(thd, user->user, &auth, user->auth->pwtext);
4010 if (r == ER_SET_PASSWORD_AUTH_PLUGIN)
4011 password_plugin= auth.plugin.str;
4012 else if (r)
4013 goto end;
4014 else
4015 {
4016 acl_user->auth[i]= auth;
4017 password_plugin= 0;
4018 break;
4019 }
4020 }
4021 if (password_plugin)
4022 {
4023 my_error(ER_SET_PASSWORD_AUTH_PLUGIN, MYF(0), password_plugin);
4024 goto end;
4025 }
4026
4027 /* Update the acl password expired state of user */
4028 acl_user->password_last_changed= thd->query_start();
4029 acl_user->password_expired= false;
4030
4031 /* If user is the connected user, reset the password expired field on sctx
4032 and allow the user to exit sandbox mode */
4033 if (thd->security_ctx->is_priv_user(user->user.str, user->host.str))
4034 thd->security_ctx->password_expired= false;
4035
4036 if (update_user_table_password(thd, tables.user_table(), *acl_user))
4037 goto end;
4038
4039 acl_cache->clear(1); // Clear locked hostname cache
4040 mysql_mutex_unlock(&acl_cache->lock);
4041 result= acl_cache_is_locked= 0;
4042 if (mysql_bin_log.is_open())
4043 {
4044 query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
4045 user->user.str, safe_str(user->host.str), auth.auth_string.str);
4046 DBUG_ASSERT(query_length);
4047 thd->clear_error();
4048 result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
4049 FALSE, FALSE, FALSE, 0) > 0;
4050 }
4051 end:
4052 if (acl_cache_is_locked)
4053 mysql_mutex_unlock(&acl_cache->lock);
4054 close_mysql_tables(thd);
4055
4056 #ifdef WITH_WSREP
4057 wsrep_error_label:
4058 if (WSREP(thd) && !thd->wsrep_applier)
4059 {
4060 WSREP_TO_ISOLATION_END;
4061
4062 thd->set_query(query_save);
4063 }
4064 #endif /* WITH_WSREP */
4065 thd->restore_stmt_binlog_format(save_binlog_format);
4066
4067 DBUG_RETURN(result);
4068 }
4069
acl_check_set_default_role(THD * thd,const char * host,const char * user,const char * role)4070 int acl_check_set_default_role(THD *thd, const char *host, const char *user,
4071 const char *role)
4072 {
4073 DBUG_ENTER("acl_check_set_default_role");
4074 DBUG_RETURN(check_alter_user(thd, host, user) ||
4075 check_user_can_set_role(thd, user, host, NULL, role, NULL));
4076 }
4077
acl_set_default_role(THD * thd,const char * host,const char * user,const char * rolename)4078 int acl_set_default_role(THD *thd, const char *host, const char *user,
4079 const char *rolename)
4080 {
4081 Grant_tables tables;
4082 char user_key[MAX_KEY_LENGTH];
4083 int result= 1;
4084 int error;
4085 ulong query_length= 0;
4086 bool clear_role= FALSE;
4087 char buff[512];
4088 enum_binlog_format save_binlog_format= thd->get_current_stmt_binlog_format();
4089 const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
4090
4091 DBUG_ENTER("acl_set_default_role");
4092 DBUG_PRINT("enter",("host: '%s' user: '%s' rolename: '%s'",
4093 user, safe_str(host), safe_str(rolename)));
4094
4095 if (!strcasecmp(rolename, "NONE"))
4096 clear_role= TRUE;
4097
4098 if (mysql_bin_log.is_open() ||
4099 (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)))
4100 {
4101 query_length=
4102 sprintf(buff,"SET DEFAULT ROLE '%-.120s' FOR '%-.120s'@'%-.120s'",
4103 safe_str(rolename), user, safe_str(host));
4104 }
4105
4106 /*
4107 This statement will be replicated as a statement, even when using
4108 row-based replication. The flag will be reset at the end of the
4109 statement.
4110 This has to be handled here as it's called by set_var.cc, which is
4111 not automaticly handled by sql_parse.cc
4112 */
4113 save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
4114
4115 if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
4116 {
4117 thd->set_query(buff, query_length, system_charset_info);
4118 // Attention!!! here is implicit goto error;
4119 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
4120 }
4121
4122 /*
4123 Extra block due to WSREP_TO_ISOLATION_BEGIN using goto.
4124 TODO(cvicentiu) Should move this block out in a new function.
4125 */
4126 {
4127 if ((result= tables.open_and_lock(thd, Table_user, TL_WRITE)))
4128 DBUG_RETURN(result != 1);
4129
4130 const User_table& user_table= tables.user_table();
4131 TABLE *table= user_table.table();
4132
4133 result= 1;
4134
4135 mysql_mutex_lock(&acl_cache->lock);
4136 ACL_USER *acl_user;
4137 if (!(acl_user= find_user_exact(host, user)))
4138 {
4139 mysql_mutex_unlock(&acl_cache->lock);
4140 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
4141 MYF(0));
4142 goto end;
4143 }
4144
4145 if (!clear_role)
4146 {
4147 /* set new default_rolename */
4148 acl_user->default_rolename.str= safe_strdup_root(&acl_memroot, rolename);
4149 acl_user->default_rolename.length= strlen(rolename);
4150 }
4151 else
4152 {
4153 /* clear the default_rolename */
4154 acl_user->default_rolename.str = NULL;
4155 acl_user->default_rolename.length = 0;
4156 }
4157
4158 /* update the mysql.user table with the new default role */
4159 tables.user_table().table()->use_all_columns();
4160 user_table.set_host(host, strlen(host));
4161 user_table.set_user(user, strlen(user));
4162 key_copy((uchar *) user_key, table->record[0], table->key_info,
4163 table->key_info->key_length);
4164
4165 if (table->file->ha_index_read_idx_map(table->record[0], 0,
4166 (uchar *) user_key, HA_WHOLE_KEY,
4167 HA_READ_KEY_EXACT))
4168 {
4169 mysql_mutex_unlock(&acl_cache->lock);
4170 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
4171 MYF(0));
4172 goto end;
4173 }
4174 store_record(table, record[1]);
4175 user_table.set_default_role(acl_user->default_rolename.str,
4176 acl_user->default_rolename.length);
4177 if (unlikely(error= table->file->ha_update_row(table->record[1],
4178 table->record[0])) &&
4179 error != HA_ERR_RECORD_IS_THE_SAME)
4180 {
4181 mysql_mutex_unlock(&acl_cache->lock);
4182 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4183 goto end;
4184 }
4185
4186 acl_cache->clear(1);
4187 mysql_mutex_unlock(&acl_cache->lock);
4188 result= 0;
4189 if (mysql_bin_log.is_open())
4190 {
4191 DBUG_ASSERT(query_length);
4192 thd->clear_error();
4193 result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
4194 FALSE, FALSE, FALSE, 0) > 0;
4195 }
4196 end:
4197 close_mysql_tables(thd);
4198 }
4199
4200 #ifdef WITH_WSREP
4201 wsrep_error_label:
4202 if (WSREP(thd) && !thd->wsrep_applier)
4203 {
4204 WSREP_TO_ISOLATION_END;
4205
4206 thd->set_query(query_save);
4207 }
4208 #endif /* WITH_WSREP */
4209
4210 thd->restore_stmt_binlog_format(save_binlog_format);
4211
4212 DBUG_RETURN(result);
4213 }
4214
4215
4216 /*
4217 Find user in ACL
4218
4219 SYNOPSIS
4220 is_acl_user()
4221 host host name
4222 user user name
4223
4224 RETURN
4225 FALSE user not fond
4226 TRUE there is such user
4227 */
4228
is_acl_user(const char * host,const char * user)4229 bool is_acl_user(const char *host, const char *user)
4230 {
4231 bool res;
4232
4233 /* --skip-grants */
4234 if (!initialized)
4235 return TRUE;
4236
4237 mysql_mutex_lock(&acl_cache->lock);
4238
4239 if (*host) // User
4240 res= find_user_exact(host, user) != NULL;
4241 else // Role
4242 res= find_acl_role(user) != NULL;
4243
4244 mysql_mutex_unlock(&acl_cache->lock);
4245 return res;
4246 }
4247
4248
4249 /*
4250 Find first entry that matches the specified user@host pair
4251 */
find_user_exact(const char * host,const char * user)4252 static ACL_USER *find_user_exact(const char *host, const char *user)
4253 {
4254 mysql_mutex_assert_owner(&acl_cache->lock);
4255 size_t start= acl_find_user_by_name(user);
4256
4257 for (size_t i= start; i < acl_users.elements; i++)
4258 {
4259 ACL_USER *acl_user= dynamic_element(&acl_users, i, ACL_USER*);
4260 if (i > start && strcmp(acl_user->user.str, user))
4261 return 0;
4262
4263 if (!my_strcasecmp(system_charset_info, acl_user->host.hostname, host))
4264 return acl_user;
4265 }
4266 return 0;
4267 }
4268
4269 /*
4270 Find first entry that matches the specified user@host pair
4271 */
find_user_wild(const char * host,const char * user,const char * ip)4272 static ACL_USER * find_user_wild(const char *host, const char *user, const char *ip)
4273 {
4274 mysql_mutex_assert_owner(&acl_cache->lock);
4275
4276 size_t start = acl_find_user_by_name(user);
4277
4278 for (size_t i= start; i < acl_users.elements; i++)
4279 {
4280 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
4281 if (i > start && strcmp(acl_user->user.str, user))
4282 break;
4283 if (compare_hostname(&acl_user->host, host, ip ? ip : host))
4284 return acl_user;
4285 }
4286 return 0;
4287 }
4288
4289 /*
4290 Find a role with the specified name
4291 */
find_acl_role(const char * role)4292 static ACL_ROLE *find_acl_role(const char *role)
4293 {
4294 DBUG_ENTER("find_acl_role");
4295 DBUG_PRINT("enter",("role: '%s'", role));
4296 DBUG_PRINT("info", ("Hash elements: %ld", acl_roles.records));
4297
4298 mysql_mutex_assert_owner(&acl_cache->lock);
4299
4300 ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role,
4301 strlen(role));
4302 DBUG_RETURN(r);
4303 }
4304
4305
find_acl_user_base(const char * user,const char * host)4306 static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host)
4307 {
4308 if (*host)
4309 return find_user_exact(host, user);
4310
4311 return find_acl_role(user);
4312 }
4313
4314
4315 /*
4316 Comparing of hostnames
4317
4318 NOTES
4319 A hostname may be of type:
4320 hostname (May include wildcards); monty.pp.sci.fi
4321 ip (May include wildcards); 192.168.0.0
4322 ip/netmask 192.168.0.0/255.255.255.0
4323
4324 A net mask of 0.0.0.0 is not allowed.
4325 */
4326
calc_ip(const char * ip,long * val,char end)4327 static const char *calc_ip(const char *ip, long *val, char end)
4328 {
4329 long ip_val,tmp;
4330 if (!(ip=str2int(ip,10,0,255,&ip_val)) || *ip != '.')
4331 return 0;
4332 ip_val<<=24;
4333 if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
4334 return 0;
4335 ip_val+=tmp<<16;
4336 if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
4337 return 0;
4338 ip_val+=tmp<<8;
4339 if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != end)
4340 return 0;
4341 *val=ip_val+tmp;
4342 return ip;
4343 }
4344
4345
update_hostname(acl_host_and_ip * host,const char * hostname)4346 static void update_hostname(acl_host_and_ip *host, const char *hostname)
4347 {
4348 // fix historical undocumented convention that empty host is the same as '%'
4349 hostname=const_cast<char*>(hostname ? hostname : host_not_specified.str);
4350 host->hostname=(char*) hostname; // This will not be modified!
4351 if (!(hostname= calc_ip(hostname,&host->ip,'/')) ||
4352 !(hostname= calc_ip(hostname+1,&host->ip_mask,'\0')))
4353 {
4354 host->ip= host->ip_mask=0; // Not a masked ip
4355 }
4356 }
4357
4358
compare_hostname(const acl_host_and_ip * host,const char * hostname,const char * ip)4359 static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
4360 const char *ip)
4361 {
4362 long tmp;
4363 if (host->ip_mask && ip && calc_ip(ip,&tmp,'\0'))
4364 {
4365 return (tmp & host->ip_mask) == host->ip;
4366 }
4367 return (!host->hostname ||
4368 (hostname && !wild_case_compare(system_charset_info,
4369 hostname, host->hostname)) ||
4370 (ip && !wild_compare(ip, host->hostname, 0)));
4371 }
4372
4373 /**
4374 Check if the given host name needs to be resolved or not.
4375 Host name has to be resolved if it actually contains *name*.
4376
4377 For example:
4378 192.168.1.1 --> FALSE
4379 192.168.1.0/255.255.255.0 --> FALSE
4380 % --> FALSE
4381 192.168.1.% --> FALSE
4382 AB% --> FALSE
4383
4384 AAAAFFFF --> TRUE (Hostname)
4385 AAAA:FFFF:1234:5678 --> FALSE
4386 ::1 --> FALSE
4387
4388 This function does not check if the given string is a valid host name or
4389 not. It assumes that the argument is a valid host name.
4390
4391 @param hostname the string to check.
4392
4393 @return a flag telling if the argument needs to be resolved or not.
4394 @retval TRUE the argument is a host name and needs to be resolved.
4395 @retval FALSE the argument is either an IP address, or a patter and
4396 should not be resolved.
4397 */
4398
hostname_requires_resolving(const char * hostname)4399 bool hostname_requires_resolving(const char *hostname)
4400 {
4401 if (!hostname)
4402 return FALSE;
4403
4404 /* Check if hostname is the localhost. */
4405
4406 size_t hostname_len= strlen(hostname);
4407 size_t localhost_len= strlen(my_localhost);
4408
4409 if (hostname == my_localhost ||
4410 (hostname_len == localhost_len &&
4411 !system_charset_info->strnncoll(
4412 (const uchar *) hostname, hostname_len,
4413 (const uchar *) my_localhost, strlen(my_localhost))))
4414 {
4415 return FALSE;
4416 }
4417
4418 /*
4419 If the string contains any of {':', '%', '_', '/'}, it is definitely
4420 not a host name:
4421 - ':' means that the string is an IPv6 address;
4422 - '%' or '_' means that the string is a pattern;
4423 - '/' means that the string is an IPv4 network address;
4424 */
4425
4426 for (const char *p= hostname; *p; ++p)
4427 {
4428 switch (*p) {
4429 case ':':
4430 case '%':
4431 case '_':
4432 case '/':
4433 return FALSE;
4434 }
4435 }
4436
4437 /*
4438 Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
4439 (12.34.56.78). The assumption is that if the string contains only
4440 digits and dots, it is an IPv4 address. Otherwise -- a host name.
4441 */
4442
4443 for (const char *p= hostname; *p; ++p)
4444 {
4445 if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
4446 return TRUE; /* a "letter" has been found. */
4447 }
4448
4449 return FALSE; /* all characters are either dots or digits. */
4450 }
4451
4452
4453 /**
4454 Update record for user in mysql.user privilege table with new password.
4455
4456 @see change_password
4457 */
4458
update_user_table_password(THD * thd,const User_table & user_table,const ACL_USER & user)4459 static bool update_user_table_password(THD *thd, const User_table& user_table,
4460 const ACL_USER &user)
4461 {
4462 char user_key[MAX_KEY_LENGTH];
4463 int error;
4464 DBUG_ENTER("update_user_table_password");
4465
4466 TABLE *table= user_table.table();
4467 table->use_all_columns();
4468 user_table.set_host(user.host.hostname, user.hostname_length);
4469 user_table.set_user(user.user.str, user.user.length);
4470 key_copy((uchar *) user_key, table->record[0], table->key_info,
4471 table->key_info->key_length);
4472
4473 if (table->file->ha_index_read_idx_map(table->record[0], 0,
4474 (uchar *) user_key, HA_WHOLE_KEY,
4475 HA_READ_KEY_EXACT))
4476 {
4477 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
4478 MYF(0));
4479 DBUG_RETURN(1);
4480 }
4481 store_record(table, record[1]);
4482
4483 if (user_table.set_auth(user))
4484 {
4485 my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
4486 user_table.name().str, 3, user_table.num_fields(),
4487 static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
4488 DBUG_RETURN(1);
4489 }
4490
4491 user_table.set_password_expired(user.password_expired);
4492 user_table.set_password_last_changed(user.password_last_changed);
4493
4494 if (unlikely(error= table->file->ha_update_row(table->record[1],
4495 table->record[0])) &&
4496 error != HA_ERR_RECORD_IS_THE_SAME)
4497 {
4498 table->file->print_error(error,MYF(0));
4499 DBUG_RETURN(1);
4500 }
4501
4502 DBUG_RETURN(0);
4503 }
4504
4505
4506 /*
4507 Return 1 if we are allowed to create new users
4508 the logic here is: INSERT_ACL is sufficient.
4509 It's also a requirement in opt_safe_user_create,
4510 otherwise CREATE_USER_ACL is enough.
4511 */
4512
test_if_create_new_users(THD * thd)4513 static bool test_if_create_new_users(THD *thd)
4514 {
4515 Security_context *sctx= thd->security_ctx;
4516 bool create_new_users= MY_TEST(sctx->master_access & INSERT_ACL) ||
4517 (!opt_safe_user_create &&
4518 MY_TEST(sctx->master_access & CREATE_USER_ACL));
4519 if (!create_new_users)
4520 {
4521 TABLE_LIST tl;
4522 privilege_t db_access(NO_ACL);
4523 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLE_NAME[USER_TABLE],
4524 NULL, TL_WRITE);
4525 create_new_users= 1;
4526
4527 db_access=acl_get(sctx->host, sctx->ip,
4528 sctx->priv_user, tl.db.str, 0);
4529 if (sctx->priv_role[0])
4530 db_access|= acl_get("", "", sctx->priv_role, tl.db.str, 0);
4531 if (!(db_access & INSERT_ACL))
4532 {
4533 if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
4534 create_new_users=0;
4535 }
4536 }
4537 return create_new_users;
4538 }
4539
4540
4541 /****************************************************************************
4542 Handle GRANT commands
4543 ****************************************************************************/
4544 static USER_AUTH auth_no_password;
4545
replace_user_table(THD * thd,const User_table & user_table,LEX_USER * const combo,privilege_t rights,const bool revoke_grant,const bool can_create_user,const bool no_auto_create)4546 static int replace_user_table(THD *thd, const User_table &user_table,
4547 LEX_USER * const combo, privilege_t rights,
4548 const bool revoke_grant,
4549 const bool can_create_user,
4550 const bool no_auto_create)
4551 {
4552 int error = -1;
4553 uint nauth= 0;
4554 bool old_row_exists=0;
4555 uchar user_key[MAX_KEY_LENGTH];
4556 bool handle_as_role= combo->is_role();
4557 LEX *lex= thd->lex;
4558 TABLE *table= user_table.table();
4559 ACL_USER new_acl_user, *old_acl_user= 0;
4560 DBUG_ENTER("replace_user_table");
4561
4562 mysql_mutex_assert_owner(&acl_cache->lock);
4563
4564 table->use_all_columns();
4565 user_table.set_host(combo->host.str,combo->host.length);
4566 user_table.set_user(combo->user.str,combo->user.length);
4567 key_copy(user_key, table->record[0], table->key_info,
4568 table->key_info->key_length);
4569
4570 if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
4571 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
4572 {
4573 if (revoke_grant)
4574 {
4575 if (combo->host.length)
4576 my_error(ER_NONEXISTING_GRANT, MYF(0), combo->user.str,
4577 combo->host.str);
4578 else
4579 my_error(ER_INVALID_ROLE, MYF(0), combo->user.str);
4580 goto end;
4581 }
4582 /*
4583 There are four options which affect the process of creation of
4584 a new user (mysqld option --safe-create-user, 'insert' privilege
4585 on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
4586 SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
4587 how it should work.
4588 if (safe-user-create && ! INSERT_priv) => reject
4589 else if (identified_by) => create
4590 else if (no_auto_create_user) => reject
4591 else create
4592
4593 see also test_if_create_new_users()
4594 */
4595 else if (!combo->has_auth() && no_auto_create)
4596 {
4597 my_error(ER_PASSWORD_NO_MATCH, MYF(0));
4598 goto end;
4599 }
4600 else if (!can_create_user)
4601 {
4602 my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
4603 goto end;
4604 }
4605
4606 if (!combo->auth)
4607 combo->auth= &auth_no_password;
4608
4609 old_row_exists = 0;
4610 restore_record(table, s->default_values);
4611 user_table.set_host(combo->host.str, combo->host.length);
4612 user_table.set_user(combo->user.str, combo->user.length);
4613 }
4614 else
4615 {
4616 old_row_exists = 1;
4617 store_record(table,record[1]); // Save copy for update
4618 }
4619
4620 for (USER_AUTH *auth= combo->auth; auth; auth= auth->next)
4621 {
4622 nauth++;
4623 if (auth->plugin.length)
4624 {
4625 if (!plugin_is_ready(&auth->plugin, MYSQL_AUTHENTICATION_PLUGIN))
4626 {
4627 my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth->plugin.str);
4628 goto end;
4629 }
4630 }
4631 else
4632 auth->plugin= guess_auth_plugin(thd, auth->auth_str.length);
4633 }
4634
4635 /* Update table columns with new privileges */
4636 user_table.set_access(rights, revoke_grant);
4637 rights= user_table.get_access();
4638
4639 if (handle_as_role)
4640 {
4641 if (old_row_exists && !user_table.get_is_role())
4642 {
4643 goto end;
4644 }
4645 if (user_table.set_is_role(true))
4646 {
4647 my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
4648 user_table.name().str,
4649 ROLE_ASSIGN_COLUMN_IDX + 1, user_table.num_fields(),
4650 static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
4651 goto end;
4652 }
4653 }
4654 else
4655 {
4656 old_acl_user= find_user_exact(combo->host.str, combo->user.str);
4657 if ((old_acl_user != NULL) != old_row_exists)
4658 {
4659 my_error(ER_PASSWORD_NO_MATCH, MYF(0));
4660 goto end;
4661 }
4662 new_acl_user= old_row_exists ? *old_acl_user :
4663 ACL_USER(thd, *combo, lex->account_options, rights);
4664 if (acl_user_update(thd, &new_acl_user, nauth,
4665 *combo, lex->account_options, rights))
4666 goto end;
4667
4668 if (user_table.set_auth(new_acl_user))
4669 {
4670 my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
4671 user_table.name().str, 3, user_table.num_fields(),
4672 static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
4673 DBUG_RETURN(1);
4674 }
4675
4676 switch (lex->account_options.ssl_type) {
4677 case SSL_TYPE_NOT_SPECIFIED:
4678 break;
4679 case SSL_TYPE_NONE:
4680 case SSL_TYPE_ANY:
4681 case SSL_TYPE_X509:
4682 user_table.set_ssl_type(lex->account_options.ssl_type);
4683 user_table.set_ssl_cipher("", 0);
4684 user_table.set_x509_issuer("", 0);
4685 user_table.set_x509_subject("", 0);
4686 break;
4687 case SSL_TYPE_SPECIFIED:
4688 user_table.set_ssl_type(lex->account_options.ssl_type);
4689 if (lex->account_options.ssl_cipher.str)
4690 user_table.set_ssl_cipher(lex->account_options.ssl_cipher.str,
4691 lex->account_options.ssl_cipher.length);
4692 else
4693 user_table.set_ssl_cipher("", 0);
4694 if (lex->account_options.x509_issuer.str)
4695 user_table.set_x509_issuer(lex->account_options.x509_issuer.str,
4696 lex->account_options.x509_issuer.length);
4697 else
4698 user_table.set_x509_issuer("", 0);
4699 if (lex->account_options.x509_subject.str)
4700 user_table.set_x509_subject(lex->account_options.x509_subject.str,
4701 lex->account_options.x509_subject.length);
4702 else
4703 user_table.set_x509_subject("", 0);
4704 break;
4705 }
4706
4707 if (lex->account_options.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
4708 user_table.set_max_questions(lex->account_options.questions);
4709 if (lex->account_options.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
4710 user_table.set_max_updates(lex->account_options.updates);
4711 if (lex->account_options.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
4712 user_table.set_max_connections(lex->account_options.conn_per_hour);
4713 if (lex->account_options.specified_limits & USER_RESOURCES::USER_CONNECTIONS)
4714 user_table.set_max_user_connections(lex->account_options.user_conn);
4715 if (lex->account_options.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
4716 user_table.set_max_statement_time(lex->account_options.max_statement_time);
4717
4718 mqh_used= (mqh_used || lex->account_options.questions || lex->account_options.updates ||
4719 lex->account_options.conn_per_hour || lex->account_options.user_conn ||
4720 lex->account_options.max_statement_time != 0.0);
4721
4722 if (lex->account_options.account_locked != ACCOUNTLOCK_UNSPECIFIED)
4723 user_table.set_account_locked(new_acl_user.account_locked);
4724
4725 if (nauth)
4726 user_table.set_password_last_changed(new_acl_user.password_last_changed);
4727 if (lex->account_options.password_expire != PASSWORD_EXPIRE_UNSPECIFIED)
4728 {
4729 user_table.set_password_lifetime(new_acl_user.password_lifetime);
4730 user_table.set_password_expired(new_acl_user.password_expired);
4731 }
4732 }
4733
4734 if (old_row_exists)
4735 {
4736 /*
4737 We should NEVER delete from the user table, as a uses can still
4738 use mysqld even if he doesn't have any privileges in the user table!
4739 */
4740 if (cmp_record(table, record[1]))
4741 {
4742 if (unlikely(error= table->file->ha_update_row(table->record[1],
4743 table->record[0])) &&
4744 error != HA_ERR_RECORD_IS_THE_SAME)
4745 { // This should never happen
4746 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4747 error= -1; /* purecov: deadcode */
4748 goto end; /* purecov: deadcode */
4749 }
4750 else
4751 error= 0;
4752 }
4753 }
4754 else if (unlikely(error=table->file->ha_write_row(table->record[0])))
4755 {
4756 // This should never happen
4757 if (table->file->is_fatal_error(error, HA_CHECK_DUP))
4758 {
4759 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4760 error= -1; /* purecov: deadcode */
4761 goto end; /* purecov: deadcode */
4762 }
4763 }
4764 error=0; // Privileges granted / revoked
4765
4766 end:
4767 if (likely(!error))
4768 {
4769 acl_cache->clear(1); // Clear privilege cache
4770 if (handle_as_role)
4771 {
4772 if (old_row_exists)
4773 acl_update_role(combo->user.str, rights);
4774 else
4775 acl_insert_role(combo->user.str, rights);
4776 }
4777 else
4778 {
4779 if (old_acl_user)
4780 *old_acl_user= new_acl_user;
4781 else
4782 {
4783 push_new_user(new_acl_user);
4784 rebuild_acl_users();
4785
4786 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
4787 rebuild_check_host();
4788
4789 /*
4790 Rebuild every user's role_grants since 'acl_users' has been sorted
4791 and old pointers to ACL_USER elements are no longer valid
4792 */
4793 rebuild_role_grants();
4794 }
4795 }
4796 }
4797 DBUG_RETURN(error);
4798 }
4799
4800
4801 /*
4802 change grants in the mysql.db table
4803 */
4804
replace_db_table(TABLE * table,const char * db,const LEX_USER & combo,privilege_t rights,const bool revoke_grant)4805 static int replace_db_table(TABLE *table, const char *db,
4806 const LEX_USER &combo,
4807 privilege_t rights, const bool revoke_grant)
4808 {
4809 uint i;
4810 ulonglong priv;
4811 privilege_t store_rights(NO_ACL);
4812 bool old_row_exists=0;
4813 int error;
4814 char what= revoke_grant ? 'N' : 'Y';
4815 uchar user_key[MAX_KEY_LENGTH];
4816 DBUG_ENTER("replace_db_table");
4817
4818 /* Check if there is such a user in user table in memory? */
4819 if (!find_user_wild(combo.host.str,combo.user.str))
4820 {
4821 /* The user could be a role, check if the user is registered as a role */
4822 if (!combo.host.length && !find_acl_role(combo.user.str))
4823 {
4824 my_message(ER_PASSWORD_NO_MATCH, ER_THD(table->in_use,
4825 ER_PASSWORD_NO_MATCH), MYF(0));
4826 DBUG_RETURN(-1);
4827 }
4828 }
4829
4830 table->use_all_columns();
4831 table->field[0]->store(combo.host.str,combo.host.length,
4832 system_charset_info);
4833 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4834 table->field[2]->store(combo.user.str,combo.user.length,
4835 system_charset_info);
4836 key_copy(user_key, table->record[0], table->key_info,
4837 table->key_info->key_length);
4838
4839 if (table->file->ha_index_read_idx_map(table->record[0],0, user_key,
4840 HA_WHOLE_KEY,
4841 HA_READ_KEY_EXACT))
4842 {
4843 if (revoke_grant)
4844 { // no row, no revoke
4845 my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
4846 goto abort;
4847 }
4848 old_row_exists = 0;
4849 restore_record(table, s->default_values);
4850 table->field[0]->store(combo.host.str,combo.host.length,
4851 system_charset_info);
4852 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4853 table->field[2]->store(combo.user.str,combo.user.length,
4854 system_charset_info);
4855 }
4856 else
4857 {
4858 old_row_exists = 1;
4859 store_record(table,record[1]);
4860 }
4861
4862 store_rights=get_rights_for_db(rights);
4863 for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
4864 {
4865 if (priv & store_rights) // do it if priv is chosen
4866 table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
4867 }
4868 rights=get_access(table,3);
4869 rights=fix_rights_for_db(rights);
4870
4871 if (old_row_exists)
4872 {
4873 /* update old existing row */
4874 if (rights)
4875 {
4876 if (unlikely((error= table->file->ha_update_row(table->record[1],
4877 table->record[0]))) &&
4878 error != HA_ERR_RECORD_IS_THE_SAME)
4879 goto table_error; /* purecov: deadcode */
4880 }
4881 else /* must have been a revoke of all privileges */
4882 {
4883 if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4884 goto table_error; /* purecov: deadcode */
4885 }
4886 }
4887 else if (rights &&
4888 (unlikely(error= table->file->ha_write_row(table->record[0]))))
4889 {
4890 if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4891 goto table_error; /* purecov: deadcode */
4892 }
4893
4894 acl_cache->clear(1); // Clear privilege cache
4895 if (old_row_exists)
4896 acl_update_db(combo.user.str,combo.host.str,db,rights);
4897 else if (rights)
4898 {
4899 /*
4900 If we did not have an already existing row, for users, we must always
4901 insert an ACL_DB entry. For roles however, it is possible that one was
4902 already created when DB privileges were propagated from other granted
4903 roles onto the current role. For this case, first try to update the
4904 existing entry, otherwise insert a new one.
4905 */
4906 if (!combo.is_role() ||
4907 !acl_update_db(combo.user.str, combo.host.str, db, rights))
4908 {
4909 acl_insert_db(combo.user.str,combo.host.str,db,rights);
4910 }
4911 }
4912 DBUG_RETURN(0);
4913
4914 /* This could only happen if the grant tables got corrupted */
4915 table_error:
4916 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4917
4918 abort:
4919 DBUG_RETURN(-1);
4920 }
4921
4922 /**
4923 Updates the mysql.roles_mapping table
4924
4925 @param table TABLE to update
4926 @param user user name of the grantee
4927 @param host host name of the grantee
4928 @param role role name to grant
4929 @param with_admin WITH ADMIN OPTION flag
4930 @param existing the entry in the acl_roles_mappings hash or NULL.
4931 it is never NULL if revoke_grant is true.
4932 it is NULL when a new pair is added, it's not NULL
4933 when an existing pair is updated.
4934 @param revoke_grant true for REVOKE, false for GRANT
4935 */
4936 static int
replace_roles_mapping_table(TABLE * table,LEX_CSTRING * user,LEX_CSTRING * host,LEX_CSTRING * role,bool with_admin,ROLE_GRANT_PAIR * existing,bool revoke_grant)4937 replace_roles_mapping_table(TABLE *table, LEX_CSTRING *user, LEX_CSTRING *host,
4938 LEX_CSTRING *role, bool with_admin,
4939 ROLE_GRANT_PAIR *existing, bool revoke_grant)
4940 {
4941 DBUG_ENTER("replace_roles_mapping_table");
4942
4943 uchar row_key[MAX_KEY_LENGTH];
4944 int error;
4945 table->use_all_columns();
4946 restore_record(table, s->default_values);
4947 table->field[0]->store(host->str, host->length, system_charset_info);
4948 table->field[1]->store(user->str, user->length, system_charset_info);
4949 table->field[2]->store(role->str, role->length, system_charset_info);
4950
4951 DBUG_ASSERT(!revoke_grant || existing);
4952
4953 if (existing) // delete or update
4954 {
4955 key_copy(row_key, table->record[0], table->key_info,
4956 table->key_info->key_length);
4957 if (table->file->ha_index_read_idx_map(table->record[1], 0, row_key,
4958 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
4959 {
4960 /* No match */
4961 DBUG_RETURN(1);
4962 }
4963 if (revoke_grant && !with_admin)
4964 {
4965 if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4966 {
4967 DBUG_PRINT("info", ("error deleting row '%s' '%s' '%s'",
4968 host->str, user->str, role->str));
4969 goto table_error;
4970 }
4971 }
4972 else if (with_admin)
4973 {
4974 table->field[3]->store(!revoke_grant + 1);
4975
4976 if (unlikely((error= table->file->ha_update_row(table->record[1],
4977 table->record[0]))))
4978 {
4979 DBUG_PRINT("info", ("error updating row '%s' '%s' '%s'",
4980 host->str, user->str, role->str));
4981 goto table_error;
4982 }
4983 }
4984 DBUG_RETURN(0);
4985 }
4986
4987 table->field[3]->store(with_admin + 1);
4988
4989 if (unlikely((error= table->file->ha_write_row(table->record[0]))))
4990 {
4991 DBUG_PRINT("info", ("error inserting row '%s' '%s' '%s'",
4992 host->str, user->str, role->str));
4993 goto table_error;
4994 }
4995
4996 /* all ok */
4997 DBUG_RETURN(0);
4998
4999 table_error:
5000 DBUG_PRINT("info", ("table error"));
5001 table->file->print_error(error, MYF(0));
5002 DBUG_RETURN(1);
5003 }
5004
5005
5006 /**
5007 Updates the acl_roles_mappings hash
5008
5009 @param user user name of the grantee
5010 @param host host name of the grantee
5011 @param role role name to grant
5012 @param with_admin WITH ADMIN OPTION flag
5013 @param existing the entry in the acl_roles_mappings hash or NULL.
5014 it is never NULL if revoke_grant is true.
5015 it is NULL when a new pair is added, it's not NULL
5016 when an existing pair is updated.
5017 @param revoke_grant true for REVOKE, false for GRANT
5018 */
5019 static int
update_role_mapping(LEX_CSTRING * user,LEX_CSTRING * host,LEX_CSTRING * role,bool with_admin,ROLE_GRANT_PAIR * existing,bool revoke_grant)5020 update_role_mapping(LEX_CSTRING *user, LEX_CSTRING *host, LEX_CSTRING *role,
5021 bool with_admin, ROLE_GRANT_PAIR *existing, bool revoke_grant)
5022 {
5023 if (revoke_grant)
5024 {
5025 if (with_admin)
5026 {
5027 existing->with_admin= false;
5028 return 0;
5029 }
5030 return my_hash_delete(&acl_roles_mappings, (uchar*)existing);
5031 }
5032
5033 if (existing)
5034 {
5035 existing->with_admin|= with_admin;
5036 return 0;
5037 }
5038
5039 /* allocate a new entry that will go in the hash */
5040 ROLE_GRANT_PAIR *hash_entry= new (&acl_memroot) ROLE_GRANT_PAIR;
5041 if (hash_entry->init(&acl_memroot, user->str, host->str,
5042 role->str, with_admin))
5043 return 1;
5044 return my_hash_insert(&acl_roles_mappings, (uchar*) hash_entry);
5045 }
5046
5047 static void
acl_update_proxy_user(ACL_PROXY_USER * new_value,bool is_revoke)5048 acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
5049 {
5050 mysql_mutex_assert_owner(&acl_cache->lock);
5051
5052 DBUG_ENTER("acl_update_proxy_user");
5053 for (uint i= 0; i < acl_proxy_users.elements; i++)
5054 {
5055 ACL_PROXY_USER *acl_user=
5056 dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
5057
5058 if (acl_user->pk_equals(new_value))
5059 {
5060 if (is_revoke)
5061 {
5062 DBUG_PRINT("info", ("deleting ACL_PROXY_USER"));
5063 delete_dynamic_element(&acl_proxy_users, i);
5064 }
5065 else
5066 {
5067 DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
5068 acl_user->set_data(new_value);
5069 }
5070 break;
5071 }
5072 }
5073 DBUG_VOID_RETURN;
5074 }
5075
5076
5077 static void
acl_insert_proxy_user(ACL_PROXY_USER * new_value)5078 acl_insert_proxy_user(ACL_PROXY_USER *new_value)
5079 {
5080 DBUG_ENTER("acl_insert_proxy_user");
5081 mysql_mutex_assert_owner(&acl_cache->lock);
5082 (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
5083 my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
5084 acl_proxy_users.elements,
5085 sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
5086 DBUG_VOID_RETURN;
5087 }
5088
5089
5090 static int
replace_proxies_priv_table(THD * thd,TABLE * table,const LEX_USER * user,const LEX_USER * proxied_user,bool with_grant_arg,bool revoke_grant)5091 replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
5092 const LEX_USER *proxied_user, bool with_grant_arg,
5093 bool revoke_grant)
5094 {
5095 bool old_row_exists= 0;
5096 int error;
5097 uchar user_key[MAX_KEY_LENGTH];
5098 ACL_PROXY_USER new_grant;
5099 char grantor[USER_HOST_BUFF_SIZE];
5100
5101 DBUG_ENTER("replace_proxies_priv_table");
5102
5103 if (!table)
5104 {
5105 my_error(ER_NO_SUCH_TABLE, MYF(0), MYSQL_SCHEMA_NAME.str,
5106 MYSQL_TABLE_NAME[PROXIES_PRIV_TABLE].str);
5107 DBUG_RETURN(-1);
5108 }
5109
5110 /* Check if there is such a user in user table in memory? */
5111 if (!find_user_wild(user->host.str,user->user.str))
5112 {
5113 my_message(ER_PASSWORD_NO_MATCH,
5114 ER_THD(thd, ER_PASSWORD_NO_MATCH), MYF(0));
5115 DBUG_RETURN(-1);
5116 }
5117
5118 table->use_all_columns();
5119 ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
5120 &proxied_user->host, &proxied_user->user);
5121
5122 key_copy(user_key, table->record[0], table->key_info,
5123 table->key_info->key_length);
5124
5125 get_grantor(thd, grantor);
5126
5127 if (unlikely((error= table->file->ha_index_init(0, 1))))
5128 {
5129 table->file->print_error(error, MYF(0));
5130 DBUG_PRINT("info", ("ha_index_init error"));
5131 DBUG_RETURN(-1);
5132 }
5133
5134 if (table->file->ha_index_read_map(table->record[0], user_key,
5135 HA_WHOLE_KEY,
5136 HA_READ_KEY_EXACT))
5137 {
5138 DBUG_PRINT ("info", ("Row not found"));
5139 if (revoke_grant)
5140 { // no row, no revoke
5141 my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
5142 goto abort;
5143 }
5144 old_row_exists= 0;
5145 restore_record(table, s->default_values);
5146 ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
5147 &proxied_user->host,
5148 &proxied_user->user,
5149 with_grant_arg,
5150 grantor);
5151 }
5152 else
5153 {
5154 DBUG_PRINT("info", ("Row found"));
5155 old_row_exists= 1;
5156 store_record(table, record[1]);
5157 }
5158
5159 if (old_row_exists)
5160 {
5161 /* update old existing row */
5162 if (!revoke_grant)
5163 {
5164 if (unlikely(error= table->file->ha_update_row(table->record[1],
5165 table->record[0])) &&
5166 error != HA_ERR_RECORD_IS_THE_SAME)
5167 goto table_error; /* purecov: inspected */
5168 }
5169 else
5170 {
5171 if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
5172 goto table_error; /* purecov: inspected */
5173 }
5174 }
5175 else if (unlikely((error= table->file->ha_write_row(table->record[0]))))
5176 {
5177 DBUG_PRINT("info", ("error inserting the row"));
5178 if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
5179 goto table_error; /* purecov: inspected */
5180 }
5181
5182 acl_cache->clear(1); // Clear privilege cache
5183 if (old_row_exists)
5184 {
5185 new_grant.init(user->host.str, user->user.str,
5186 proxied_user->host.str, proxied_user->user.str,
5187 with_grant_arg);
5188 acl_update_proxy_user(&new_grant, revoke_grant);
5189 }
5190 else
5191 {
5192 new_grant.init(&acl_memroot, user->host.str, user->user.str,
5193 proxied_user->host.str, proxied_user->user.str,
5194 with_grant_arg);
5195 acl_insert_proxy_user(&new_grant);
5196 }
5197
5198 table->file->ha_index_end();
5199 DBUG_RETURN(0);
5200
5201 /* This could only happen if the grant tables got corrupted */
5202 table_error:
5203 DBUG_PRINT("info", ("table error"));
5204 table->file->print_error(error, MYF(0)); /* purecov: inspected */
5205
5206 abort:
5207 DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
5208 table->file->ha_index_end();
5209 DBUG_RETURN(-1);
5210 }
5211
5212
5213 class GRANT_COLUMN :public Sql_alloc
5214 {
5215 public:
5216 char *column;
5217 privilege_t rights;
5218 privilege_t init_rights;
5219 uint key_length;
GRANT_COLUMN(String & c,privilege_t y)5220 GRANT_COLUMN(String &c, privilege_t y) :rights (y), init_rights(y)
5221 {
5222 column= (char*) memdup_root(&grant_memroot,c.ptr(), key_length=c.length());
5223 }
5224
5225 /* this constructor assumes thas source->column is allocated in grant_memroot */
GRANT_COLUMN(GRANT_COLUMN * source)5226 GRANT_COLUMN(GRANT_COLUMN *source) : column(source->column),
5227 rights (source->rights), init_rights(NO_ACL), key_length(source->key_length) { }
5228 };
5229
5230
get_key_column(GRANT_COLUMN * buff,size_t * length,my_bool not_used)5231 static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
5232 my_bool not_used __attribute__((unused)))
5233 {
5234 *length=buff->key_length;
5235 return (uchar*) buff->column;
5236 }
5237
5238 class GRANT_NAME :public Sql_alloc
5239 {
5240 public:
5241 acl_host_and_ip host;
5242 char *db, *user, *tname, *hash_key;
5243 privilege_t privs;
5244 privilege_t init_privs; /* privileges found in physical table */
5245 ulonglong sort;
5246 size_t key_length;
5247 GRANT_NAME(const char *h, const char *d,const char *u,
5248 const char *t, privilege_t p, bool is_routine);
5249 GRANT_NAME (TABLE *form, bool is_routine);
~GRANT_NAME()5250 virtual ~GRANT_NAME() {};
ok()5251 virtual bool ok() { return privs != NO_ACL; }
5252 void set_user_details(const char *h, const char *d,
5253 const char *u, const char *t,
5254 bool is_routine);
5255 };
5256
5257
get_access_value_from_val_int(Field * field)5258 static privilege_t get_access_value_from_val_int(Field *field)
5259 {
5260 return privilege_t(ALL_KNOWN_ACL & (ulonglong) field->val_int());
5261 }
5262
5263
5264 class GRANT_TABLE :public GRANT_NAME
5265 {
5266 public:
5267 privilege_t cols;
5268 privilege_t init_cols; /* privileges found in physical table */
5269 HASH hash_columns;
5270
5271 GRANT_TABLE(const char *h, const char *d,const char *u,
5272 const char *t, privilege_t p, privilege_t c);
5273 GRANT_TABLE (TABLE *form, TABLE *col_privs);
5274 ~GRANT_TABLE();
ok()5275 bool ok() { return privs != NO_ACL || cols != NO_ACL; }
init_hash()5276 void init_hash()
5277 {
5278 my_hash_init2(key_memory_acl_memex, &hash_columns, 4, system_charset_info,
5279 0, 0, 0, (my_hash_get_key) get_key_column, 0, 0, 0);
5280 }
5281 };
5282
5283
set_user_details(const char * h,const char * d,const char * u,const char * t,bool is_routine)5284 void GRANT_NAME::set_user_details(const char *h, const char *d,
5285 const char *u, const char *t,
5286 bool is_routine)
5287 {
5288 /* Host given by user */
5289 update_hostname(&host, strdup_root(&grant_memroot, h));
5290 if (db != d)
5291 {
5292 db= strdup_root(&grant_memroot, d);
5293 if (lower_case_table_names)
5294 my_casedn_str(files_charset_info, db);
5295 }
5296 user = strdup_root(&grant_memroot,u);
5297 sort= get_magic_sort("hdu", host.hostname, db, user);
5298 if (tname != t)
5299 {
5300 tname= strdup_root(&grant_memroot, t);
5301 if (lower_case_table_names || is_routine)
5302 my_casedn_str(files_charset_info, tname);
5303 }
5304 key_length= strlen(d) + strlen(u)+ strlen(t)+3;
5305 hash_key= (char*) alloc_root(&grant_memroot,key_length);
5306 strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
5307 }
5308
GRANT_NAME(const char * h,const char * d,const char * u,const char * t,privilege_t p,bool is_routine)5309 GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
5310 const char *t, privilege_t p, bool is_routine)
5311 :db(0), tname(0), privs(p), init_privs(p)
5312 {
5313 set_user_details(h, d, u, t, is_routine);
5314 }
5315
GRANT_TABLE(const char * h,const char * d,const char * u,const char * t,privilege_t p,privilege_t c)5316 GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
5317 const char *t, privilege_t p, privilege_t c)
5318 :GRANT_NAME(h,d,u,t,p, FALSE), cols(c), init_cols(NO_ACL)
5319 {
5320 init_hash();
5321 }
5322
5323 /*
5324 create a new GRANT_TABLE entry for role inheritance. init_* fields are set
5325 to 0
5326 */
GRANT_NAME(TABLE * form,bool is_routine)5327 GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
5328 :privs(NO_ACL), init_privs(NO_ACL)
5329 {
5330 user= safe_str(get_field(&grant_memroot,form->field[2]));
5331
5332 const char *hostname= get_field(&grant_memroot, form->field[0]);
5333 mysql_mutex_lock(&acl_cache->lock);
5334 if (!hostname && find_acl_role(user))
5335 hostname= "";
5336 mysql_mutex_unlock(&acl_cache->lock);
5337 update_hostname(&host, hostname);
5338
5339 db= get_field(&grant_memroot,form->field[1]);
5340 tname= get_field(&grant_memroot,form->field[3]);
5341 if (!db || !tname)
5342 {
5343 /* Wrong table row; Ignore it */
5344 return; /* purecov: inspected */
5345 }
5346 sort= get_magic_sort("hdu", host.hostname, db, user);
5347 if (lower_case_table_names)
5348 {
5349 my_casedn_str(files_charset_info, db);
5350 }
5351 if (lower_case_table_names || is_routine)
5352 {
5353 my_casedn_str(files_charset_info, tname);
5354 }
5355 key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
5356 hash_key= (char*) alloc_root(&grant_memroot, key_length);
5357 strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
5358 privs = get_access_value_from_val_int(form->field[6]);
5359 privs = fix_rights_for_table(privs);
5360 init_privs= privs;
5361 }
5362
5363
GRANT_TABLE(TABLE * form,TABLE * col_privs)5364 GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
5365 :GRANT_NAME(form, FALSE), cols(NO_ACL), init_cols(NO_ACL)
5366 {
5367 uchar key[MAX_KEY_LENGTH];
5368
5369 if (!db || !tname)
5370 {
5371 /* Wrong table row; Ignore it */
5372 my_hash_clear(&hash_columns); /* allow for destruction */
5373 cols= NO_ACL;
5374 return;
5375 }
5376 cols= get_access_value_from_val_int(form->field[7]);
5377 cols= fix_rights_for_column(cols);
5378 /*
5379 Initial columns privileges are the same as column privileges on creation.
5380 In case of roles, the cols privilege bits can get inherited and thus
5381 cause the cols field to change. The init_cols field is always the same
5382 as the physical table entry
5383 */
5384 init_cols= cols;
5385
5386 init_hash();
5387
5388 if (cols)
5389 {
5390 uint key_prefix_len;
5391 KEY_PART_INFO *key_part= col_privs->key_info->key_part;
5392 col_privs->field[0]->store(host.hostname,
5393 (uint) safe_strlen(host.hostname),
5394 system_charset_info);
5395 col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
5396 col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
5397 col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
5398
5399 key_prefix_len= (key_part[0].store_length +
5400 key_part[1].store_length +
5401 key_part[2].store_length +
5402 key_part[3].store_length);
5403 key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
5404 col_privs->field[4]->store("",0, &my_charset_latin1);
5405
5406 if (col_privs->file->ha_index_init(0, 1))
5407 {
5408 cols= NO_ACL;
5409 init_cols= NO_ACL;
5410 return;
5411 }
5412
5413 if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
5414 (key_part_map)15,
5415 HA_READ_KEY_EXACT))
5416 {
5417 cols= NO_ACL; /* purecov: deadcode */
5418 init_cols= NO_ACL;
5419 col_privs->file->ha_index_end();
5420 return;
5421 }
5422 do
5423 {
5424 String *res,column_name;
5425 GRANT_COLUMN *mem_check;
5426 /* As column name is a string, we don't have to supply a buffer */
5427 res=col_privs->field[4]->val_str(&column_name);
5428 privilege_t priv= get_access_value_from_val_int(col_privs->field[6]);
5429 if (!(mem_check = new GRANT_COLUMN(*res,
5430 fix_rights_for_column(priv))))
5431 {
5432 /* Don't use this entry */
5433 privs= cols= init_privs= init_cols= NO_ACL; /* purecov: deadcode */
5434 return; /* purecov: deadcode */
5435 }
5436 if (my_hash_insert(&hash_columns, (uchar *) mem_check))
5437 {
5438 /* Invalidate this entry */
5439 privs= cols= init_privs= init_cols= NO_ACL;
5440 return;
5441 }
5442 } while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
5443 !key_cmp_if_same(col_privs,key,0,key_prefix_len));
5444 col_privs->file->ha_index_end();
5445 }
5446 }
5447
5448
~GRANT_TABLE()5449 GRANT_TABLE::~GRANT_TABLE()
5450 {
5451 my_hash_free(&hash_columns);
5452 }
5453
5454
get_grant_table(GRANT_NAME * buff,size_t * length,my_bool not_used)5455 static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
5456 my_bool not_used __attribute__((unused)))
5457 {
5458 *length=buff->key_length;
5459 return (uchar*) buff->hash_key;
5460 }
5461
5462
free_grant_table(GRANT_TABLE * grant_table)5463 static void free_grant_table(GRANT_TABLE *grant_table)
5464 {
5465 grant_table->~GRANT_TABLE();
5466 }
5467
5468
5469 /* Search after a matching grant. Prefer exact grants before not exact ones */
5470
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)5471 static GRANT_NAME *name_hash_search(HASH *name_hash,
5472 const char *host,const char* ip,
5473 const char *db,
5474 const char *user, const char *tname,
5475 bool exact, bool name_tolower)
5476 {
5477 char helping[SAFE_NAME_LEN*2+USERNAME_LENGTH+3];
5478 char *hend = helping + sizeof(helping);
5479 uint len;
5480 GRANT_NAME *grant_name,*found=0;
5481 HASH_SEARCH_STATE state;
5482
5483 char *db_ptr= strmov(helping, user) + 1;
5484 char *tname_ptr= strnmov(db_ptr, db, hend - db_ptr) + 1;
5485 if (tname_ptr > hend)
5486 return 0; // invalid name = not found
5487 char *end= strnmov(tname_ptr, tname, hend - tname_ptr) + 1;
5488 if (end > hend)
5489 return 0; // invalid name = not found
5490
5491 len = (uint) (end - helping);
5492 if (name_tolower)
5493 my_casedn_str(files_charset_info, tname_ptr);
5494 for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
5495 len, &state);
5496 grant_name ;
5497 grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
5498 len, &state))
5499 {
5500 if (exact)
5501 {
5502 if (!grant_name->host.hostname ||
5503 (host &&
5504 !my_strcasecmp(system_charset_info, host,
5505 grant_name->host.hostname)) ||
5506 (ip && !strcmp(ip, grant_name->host.hostname)))
5507 return grant_name;
5508 }
5509 else
5510 {
5511 if (compare_hostname(&grant_name->host, host, ip) &&
5512 (!found || found->sort < grant_name->sort))
5513 found=grant_name; // Host ok
5514 }
5515 }
5516 return found;
5517 }
5518
5519
5520 static GRANT_NAME *
routine_hash_search(const char * host,const char * ip,const char * db,const char * user,const char * tname,const Sp_handler * sph,bool exact)5521 routine_hash_search(const char *host, const char *ip, const char *db,
5522 const char *user, const char *tname, const Sp_handler *sph,
5523 bool exact)
5524 {
5525 return (GRANT_NAME*)
5526 name_hash_search(sph->get_priv_hash(),
5527 host, ip, db, user, tname, exact, TRUE);
5528 }
5529
5530
5531 static GRANT_TABLE *
table_hash_search(const char * host,const char * ip,const char * db,const char * user,const char * tname,bool exact)5532 table_hash_search(const char *host, const char *ip, const char *db,
5533 const char *user, const char *tname, bool exact)
5534 {
5535 return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
5536 user, tname, exact, FALSE);
5537 }
5538
column_priv_insert(GRANT_TABLE * grant)5539 static bool column_priv_insert(GRANT_TABLE *grant)
5540 {
5541 return my_hash_insert(&column_priv_hash,(uchar*) grant);
5542 }
5543
5544 static GRANT_COLUMN *
column_hash_search(GRANT_TABLE * t,const char * cname,size_t length)5545 column_hash_search(GRANT_TABLE *t, const char *cname, size_t length)
5546 {
5547 if (!my_hash_inited(&t->hash_columns))
5548 return (GRANT_COLUMN*) 0;
5549 return (GRANT_COLUMN*)my_hash_search(&t->hash_columns, (uchar*)cname, length);
5550 }
5551
5552
replace_column_table(GRANT_TABLE * g_t,TABLE * table,const LEX_USER & combo,List<LEX_COLUMN> & columns,const char * db,const char * table_name,privilege_t rights,bool revoke_grant)5553 static int replace_column_table(GRANT_TABLE *g_t,
5554 TABLE *table, const LEX_USER &combo,
5555 List <LEX_COLUMN> &columns,
5556 const char *db, const char *table_name,
5557 privilege_t rights, bool revoke_grant)
5558 {
5559 int result=0;
5560 uchar key[MAX_KEY_LENGTH];
5561 uint key_prefix_length;
5562 KEY_PART_INFO *key_part= table->key_info->key_part;
5563 DBUG_ENTER("replace_column_table");
5564
5565 table->use_all_columns();
5566 table->field[0]->store(combo.host.str,combo.host.length,
5567 system_charset_info);
5568 table->field[1]->store(db,(uint) strlen(db),
5569 system_charset_info);
5570 table->field[2]->store(combo.user.str,combo.user.length,
5571 system_charset_info);
5572 table->field[3]->store(table_name,(uint) strlen(table_name),
5573 system_charset_info);
5574
5575 /* Get length of 4 first key parts */
5576 key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
5577 key_part[2].store_length + key_part[3].store_length);
5578 key_copy(key, table->record[0], table->key_info, key_prefix_length);
5579
5580 rights&= COL_ACLS; // Only ACL for columns
5581
5582 /* first fix privileges for all columns in column list */
5583
5584 List_iterator <LEX_COLUMN> iter(columns);
5585 class LEX_COLUMN *column;
5586
5587 int error= table->file->ha_index_init(0, 1);
5588 if (unlikely(error))
5589 {
5590 table->file->print_error(error, MYF(0));
5591 DBUG_RETURN(-1);
5592 }
5593
5594 while ((column= iter++))
5595 {
5596 privilege_t privileges= column->rights;
5597 bool old_row_exists=0;
5598 uchar user_key[MAX_KEY_LENGTH];
5599
5600 key_restore(table->record[0],key,table->key_info,
5601 key_prefix_length);
5602 table->field[4]->store(column->column.ptr(), column->column.length(),
5603 system_charset_info);
5604 /* Get key for the first 4 columns */
5605 key_copy(user_key, table->record[0], table->key_info,
5606 table->key_info->key_length);
5607
5608 if (table->file->ha_index_read_map(table->record[0], user_key,
5609 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
5610 {
5611 if (revoke_grant)
5612 {
5613 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
5614 combo.user.str, combo.host.str,
5615 table_name); /* purecov: inspected */
5616 result= -1; /* purecov: inspected */
5617 continue; /* purecov: inspected */
5618 }
5619 old_row_exists = 0;
5620 restore_record(table, s->default_values); // Get empty record
5621 key_restore(table->record[0],key,table->key_info,
5622 key_prefix_length);
5623 table->field[4]->store(column->column.ptr(),column->column.length(),
5624 system_charset_info);
5625 }
5626 else
5627 {
5628 privilege_t tmp= get_access_value_from_val_int(table->field[6]);
5629 tmp=fix_rights_for_column(tmp);
5630
5631 if (revoke_grant)
5632 privileges = tmp & ~(privileges | rights);
5633 else
5634 privileges |= tmp;
5635 old_row_exists = 1;
5636 store_record(table,record[1]); // copy original row
5637 }
5638
5639 table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
5640
5641 if (old_row_exists)
5642 {
5643 GRANT_COLUMN *grant_column;
5644 if (privileges)
5645 error=table->file->ha_update_row(table->record[1],table->record[0]);
5646 else
5647 error=table->file->ha_delete_row(table->record[1]);
5648 if (unlikely(error) && error != HA_ERR_RECORD_IS_THE_SAME)
5649 {
5650 table->file->print_error(error,MYF(0)); /* purecov: inspected */
5651 result= -1; /* purecov: inspected */
5652 goto end; /* purecov: inspected */
5653 }
5654 else
5655 error= 0;
5656 grant_column= column_hash_search(g_t, column->column.ptr(),
5657 column->column.length());
5658 if (grant_column) // Should always be true
5659 grant_column->rights= privileges; // Update hash
5660 }
5661 else // new grant
5662 {
5663 GRANT_COLUMN *grant_column;
5664 if (unlikely((error=table->file->ha_write_row(table->record[0]))))
5665 {
5666 table->file->print_error(error,MYF(0)); /* purecov: inspected */
5667 result= -1; /* purecov: inspected */
5668 goto end; /* purecov: inspected */
5669 }
5670 grant_column= new GRANT_COLUMN(column->column,privileges);
5671 if (my_hash_insert(&g_t->hash_columns,(uchar*) grant_column))
5672 {
5673 result= -1;
5674 goto end;
5675 }
5676 }
5677 }
5678
5679 /*
5680 If revoke of privileges on the table level, remove all such privileges
5681 for all columns
5682 */
5683
5684 if (revoke_grant)
5685 {
5686 uchar user_key[MAX_KEY_LENGTH];
5687 key_copy(user_key, table->record[0], table->key_info,
5688 key_prefix_length);
5689
5690 if (table->file->ha_index_read_map(table->record[0], user_key,
5691 (key_part_map)15,
5692 HA_READ_KEY_EXACT))
5693 goto end;
5694
5695 /* Scan through all rows with the same host,db,user and table */
5696 do
5697 {
5698 privilege_t privileges = get_access_value_from_val_int(table->field[6]);
5699 privileges=fix_rights_for_column(privileges);
5700 store_record(table,record[1]);
5701
5702 if (privileges & rights) // is in this record the priv to be revoked ??
5703 {
5704 GRANT_COLUMN *grant_column = NULL;
5705 char colum_name_buf[HOSTNAME_LENGTH+1];
5706 String column_name(colum_name_buf,sizeof(colum_name_buf),
5707 system_charset_info);
5708
5709 privileges&= ~rights;
5710 table->field[6]->store((longlong)
5711 get_rights_for_column(privileges), TRUE);
5712 table->field[4]->val_str(&column_name);
5713 grant_column = column_hash_search(g_t,
5714 column_name.ptr(),
5715 column_name.length());
5716 if (privileges)
5717 {
5718 int tmp_error;
5719 if (unlikely(tmp_error=
5720 table->file->ha_update_row(table->record[1],
5721 table->record[0])) &&
5722 tmp_error != HA_ERR_RECORD_IS_THE_SAME)
5723 { /* purecov: deadcode */
5724 table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
5725 result= -1; /* purecov: deadcode */
5726 goto end; /* purecov: deadcode */
5727 }
5728 if (grant_column)
5729 {
5730 grant_column->rights = privileges; // Update hash
5731 grant_column->init_rights = privileges;
5732 }
5733 }
5734 else
5735 {
5736 int tmp_error;
5737 if (unlikely((tmp_error=
5738 table->file->ha_delete_row(table->record[1]))))
5739 { /* purecov: deadcode */
5740 table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
5741 result= -1; /* purecov: deadcode */
5742 goto end; /* purecov: deadcode */
5743 }
5744 if (grant_column)
5745 my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
5746 }
5747 }
5748 } while (!table->file->ha_index_next(table->record[0]) &&
5749 !key_cmp_if_same(table, key, 0, key_prefix_length));
5750 }
5751
5752 end:
5753 table->file->ha_index_end();
5754 DBUG_RETURN(result);
5755 }
5756
get_grantor(THD * thd,char * grantor)5757 static inline void get_grantor(THD *thd, char *grantor)
5758 {
5759 const char *user= thd->security_ctx->user;
5760 const char *host= thd->security_ctx->host_or_ip;
5761
5762 #if defined(HAVE_REPLICATION)
5763 if (thd->slave_thread && thd->has_invoker())
5764 {
5765 user= thd->get_invoker_user().str;
5766 host= thd->get_invoker_host().str;
5767 }
5768 #endif
5769 strxmov(grantor, user, "@", host, NullS);
5770 }
5771
5772
5773 /**
5774 Revoke rights from a grant table entry.
5775
5776 @return 0 ok
5777 @return 1 fatal error (error given)
5778 @return -1 grant table was revoked
5779 */
5780
replace_table_table(THD * thd,GRANT_TABLE * grant_table,TABLE * table,const LEX_USER & combo,const char * db,const char * table_name,privilege_t rights,privilege_t col_rights,bool revoke_grant)5781 static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
5782 TABLE *table, const LEX_USER &combo,
5783 const char *db, const char *table_name,
5784 privilege_t rights, privilege_t col_rights,
5785 bool revoke_grant)
5786 {
5787 char grantor[USER_HOST_BUFF_SIZE];
5788 int old_row_exists = 1;
5789 int error=0;
5790 privilege_t store_table_rights(NO_ACL), store_col_rights(NO_ACL);
5791 uchar user_key[MAX_KEY_LENGTH];
5792 DBUG_ENTER("replace_table_table");
5793
5794 get_grantor(thd, grantor);
5795 /*
5796 The following should always succeed as new users are created before
5797 this function is called!
5798 */
5799 if (!find_user_wild(combo.host.str,combo.user.str))
5800 {
5801 if (!combo.host.length && !find_acl_role(combo.user.str))
5802 {
5803 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
5804 MYF(0)); /* purecov: deadcode */
5805 DBUG_RETURN(1); /* purecov: deadcode */
5806 }
5807 }
5808
5809 table->use_all_columns();
5810 restore_record(table, s->default_values); // Get empty record
5811 table->field[0]->store(combo.host.str,combo.host.length,
5812 system_charset_info);
5813 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
5814 table->field[2]->store(combo.user.str,combo.user.length,
5815 system_charset_info);
5816 table->field[3]->store(table_name,(uint) strlen(table_name),
5817 system_charset_info);
5818 store_record(table,record[1]); // store at pos 1
5819 key_copy(user_key, table->record[0], table->key_info,
5820 table->key_info->key_length);
5821
5822 if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
5823 HA_WHOLE_KEY,
5824 HA_READ_KEY_EXACT))
5825 {
5826 /*
5827 The following should never happen as we first check the in memory
5828 grant tables for the user. There is however always a small change that
5829 the user has modified the grant tables directly.
5830 */
5831 if (revoke_grant)
5832 { // no row, no revoke
5833 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
5834 combo.user.str, combo.host.str,
5835 table_name); /* purecov: deadcode */
5836 DBUG_RETURN(1); /* purecov: deadcode */
5837 }
5838 old_row_exists = 0;
5839 restore_record(table,record[1]); // Get saved record
5840 }
5841
5842 store_table_rights= get_rights_for_table(rights);
5843 store_col_rights= get_rights_for_column(col_rights);
5844 if (old_row_exists)
5845 {
5846 store_record(table,record[1]);
5847 privilege_t j= get_access_value_from_val_int(table->field[6]);
5848 privilege_t k= get_access_value_from_val_int(table->field[7]);
5849
5850 if (revoke_grant)
5851 {
5852 /* column rights are already fixed in mysql_table_grant */
5853 store_table_rights=j & ~store_table_rights;
5854 }
5855 else
5856 {
5857 store_table_rights|= j;
5858 store_col_rights|= k;
5859 }
5860 }
5861
5862 table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
5863 table->field[6]->store((longlong) store_table_rights, TRUE);
5864 table->field[7]->store((longlong) store_col_rights, TRUE);
5865 rights=fix_rights_for_table(store_table_rights);
5866 col_rights=fix_rights_for_column(store_col_rights);
5867
5868 if (old_row_exists)
5869 {
5870 if (store_table_rights || store_col_rights)
5871 {
5872 if (unlikely(error=table->file->ha_update_row(table->record[1],
5873 table->record[0])) &&
5874 error != HA_ERR_RECORD_IS_THE_SAME)
5875 goto table_error; /* purecov: deadcode */
5876 }
5877 else if (unlikely((error = table->file->ha_delete_row(table->record[1]))))
5878 goto table_error; /* purecov: deadcode */
5879 }
5880 else
5881 {
5882 error=table->file->ha_write_row(table->record[0]);
5883 if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
5884 goto table_error; /* purecov: deadcode */
5885 }
5886
5887 if (rights | col_rights)
5888 {
5889 grant_table->init_privs= rights;
5890 grant_table->init_cols= col_rights;
5891
5892 grant_table->privs= rights;
5893 grant_table->cols= col_rights;
5894 }
5895 else
5896 {
5897 my_hash_delete(&column_priv_hash,(uchar*) grant_table);
5898 DBUG_RETURN(-1); // Entry revoked
5899 }
5900 DBUG_RETURN(0);
5901
5902 /* This should never happen */
5903 table_error:
5904 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
5905 DBUG_RETURN(1); /* purecov: deadcode */
5906 }
5907
5908
5909 /**
5910 @retval 0 success
5911 @retval -1 error
5912 */
replace_routine_table(THD * thd,GRANT_NAME * grant_name,TABLE * table,const LEX_USER & combo,const char * db,const char * routine_name,const Sp_handler * sph,privilege_t rights,bool revoke_grant)5913 static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
5914 TABLE *table, const LEX_USER &combo,
5915 const char *db, const char *routine_name,
5916 const Sp_handler *sph,
5917 privilege_t rights, bool revoke_grant)
5918 {
5919 char grantor[USER_HOST_BUFF_SIZE];
5920 int old_row_exists= 1;
5921 int error=0;
5922 HASH *hash= sph->get_priv_hash();
5923 DBUG_ENTER("replace_routine_table");
5924
5925 if (!table)
5926 {
5927 my_error(ER_NO_SUCH_TABLE, MYF(0), MYSQL_SCHEMA_NAME.str,
5928 MYSQL_TABLE_NAME[PROCS_PRIV_TABLE].str);
5929 DBUG_RETURN(-1);
5930 }
5931
5932 if (revoke_grant && !grant_name->init_privs) // only inherited role privs
5933 {
5934 my_hash_delete(hash, (uchar*) grant_name);
5935 DBUG_RETURN(0);
5936 }
5937
5938 get_grantor(thd, grantor);
5939 /*
5940 New users are created before this function is called.
5941
5942 There may be some cases where a routine's definer is removed but the
5943 routine remains.
5944 */
5945
5946 table->use_all_columns();
5947 restore_record(table, s->default_values); // Get empty record
5948 table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
5949 table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
5950 table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
5951 table->field[3]->store(routine_name,(uint) strlen(routine_name),
5952 &my_charset_latin1);
5953 table->field[4]->store((longlong) sph->type(), true);
5954 store_record(table,record[1]); // store at pos 1
5955
5956 if (table->file->ha_index_read_idx_map(table->record[0], 0,
5957 (uchar*) table->field[0]->ptr,
5958 HA_WHOLE_KEY,
5959 HA_READ_KEY_EXACT))
5960 {
5961 /*
5962 The following should never happen as we first check the in memory
5963 grant tables for the user. There is however always a small change that
5964 the user has modified the grant tables directly.
5965
5966 Also, there is also a second posibility that this routine entry
5967 is created for a role by being inherited from a granted role.
5968 */
5969 if (revoke_grant)
5970 { // no row, no revoke
5971 my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
5972 combo.user.str, combo.host.str, routine_name);
5973 DBUG_RETURN(-1);
5974 }
5975 old_row_exists= 0;
5976 restore_record(table,record[1]); // Get saved record
5977 }
5978
5979 privilege_t store_proc_rights= get_rights_for_procedure(rights);
5980 if (old_row_exists)
5981 {
5982 store_record(table,record[1]);
5983 privilege_t j= get_access_value_from_val_int(table->field[6]);
5984
5985 if (revoke_grant)
5986 {
5987 /* column rights are already fixed in mysql_table_grant */
5988 store_proc_rights=j & ~store_proc_rights;
5989 }
5990 else
5991 {
5992 store_proc_rights|= j;
5993 }
5994 }
5995
5996 table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
5997 table->field[6]->store((longlong) store_proc_rights, TRUE);
5998 rights=fix_rights_for_procedure(store_proc_rights);
5999
6000 if (old_row_exists)
6001 {
6002 if (store_proc_rights)
6003 {
6004 if (unlikely(error=table->file->ha_update_row(table->record[1],
6005 table->record[0])) &&
6006 error != HA_ERR_RECORD_IS_THE_SAME)
6007 goto table_error;
6008 }
6009 else if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
6010 goto table_error;
6011 }
6012 else
6013 {
6014 error=table->file->ha_write_row(table->record[0]);
6015 if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
6016 goto table_error;
6017 }
6018
6019 if (rights)
6020 {
6021 grant_name->init_privs= rights;
6022 grant_name->privs= rights;
6023 }
6024 else
6025 {
6026 my_hash_delete(hash, (uchar*) grant_name);
6027 }
6028 DBUG_RETURN(0);
6029
6030 /* This should never happen */
6031 table_error:
6032 table->file->print_error(error,MYF(0));
6033 DBUG_RETURN(-1);
6034 }
6035
6036
6037 /*****************************************************************
6038 Role privilege propagation and graph traversal functionality
6039
6040 According to the SQL standard, a role can be granted to a role,
6041 thus role grants can create an arbitrarily complex directed acyclic
6042 graph (a standard explicitly specifies that cycles are not allowed).
6043
6044 When a privilege is granted to a role, it becomes available to all grantees.
6045 The code below recursively traverses a DAG of role grants, propagating
6046 privilege changes.
6047
6048 The traversal function can work both ways, from roles to grantees or
6049 from grantees to roles. The first is used for privilege propagation,
6050 the second - for SHOW GRANTS and I_S.APPLICABLE_ROLES
6051
6052 The role propagation code is smart enough to propagate only privilege
6053 changes to one specific database, table, or routine, if only they
6054 were changed (like in GRANT ... ON ... TO ...) or it can propagate
6055 everything (on startup or after FLUSH PRIVILEGES).
6056
6057 It traverses only a subgraph that's accessible from the modified role,
6058 only visiting roles that can be possibly affected by the GRANT statement.
6059
6060 Additionally, it stops traversal early, if this particular GRANT statement
6061 didn't result in any changes of privileges (e.g. both role1 and role2
6062 are granted to the role3, both role1 and role2 have SELECT privilege.
6063 if SELECT is revoked from role1 it won't change role3 privileges,
6064 so we won't traverse from role3 to its grantees).
6065 ******************************************************************/
6066 struct PRIVS_TO_MERGE
6067 {
6068 enum what
6069 {
6070 ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC, PACKAGE_SPEC, PACKAGE_BODY
6071 } what;
6072 const char *db, *name;
6073 };
6074
6075
sp_privs_to_merge(enum_sp_type type)6076 static enum PRIVS_TO_MERGE::what sp_privs_to_merge(enum_sp_type type)
6077 {
6078 switch (type) {
6079 case SP_TYPE_FUNCTION:
6080 return PRIVS_TO_MERGE::FUNC;
6081 case SP_TYPE_PROCEDURE:
6082 return PRIVS_TO_MERGE::PROC;
6083 case SP_TYPE_PACKAGE:
6084 return PRIVS_TO_MERGE::PACKAGE_SPEC;
6085 case SP_TYPE_PACKAGE_BODY:
6086 return PRIVS_TO_MERGE::PACKAGE_BODY;
6087 case SP_TYPE_EVENT:
6088 case SP_TYPE_TRIGGER:
6089 break;
6090 }
6091 DBUG_ASSERT(0);
6092 return PRIVS_TO_MERGE::PROC;
6093 }
6094
6095
init_role_for_merging(ACL_ROLE * role,void * context)6096 static int init_role_for_merging(ACL_ROLE *role, void *context)
6097 {
6098 role->counter= 0;
6099 return 0;
6100 }
6101
count_subgraph_nodes(ACL_ROLE * role,ACL_ROLE * grantee,void * context)6102 static int count_subgraph_nodes(ACL_ROLE *role, ACL_ROLE *grantee, void *context)
6103 {
6104 grantee->counter++;
6105 return 0;
6106 }
6107
6108 static int merge_role_privileges(ACL_ROLE *, ACL_ROLE *, void *);
6109
6110 /**
6111 rebuild privileges of all affected roles
6112
6113 entry point into role privilege propagation. after privileges of the
6114 'role' were changed, this function rebuilds privileges of all affected roles
6115 as necessary.
6116 */
propagate_role_grants(ACL_ROLE * role,enum PRIVS_TO_MERGE::what what,const char * db=0,const char * name=0)6117 static void propagate_role_grants(ACL_ROLE *role,
6118 enum PRIVS_TO_MERGE::what what,
6119 const char *db= 0, const char *name= 0)
6120 {
6121 if (!role)
6122 return;
6123
6124 mysql_mutex_assert_owner(&acl_cache->lock);
6125 PRIVS_TO_MERGE data= { what, db, name };
6126
6127 /*
6128 Changing privileges of a role causes all other roles that had
6129 this role granted to them to have their rights invalidated.
6130
6131 We need to rebuild all roles' related access bits.
6132
6133 This cannot be a simple depth-first search, instead we have to merge
6134 privieges for all roles granted to a specific grantee, *before*
6135 merging privileges for this grantee. In other words, we must visit all
6136 parent nodes of a specific node, before descencing into this node.
6137
6138 For example, if role1 is granted to role2 and role3, and role3 is
6139 granted to role2, after "GRANT ... role1", we cannot merge privileges
6140 for role2, until role3 is merged. The counter will be 0 for role1, 2
6141 for role2, 1 for role3. Traversal will start from role1, go to role2,
6142 decrement the counter, backtrack, go to role3, merge it, go to role2
6143 again, merge it.
6144
6145 And the counter is not just "all parent nodes", but only parent nodes
6146 that are part of the subgraph we're interested in. For example, if
6147 both roleA and roleB are granted to roleC, then roleC has two parent
6148 nodes. But when granting a privilege to roleA, we're only looking at a
6149 subgraph that includes roleA and roleC (roleB cannot be possibly
6150 affected by that grant statement). In this subgraph roleC has only one
6151 parent.
6152
6153 (on the other hand, in acl_load we want to update all roles, and
6154 the counter is exactly equal to the number of all parent nodes)
6155
6156 Thus, we do two graph traversals here. First we only count parents
6157 that are part of the subgraph. On the second traversal we decrement
6158 the counter and actually merge privileges for a node when a counter
6159 drops to zero.
6160 */
6161 traverse_role_graph_up(role, &data, init_role_for_merging, count_subgraph_nodes);
6162 traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
6163 }
6164
6165
6166 // State of a node during a Depth First Search exploration
6167 struct NODE_STATE
6168 {
6169 ACL_USER_BASE *node_data; /* pointer to the node data */
6170 uint neigh_idx; /* the neighbour that needs to be evaluated next */
6171 };
6172
6173 /**
6174 Traverse the role grant graph and invoke callbacks at the specified points.
6175
6176 @param user user or role to start traversal from
6177 @param context opaque parameter to pass to callbacks
6178 @param offset offset to ACL_ROLE::parent_grantee or to
6179 ACL_USER_BASE::role_grants. Depending on this value,
6180 traversal will go from roles to grantees or from
6181 grantees to roles.
6182 @param on_node called when a node is visited for the first time.
6183 Returning a value <0 will abort the traversal.
6184 @param on_edge called for every edge in the graph, when traversal
6185 goes from a node to a neighbour node.
6186 Returning <0 will abort the traversal. Returning >0
6187 will make the traversal not to follow this edge.
6188
6189 @note
6190 The traverse method is a DEPTH FIRST SEARCH, but callbacks can influence
6191 that (on_edge returning >0 value).
6192
6193 @note
6194 This function should not be called directly, use
6195 traverse_role_graph_up() and traverse_role_graph_down() instead.
6196
6197 @retval 0 traversal finished successfully
6198 @retval ROLE_CYCLE_FOUND traversal aborted, cycle detected
6199 @retval <0 traversal was aborted, because a callback returned
6200 this error code
6201 */
traverse_role_graph_impl(ACL_USER_BASE * user,void * context,off_t offset,int (* on_node)(ACL_USER_BASE * role,void * context),int (* on_edge)(ACL_USER_BASE * current,ACL_ROLE * neighbour,void * context))6202 static int traverse_role_graph_impl(ACL_USER_BASE *user, void *context,
6203 off_t offset,
6204 int (*on_node) (ACL_USER_BASE *role, void *context),
6205 int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
6206 {
6207 DBUG_ENTER("traverse_role_graph_impl");
6208 DBUG_ASSERT(user);
6209 DBUG_PRINT("enter",("role: '%s'", user->user.str));
6210 /*
6211 The search operation should always leave the ROLE_ON_STACK and
6212 ROLE_EXPLORED flags clean for all nodes involved in the search
6213 */
6214 DBUG_ASSERT(!(user->flags & ROLE_ON_STACK));
6215 DBUG_ASSERT(!(user->flags & ROLE_EXPLORED));
6216 mysql_mutex_assert_owner(&acl_cache->lock);
6217
6218 /*
6219 Stack used to simulate the recursive calls of DFS.
6220 It uses a Dynamic_array to reduce the number of
6221 malloc calls to a minimum
6222 */
6223 Dynamic_array<NODE_STATE> stack(20,50);
6224 Dynamic_array<ACL_USER_BASE *> to_clear(20,50);
6225 NODE_STATE state; /* variable used to insert elements in the stack */
6226 int result= 0;
6227
6228 state.neigh_idx= 0;
6229 state.node_data= user;
6230 user->flags|= ROLE_ON_STACK;
6231
6232 stack.push(state);
6233 to_clear.push(user);
6234
6235 user->flags|= ROLE_OPENED;
6236 if (on_node && ((result= on_node(user, context)) < 0))
6237 goto end;
6238
6239 while (stack.elements())
6240 {
6241 NODE_STATE *curr_state= stack.back();
6242
6243 DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
6244
6245 ACL_USER_BASE *current= curr_state->node_data;
6246 ACL_USER_BASE *neighbour= NULL;
6247 DBUG_PRINT("info", ("Examining role %s", current->user.str));
6248 /*
6249 Iterate through the neighbours until a first valid jump-to
6250 neighbour is found
6251 */
6252 bool found= FALSE;
6253 uint i;
6254 DYNAMIC_ARRAY *array= (DYNAMIC_ARRAY *)(((char*)current) + offset);
6255
6256 DBUG_ASSERT(array == ¤t->role_grants || current->flags & IS_ROLE);
6257 for (i= curr_state->neigh_idx; i < array->elements; i++)
6258 {
6259 neighbour= *(dynamic_element(array, i, ACL_ROLE**));
6260 if (!(neighbour->flags & IS_ROLE))
6261 continue;
6262
6263 DBUG_PRINT("info", ("Examining neighbour role %s", neighbour->user.str));
6264
6265 /* check if it forms a cycle */
6266 if (neighbour->flags & ROLE_ON_STACK)
6267 {
6268 DBUG_PRINT("info", ("Found cycle"));
6269 result= ROLE_CYCLE_FOUND;
6270 goto end;
6271 }
6272
6273 if (!(neighbour->flags & ROLE_OPENED))
6274 {
6275 neighbour->flags|= ROLE_OPENED;
6276 to_clear.push(neighbour);
6277 if (on_node && ((result= on_node(neighbour, context)) < 0))
6278 goto end;
6279 }
6280
6281 if (on_edge)
6282 {
6283 result= on_edge(current, (ACL_ROLE*)neighbour, context);
6284 if (result < 0)
6285 goto end;
6286 if (result > 0)
6287 continue;
6288 }
6289
6290 /* Check if it was already explored, in that case, move on */
6291 if (neighbour->flags & ROLE_EXPLORED)
6292 continue;
6293
6294 found= TRUE;
6295 break;
6296 }
6297
6298 /* found states that we have found a node to jump next into */
6299 if (found)
6300 {
6301 curr_state->neigh_idx= i + 1;
6302
6303 /* some sanity checks */
6304 DBUG_ASSERT(!(neighbour->flags & ROLE_ON_STACK));
6305
6306 /* add the neighbour on the stack */
6307 neighbour->flags|= ROLE_ON_STACK;
6308 state.neigh_idx= 0;
6309 state.node_data= neighbour;
6310 stack.push(state);
6311 }
6312 else
6313 {
6314 /* Make sure we got a correct node */
6315 DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
6316 /* Finished with exploring the current node, pop it off the stack */
6317 curr_state= &stack.pop();
6318 curr_state->node_data->flags&= ~ROLE_ON_STACK; /* clear the on-stack bit */
6319 curr_state->node_data->flags|= ROLE_EXPLORED;
6320 }
6321 }
6322
6323 end:
6324 /* Cleanup */
6325 for (uint i= 0; i < to_clear.elements(); i++)
6326 {
6327 ACL_USER_BASE *current= to_clear.at(i);
6328 DBUG_ASSERT(current->flags & (ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED));
6329 current->flags&= ~(ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED);
6330 }
6331 DBUG_RETURN(result);
6332 }
6333
6334 /**
6335 Traverse the role grant graph, going from a role to its grantees.
6336
6337 This is used to propagate changes in privileges, for example,
6338 when GRANT or REVOKE is issued for a role.
6339 */
6340
traverse_role_graph_up(ACL_ROLE * role,void * context,int (* on_node)(ACL_ROLE * role,void * context),int (* on_edge)(ACL_ROLE * current,ACL_ROLE * neighbour,void * context))6341 static int traverse_role_graph_up(ACL_ROLE *role, void *context,
6342 int (*on_node) (ACL_ROLE *role, void *context),
6343 int (*on_edge) (ACL_ROLE *current, ACL_ROLE *neighbour, void *context))
6344 {
6345 return traverse_role_graph_impl(role, context,
6346 my_offsetof(ACL_ROLE, parent_grantee),
6347 (int (*)(ACL_USER_BASE *, void *))on_node,
6348 (int (*)(ACL_USER_BASE *, ACL_ROLE *, void *))on_edge);
6349 }
6350
6351 /**
6352 Traverse the role grant graph, going from a user or a role to granted roles.
6353
6354 This is used, for example, to print all grants available to a user or a role
6355 (as in SHOW GRANTS).
6356 */
6357
traverse_role_graph_down(ACL_USER_BASE * user,void * context,int (* on_node)(ACL_USER_BASE * role,void * context),int (* on_edge)(ACL_USER_BASE * current,ACL_ROLE * neighbour,void * context))6358 static int traverse_role_graph_down(ACL_USER_BASE *user, void *context,
6359 int (*on_node) (ACL_USER_BASE *role, void *context),
6360 int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
6361 {
6362 return traverse_role_graph_impl(user, context,
6363 my_offsetof(ACL_USER_BASE, role_grants),
6364 on_node, on_edge);
6365 }
6366
6367 /*
6368 To find all db/table/routine privilege for a specific role
6369 we need to scan the array of privileges. It can be big.
6370 But the set of privileges granted to a role in question (or
6371 to roles directly granted to the role in question) is supposedly
6372 much smaller.
6373
6374 We put a role and all roles directly granted to it in a hash, and iterate
6375 the (suposedly long) array of privileges, filtering out "interesting"
6376 entries using the role hash. We put all these "interesting"
6377 entries in a (suposedly small) dynamic array and them use it for merging.
6378 */
role_key(const ACL_ROLE * role,size_t * klen,my_bool)6379 static uchar* role_key(const ACL_ROLE *role, size_t *klen, my_bool)
6380 {
6381 *klen= role->user.length;
6382 return (uchar*) role->user.str;
6383 }
6384 typedef Hash_set<ACL_ROLE> role_hash_t;
6385
merge_role_global_privileges(ACL_ROLE * grantee)6386 static bool merge_role_global_privileges(ACL_ROLE *grantee)
6387 {
6388 privilege_t old= grantee->access;
6389 grantee->access= grantee->initial_role_access;
6390
6391 DBUG_EXECUTE_IF("role_merge_stats", role_global_merges++;);
6392
6393 for (uint i= 0; i < grantee->role_grants.elements; i++)
6394 {
6395 ACL_ROLE *r= *dynamic_element(&grantee->role_grants, i, ACL_ROLE**);
6396 grantee->access|= r->access;
6397 }
6398 return old != grantee->access;
6399 }
6400
db_name_sort(const int * db1,const int * db2)6401 static int db_name_sort(const int *db1, const int *db2)
6402 {
6403 return strcmp(acl_dbs.at(*db1).db, acl_dbs.at(*db2).db);
6404 }
6405
6406 /**
6407 update ACL_DB for given database and a given role with merged privileges
6408
6409 @param merged ACL_DB of the role in question (or -1 if it wasn't found)
6410 @param first first ACL_DB in an array for the database in question
6411 @param access new privileges for the given role on the gived database
6412 @param role the name of the given role
6413
6414 @return a bitmap of
6415 1 - privileges were changed
6416 2 - ACL_DB was added
6417 4 - ACL_DB was deleted
6418 */
update_role_db(int merged,int first,privilege_t access,const char * role)6419 static int update_role_db(int merged, int first, privilege_t access,
6420 const char *role)
6421 {
6422 if (first < 0)
6423 return 0;
6424
6425 DBUG_EXECUTE_IF("role_merge_stats", role_db_merges++;);
6426
6427 if (merged < 0)
6428 {
6429 /*
6430 there's no ACL_DB for this role (all db grants come from granted roles)
6431 we need to create it
6432
6433 Note that we cannot use acl_insert_db() now:
6434 1. it'll sort elements in the acl_dbs, so the pointers will become invalid
6435 2. we may need many of them, no need to sort every time
6436 */
6437 DBUG_ASSERT(access);
6438 ACL_DB acl_db;
6439 acl_db.user= role;
6440 acl_db.host.hostname= const_cast<char*>("");
6441 acl_db.host.ip= acl_db.host.ip_mask= 0;
6442 acl_db.db= acl_dbs.at(first).db;
6443 acl_db.access= access;
6444 acl_db.initial_access= NO_ACL;
6445 acl_db.sort= get_magic_sort("hdu", "", acl_db.db, role);
6446 acl_dbs.push(acl_db);
6447 return 2;
6448 }
6449 else if (access == NO_ACL)
6450 {
6451 /*
6452 there is ACL_DB but the role has no db privileges granted
6453 (all privileges were coming from granted roles, and now those roles
6454 were dropped or had their privileges revoked).
6455 we need to remove this ACL_DB entry
6456
6457 Note, that we cannot delete now:
6458 1. it'll shift elements in the acl_dbs, so the pointers will become invalid
6459 2. it's O(N) operation, and we may need many of them
6460 so we only mark elements deleted and will delete later.
6461 */
6462 acl_dbs.at(merged).sort= 0; // lower than any valid ACL_DB sort value, will be sorted last
6463 return 4;
6464 }
6465 else if (acl_dbs.at(merged).access != access)
6466 {
6467 /* this is easy */
6468 acl_dbs.at(merged).access= access;
6469 return 1;
6470 }
6471 return 0;
6472 }
6473
6474 /**
6475 merges db privileges from roles granted to the role 'grantee'.
6476
6477 @return true if database privileges of the 'grantee' were changed
6478
6479 */
merge_role_db_privileges(ACL_ROLE * grantee,const char * dbname,role_hash_t * rhash)6480 static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname,
6481 role_hash_t *rhash)
6482 {
6483 Dynamic_array<int> dbs(PSI_INSTRUMENT_MEM);
6484
6485 /*
6486 Supposedly acl_dbs can be huge, but only a handful of db grants
6487 apply to grantee or roles directly granted to grantee.
6488
6489 Collect these applicable db grants.
6490 */
6491 for (uint i=0 ; i < acl_dbs.elements() ; i++)
6492 {
6493 ACL_DB *db= &acl_dbs.at(i);
6494 if (db->host.hostname[0])
6495 continue;
6496 if (dbname && strcmp(db->db, dbname))
6497 continue;
6498 ACL_ROLE *r= rhash->find(db->user, strlen(db->user));
6499 if (!r)
6500 continue;
6501 dbs.append(i);
6502 }
6503 dbs.sort(db_name_sort);
6504
6505 /*
6506 Because dbs array is sorted by the db name, all grants for the same db
6507 (that should be merged) are sorted together. The grantee's ACL_DB element
6508 is not necessarily the first and may be not present at all.
6509 */
6510 int first= -1, merged= -1;
6511 privilege_t access(NO_ACL);
6512 ulong update_flags= 0;
6513 for (int *p= dbs.front(); p <= dbs.back(); p++)
6514 {
6515 if (first<0 || (!dbname && strcmp(acl_dbs.at(p[0]).db, acl_dbs.at(p[-1]).db)))
6516 { // new db name series
6517 update_flags|= update_role_db(merged, first, access, grantee->user.str);
6518 merged= -1;
6519 access= NO_ACL;
6520 first= *p;
6521 }
6522 if (strcmp(acl_dbs.at(*p).user, grantee->user.str) == 0)
6523 access|= acl_dbs.at(merged= *p).initial_access;
6524 else
6525 access|= acl_dbs.at(*p).access;
6526 }
6527 update_flags|= update_role_db(merged, first, access, grantee->user.str);
6528
6529 if (update_flags & 4)
6530 {
6531 // Remove elements marked for deletion.
6532 uint count= 0;
6533 for(uint i= 0; i < acl_dbs.elements(); i++)
6534 {
6535 ACL_DB *acl_db= &acl_dbs.at(i);
6536 if (acl_db->sort)
6537 {
6538 if (i > count)
6539 acl_dbs.set(count, *acl_db);
6540 count++;
6541 }
6542 }
6543 acl_dbs.elements(count);
6544 }
6545
6546
6547 if (update_flags & 2)
6548 { // inserted, need to sort
6549 rebuild_acl_dbs();
6550 }
6551
6552 return update_flags;
6553 }
6554
table_name_sort(GRANT_TABLE * const * tbl1,GRANT_TABLE * const * tbl2)6555 static int table_name_sort(GRANT_TABLE * const *tbl1, GRANT_TABLE * const *tbl2)
6556 {
6557 int res = strcmp((*tbl1)->db, (*tbl2)->db);
6558 if (res) return res;
6559 return strcmp((*tbl1)->tname, (*tbl2)->tname);
6560 }
6561
6562 /**
6563 merges column privileges for the entry 'merged'
6564
6565 @param merged GRANT_TABLE to merge the privileges into
6566 @param cur first entry in the array of GRANT_TABLE's for a given table
6567 @param last last entry in the array of GRANT_TABLE's for a given table,
6568 all entries between cur and last correspond to the *same* table
6569
6570 @return 1 if the _set of columns_ in 'merged' was changed
6571 (not if the _set of privileges_ was changed).
6572 */
update_role_columns(GRANT_TABLE * merged,GRANT_TABLE ** cur,GRANT_TABLE ** last)6573 static int update_role_columns(GRANT_TABLE *merged,
6574 GRANT_TABLE **cur, GRANT_TABLE **last)
6575
6576 {
6577 privilege_t rights __attribute__((unused)) (NO_ACL);
6578 int changed= 0;
6579 if (!merged->cols)
6580 {
6581 changed= merged->hash_columns.records > 0;
6582 my_hash_reset(&merged->hash_columns);
6583 return changed;
6584 }
6585
6586 DBUG_EXECUTE_IF("role_merge_stats", role_column_merges++;);
6587
6588 HASH *mh= &merged->hash_columns;
6589 for (uint i=0 ; i < mh->records ; i++)
6590 {
6591 GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
6592 col->rights= col->init_rights;
6593 }
6594
6595 for (; cur < last; cur++)
6596 {
6597 if (*cur == merged)
6598 continue;
6599 HASH *ch= &cur[0]->hash_columns;
6600 for (uint i=0 ; i < ch->records ; i++)
6601 {
6602 GRANT_COLUMN *ccol = (GRANT_COLUMN *)my_hash_element(ch, i);
6603 GRANT_COLUMN *mcol = (GRANT_COLUMN *)my_hash_search(mh,
6604 (uchar *)ccol->column, ccol->key_length);
6605 if (mcol)
6606 mcol->rights|= ccol->rights;
6607 else
6608 {
6609 changed= 1;
6610 my_hash_insert(mh, (uchar*)new (&grant_memroot) GRANT_COLUMN(ccol));
6611 }
6612 }
6613 }
6614
6615 for (uint i=0 ; i < mh->records ; i++)
6616 {
6617 GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
6618 rights|= col->rights;
6619 if (!col->rights)
6620 {
6621 changed= 1;
6622 my_hash_delete(mh, (uchar*)col);
6623 }
6624 }
6625 DBUG_ASSERT(rights == merged->cols);
6626 return changed;
6627 }
6628
6629 /**
6630 update GRANT_TABLE for a given table and a given role with merged privileges
6631
6632 @param merged GRANT_TABLE of the role in question (or NULL if it wasn't found)
6633 @param first first GRANT_TABLE in an array for the table in question
6634 @param last last entry in the array of GRANT_TABLE's for a given table,
6635 all entries between first and last correspond to the *same* table
6636 @param privs new table-level privileges for 'merged'
6637 @param cols new OR-ed column-level privileges for 'merged'
6638 @param role the name of the given role
6639
6640 @return a bitmap of
6641 1 - privileges were changed
6642 2 - GRANT_TABLE was added
6643 4 - GRANT_TABLE was deleted
6644 */
update_role_table_columns(GRANT_TABLE * merged,GRANT_TABLE ** first,GRANT_TABLE ** last,privilege_t privs,privilege_t cols,const char * role)6645 static int update_role_table_columns(GRANT_TABLE *merged,
6646 GRANT_TABLE **first, GRANT_TABLE **last,
6647 privilege_t privs, privilege_t cols,
6648 const char *role)
6649 {
6650 if (!first)
6651 return 0;
6652
6653 DBUG_EXECUTE_IF("role_merge_stats", role_table_merges++;);
6654
6655 if (merged == NULL)
6656 {
6657 /*
6658 there's no GRANT_TABLE for this role (all table grants come from granted
6659 roles) we need to create it
6660 */
6661 DBUG_ASSERT(privs | cols);
6662 merged= new (&grant_memroot) GRANT_TABLE("", first[0]->db, role, first[0]->tname,
6663 privs, cols);
6664 merged->init_privs= merged->init_cols= NO_ACL;
6665 update_role_columns(merged, first, last);
6666 column_priv_insert(merged);
6667 return 2;
6668 }
6669 else if ((privs | cols) == NO_ACL)
6670 {
6671 /*
6672 there is GRANT_TABLE object but the role has no table or column
6673 privileges granted (all privileges were coming from granted roles, and
6674 now those roles were dropped or had their privileges revoked).
6675 we need to remove this GRANT_TABLE
6676 */
6677 DBUG_EXECUTE_IF("role_merge_stats",
6678 role_column_merges+= MY_TEST(merged->cols););
6679 my_hash_delete(&column_priv_hash,(uchar*) merged);
6680 return 4;
6681 }
6682 else
6683 {
6684 bool changed= merged->cols != cols || merged->privs != privs;
6685 merged->cols= cols;
6686 merged->privs= privs;
6687 if (update_role_columns(merged, first, last))
6688 changed= true;
6689 return changed;
6690 }
6691 }
6692
6693 /**
6694 merges table privileges from roles granted to the role 'grantee'.
6695
6696 @return true if table privileges of the 'grantee' were changed
6697
6698 */
merge_role_table_and_column_privileges(ACL_ROLE * grantee,const char * db,const char * tname,role_hash_t * rhash)6699 static bool merge_role_table_and_column_privileges(ACL_ROLE *grantee,
6700 const char *db, const char *tname, role_hash_t *rhash)
6701 {
6702 Dynamic_array<GRANT_TABLE *> grants(PSI_INSTRUMENT_MEM);
6703 DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
6704
6705 /*
6706 first, collect table/column privileges granted to
6707 roles in question.
6708 */
6709 for (uint i=0 ; i < column_priv_hash.records ; i++)
6710 {
6711 GRANT_TABLE *grant= (GRANT_TABLE *) my_hash_element(&column_priv_hash, i);
6712 if (grant->host.hostname[0])
6713 continue;
6714 if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
6715 continue;
6716 ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
6717 if (!r)
6718 continue;
6719 grants.append(grant);
6720 }
6721 grants.sort(table_name_sort);
6722
6723 GRANT_TABLE **first= NULL, *merged= NULL, **cur;
6724 privilege_t privs(NO_ACL), cols(NO_ACL);
6725 ulong update_flags= 0;
6726 for (cur= grants.front(); cur <= grants.back(); cur++)
6727 {
6728 if (!first ||
6729 (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
6730 strcmp(cur[0]->tname, cur[-1]->tname))))
6731 { // new db.tname series
6732 update_flags|= update_role_table_columns(merged, first, cur,
6733 privs, cols, grantee->user.str);
6734 merged= NULL;
6735 privs= cols= NO_ACL;
6736 first= cur;
6737 }
6738 if (strcmp(cur[0]->user, grantee->user.str) == 0)
6739 {
6740 merged= cur[0];
6741 cols|= cur[0]->init_cols;
6742 privs|= cur[0]->init_privs;
6743 }
6744 else
6745 {
6746 cols|= cur[0]->cols;
6747 privs|= cur[0]->privs;
6748 }
6749 }
6750 update_flags|= update_role_table_columns(merged, first, cur,
6751 privs, cols, grantee->user.str);
6752
6753 return update_flags;
6754 }
6755
routine_name_sort(GRANT_NAME * const * r1,GRANT_NAME * const * r2)6756 static int routine_name_sort(GRANT_NAME * const *r1, GRANT_NAME * const *r2)
6757 {
6758 int res= strcmp((*r1)->db, (*r2)->db);
6759 if (res) return res;
6760 return strcmp((*r1)->tname, (*r2)->tname);
6761 }
6762
6763 /**
6764 update GRANT_NAME for a given routine and a given role with merged privileges
6765
6766 @param merged GRANT_NAME of the role in question (or NULL if it wasn't found)
6767 @param first first GRANT_NAME in an array for the routine in question
6768 @param privs new routine-level privileges for 'merged'
6769 @param role the name of the given role
6770 @param hash proc_priv_hash or func_priv_hash
6771
6772 @return a bitmap of
6773 1 - privileges were changed
6774 2 - GRANT_NAME was added
6775 4 - GRANT_NAME was deleted
6776 */
update_role_routines(GRANT_NAME * merged,GRANT_NAME ** first,privilege_t privs,const char * role,HASH * hash)6777 static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first,
6778 privilege_t privs, const char *role, HASH *hash)
6779 {
6780 if (!first)
6781 return 0;
6782
6783 DBUG_EXECUTE_IF("role_merge_stats", role_routine_merges++;);
6784
6785 if (merged == NULL)
6786 {
6787 /*
6788 there's no GRANT_NAME for this role (all routine grants come from granted
6789 roles) we need to create it
6790 */
6791 DBUG_ASSERT(privs);
6792 merged= new (&grant_memroot) GRANT_NAME("", first[0]->db, role, first[0]->tname,
6793 privs, true);
6794 merged->init_privs= NO_ACL; // all privs are inherited
6795 my_hash_insert(hash, (uchar *)merged);
6796 return 2;
6797 }
6798 else if (privs == NO_ACL)
6799 {
6800 /*
6801 there is GRANT_NAME but the role has no privileges granted
6802 (all privileges were coming from granted roles, and now those roles
6803 were dropped or had their privileges revoked).
6804 we need to remove this entry
6805 */
6806 my_hash_delete(hash, (uchar*)merged);
6807 return 4;
6808 }
6809 else if (merged->privs != privs)
6810 {
6811 /* this is easy */
6812 merged->privs= privs;
6813 return 1;
6814 }
6815 return 0;
6816 }
6817
6818 /**
6819 merges routine privileges from roles granted to the role 'grantee'.
6820
6821 @return true if routine privileges of the 'grantee' were changed
6822
6823 */
merge_role_routine_grant_privileges(ACL_ROLE * grantee,const char * db,const char * tname,role_hash_t * rhash,HASH * hash)6824 static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee,
6825 const char *db, const char *tname, role_hash_t *rhash, HASH *hash)
6826 {
6827 ulong update_flags= 0;
6828
6829 DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
6830
6831 Dynamic_array<GRANT_NAME *> grants(PSI_INSTRUMENT_MEM);
6832
6833 /* first, collect routine privileges granted to roles in question */
6834 for (uint i=0 ; i < hash->records ; i++)
6835 {
6836 GRANT_NAME *grant= (GRANT_NAME *) my_hash_element(hash, i);
6837 if (grant->host.hostname[0])
6838 continue;
6839 if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
6840 continue;
6841 ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
6842 if (!r)
6843 continue;
6844 grants.append(grant);
6845 }
6846 grants.sort(routine_name_sort);
6847
6848 GRANT_NAME **first= NULL, *merged= NULL;
6849 privilege_t privs(NO_ACL);
6850 for (GRANT_NAME **cur= grants.front(); cur <= grants.back(); cur++)
6851 {
6852 if (!first ||
6853 (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
6854 strcmp(cur[0]->tname, cur[-1]->tname))))
6855 { // new db.tname series
6856 update_flags|= update_role_routines(merged, first, privs,
6857 grantee->user.str, hash);
6858 merged= NULL;
6859 privs= NO_ACL;
6860 first= cur;
6861 }
6862 if (strcmp(cur[0]->user, grantee->user.str) == 0)
6863 {
6864 merged= cur[0];
6865 privs|= cur[0]->init_privs;
6866 }
6867 else
6868 {
6869 privs|= cur[0]->privs;
6870 }
6871 }
6872 update_flags|= update_role_routines(merged, first, privs,
6873 grantee->user.str, hash);
6874 return update_flags;
6875 }
6876
6877 /**
6878 update privileges of the 'grantee' from all roles, granted to it
6879 */
merge_role_privileges(ACL_ROLE * role,ACL_ROLE * grantee,void * context)6880 static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
6881 ACL_ROLE *grantee, void *context)
6882 {
6883 PRIVS_TO_MERGE *data= (PRIVS_TO_MERGE *)context;
6884
6885 DBUG_ASSERT(grantee->counter > 0);
6886 if (--grantee->counter)
6887 return 1; // don't recurse into grantee just yet
6888
6889 grantee->counter= 1; // Mark the grantee as merged.
6890
6891 /* if we'll do db/table/routine privileges, create a hash of role names */
6892 role_hash_t role_hash(PSI_INSTRUMENT_MEM, role_key);
6893 if (data->what != PRIVS_TO_MERGE::GLOBAL)
6894 {
6895 role_hash.insert(grantee);
6896 for (uint i= 0; i < grantee->role_grants.elements; i++)
6897 role_hash.insert(*dynamic_element(&grantee->role_grants, i, ACL_ROLE**));
6898 }
6899
6900 bool all= data->what == PRIVS_TO_MERGE::ALL;
6901 bool changed= false;
6902 if (all || data->what == PRIVS_TO_MERGE::GLOBAL)
6903 changed|= merge_role_global_privileges(grantee);
6904 if (all || data->what == PRIVS_TO_MERGE::DB)
6905 changed|= merge_role_db_privileges(grantee, data->db, &role_hash);
6906 if (all || data->what == PRIVS_TO_MERGE::TABLE_COLUMN)
6907 changed|= merge_role_table_and_column_privileges(grantee,
6908 data->db, data->name, &role_hash);
6909 if (all || data->what == PRIVS_TO_MERGE::PROC)
6910 changed|= merge_role_routine_grant_privileges(grantee,
6911 data->db, data->name, &role_hash, &proc_priv_hash);
6912 if (all || data->what == PRIVS_TO_MERGE::FUNC)
6913 changed|= merge_role_routine_grant_privileges(grantee,
6914 data->db, data->name, &role_hash, &func_priv_hash);
6915 if (all || data->what == PRIVS_TO_MERGE::PACKAGE_SPEC)
6916 changed|= merge_role_routine_grant_privileges(grantee,
6917 data->db, data->name, &role_hash,
6918 &package_spec_priv_hash);
6919 if (all || data->what == PRIVS_TO_MERGE::PACKAGE_BODY)
6920 changed|= merge_role_routine_grant_privileges(grantee,
6921 data->db, data->name, &role_hash,
6922 &package_body_priv_hash);
6923 return !changed; // don't recurse into the subgraph if privs didn't change
6924 }
6925
merge_one_role_privileges(ACL_ROLE * grantee)6926 static bool merge_one_role_privileges(ACL_ROLE *grantee)
6927 {
6928 PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
6929 grantee->counter= 1;
6930 return merge_role_privileges(0, grantee, &data);
6931 }
6932
6933 /*****************************************************************
6934 End of the role privilege propagation and graph traversal code
6935 ******************************************************************/
6936
has_auth(LEX_USER * user,LEX * lex)6937 static bool has_auth(LEX_USER *user, LEX *lex)
6938 {
6939 return user->has_auth() ||
6940 lex->account_options.ssl_type != SSL_TYPE_NOT_SPECIFIED ||
6941 lex->account_options.ssl_cipher.str ||
6942 lex->account_options.x509_issuer.str ||
6943 lex->account_options.x509_subject.str ||
6944 lex->account_options.specified_limits;
6945 }
6946
copy_and_check_auth(LEX_USER * to,LEX_USER * from,THD * thd)6947 static bool copy_and_check_auth(LEX_USER *to, LEX_USER *from, THD *thd)
6948 {
6949 to->auth= from->auth;
6950
6951 // if changing auth for an existing user
6952 if (has_auth(to, thd->lex) && find_user_exact(to->host.str, to->user.str))
6953 {
6954 mysql_mutex_unlock(&acl_cache->lock);
6955 bool res= check_alter_user(thd, to->host.str, to->user.str);
6956 mysql_mutex_lock(&acl_cache->lock);
6957 return res;
6958 }
6959
6960 return false;
6961 }
6962
6963
6964 /*
6965 Store table level and column level grants in the privilege tables
6966
6967 SYNOPSIS
6968 mysql_table_grant()
6969 thd Thread handle
6970 table_list List of tables to give grant
6971 user_list List of users to give grant
6972 columns List of columns to give grant
6973 rights Table level grant
6974 revoke_grant Set to 1 if this is a REVOKE command
6975
6976 RETURN
6977 FALSE ok
6978 TRUE error
6979 */
6980
mysql_table_grant(THD * thd,TABLE_LIST * table_list,List<LEX_USER> & user_list,List<LEX_COLUMN> & columns,privilege_t rights,bool revoke_grant)6981 int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
6982 List <LEX_USER> &user_list,
6983 List <LEX_COLUMN> &columns, privilege_t rights,
6984 bool revoke_grant)
6985 {
6986 privilege_t column_priv(NO_ACL);
6987 int result;
6988 List_iterator <LEX_USER> str_list (user_list);
6989 LEX_USER *Str, *tmp_Str;
6990 bool create_new_users=0;
6991 const char *db_name, *table_name;
6992 DBUG_ENTER("mysql_table_grant");
6993
6994 if (rights & ~TABLE_ACLS)
6995 {
6996 my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
6997 ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
6998 MYF(0));
6999 DBUG_RETURN(TRUE);
7000 }
7001
7002 if (!revoke_grant)
7003 {
7004 if (columns.elements)
7005 {
7006 class LEX_COLUMN *column;
7007 List_iterator <LEX_COLUMN> column_iter(columns);
7008
7009 if (open_normal_and_derived_tables(thd, table_list, 0, DT_PREPARE))
7010 DBUG_RETURN(TRUE);
7011
7012 while ((column = column_iter++))
7013 {
7014 uint unused_field_idx= NO_CACHED_FIELD_INDEX;
7015 TABLE_LIST *dummy;
7016 Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
7017 column->column.length(),
7018 column->column.ptr(), NULL, NULL,
7019 NULL, TRUE, FALSE,
7020 &unused_field_idx, FALSE, &dummy);
7021 if (unlikely(f == (Field*)0))
7022 {
7023 my_error(ER_BAD_FIELD_ERROR, MYF(0),
7024 column->column.c_ptr(), table_list->alias.str);
7025 DBUG_RETURN(TRUE);
7026 }
7027 if (unlikely(f == (Field *)-1))
7028 DBUG_RETURN(TRUE);
7029 column_priv|= column->rights;
7030 }
7031 close_mysql_tables(thd);
7032 }
7033 else
7034 {
7035 if (!(rights & CREATE_ACL))
7036 {
7037 if (!ha_table_exists(thd, &table_list->db, &table_list->table_name))
7038 {
7039 my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db.str,
7040 table_list->alias.str);
7041 DBUG_RETURN(TRUE);
7042 }
7043 }
7044 if (table_list->grant.want_privilege)
7045 {
7046 char command[128];
7047 get_privilege_desc(command, sizeof(command),
7048 table_list->grant.want_privilege);
7049 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
7050 command, thd->security_ctx->priv_user,
7051 thd->security_ctx->host_or_ip, table_list->alias.str);
7052 DBUG_RETURN(-1);
7053 }
7054 }
7055 }
7056
7057 /*
7058 Open the mysql.user and mysql.tables_priv tables.
7059 Don't open column table if we don't need it !
7060 */
7061 int tables_to_open= Table_user | Table_tables_priv;
7062 if (column_priv ||
7063 (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
7064 tables_to_open|= Table_columns_priv;
7065
7066 /*
7067 The lock api is depending on the thd->lex variable which needs to be
7068 re-initialized.
7069 */
7070 Query_tables_list backup;
7071 thd->lex->reset_n_backup_query_tables_list(&backup);
7072 /*
7073 Restore Query_tables_list::sql_command value, which was reset
7074 above, as the code writing query to the binary log assumes that
7075 this value corresponds to the statement being executed.
7076 */
7077 thd->lex->sql_command= backup.sql_command;
7078
7079 Grant_tables tables;
7080 if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
7081 {
7082 thd->lex->restore_backup_query_tables_list(&backup);
7083 DBUG_RETURN(result != 1);
7084 }
7085
7086 if (!revoke_grant)
7087 create_new_users= test_if_create_new_users(thd);
7088 mysql_rwlock_wrlock(&LOCK_grant);
7089 mysql_mutex_lock(&acl_cache->lock);
7090 MEM_ROOT *old_root= thd->mem_root;
7091 thd->mem_root= &grant_memroot;
7092 grant_version++;
7093
7094 while ((tmp_Str = str_list++))
7095 {
7096 int error;
7097 GRANT_TABLE *grant_table;
7098 if (!(Str= get_current_user(thd, tmp_Str, false)))
7099 {
7100 result= TRUE;
7101 continue;
7102 }
7103 /* Create user if needed */
7104 error= copy_and_check_auth(Str, tmp_Str, thd) ||
7105 replace_user_table(thd, tables.user_table(), Str,
7106 NO_ACL, revoke_grant, create_new_users,
7107 MY_TEST(thd->variables.sql_mode &
7108 MODE_NO_AUTO_CREATE_USER));
7109 if (unlikely(error))
7110 {
7111 result= TRUE; // Remember error
7112 continue; // Add next user
7113 }
7114
7115 db_name= table_list->get_db_name();
7116 table_name= table_list->get_table_name();
7117
7118 /* Find/create cached table grant */
7119 grant_table= table_hash_search(Str->host.str, NullS, db_name,
7120 Str->user.str, table_name, 1);
7121 if (!grant_table)
7122 {
7123 if (revoke_grant)
7124 {
7125 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
7126 Str->user.str, Str->host.str, table_list->table_name.str);
7127 result= TRUE;
7128 continue;
7129 }
7130 grant_table= new (&grant_memroot) GRANT_TABLE(Str->host.str, db_name,
7131 Str->user.str, table_name,
7132 rights,
7133 column_priv);
7134 if (!grant_table ||
7135 column_priv_insert(grant_table))
7136 {
7137 result= TRUE; /* purecov: deadcode */
7138 continue; /* purecov: deadcode */
7139 }
7140 }
7141
7142 /* If revoke_grant, calculate the new column privilege for tables_priv */
7143 if (revoke_grant)
7144 {
7145 class LEX_COLUMN *column;
7146 List_iterator <LEX_COLUMN> column_iter(columns);
7147 GRANT_COLUMN *grant_column;
7148
7149 /* Fix old grants */
7150 while ((column = column_iter++))
7151 {
7152 grant_column = column_hash_search(grant_table,
7153 column->column.ptr(),
7154 column->column.length());
7155 if (grant_column)
7156 grant_column->rights&= ~(column->rights | rights);
7157 }
7158 /* scan trough all columns to get new column grant */
7159 column_priv= NO_ACL;
7160 for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
7161 {
7162 grant_column= (GRANT_COLUMN*)
7163 my_hash_element(&grant_table->hash_columns, idx);
7164 grant_column->rights&= ~rights; // Fix other columns
7165 column_priv|= grant_column->rights;
7166 }
7167 }
7168 else
7169 {
7170 column_priv|= grant_table->cols;
7171 }
7172
7173
7174 /* update table and columns */
7175
7176 /* TODO(cvicentiu) refactor replace_table_table to use Tables_priv_table
7177 instead of TABLE directly. */
7178 if (tables.columns_priv_table().table_exists())
7179 {
7180 /* TODO(cvicentiu) refactor replace_column_table to use Columns_priv_table
7181 instead of TABLE directly. */
7182 if (replace_column_table(grant_table, tables.columns_priv_table().table(),
7183 *Str, columns, db_name, table_name, rights,
7184 revoke_grant))
7185 result= TRUE;
7186 }
7187 if (int res= replace_table_table(thd, grant_table,
7188 tables.tables_priv_table().table(),
7189 *Str, db_name, table_name,
7190 rights, column_priv, revoke_grant))
7191 {
7192 if (res > 0)
7193 {
7194 /* Should only happen if table is crashed */
7195 result= TRUE; /* purecov: deadcode */
7196 }
7197 }
7198 if (Str->is_role())
7199 propagate_role_grants(find_acl_role(Str->user.str),
7200 PRIVS_TO_MERGE::TABLE_COLUMN, db_name, table_name);
7201 }
7202
7203 thd->mem_root= old_root;
7204 mysql_mutex_unlock(&acl_cache->lock);
7205
7206 if (!result) /* success */
7207 result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7208
7209 mysql_rwlock_unlock(&LOCK_grant);
7210
7211 if (!result) /* success */
7212 my_ok(thd);
7213
7214 thd->lex->restore_backup_query_tables_list(&backup);
7215 DBUG_RETURN(result);
7216 }
7217
7218
7219 /**
7220 Store routine level grants in the privilege tables
7221
7222 @param thd Thread handle
7223 @param table_list List of routines to give grant
7224 @param sph SP handler
7225 @param user_list List of users to give grant
7226 @param rights Table level grant
7227 @param revoke_grant Is this is a REVOKE command?
7228
7229 @return
7230 @retval FALSE Success.
7231 @retval TRUE An error occurred.
7232 */
7233
mysql_routine_grant(THD * thd,TABLE_LIST * table_list,const Sp_handler * sph,List<LEX_USER> & user_list,privilege_t rights,bool revoke_grant,bool write_to_binlog)7234 bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list,
7235 const Sp_handler *sph,
7236 List <LEX_USER> &user_list, privilege_t rights,
7237 bool revoke_grant, bool write_to_binlog)
7238 {
7239 List_iterator <LEX_USER> str_list (user_list);
7240 LEX_USER *Str, *tmp_Str;
7241 bool create_new_users= 0;
7242 int result;
7243 const char *db_name, *table_name;
7244 DBUG_ENTER("mysql_routine_grant");
7245
7246 if (rights & ~PROC_ACLS)
7247 {
7248 my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
7249 ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
7250 MYF(0));
7251 DBUG_RETURN(TRUE);
7252 }
7253
7254 if (!revoke_grant)
7255 {
7256 if (sph->sp_exist_routines(thd, table_list))
7257 DBUG_RETURN(TRUE);
7258 }
7259
7260 Grant_tables tables;
7261 if ((result= tables.open_and_lock(thd, Table_user | Table_procs_priv, TL_WRITE)))
7262 DBUG_RETURN(result != 1);
7263
7264 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7265
7266 if (!revoke_grant)
7267 create_new_users= test_if_create_new_users(thd);
7268 mysql_rwlock_wrlock(&LOCK_grant);
7269 mysql_mutex_lock(&acl_cache->lock);
7270 MEM_ROOT *old_root= thd->mem_root;
7271 thd->mem_root= &grant_memroot;
7272
7273 DBUG_PRINT("info",("now time to iterate and add users"));
7274
7275 while ((tmp_Str= str_list++))
7276 {
7277 GRANT_NAME *grant_name;
7278 if (!(Str= get_current_user(thd, tmp_Str, false)))
7279 {
7280 result= TRUE;
7281 continue;
7282 }
7283 /* Create user if needed */
7284 if (copy_and_check_auth(Str, tmp_Str, thd) ||
7285 replace_user_table(thd, tables.user_table(), Str,
7286 NO_ACL, revoke_grant, create_new_users,
7287 MY_TEST(thd->variables.sql_mode &
7288 MODE_NO_AUTO_CREATE_USER)))
7289 {
7290 result= TRUE;
7291 continue;
7292 }
7293
7294 db_name= table_list->db.str;
7295 table_name= table_list->table_name.str;
7296 grant_name= routine_hash_search(Str->host.str, NullS, db_name,
7297 Str->user.str, table_name, sph, 1);
7298 if (!grant_name || !grant_name->init_privs)
7299 {
7300 if (revoke_grant)
7301 {
7302 my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
7303 Str->user.str, Str->host.str, table_name);
7304 result= TRUE;
7305 continue;
7306 }
7307 grant_name= new GRANT_NAME(Str->host.str, db_name,
7308 Str->user.str, table_name,
7309 rights, TRUE);
7310 if (!grant_name ||
7311 my_hash_insert(sph->get_priv_hash(), (uchar*) grant_name))
7312 {
7313 result= TRUE;
7314 continue;
7315 }
7316 }
7317
7318 if (replace_routine_table(thd, grant_name, tables.procs_priv_table().table(),
7319 *Str, db_name, table_name, sph, rights, revoke_grant) != 0)
7320 {
7321 result= TRUE;
7322 continue;
7323 }
7324 if (Str->is_role())
7325 propagate_role_grants(find_acl_role(Str->user.str),
7326 sp_privs_to_merge(sph->type()),
7327 db_name, table_name);
7328 }
7329 thd->mem_root= old_root;
7330 mysql_mutex_unlock(&acl_cache->lock);
7331
7332 if (write_to_binlog)
7333 {
7334 if (write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
7335 result= TRUE;
7336 }
7337
7338 mysql_rwlock_unlock(&LOCK_grant);
7339
7340 /* Tables are automatically closed */
7341 DBUG_RETURN(result);
7342 }
7343
7344 /**
7345 append a user or role name to a buffer that will be later used as an error message
7346 */
append_user(THD * thd,String * str,const LEX_CSTRING * u,const LEX_CSTRING * h)7347 static void append_user(THD *thd, String *str,
7348 const LEX_CSTRING *u, const LEX_CSTRING *h)
7349 {
7350 if (str->length())
7351 str->append(',');
7352 append_query_string(system_charset_info, str, u->str, u->length,
7353 thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
7354 /* hostname part is not relevant for roles, it is always empty */
7355 if (u->length == 0 || h->length != 0)
7356 {
7357 str->append('@');
7358 append_query_string(system_charset_info, str, h->str, h->length,
7359 thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
7360 }
7361 }
7362
append_user(THD * thd,String * str,LEX_USER * user)7363 static void append_user(THD *thd, String *str, LEX_USER *user)
7364 {
7365 append_user(thd, str, & user->user, & user->host);
7366 }
7367
7368 /**
7369 append a string to a buffer that will be later used as an error message
7370
7371 @note
7372 a string can be either CURRENT_USER or CURRENT_ROLE or NONE, it should be
7373 neither quoted nor escaped.
7374 */
append_str(String * str,const char * s,size_t l)7375 static void append_str(String *str, const char *s, size_t l)
7376 {
7377 if (str->length())
7378 str->append(',');
7379 str->append(s, l);
7380 }
7381
can_grant_role_callback(ACL_USER_BASE * grantee,ACL_ROLE * role,void * data)7382 static int can_grant_role_callback(ACL_USER_BASE *grantee,
7383 ACL_ROLE *role, void *data)
7384 {
7385 ROLE_GRANT_PAIR *pair;
7386
7387 if (role != (ACL_ROLE*)data)
7388 return 0; // keep searching
7389
7390 if (grantee->flags & IS_ROLE)
7391 pair= find_role_grant_pair(&grantee->user, &empty_clex_str, &role->user);
7392 else
7393 {
7394 ACL_USER *user= (ACL_USER *)grantee;
7395 LEX_CSTRING host= { user->host.hostname, user->hostname_length };
7396 pair= find_role_grant_pair(&user->user, &host, &role->user);
7397 }
7398 if (!pair->with_admin)
7399 return 0; // keep searching
7400
7401 return -1; // abort the traversal
7402 }
7403
7404
7405 /*
7406 One can only grant a role if SELECT * FROM I_S.APPLICABLE_ROLES shows this
7407 role as grantable.
7408
7409 What this really means - we need to traverse role graph for the current user
7410 looking for our role being granted with the admin option.
7411 */
can_grant_role(THD * thd,ACL_ROLE * role)7412 static bool can_grant_role(THD *thd, ACL_ROLE *role)
7413 {
7414 Security_context *sctx= thd->security_ctx;
7415
7416 if (!sctx->user) // replication
7417 return true;
7418
7419 ACL_USER *grantee= find_user_exact(sctx->priv_host, sctx->priv_user);
7420 if (!grantee)
7421 return false;
7422
7423 return traverse_role_graph_down(grantee, role, NULL,
7424 can_grant_role_callback) == -1;
7425 }
7426
7427
mysql_grant_role(THD * thd,List<LEX_USER> & list,bool revoke)7428 bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
7429 {
7430 DBUG_ENTER("mysql_grant_role");
7431 /*
7432 The first entry in the list is the granted role. Need at least two
7433 entries for the command to be valid
7434 */
7435 DBUG_ASSERT(list.elements >= 2);
7436 int result;
7437 bool create_new_user, no_auto_create_user;
7438 String wrong_users;
7439 LEX_USER *user, *granted_role;
7440 LEX_CSTRING rolename;
7441 LEX_CSTRING username;
7442 LEX_CSTRING hostname;
7443 ACL_ROLE *role, *role_as_user;
7444
7445 List_iterator <LEX_USER> user_list(list);
7446 granted_role= user_list++;
7447 if (!(granted_role= get_current_user(thd, granted_role)))
7448 DBUG_RETURN(TRUE);
7449
7450 DBUG_ASSERT(granted_role->is_role());
7451 rolename= granted_role->user;
7452
7453 create_new_user= test_if_create_new_users(thd);
7454 no_auto_create_user= MY_TEST(thd->variables.sql_mode &
7455 MODE_NO_AUTO_CREATE_USER);
7456
7457 Grant_tables tables;
7458 if ((result= tables.open_and_lock(thd, Table_user | Table_roles_mapping, TL_WRITE)))
7459 DBUG_RETURN(result != 1);
7460
7461 mysql_rwlock_wrlock(&LOCK_grant);
7462 mysql_mutex_lock(&acl_cache->lock);
7463 if (!(role= find_acl_role(rolename.str)))
7464 {
7465 mysql_mutex_unlock(&acl_cache->lock);
7466 mysql_rwlock_unlock(&LOCK_grant);
7467 my_error(ER_INVALID_ROLE, MYF(0), rolename.str);
7468 DBUG_RETURN(TRUE);
7469 }
7470
7471 if (!can_grant_role(thd, role))
7472 {
7473 mysql_mutex_unlock(&acl_cache->lock);
7474 mysql_rwlock_unlock(&LOCK_grant);
7475 my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
7476 thd->security_ctx->priv_user, thd->security_ctx->priv_host);
7477 DBUG_RETURN(TRUE);
7478 }
7479
7480 while ((user= user_list++))
7481 {
7482 role_as_user= NULL;
7483 /* current_role is treated slightly different */
7484 if (user->user.str == current_role.str)
7485 {
7486 /* current_role is NONE */
7487 if (!thd->security_ctx->priv_role[0])
7488 {
7489 my_error(ER_INVALID_ROLE, MYF(0), "NONE");
7490 append_str(&wrong_users, STRING_WITH_LEN("NONE"));
7491 result= 1;
7492 continue;
7493 }
7494 if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role)))
7495 {
7496 LEX_CSTRING ls= { thd->security_ctx->priv_role,
7497 strlen(thd->security_ctx->priv_role) };
7498 append_user(thd, &wrong_users, &ls, &empty_clex_str);
7499 result= 1;
7500 continue;
7501 }
7502
7503 /* can not grant current_role to current_role */
7504 if (granted_role->user.str == current_role.str)
7505 {
7506 append_user(thd, &wrong_users, &role_as_user->user, &empty_clex_str);
7507 result= 1;
7508 continue;
7509 }
7510 username.str= thd->security_ctx->priv_role;
7511 username.length= strlen(username.str);
7512 hostname= empty_clex_str;
7513 }
7514 else if (user->user.str == current_user.str)
7515 {
7516 username.str= thd->security_ctx->priv_user;
7517 username.length= strlen(username.str);
7518 hostname.str= thd->security_ctx->priv_host;
7519 hostname.length= strlen(hostname.str);
7520 }
7521 else
7522 {
7523 username= user->user;
7524 if (user->host.str)
7525 hostname= user->host;
7526 else
7527 if ((role_as_user= find_acl_role(user->user.str)))
7528 hostname= empty_clex_str;
7529 else
7530 {
7531 if (is_invalid_role_name(username.str))
7532 {
7533 append_user(thd, &wrong_users, &username, &empty_clex_str);
7534 result= 1;
7535 continue;
7536 }
7537 hostname= host_not_specified;
7538 }
7539 }
7540
7541 ROLE_GRANT_PAIR *hash_entry= find_role_grant_pair(&username, &hostname,
7542 &rolename);
7543 ACL_USER_BASE *grantee= role_as_user;
7544
7545 if (has_auth(user, thd->lex))
7546 DBUG_ASSERT(!grantee);
7547 else if (!grantee)
7548 grantee= find_user_exact(hostname.str, username.str);
7549
7550 if (!grantee && !revoke)
7551 {
7552 LEX_USER user_combo = *user;
7553 user_combo.host = hostname;
7554 user_combo.user = username;
7555
7556 if (copy_and_check_auth(&user_combo, &user_combo, thd) ||
7557 replace_user_table(thd, tables.user_table(), &user_combo, NO_ACL,
7558 false, create_new_user,
7559 no_auto_create_user))
7560 {
7561 append_user(thd, &wrong_users, &username, &hostname);
7562 result= 1;
7563 continue;
7564 }
7565 grantee= find_user_exact(hostname.str, username.str);
7566
7567 /* either replace_user_table failed, or we've added the user */
7568 DBUG_ASSERT(grantee);
7569 }
7570
7571 if (!grantee)
7572 {
7573 append_user(thd, &wrong_users, &username, &hostname);
7574 result= 1;
7575 continue;
7576 }
7577
7578 if (!revoke)
7579 {
7580 if (hash_entry)
7581 {
7582 // perhaps, updating an existing grant, adding WITH ADMIN OPTION
7583 }
7584 else
7585 {
7586 add_role_user_mapping(grantee, role);
7587
7588 /*
7589 Check if this grant would cause a cycle. It only needs to be run
7590 if we're granting a role to a role
7591 */
7592 if (role_as_user &&
7593 traverse_role_graph_down(role, 0, 0, 0) == ROLE_CYCLE_FOUND)
7594 {
7595 append_user(thd, &wrong_users, &username, &empty_clex_str);
7596 result= 1;
7597 undo_add_role_user_mapping(grantee, role);
7598 continue;
7599 }
7600 }
7601 }
7602 else
7603 {
7604 /* grant was already removed or never existed */
7605 if (!hash_entry)
7606 {
7607 append_user(thd, &wrong_users, &username, &hostname);
7608 result= 1;
7609 continue;
7610 }
7611 if (thd->lex->with_admin_option)
7612 {
7613 // only revoking an admin option, not the complete grant
7614 }
7615 else
7616 {
7617 /* revoke a role grant */
7618 remove_role_user_mapping(grantee, role);
7619 }
7620 }
7621
7622 /* write into the roles_mapping table */
7623 /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
7624 Roles_mapping_table instead of TABLE directly. */
7625 if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
7626 &username, &hostname, &rolename,
7627 thd->lex->with_admin_option,
7628 hash_entry, revoke))
7629 {
7630 append_user(thd, &wrong_users, &username, &empty_clex_str);
7631 result= 1;
7632 if (!revoke)
7633 {
7634 /* need to remove the mapping added previously */
7635 undo_add_role_user_mapping(grantee, role);
7636 }
7637 else
7638 {
7639 /* need to restore the mapping deleted previously */
7640 add_role_user_mapping(grantee, role);
7641 }
7642 continue;
7643 }
7644 update_role_mapping(&username, &hostname, &rolename,
7645 thd->lex->with_admin_option, hash_entry, revoke);
7646
7647 /*
7648 Only need to propagate grants when granting/revoking a role to/from
7649 a role
7650 */
7651 if (role_as_user && merge_one_role_privileges(role_as_user) == 0)
7652 propagate_role_grants(role_as_user, PRIVS_TO_MERGE::ALL);
7653 }
7654
7655 mysql_mutex_unlock(&acl_cache->lock);
7656
7657 if (result)
7658 my_error(revoke ? ER_CANNOT_REVOKE_ROLE : ER_CANNOT_GRANT_ROLE, MYF(0),
7659 rolename.str, wrong_users.c_ptr_safe());
7660 else
7661 result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7662
7663 mysql_rwlock_unlock(&LOCK_grant);
7664
7665 DBUG_RETURN(result);
7666 }
7667
7668
mysql_grant(THD * thd,const char * db,List<LEX_USER> & list,privilege_t rights,bool revoke_grant,bool is_proxy)7669 bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
7670 privilege_t rights, bool revoke_grant, bool is_proxy)
7671 {
7672 List_iterator <LEX_USER> str_list (list);
7673 LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
7674 char tmp_db[SAFE_NAME_LEN+1];
7675 bool create_new_users=0;
7676 int result;
7677 DBUG_ENTER("mysql_grant");
7678
7679 if (lower_case_table_names && db)
7680 {
7681 char *end= strnmov(tmp_db,db, sizeof(tmp_db));
7682 if (end >= tmp_db + sizeof(tmp_db))
7683 {
7684 my_error(ER_WRONG_DB_NAME ,MYF(0), db);
7685 DBUG_RETURN(TRUE);
7686 }
7687 my_casedn_str(files_charset_info, tmp_db);
7688 db=tmp_db;
7689 }
7690
7691 if (is_proxy)
7692 {
7693 DBUG_ASSERT(!db);
7694 proxied_user= str_list++;
7695 }
7696
7697 const uint tables_to_open= Table_user | (is_proxy ? Table_proxies_priv : Table_db);
7698 Grant_tables tables;
7699 if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
7700 DBUG_RETURN(result != 1);
7701
7702 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7703
7704 if (!revoke_grant)
7705 create_new_users= test_if_create_new_users(thd);
7706
7707 /* go through users in user_list */
7708 mysql_rwlock_wrlock(&LOCK_grant);
7709 mysql_mutex_lock(&acl_cache->lock);
7710 grant_version++;
7711
7712 if (proxied_user)
7713 {
7714 if (!(proxied_user= get_current_user(thd, proxied_user, false)))
7715 DBUG_RETURN(TRUE);
7716 DBUG_ASSERT(proxied_user->host.length); // not a Role
7717 }
7718
7719 while ((tmp_Str = str_list++))
7720 {
7721 if (!(Str= get_current_user(thd, tmp_Str, false)))
7722 {
7723 result= true;
7724 continue;
7725 }
7726
7727 if (copy_and_check_auth(Str, tmp_Str, thd) ||
7728 replace_user_table(thd, tables.user_table(), Str,
7729 (!db ? rights : NO_ACL),
7730 revoke_grant, create_new_users,
7731 MY_TEST(thd->variables.sql_mode &
7732 MODE_NO_AUTO_CREATE_USER)))
7733 result= true;
7734 else if (db)
7735 {
7736 privilege_t db_rights(rights & DB_ACLS);
7737 if (db_rights == rights)
7738 {
7739 if (replace_db_table(tables.db_table().table(), db, *Str, db_rights,
7740 revoke_grant))
7741 result= true;
7742 }
7743 else
7744 {
7745 my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
7746 result= true;
7747 }
7748 }
7749 else if (is_proxy)
7750 {
7751 if (replace_proxies_priv_table(thd, tables.proxies_priv_table().table(),
7752 Str, proxied_user, rights & GRANT_ACL ? TRUE : FALSE, revoke_grant))
7753 result= true;
7754 }
7755 if (Str->is_role())
7756 propagate_role_grants(find_acl_role(Str->user.str),
7757 db ? PRIVS_TO_MERGE::DB : PRIVS_TO_MERGE::GLOBAL,
7758 db);
7759 }
7760 mysql_mutex_unlock(&acl_cache->lock);
7761
7762 if (!result)
7763 {
7764 result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7765 }
7766
7767 mysql_rwlock_unlock(&LOCK_grant);
7768
7769 if (!result)
7770 my_ok(thd);
7771
7772 DBUG_RETURN(result);
7773 }
7774
7775
7776 /* Free grant array if possible */
7777
grant_free(void)7778 void grant_free(void)
7779 {
7780 DBUG_ENTER("grant_free");
7781 my_hash_free(&column_priv_hash);
7782 my_hash_free(&proc_priv_hash);
7783 my_hash_free(&func_priv_hash);
7784 my_hash_free(&package_spec_priv_hash);
7785 my_hash_free(&package_body_priv_hash);
7786 free_root(&grant_memroot,MYF(0));
7787 DBUG_VOID_RETURN;
7788 }
7789
7790
7791 /**
7792 @brief Initialize structures responsible for table/column-level privilege
7793 checking and load information for them from tables in the 'mysql' database.
7794
7795 @return Error status
7796 @retval 0 OK
7797 @retval 1 Could not initialize grant subsystem.
7798 */
7799
grant_init()7800 bool grant_init()
7801 {
7802 THD *thd;
7803 bool return_val;
7804 DBUG_ENTER("grant_init");
7805
7806 if (!(thd= new THD(0)))
7807 DBUG_RETURN(1); /* purecov: deadcode */
7808 thd->thread_stack= (char*) &thd;
7809 thd->store_globals();
7810 return_val= grant_reload(thd);
7811 delete thd;
7812 DBUG_RETURN(return_val);
7813 }
7814
7815
7816 /**
7817 @brief Initialize structures responsible for table/column-level privilege
7818 checking and load information about grants from open privilege tables.
7819
7820 @param thd Current thread
7821 @param tables List containing open "mysql.tables_priv" and
7822 "mysql.columns_priv" tables.
7823
7824 @see grant_reload
7825
7826 @return Error state
7827 @retval FALSE Success
7828 @retval TRUE Error
7829 */
7830
grant_load(THD * thd,const Tables_priv_table & tables_priv,const Columns_priv_table & columns_priv,const Procs_priv_table & procs_priv)7831 static bool grant_load(THD *thd,
7832 const Tables_priv_table& tables_priv,
7833 const Columns_priv_table& columns_priv,
7834 const Procs_priv_table& procs_priv)
7835 {
7836 bool return_val= 1;
7837 TABLE *t_table, *c_table, *p_table;
7838 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
7839 MEM_ROOT *save_mem_root= thd->mem_root;
7840 DBUG_ENTER("grant_load");
7841
7842 Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);
7843
7844 (void) my_hash_init(key_memory_acl_memex, &column_priv_hash,
7845 &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
7846 get_grant_table, (my_hash_free_key) free_grant_table, 0);
7847 (void) my_hash_init(key_memory_acl_memex, &proc_priv_hash,
7848 &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
7849 get_grant_table, 0,0);
7850 (void) my_hash_init(key_memory_acl_memex, &func_priv_hash,
7851 &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
7852 get_grant_table, 0,0);
7853 (void) my_hash_init(key_memory_acl_memex, &package_spec_priv_hash,
7854 &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
7855 get_grant_table, 0,0);
7856 (void) my_hash_init(key_memory_acl_memex, &package_body_priv_hash,
7857 &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
7858 get_grant_table, 0,0);
7859 init_sql_alloc(key_memory_acl_mem, &grant_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
7860
7861 t_table= tables_priv.table();
7862 c_table= columns_priv.table();
7863 p_table= procs_priv.table(); // this can be NULL
7864
7865 if (t_table->file->ha_index_init(0, 1))
7866 goto end_index_init;
7867
7868 t_table->use_all_columns();
7869 c_table->use_all_columns();
7870
7871 thd->mem_root= &grant_memroot;
7872
7873 if (!t_table->file->ha_index_first(t_table->record[0]))
7874 {
7875 do
7876 {
7877 GRANT_TABLE *mem_check;
7878 /* TODO(cvicentiu) convert this to use tables_priv and columns_priv. */
7879 if (!(mem_check= new (&grant_memroot) GRANT_TABLE(t_table, c_table)))
7880 {
7881 /* This could only happen if we are out memory */
7882 goto end_unlock;
7883 }
7884
7885 if (check_no_resolve)
7886 {
7887 if (hostname_requires_resolving(mem_check->host.hostname))
7888 {
7889 sql_print_warning("'tables_priv' entry '%s %s@%s' "
7890 "ignored in --skip-name-resolve mode.",
7891 mem_check->tname, mem_check->user,
7892 safe_str(mem_check->host.hostname));
7893 continue;
7894 }
7895 }
7896
7897 if (! mem_check->ok())
7898 delete mem_check;
7899 else if (column_priv_insert(mem_check))
7900 {
7901 delete mem_check;
7902 goto end_unlock;
7903 }
7904 }
7905 while (!t_table->file->ha_index_next(t_table->record[0]));
7906 }
7907
7908 return_val= 0;
7909
7910 if (p_table)
7911 {
7912 if (p_table->file->ha_index_init(0, 1))
7913 goto end_unlock;
7914
7915 p_table->use_all_columns();
7916
7917 if (!p_table->file->ha_index_first(p_table->record[0]))
7918 {
7919 do
7920 {
7921 GRANT_NAME *mem_check;
7922 HASH *hash;
7923 if (!(mem_check= new (&grant_memroot) GRANT_NAME(p_table, TRUE)))
7924 {
7925 /* This could only happen if we are out memory */
7926 goto end_unlock_p;
7927 }
7928
7929 if (check_no_resolve)
7930 {
7931 if (hostname_requires_resolving(mem_check->host.hostname))
7932 {
7933 sql_print_warning("'procs_priv' entry '%s %s@%s' "
7934 "ignored in --skip-name-resolve mode.",
7935 mem_check->tname, mem_check->user,
7936 safe_str(mem_check->host.hostname));
7937 continue;
7938 }
7939 }
7940 enum_sp_type type= (enum_sp_type)procs_priv.routine_type()->val_int();
7941 const Sp_handler *sph= Sp_handler::handler(type);
7942 if (!sph || !(hash= sph->get_priv_hash()))
7943 {
7944 sql_print_warning("'procs_priv' entry '%s' "
7945 "ignored, bad routine type",
7946 mem_check->tname);
7947 continue;
7948 }
7949
7950 mem_check->privs= fix_rights_for_procedure(mem_check->privs);
7951 mem_check->init_privs= mem_check->privs;
7952 if (! mem_check->ok())
7953 delete mem_check;
7954 else if (my_hash_insert(hash, (uchar*) mem_check))
7955 {
7956 delete mem_check;
7957 goto end_unlock_p;
7958 }
7959 }
7960 while (!p_table->file->ha_index_next(p_table->record[0]));
7961 }
7962 }
7963
7964 end_unlock_p:
7965 if (p_table)
7966 p_table->file->ha_index_end();
7967 end_unlock:
7968 t_table->file->ha_index_end();
7969 thd->mem_root= save_mem_root;
7970 end_index_init:
7971 DBUG_RETURN(return_val);
7972 }
7973
propagate_role_grants_action(void * role_ptr,void * ptr)7974 static my_bool propagate_role_grants_action(void *role_ptr,
7975 void *ptr __attribute__((unused)))
7976 {
7977 ACL_ROLE *role= static_cast<ACL_ROLE *>(role_ptr);
7978 if (role->counter)
7979 return 0;
7980
7981 mysql_mutex_assert_owner(&acl_cache->lock);
7982 PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
7983 traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
7984 return 0;
7985 }
7986
7987
7988 /**
7989 @brief Reload information about table and column level privileges if possible
7990
7991 @param thd Current thread
7992
7993 Locked tables are checked by acl_reload() and doesn't have to be checked
7994 in this call.
7995 This function is also used for initialization of structures responsible
7996 for table/column-level privilege checking.
7997
7998 @return Error state
7999 @retval FALSE Success
8000 @retval TRUE Error
8001 */
8002
grant_reload(THD * thd)8003 bool grant_reload(THD *thd)
8004 {
8005 HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
8006 HASH old_package_spec_priv_hash, old_package_body_priv_hash;
8007 MEM_ROOT old_mem;
8008 int result;
8009 DBUG_ENTER("grant_reload");
8010
8011 /*
8012 To avoid deadlocks we should obtain table locks before
8013 obtaining LOCK_grant rwlock.
8014 */
8015
8016 Grant_tables tables;
8017 const uint tables_to_open= Table_tables_priv | Table_columns_priv| Table_procs_priv;
8018 if ((result= tables.open_and_lock(thd, tables_to_open, TL_READ)))
8019 DBUG_RETURN(result != 1);
8020
8021 mysql_rwlock_wrlock(&LOCK_grant);
8022 grant_version++;
8023 old_column_priv_hash= column_priv_hash;
8024 old_proc_priv_hash= proc_priv_hash;
8025 old_func_priv_hash= func_priv_hash;
8026 old_package_spec_priv_hash= package_spec_priv_hash;
8027 old_package_body_priv_hash= package_body_priv_hash;
8028
8029 /*
8030 Create a new memory pool but save the current memory pool to make an undo
8031 opertion possible in case of failure.
8032 */
8033 old_mem= grant_memroot;
8034
8035 if ((result= grant_load(thd,
8036 tables.tables_priv_table(),
8037 tables.columns_priv_table(),
8038 tables.procs_priv_table())))
8039 { // Error. Revert to old hash
8040 DBUG_PRINT("error",("Reverting to old privileges"));
8041 grant_free(); /* purecov: deadcode */
8042 column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
8043 proc_priv_hash= old_proc_priv_hash;
8044 func_priv_hash= old_func_priv_hash;
8045 package_spec_priv_hash= old_package_spec_priv_hash;
8046 package_body_priv_hash= old_package_body_priv_hash;
8047 grant_memroot= old_mem; /* purecov: deadcode */
8048 }
8049 else
8050 {
8051 my_hash_free(&old_column_priv_hash);
8052 my_hash_free(&old_proc_priv_hash);
8053 my_hash_free(&old_func_priv_hash);
8054 my_hash_free(&old_package_spec_priv_hash);
8055 my_hash_free(&old_package_body_priv_hash);
8056 free_root(&old_mem,MYF(0));
8057 }
8058
8059 mysql_mutex_lock(&acl_cache->lock);
8060 my_hash_iterate(&acl_roles, propagate_role_grants_action, NULL);
8061 mysql_mutex_unlock(&acl_cache->lock);
8062
8063 mysql_rwlock_unlock(&LOCK_grant);
8064
8065 close_mysql_tables(thd);
8066
8067 DBUG_RETURN(result);
8068 }
8069
8070
8071 /**
8072 @brief Check table level grants
8073
8074 @param thd Thread handler
8075 @param want_access Bits of privileges user needs to have.
8076 @param tables List of tables to check. The user should have
8077 'want_access' to all tables in list.
8078 @param any_combination_will_do TRUE if it's enough to have any privilege for
8079 any combination of the table columns.
8080 @param number Check at most this number of tables.
8081 @param no_errors TRUE if no error should be sent directly to the client.
8082
8083 If table->grant.want_privilege != 0 then the requested privileges where
8084 in the set of COL_ACLS but access was not granted on the table level. As
8085 a consequence an extra check of column privileges is required.
8086
8087 Specifically if this function returns FALSE the user has some kind of
8088 privilege on a combination of columns in each table.
8089
8090 This function is usually preceeded by check_access which establish the
8091 User-, Db- and Host access rights.
8092
8093 @see check_access
8094 @see check_table_access
8095
8096 @note
8097 This functions assumes that either number of tables to be inspected
8098 by it is limited explicitly (i.e. is is not UINT_MAX) or table list
8099 used and thd->lex->query_tables_own_last value correspond to each
8100 other (the latter should be either 0 or point to next_global member
8101 of one of elements of this table list).
8102
8103 We delay locking of LOCK_grant until we really need it as we assume that
8104 most privileges be resolved with user or db level accesses.
8105
8106 @return Access status
8107 @retval FALSE Access granted; But column privileges might need to be
8108 checked.
8109 @retval TRUE The user did not have the requested privileges on any of the
8110 tables.
8111
8112 */
8113
check_grant(THD * thd,privilege_t want_access,TABLE_LIST * tables,bool any_combination_will_do,uint number,bool no_errors)8114 bool check_grant(THD *thd, privilege_t want_access, TABLE_LIST *tables,
8115 bool any_combination_will_do, uint number, bool no_errors)
8116 {
8117 TABLE_LIST *tl;
8118 TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
8119 Security_context *sctx= thd->security_ctx;
8120 uint i;
8121 privilege_t original_want_access(want_access);
8122 bool locked= 0;
8123 GRANT_TABLE *grant_table;
8124 GRANT_TABLE *grant_table_role= NULL;
8125 DBUG_ENTER("check_grant");
8126 DBUG_ASSERT(number > 0);
8127
8128 /*
8129 Walk through the list of tables that belong to the query and save the
8130 requested access (orig_want_privilege) to be able to use it when
8131 checking access rights to the underlying tables of a view. Our grant
8132 system gradually eliminates checked bits from want_privilege and thus
8133 after all checks are done we can no longer use it.
8134 The check that first_not_own_table is not reached is for the case when
8135 the given table list refers to the list for prelocking (contains tables
8136 of other queries). For simple queries first_not_own_table is 0.
8137 */
8138 for (i= 0, tl= tables;
8139 i < number && tl != first_not_own_table;
8140 tl= tl->next_global, i++)
8141 {
8142 /*
8143 Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
8144 It will be checked during making view.
8145 */
8146 tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
8147 }
8148 number= i;
8149
8150 for (tl= tables; number-- ; tl= tl->next_global)
8151 {
8152 TABLE_LIST *const t_ref=
8153 tl->correspondent_table ? tl->correspondent_table : tl;
8154 sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx;
8155 privilege_t orig_want_access(original_want_access);
8156
8157 /*
8158 If sequence is used as part of NEXT VALUE, PREVIOUS VALUE or SELECT,
8159 we need to modify the requested access rights depending on how the
8160 sequence is used.
8161 */
8162 if (t_ref->sequence &&
8163 !(want_access & ~(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL)))
8164 {
8165 /*
8166 We want to have either SELECT or INSERT rights to sequences depending
8167 on how they are accessed
8168 */
8169 orig_want_access= ((t_ref->lock_type == TL_WRITE_ALLOW_WRITE) ?
8170 INSERT_ACL : SELECT_ACL);
8171 }
8172
8173 if (tl->with || !tl->db.str ||
8174 (tl->select_lex &&
8175 (tl->with= tl->select_lex->find_table_def_in_with_clauses(tl))))
8176 continue;
8177
8178 const ACL_internal_table_access *access=
8179 get_cached_table_access(&t_ref->grant.m_internal,
8180 t_ref->get_db_name(),
8181 t_ref->get_table_name());
8182
8183 if (access)
8184 {
8185 switch(access->check(orig_want_access, &t_ref->grant.privilege))
8186 {
8187 case ACL_INTERNAL_ACCESS_GRANTED:
8188 t_ref->grant.privilege|= orig_want_access;
8189 t_ref->grant.want_privilege= NO_ACL;
8190 continue;
8191 case ACL_INTERNAL_ACCESS_DENIED:
8192 goto err;
8193 case ACL_INTERNAL_ACCESS_CHECK_GRANT:
8194 break;
8195 }
8196 }
8197
8198 want_access= orig_want_access;
8199 want_access&= ~sctx->master_access;
8200 if (!want_access)
8201 continue; // ok
8202
8203 if (!(~t_ref->grant.privilege & want_access) ||
8204 t_ref->is_anonymous_derived_table() || t_ref->schema_table)
8205 {
8206 /*
8207 It is subquery in the FROM clause. VIEW set t_ref->derived after
8208 table opening, but this function always called before table opening.
8209
8210 NOTE: is_derived() can't be used here because subquery in this case
8211 the FROM clase (derived tables) can be not be marked yet.
8212 */
8213 if (t_ref->is_anonymous_derived_table() || t_ref->schema_table)
8214 {
8215 /*
8216 If it's a temporary table created for a subquery in the FROM
8217 clause, or an INFORMATION_SCHEMA table, drop the request for
8218 a privilege.
8219 */
8220 t_ref->grant.want_privilege= NO_ACL;
8221 }
8222 continue;
8223 }
8224
8225 if (is_temporary_table(t_ref))
8226 {
8227 /*
8228 If this table list element corresponds to a pre-opened temporary
8229 table skip checking of all relevant table-level privileges for it.
8230 Note that during creation of temporary table we still need to check
8231 if user has CREATE_TMP_ACL.
8232 */
8233 t_ref->grant.privilege|= TMP_TABLE_ACLS;
8234 t_ref->grant.want_privilege= NO_ACL;
8235 continue;
8236 }
8237
8238 if (!locked)
8239 {
8240 locked= 1;
8241 mysql_rwlock_rdlock(&LOCK_grant);
8242 }
8243
8244 grant_table= table_hash_search(sctx->host, sctx->ip,
8245 t_ref->get_db_name(),
8246 sctx->priv_user,
8247 t_ref->get_table_name(),
8248 FALSE);
8249 if (sctx->priv_role[0])
8250 grant_table_role= table_hash_search("", NULL, t_ref->get_db_name(),
8251 sctx->priv_role,
8252 t_ref->get_table_name(),
8253 TRUE);
8254
8255 if (!grant_table && !grant_table_role)
8256 {
8257 want_access&= ~t_ref->grant.privilege;
8258 goto err; // No grants
8259 }
8260
8261 /*
8262 For SHOW COLUMNS, SHOW INDEX it is enough to have some
8263 privileges on any column combination on the table.
8264 */
8265 if (any_combination_will_do)
8266 continue;
8267
8268 t_ref->grant.grant_table_user= grant_table; // Remember for column test
8269 t_ref->grant.grant_table_role= grant_table_role;
8270 t_ref->grant.version= grant_version;
8271 t_ref->grant.privilege|= grant_table ? grant_table->privs : NO_ACL;
8272 t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : NO_ACL;
8273 t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege);
8274
8275 if (!(~t_ref->grant.privilege & want_access))
8276 continue;
8277
8278 if ((want_access&= ~((grant_table ? grant_table->cols : NO_ACL) |
8279 (grant_table_role ? grant_table_role->cols : NO_ACL) |
8280 t_ref->grant.privilege)))
8281 {
8282 goto err; // impossible
8283 }
8284 }
8285 if (locked)
8286 mysql_rwlock_unlock(&LOCK_grant);
8287 DBUG_RETURN(FALSE);
8288
8289 err:
8290 if (locked)
8291 mysql_rwlock_unlock(&LOCK_grant);
8292 if (!no_errors) // Not a silent skip of table
8293 {
8294 char command[128];
8295 get_privilege_desc(command, sizeof(command), want_access);
8296 status_var_increment(thd->status_var.access_denied_errors);
8297
8298 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
8299 command,
8300 sctx->priv_user,
8301 sctx->host_or_ip,
8302 tl ? tl->get_table_name() : "unknown");
8303 }
8304 DBUG_RETURN(TRUE);
8305 }
8306
8307
check_grant_column_int(GRANT_TABLE * grant_table,const char * name,uint length,privilege_t * want_access)8308 static void check_grant_column_int(GRANT_TABLE *grant_table, const char *name,
8309 uint length, privilege_t *want_access)
8310 {
8311 if (grant_table)
8312 {
8313 *want_access&= ~grant_table->privs;
8314 if (*want_access & grant_table->cols)
8315 {
8316 GRANT_COLUMN *grant_column= column_hash_search(grant_table, name, length);
8317 if (grant_column)
8318 *want_access&= ~grant_column->rights;
8319 }
8320 }
8321 }
8322
8323 /*
8324 Check column rights in given security context
8325
8326 SYNOPSIS
8327 check_grant_column()
8328 thd thread handler
8329 grant grant information structure
8330 db_name db name
8331 table_name table name
8332 name column name
8333 length column name length
8334 sctx security context
8335
8336 RETURN
8337 FALSE OK
8338 TRUE access denied
8339 */
8340
check_grant_column(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * name,size_t length,Security_context * sctx)8341 bool check_grant_column(THD *thd, GRANT_INFO *grant,
8342 const char *db_name, const char *table_name,
8343 const char *name, size_t length, Security_context *sctx)
8344 {
8345 privilege_t want_access(grant->want_privilege & ~grant->privilege);
8346 DBUG_ENTER("check_grant_column");
8347 DBUG_PRINT("enter", ("table: %s want_access: %llx",
8348 table_name, (longlong) want_access));
8349
8350 if (!want_access)
8351 DBUG_RETURN(0); // Already checked
8352
8353 mysql_rwlock_rdlock(&LOCK_grant);
8354
8355 /* reload table if someone has modified any grants */
8356
8357 if (grant->version != grant_version)
8358 {
8359 grant->grant_table_user=
8360 table_hash_search(sctx->host, sctx->ip, db_name,
8361 sctx->priv_user,
8362 table_name, 0); /* purecov: inspected */
8363 grant->grant_table_role=
8364 sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
8365 sctx->priv_role,
8366 table_name, TRUE) : NULL;
8367 grant->version= grant_version; /* purecov: inspected */
8368 }
8369
8370 check_grant_column_int(grant->grant_table_user, name, (uint)length,
8371 &want_access);
8372 check_grant_column_int(grant->grant_table_role, name, (uint)length,
8373 &want_access);
8374
8375 mysql_rwlock_unlock(&LOCK_grant);
8376 if (!want_access)
8377 DBUG_RETURN(0);
8378
8379 char command[128];
8380 get_privilege_desc(command, sizeof(command), want_access);
8381 /* TODO perhaps error should print current rolename aswell */
8382 my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), command, sctx->priv_user,
8383 sctx->host_or_ip, name, table_name);
8384 DBUG_RETURN(1);
8385 }
8386
8387
8388 /*
8389 Check the access right to a column depending on the type of table.
8390
8391 SYNOPSIS
8392 check_column_grant_in_table_ref()
8393 thd thread handler
8394 table_ref table reference where to check the field
8395 name name of field to check
8396 length length of name
8397 fld use fld object to check invisibility when it is
8398 not 0, not_found_field, view_ref_found
8399
8400 DESCRIPTION
8401 Check the access rights to a column depending on the type of table
8402 reference where the column is checked. The function provides a
8403 generic interface to check column access rights that hides the
8404 heterogeneity of the column representation - whether it is a view
8405 or a stored table colum.
8406
8407 RETURN
8408 FALSE OK
8409 TRUE access denied
8410 */
8411
check_column_grant_in_table_ref(THD * thd,TABLE_LIST * table_ref,const char * name,size_t length,Field * fld)8412 bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
8413 const char *name, size_t length,
8414 Field *fld)
8415 {
8416 GRANT_INFO *grant;
8417 const char *db_name;
8418 const char *table_name;
8419 Security_context *sctx= table_ref->security_ctx ?
8420 table_ref->security_ctx : thd->security_ctx;
8421 if (fld && fld != not_found_field && fld != view_ref_found
8422 && fld->invisible >= INVISIBLE_SYSTEM)
8423 return false;
8424
8425 if (table_ref->view || table_ref->field_translation)
8426 {
8427 /* View or derived information schema table. */
8428 privilege_t view_privs(NO_ACL);
8429 grant= &(table_ref->grant);
8430 db_name= table_ref->view_db.str;
8431 table_name= table_ref->view_name.str;
8432 if (table_ref->belong_to_view &&
8433 thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
8434 {
8435 view_privs= get_column_grant(thd, grant, db_name, table_name, name);
8436 if (view_privs & VIEW_ANY_ACL)
8437 {
8438 table_ref->belong_to_view->allowed_show= TRUE;
8439 return FALSE;
8440 }
8441 table_ref->belong_to_view->allowed_show= FALSE;
8442 my_message(ER_VIEW_NO_EXPLAIN, ER_THD(thd, ER_VIEW_NO_EXPLAIN), MYF(0));
8443 return TRUE;
8444 }
8445 }
8446 else
8447 {
8448 /* Normal or temporary table. */
8449 TABLE *table= table_ref->table;
8450 grant= &(table->grant);
8451 db_name= table->s->db.str;
8452 table_name= table->s->table_name.str;
8453 }
8454
8455 if (grant->want_privilege)
8456 return check_grant_column(thd, grant, db_name, table_name, name,
8457 length, sctx);
8458 else
8459 return FALSE;
8460
8461 }
8462
8463
8464 /**
8465 @brief check if a query can access a set of columns
8466
8467 @param thd the current thread
8468 @param want_access_arg the privileges requested
8469 @param fields an iterator over the fields of a table reference.
8470 @return Operation status
8471 @retval 0 Success
8472 @retval 1 Falure
8473 @details This function walks over the columns of a table reference
8474 The columns may originate from different tables, depending on the kind of
8475 table reference, e.g. join, view.
8476 For each table it will retrieve the grant information and will use it
8477 to check the required access privileges for the fields requested from it.
8478 */
check_grant_all_columns(THD * thd,privilege_t want_access_arg,Field_iterator_table_ref * fields)8479 bool check_grant_all_columns(THD *thd, privilege_t want_access_arg,
8480 Field_iterator_table_ref *fields)
8481 {
8482 Security_context *sctx= thd->security_ctx;
8483 privilege_t want_access(NO_ACL);
8484 const char *table_name= NULL;
8485 const char* db_name;
8486 GRANT_INFO *grant;
8487 GRANT_TABLE *UNINIT_VAR(grant_table);
8488 GRANT_TABLE *UNINIT_VAR(grant_table_role);
8489 /*
8490 Flag that gets set if privilege checking has to be performed on column
8491 level.
8492 */
8493 bool using_column_privileges= FALSE;
8494
8495 mysql_rwlock_rdlock(&LOCK_grant);
8496
8497 for (; !fields->end_of_fields(); fields->next())
8498 {
8499 if (fields->field() &&
8500 fields->field()->invisible >= INVISIBLE_SYSTEM)
8501 continue;
8502 LEX_CSTRING *field_name= fields->name();
8503
8504 if (table_name != fields->get_table_name())
8505 {
8506 table_name= fields->get_table_name();
8507 db_name= fields->get_db_name();
8508 grant= fields->grant();
8509 /* get a fresh one for each table */
8510 want_access= want_access_arg & ~grant->privilege;
8511 if (want_access)
8512 {
8513 /* reload table if someone has modified any grants */
8514 if (grant->version != grant_version)
8515 {
8516 grant->grant_table_user=
8517 table_hash_search(sctx->host, sctx->ip, db_name,
8518 sctx->priv_user,
8519 table_name, 0); /* purecov: inspected */
8520 grant->grant_table_role=
8521 sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
8522 sctx->priv_role,
8523 table_name, TRUE) : NULL;
8524 grant->version= grant_version; /* purecov: inspected */
8525 }
8526
8527 grant_table= grant->grant_table_user;
8528 grant_table_role= grant->grant_table_role;
8529 if (!grant_table && !grant_table_role)
8530 goto err;
8531 }
8532 }
8533
8534 if (want_access)
8535 {
8536 privilege_t have_access(NO_ACL);
8537 if (grant_table)
8538 {
8539 GRANT_COLUMN *grant_column=
8540 column_hash_search(grant_table, field_name->str, field_name->length);
8541 if (grant_column)
8542 have_access= grant_column->rights;
8543 }
8544 if (grant_table_role)
8545 {
8546 GRANT_COLUMN *grant_column=
8547 column_hash_search(grant_table_role, field_name->str,
8548 field_name->length);
8549 if (grant_column)
8550 have_access|= grant_column->rights;
8551 }
8552
8553 if (have_access)
8554 using_column_privileges= TRUE;
8555 if (want_access & ~have_access)
8556 goto err;
8557 }
8558 }
8559 mysql_rwlock_unlock(&LOCK_grant);
8560 return 0;
8561
8562 err:
8563 mysql_rwlock_unlock(&LOCK_grant);
8564
8565 char command[128];
8566 get_privilege_desc(command, sizeof(command), want_access);
8567 /*
8568 Do not give an error message listing a column name unless the user has
8569 privilege to see all columns.
8570 */
8571 if (using_column_privileges)
8572 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
8573 command, sctx->priv_user,
8574 sctx->host_or_ip, table_name);
8575 else
8576 my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
8577 command,
8578 sctx->priv_user,
8579 sctx->host_or_ip,
8580 fields->name()->str,
8581 table_name);
8582 return 1;
8583 }
8584
8585
check_grant_db_routine(THD * thd,const char * db,HASH * hash)8586 static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
8587 {
8588 Security_context *sctx= thd->security_ctx;
8589
8590 for (uint idx= 0; idx < hash->records; ++idx)
8591 {
8592 GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
8593
8594 if (strcmp(item->user, sctx->priv_user) == 0 &&
8595 strcmp(item->db, db) == 0 &&
8596 compare_hostname(&item->host, sctx->host, sctx->ip))
8597 {
8598 return FALSE;
8599 }
8600 if (sctx->priv_role[0] && strcmp(item->user, sctx->priv_role) == 0 &&
8601 strcmp(item->db, db) == 0 &&
8602 (!item->host.hostname || !item->host.hostname[0]))
8603 {
8604 return FALSE; /* Found current role match */
8605 }
8606 }
8607
8608 return TRUE;
8609 }
8610
8611
8612 /*
8613 Check if a user has the right to access a database
8614 Access is accepted if he has a grant for any table/routine in the database
8615 Return 1 if access is denied
8616 */
8617
check_grant_db(THD * thd,const char * db)8618 bool check_grant_db(THD *thd, const char *db)
8619 {
8620 Security_context *sctx= thd->security_ctx;
8621 char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end;
8622 char helping2 [SAFE_NAME_LEN + USERNAME_LENGTH+2], *tmp_db;
8623 uint len, UNINIT_VAR(len2);
8624 bool error= TRUE;
8625
8626 tmp_db= strmov(helping, sctx->priv_user) + 1;
8627 end= strnmov(tmp_db, db, helping + sizeof(helping) - tmp_db);
8628
8629 if (end >= helping + sizeof(helping)) // db name was truncated
8630 return 1; // no privileges for an invalid db name
8631
8632 if (lower_case_table_names)
8633 {
8634 end = tmp_db + my_casedn_str(files_charset_info, tmp_db);
8635 db=tmp_db;
8636 }
8637
8638 len= (uint) (end - helping) + 1;
8639
8640 /*
8641 If a role is set, we need to check for privileges here as well.
8642 */
8643 if (sctx->priv_role[0])
8644 {
8645 end= strmov(helping2, sctx->priv_role) + 1;
8646 end= strnmov(end, db, helping2 + sizeof(helping2) - end);
8647 len2= (uint) (end - helping2) + 1;
8648 }
8649
8650
8651 mysql_rwlock_rdlock(&LOCK_grant);
8652
8653 for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
8654 {
8655 GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
8656 idx);
8657 if (len < grant_table->key_length &&
8658 !memcmp(grant_table->hash_key, helping, len) &&
8659 compare_hostname(&grant_table->host, sctx->host, sctx->ip))
8660 {
8661 error= FALSE; /* Found match. */
8662 break;
8663 }
8664 if (sctx->priv_role[0] &&
8665 len2 < grant_table->key_length &&
8666 !memcmp(grant_table->hash_key, helping2, len2) &&
8667 (!grant_table->host.hostname || !grant_table->host.hostname[0]))
8668 {
8669 error= FALSE; /* Found role match */
8670 break;
8671 }
8672 }
8673
8674 if (error)
8675 error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
8676 check_grant_db_routine(thd, db, &func_priv_hash) &&
8677 check_grant_db_routine(thd, db, &package_spec_priv_hash) &&
8678 check_grant_db_routine(thd, db, &package_body_priv_hash);
8679
8680 mysql_rwlock_unlock(&LOCK_grant);
8681
8682 return error;
8683 }
8684
8685
8686 /****************************************************************************
8687 Check routine level grants
8688
8689 SYNPOSIS
8690 bool check_grant_routine()
8691 thd Thread handler
8692 want_access Bits of privileges user needs to have
8693 procs List of routines to check. The user should have 'want_access'
8694 sph SP handler
8695 no_errors If 0 then we write an error. The error is sent directly to
8696 the client
8697
8698 RETURN
8699 0 ok
8700 1 Error: User did not have the requested privielges
8701 ****************************************************************************/
8702
check_grant_routine(THD * thd,privilege_t want_access,TABLE_LIST * procs,const Sp_handler * sph,bool no_errors)8703 bool check_grant_routine(THD *thd, privilege_t want_access,
8704 TABLE_LIST *procs, const Sp_handler *sph,
8705 bool no_errors)
8706 {
8707 TABLE_LIST *table;
8708 Security_context *sctx= thd->security_ctx;
8709 char *user= sctx->priv_user;
8710 char *host= sctx->priv_host;
8711 char *role= sctx->priv_role;
8712 DBUG_ENTER("check_grant_routine");
8713
8714 want_access&= ~sctx->master_access;
8715 if (!want_access)
8716 DBUG_RETURN(0); // ok
8717
8718 mysql_rwlock_rdlock(&LOCK_grant);
8719 for (table= procs; table; table= table->next_global)
8720 {
8721 GRANT_NAME *grant_proc;
8722 if ((grant_proc= routine_hash_search(host, sctx->ip, table->db.str, user,
8723 table->table_name.str, sph, 0)))
8724 table->grant.privilege|= grant_proc->privs;
8725 if (role[0]) /* current role set check */
8726 {
8727 if ((grant_proc= routine_hash_search("", NULL, table->db.str, role,
8728 table->table_name.str, sph, 0)))
8729 table->grant.privilege|= grant_proc->privs;
8730 }
8731
8732 if (want_access & ~table->grant.privilege)
8733 {
8734 want_access &= ~table->grant.privilege;
8735 goto err;
8736 }
8737 }
8738 mysql_rwlock_unlock(&LOCK_grant);
8739 DBUG_RETURN(0);
8740 err:
8741 mysql_rwlock_unlock(&LOCK_grant);
8742 if (!no_errors)
8743 {
8744 char buff[1024];
8745 const char *command="";
8746 if (table)
8747 strxmov(buff, table->db.str, ".", table->table_name.str, NullS);
8748 if (want_access & EXECUTE_ACL)
8749 command= "execute";
8750 else if (want_access & ALTER_PROC_ACL)
8751 command= "alter routine";
8752 else if (want_access & GRANT_ACL)
8753 command= "grant";
8754 my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
8755 command, user, host, table ? buff : "unknown");
8756 }
8757 DBUG_RETURN(1);
8758 }
8759
8760
8761 /*
8762 Check if routine has any of the
8763 routine level grants
8764
8765 SYNPOSIS
8766 bool check_routine_level_acl()
8767 thd Thread handler
8768 db Database name
8769 name Routine name
8770
8771 RETURN
8772 0 Ok
8773 1 error
8774 */
8775
check_routine_level_acl(THD * thd,const char * db,const char * name,const Sp_handler * sph)8776 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
8777 const Sp_handler *sph)
8778 {
8779 bool no_routine_acl= 1;
8780 GRANT_NAME *grant_proc;
8781 Security_context *sctx= thd->security_ctx;
8782 mysql_rwlock_rdlock(&LOCK_grant);
8783 if ((grant_proc= routine_hash_search(sctx->priv_host,
8784 sctx->ip, db,
8785 sctx->priv_user,
8786 name, sph, 0)))
8787 no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
8788
8789 if (no_routine_acl && sctx->priv_role[0]) /* current set role check */
8790 {
8791 if ((grant_proc= routine_hash_search("",
8792 NULL, db,
8793 sctx->priv_role,
8794 name, sph, 0)))
8795 no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
8796 }
8797 mysql_rwlock_unlock(&LOCK_grant);
8798 return no_routine_acl;
8799 }
8800
8801
8802 /*****************************************************************************
8803 Functions to retrieve the grant for a table/column (for SHOW functions)
8804 *****************************************************************************/
8805
get_table_grant(THD * thd,TABLE_LIST * table)8806 privilege_t get_table_grant(THD *thd, TABLE_LIST *table)
8807 {
8808 Security_context *sctx= thd->security_ctx;
8809 const char *db = table->db.str ? table->db.str : thd->db.str;
8810 GRANT_TABLE *grant_table;
8811 GRANT_TABLE *grant_table_role= NULL;
8812
8813 mysql_rwlock_rdlock(&LOCK_grant);
8814 #ifdef EMBEDDED_LIBRARY
8815 grant_table= NULL;
8816 grant_table_role= NULL;
8817 #else
8818 grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
8819 table->table_name.str, 0);
8820 if (sctx->priv_role[0])
8821 grant_table_role= table_hash_search("", "", db, sctx->priv_role,
8822 table->table_name.str, 0);
8823 #endif
8824 table->grant.grant_table_user= grant_table; // Remember for column test
8825 table->grant.grant_table_role= grant_table_role;
8826 table->grant.version=grant_version;
8827 if (grant_table)
8828 table->grant.privilege|= grant_table->privs;
8829 if (grant_table_role)
8830 table->grant.privilege|= grant_table_role->privs;
8831 privilege_t privilege(table->grant.privilege);
8832 mysql_rwlock_unlock(&LOCK_grant);
8833 return privilege;
8834 }
8835
8836
8837 /*
8838 Determine the access priviliges for a field.
8839
8840 SYNOPSIS
8841 get_column_grant()
8842 thd thread handler
8843 grant grants table descriptor
8844 db_name name of database that the field belongs to
8845 table_name name of table that the field belongs to
8846 field_name name of field
8847
8848 DESCRIPTION
8849 The procedure may also modify: grant->grant_table and grant->version.
8850
8851 RETURN
8852 The access priviliges for the field db_name.table_name.field_name
8853 */
8854
get_column_grant(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * field_name)8855 privilege_t get_column_grant(THD *thd, GRANT_INFO *grant,
8856 const char *db_name, const char *table_name,
8857 const char *field_name)
8858 {
8859 GRANT_TABLE *grant_table;
8860 GRANT_TABLE *grant_table_role;
8861 GRANT_COLUMN *grant_column;
8862 privilege_t priv(NO_ACL);
8863
8864 mysql_rwlock_rdlock(&LOCK_grant);
8865 /* reload table if someone has modified any grants */
8866 if (grant->version != grant_version)
8867 {
8868 Security_context *sctx= thd->security_ctx;
8869 grant->grant_table_user=
8870 table_hash_search(sctx->host, sctx->ip,
8871 db_name, sctx->priv_user,
8872 table_name, 0); /* purecov: inspected */
8873 grant->grant_table_role=
8874 sctx->priv_role[0] ? table_hash_search("", "", db_name,
8875 sctx->priv_role,
8876 table_name, TRUE) : NULL;
8877 grant->version= grant_version; /* purecov: inspected */
8878 }
8879
8880 grant_table= grant->grant_table_user;
8881 grant_table_role= grant->grant_table_role;
8882
8883 if (!grant_table && !grant_table_role)
8884 priv= grant->privilege;
8885 else
8886 {
8887 if (grant_table)
8888 {
8889 grant_column= column_hash_search(grant_table, field_name,
8890 (uint) strlen(field_name));
8891 if (!grant_column)
8892 priv= (grant->privilege | grant_table->privs);
8893 else
8894 priv= (grant->privilege | grant_table->privs | grant_column->rights);
8895 }
8896
8897 if (grant_table_role)
8898 {
8899 grant_column= column_hash_search(grant_table_role, field_name,
8900 (uint) strlen(field_name));
8901 if (!grant_column)
8902 priv|= (grant->privilege | grant_table_role->privs);
8903 else
8904 priv|= (grant->privilege | grant_table_role->privs |
8905 grant_column->rights);
8906 }
8907 }
8908 mysql_rwlock_unlock(&LOCK_grant);
8909 return priv;
8910 }
8911
8912
8913 /* Help function for mysql_show_grants */
8914
add_user_option(String * grant,long value,const char * name,bool is_signed)8915 static void add_user_option(String *grant, long value, const char *name,
8916 bool is_signed)
8917 {
8918 if (value)
8919 {
8920 char buff[22], *p; // just as in int2str
8921 grant->append(' ');
8922 grant->append(name, strlen(name));
8923 grant->append(' ');
8924 p=int10_to_str(value, buff, is_signed ? -10 : 10);
8925 grant->append(buff,p-buff);
8926 }
8927 }
8928
8929
add_user_option(String * grant,double value,const char * name)8930 static void add_user_option(String *grant, double value, const char *name)
8931 {
8932 if (value != 0.0 )
8933 {
8934 char buff[FLOATING_POINT_BUFFER];
8935 size_t len;
8936 grant->append(' ');
8937 grant->append(name, strlen(name));
8938 grant->append(' ');
8939 len= my_fcvt(value, 6, buff, NULL);
8940 grant->append(buff, len);
8941 }
8942 }
8943
add_user_parameters(THD * thd,String * result,ACL_USER * acl_user,bool with_grant)8944 static void add_user_parameters(THD *thd, String *result, ACL_USER* acl_user,
8945 bool with_grant)
8946 {
8947 result->append('@');
8948 append_identifier(thd, result, acl_user->host.hostname,
8949 acl_user->hostname_length);
8950
8951 if (acl_user->nauth == 1 &&
8952 (acl_user->auth->plugin.str == native_password_plugin_name.str ||
8953 acl_user->auth->plugin.str == old_password_plugin_name.str))
8954 {
8955 if (acl_user->auth->auth_string.length)
8956 {
8957 result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
8958 result->append(&acl_user->auth->auth_string);
8959 result->append('\'');
8960 }
8961 }
8962 else
8963 {
8964 result->append(STRING_WITH_LEN(" IDENTIFIED VIA "));
8965 for (uint i=0; i < acl_user->nauth; i++)
8966 {
8967 if (i)
8968 result->append(STRING_WITH_LEN(" OR "));
8969 result->append(&acl_user->auth[i].plugin);
8970 if (acl_user->auth[i].auth_string.length)
8971 {
8972 result->append(STRING_WITH_LEN(" USING '"));
8973 result->append(&acl_user->auth[i].auth_string);
8974 result->append('\'');
8975 }
8976 }
8977 }
8978 /* "show grants" SSL related stuff */
8979 if (acl_user->ssl_type == SSL_TYPE_ANY)
8980 result->append(STRING_WITH_LEN(" REQUIRE SSL"));
8981 else if (acl_user->ssl_type == SSL_TYPE_X509)
8982 result->append(STRING_WITH_LEN(" REQUIRE X509"));
8983 else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
8984 {
8985 int ssl_options = 0;
8986 result->append(STRING_WITH_LEN(" REQUIRE "));
8987 if (acl_user->x509_issuer[0])
8988 {
8989 ssl_options++;
8990 result->append(STRING_WITH_LEN("ISSUER \'"));
8991 result->append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
8992 result->append('\'');
8993 }
8994 if (acl_user->x509_subject[0])
8995 {
8996 if (ssl_options++)
8997 result->append(' ');
8998 result->append(STRING_WITH_LEN("SUBJECT \'"));
8999 result->append(acl_user->x509_subject,strlen(acl_user->x509_subject),
9000 system_charset_info);
9001 result->append('\'');
9002 }
9003 if (acl_user->ssl_cipher)
9004 {
9005 if (ssl_options++)
9006 result->append(' ');
9007 result->append(STRING_WITH_LEN("CIPHER '"));
9008 result->append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
9009 system_charset_info);
9010 result->append('\'');
9011 }
9012 }
9013 if (with_grant ||
9014 (acl_user->user_resource.questions ||
9015 acl_user->user_resource.updates ||
9016 acl_user->user_resource.conn_per_hour ||
9017 acl_user->user_resource.user_conn ||
9018 acl_user->user_resource.max_statement_time != 0.0))
9019 {
9020 result->append(STRING_WITH_LEN(" WITH"));
9021 if (with_grant)
9022 result->append(STRING_WITH_LEN(" GRANT OPTION"));
9023 add_user_option(result, acl_user->user_resource.questions,
9024 "MAX_QUERIES_PER_HOUR", false);
9025 add_user_option(result, acl_user->user_resource.updates,
9026 "MAX_UPDATES_PER_HOUR", false);
9027 add_user_option(result, acl_user->user_resource.conn_per_hour,
9028 "MAX_CONNECTIONS_PER_HOUR", false);
9029 add_user_option(result, acl_user->user_resource.user_conn,
9030 "MAX_USER_CONNECTIONS", true);
9031 add_user_option(result, acl_user->user_resource.max_statement_time,
9032 "MAX_STATEMENT_TIME");
9033 }
9034 }
9035
9036 static const char *command_array[]=
9037 {
9038 "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
9039 "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
9040 "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
9041 "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "BINLOG MONITOR",
9042 "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
9043 "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE", "DELETE HISTORY",
9044 "SET USER", "FEDERATED ADMIN", "CONNECTION ADMIN", "READ_ONLY ADMIN",
9045 "REPLICATION SLAVE ADMIN", "REPLICATION MASTER ADMIN", "BINLOG ADMIN",
9046 "BINLOG REPLAY", "SLAVE MONITOR"
9047 };
9048
9049 static uint command_lengths[]=
9050 {
9051 6, 6, 6, 6, 6, 4, 6,
9052 8, 7, 4, 5, 10, 5,
9053 5, 14, 5, 23,
9054 11, 7, 17, 14,
9055 11, 9, 14, 13,
9056 11, 5, 7, 17, 14,
9057 8, 15, 16, 15,
9058 23, 24, 12,
9059 13, 13
9060 };
9061
9062
9063 static_assert(array_elements(command_array) == PRIVILEGE_T_MAX_BIT + 1,
9064 "The definition of command_array does not match privilege_t");
9065 static_assert(array_elements(command_lengths) == PRIVILEGE_T_MAX_BIT + 1,
9066 "The definition of command_lengths does not match privilege_t");
9067
9068
print_grants_for_role(THD * thd,ACL_ROLE * role)9069 static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
9070 {
9071 char buff[1024];
9072
9073 if (show_role_grants(thd, "", role, buff, sizeof(buff)))
9074 return TRUE;
9075
9076 if (show_global_privileges(thd, role, TRUE, buff, sizeof(buff)))
9077 return TRUE;
9078
9079 if (show_database_privileges(thd, role->user.str, "", buff, sizeof(buff)))
9080 return TRUE;
9081
9082 if (show_table_and_column_privileges(thd, role->user.str, "", buff, sizeof(buff)))
9083 return TRUE;
9084
9085 if (show_routine_grants(thd, role->user.str, "", &sp_handler_procedure,
9086 buff, sizeof(buff)))
9087 return TRUE;
9088
9089 if (show_routine_grants(thd, role->user.str, "", &sp_handler_function,
9090 buff, sizeof(buff)))
9091 return TRUE;
9092
9093 if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_spec,
9094 buff, sizeof(buff)))
9095 return TRUE;
9096
9097 if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_body,
9098 buff, sizeof(buff)))
9099 return TRUE;
9100
9101 return FALSE;
9102
9103 }
9104
append_auto_expiration_policy(ACL_USER * acl_user,String * r)9105 static void append_auto_expiration_policy(ACL_USER *acl_user, String *r) {
9106 if (!acl_user->password_lifetime)
9107 r->append(STRING_WITH_LEN(" PASSWORD EXPIRE NEVER"));
9108 else if (acl_user->password_lifetime > 0)
9109 {
9110 r->append(STRING_WITH_LEN(" PASSWORD EXPIRE INTERVAL "));
9111 r->append_longlong(acl_user->password_lifetime);
9112 r->append(STRING_WITH_LEN(" DAY"));
9113 }
9114 }
9115
mysql_show_create_user(THD * thd,LEX_USER * lex_user)9116 bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
9117 {
9118 const char *username= NULL, *hostname= NULL;
9119 char buff[1024]; //Show create user should not take more than 1024 bytes.
9120 Protocol *protocol= thd->protocol;
9121 bool error= false;
9122 ACL_USER *acl_user;
9123 uint head_length;
9124 DBUG_ENTER("mysql_show_create_user");
9125
9126 if (!initialized)
9127 {
9128 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
9129 DBUG_RETURN(TRUE);
9130 }
9131 if (get_show_user(thd, lex_user, &username, &hostname, NULL))
9132 DBUG_RETURN(TRUE);
9133
9134 List<Item> field_list;
9135 head_length= (uint) (strxmov(buff, "CREATE USER for ", username, "@",
9136 hostname, NullS) - buff);
9137 Item_string *field = new (thd->mem_root) Item_string_ascii(thd, "", 0);
9138 if (!field)
9139 DBUG_RETURN(true); // Error given my my_alloc()
9140
9141 field->name.str= buff;
9142 field->name.length= head_length;
9143 field->max_length= sizeof(buff);
9144 field_list.push_back(field, thd->mem_root);
9145 if (protocol->send_result_set_metadata(&field_list,
9146 Protocol::SEND_NUM_ROWS |
9147 Protocol::SEND_EOF))
9148 DBUG_RETURN(true);
9149
9150 String result(buff, sizeof(buff), system_charset_info);
9151 result.length(0);
9152 mysql_rwlock_rdlock(&LOCK_grant);
9153 mysql_mutex_lock(&acl_cache->lock);
9154
9155 acl_user= find_user_exact(hostname, username);
9156
9157 // User not found in the internal data structures.
9158 if (!acl_user)
9159 {
9160 my_error(ER_PASSWORD_NO_MATCH, MYF(0));
9161 error= true;
9162 goto end;
9163 }
9164
9165 result.append("CREATE USER ");
9166 append_identifier(thd, &result, username, strlen(username));
9167 add_user_parameters(thd, &result, acl_user, false);
9168
9169 if (acl_user->account_locked)
9170 result.append(STRING_WITH_LEN(" ACCOUNT LOCK"));
9171
9172 if (acl_user->password_expired)
9173 result.append(STRING_WITH_LEN(" PASSWORD EXPIRE"));
9174 else
9175 append_auto_expiration_policy(acl_user, &result);
9176
9177 protocol->prepare_for_resend();
9178 protocol->store(result.ptr(), result.length(), result.charset());
9179 if (protocol->write())
9180 {
9181 error= true;
9182 }
9183
9184 /* MDEV-24114 - PASSWORD EXPIRE and PASSWORD EXPIRE [NEVER | INTERVAL X DAY]
9185 are two different mechanisms. To make sure a tool can restore the state
9186 of a user account, including both the manual expiration state of the
9187 account and the automatic expiration policy attached to it, we should
9188 print two statements here, a CREATE USER (printed above) and an ALTER USER */
9189 if (acl_user->password_expired && acl_user->password_lifetime > -1) {
9190 result.length(0);
9191 result.append("ALTER USER ");
9192 append_identifier(thd, &result, username, strlen(username));
9193 result.append('@');
9194 append_identifier(thd, &result, acl_user->host.hostname,
9195 acl_user->hostname_length);
9196 append_auto_expiration_policy(acl_user, &result);
9197 protocol->prepare_for_resend();
9198 protocol->store(result.ptr(), result.length(), result.charset());
9199 if (protocol->write())
9200 {
9201 error= true;
9202 }
9203 }
9204
9205 my_eof(thd);
9206
9207 end:
9208 mysql_rwlock_unlock(&LOCK_grant);
9209 mysql_mutex_unlock(&acl_cache->lock);
9210
9211 DBUG_RETURN(error);
9212 }
9213
9214
show_grants_callback(ACL_USER_BASE * role,void * data)9215 static int show_grants_callback(ACL_USER_BASE *role, void *data)
9216 {
9217 THD *thd= (THD *)data;
9218 DBUG_ASSERT(role->flags & IS_ROLE);
9219 if (print_grants_for_role(thd, (ACL_ROLE *)role))
9220 return -1;
9221 return 0;
9222 }
9223
mysql_show_grants_get_fields(THD * thd,List<Item> * fields,const char * name,size_t length)9224 void mysql_show_grants_get_fields(THD *thd, List<Item> *fields,
9225 const char *name, size_t length)
9226 {
9227 Item_string *field=new (thd->mem_root) Item_string_ascii(thd, "", 0);
9228 /* Set name explicit to avoid character set conversions */
9229 field->name.str= name;
9230 field->name.length= length;
9231 field->max_length=1024;
9232 fields->push_back(field, thd->mem_root);
9233 }
9234
9235 /** checks privileges for SHOW GRANTS and SHOW CREATE USER
9236
9237 @note that in case of SHOW CREATE USER the parser guarantees
9238 that a role can never happen here, so *rolename will never
9239 be assigned to
9240 */
get_show_user(THD * thd,LEX_USER * lex_user,const char ** username,const char ** hostname,const char ** rolename)9241 bool get_show_user(THD *thd, LEX_USER *lex_user, const char **username,
9242 const char **hostname, const char **rolename)
9243 {
9244 if (lex_user->user.str == current_user.str)
9245 {
9246 *username= thd->security_ctx->priv_user;
9247 *hostname= thd->security_ctx->priv_host;
9248 return 0;
9249 }
9250 if (lex_user->user.str == current_role.str)
9251 {
9252 *rolename= thd->security_ctx->priv_role;
9253 return 0;
9254 }
9255 if (lex_user->user.str == current_user_and_current_role.str)
9256 {
9257 *username= thd->security_ctx->priv_user;
9258 *hostname= thd->security_ctx->priv_host;
9259 *rolename= thd->security_ctx->priv_role;
9260 return 0;
9261 }
9262
9263 Security_context *sctx= thd->security_ctx;
9264 bool do_check_access;
9265
9266 if (!(lex_user= get_current_user(thd, lex_user)))
9267 return 1;
9268
9269 if (lex_user->is_role())
9270 {
9271 *rolename= lex_user->user.str;
9272 do_check_access= strcmp(*rolename, sctx->priv_role);
9273 }
9274 else
9275 {
9276 *username= lex_user->user.str;
9277 *hostname= lex_user->host.str;
9278 do_check_access= strcmp(*username, sctx->priv_user) ||
9279 strcmp(*hostname, sctx->priv_host);
9280 }
9281
9282 if (do_check_access && check_access(thd, SELECT_ACL, "mysql", 0, 0, 1, 0))
9283 return 1;
9284 return 0;
9285 }
9286
9287 /*
9288 SHOW GRANTS; Send grants for a user to the client
9289
9290 IMPLEMENTATION
9291 Send to client grant-like strings depicting user@host privileges
9292 */
9293
mysql_show_grants(THD * thd,LEX_USER * lex_user)9294 bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
9295 {
9296 int error = -1;
9297 ACL_USER *UNINIT_VAR(acl_user);
9298 ACL_ROLE *acl_role= NULL;
9299 char buff[1024];
9300 Protocol *protocol= thd->protocol;
9301 const char *username= NULL, *hostname= NULL, *rolename= NULL, *end;
9302 DBUG_ENTER("mysql_show_grants");
9303
9304 if (!initialized)
9305 {
9306 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
9307 DBUG_RETURN(TRUE);
9308 }
9309
9310 if (get_show_user(thd, lex_user, &username, &hostname, &rolename))
9311 DBUG_RETURN(TRUE);
9312
9313 DBUG_ASSERT(rolename || username);
9314
9315 List<Item> field_list;
9316 if (username)
9317 end= strxmov(buff,"Grants for ",username,"@",hostname, NullS);
9318 else
9319 end= strxmov(buff,"Grants for ",rolename, NullS);
9320
9321 mysql_show_grants_get_fields(thd, &field_list, buff, (uint) (end-buff));
9322
9323 if (protocol->send_result_set_metadata(&field_list,
9324 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
9325 DBUG_RETURN(TRUE);
9326
9327 mysql_rwlock_rdlock(&LOCK_grant);
9328 mysql_mutex_lock(&acl_cache->lock);
9329
9330 if (username)
9331 {
9332 acl_user= find_user_exact(hostname, username);
9333 if (!acl_user)
9334 {
9335 mysql_mutex_unlock(&acl_cache->lock);
9336 mysql_rwlock_unlock(&LOCK_grant);
9337
9338 my_error(ER_NONEXISTING_GRANT, MYF(0),
9339 username, hostname);
9340 DBUG_RETURN(TRUE);
9341 }
9342
9343 /* Show granted roles to acl_user */
9344 if (show_role_grants(thd, hostname, acl_user, buff, sizeof(buff)))
9345 goto end;
9346
9347 /* Add first global access grants */
9348 if (show_global_privileges(thd, acl_user, FALSE, buff, sizeof(buff)))
9349 goto end;
9350
9351 /* Add database access */
9352 if (show_database_privileges(thd, username, hostname, buff, sizeof(buff)))
9353 goto end;
9354
9355 /* Add table & column access */
9356 if (show_table_and_column_privileges(thd, username, hostname, buff, sizeof(buff)))
9357 goto end;
9358
9359 if (show_routine_grants(thd, username, hostname, &sp_handler_procedure,
9360 buff, sizeof(buff)))
9361 goto end;
9362
9363 if (show_routine_grants(thd, username, hostname, &sp_handler_function,
9364 buff, sizeof(buff)))
9365 goto end;
9366
9367 if (show_routine_grants(thd, username, hostname, &sp_handler_package_spec,
9368 buff, sizeof(buff)))
9369 goto end;
9370
9371 if (show_routine_grants(thd, username, hostname, &sp_handler_package_body,
9372 buff, sizeof(buff)))
9373 goto end;
9374
9375 if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff)))
9376 goto end;
9377 }
9378
9379 if (rolename)
9380 {
9381 acl_role= find_acl_role(rolename);
9382 if (acl_role)
9383 {
9384 /* get a list of all inherited roles */
9385 traverse_role_graph_down(acl_role, thd, show_grants_callback, NULL);
9386 }
9387 else
9388 {
9389 if (lex_user->user.str == current_role.str)
9390 {
9391 mysql_mutex_unlock(&acl_cache->lock);
9392 mysql_rwlock_unlock(&LOCK_grant);
9393 my_error(ER_NONEXISTING_GRANT, MYF(0),
9394 thd->security_ctx->priv_user,
9395 thd->security_ctx->priv_host);
9396 DBUG_RETURN(TRUE);
9397 }
9398 }
9399 }
9400
9401 if (username)
9402 {
9403 /* Show default role to acl_user */
9404 if (show_default_role(thd, acl_user, buff, sizeof(buff)))
9405 goto end;
9406 }
9407
9408
9409 error= 0;
9410 end:
9411 mysql_mutex_unlock(&acl_cache->lock);
9412 mysql_rwlock_unlock(&LOCK_grant);
9413
9414 my_eof(thd);
9415 DBUG_RETURN(error);
9416 }
9417
find_role_grant_pair(const LEX_CSTRING * u,const LEX_CSTRING * h,const LEX_CSTRING * r)9418 static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u,
9419 const LEX_CSTRING *h,
9420 const LEX_CSTRING *r)
9421 {
9422 char buf[1024];
9423 String pair_key(buf, sizeof(buf), &my_charset_bin);
9424
9425 size_t key_length= u->length + h->length + r->length + 3;
9426 pair_key.alloc(key_length);
9427
9428 strmov(strmov(strmov(const_cast<char*>(pair_key.ptr()),
9429 safe_str(u->str)) + 1, h->str) + 1, r->str);
9430
9431 return (ROLE_GRANT_PAIR *)
9432 my_hash_search(&acl_roles_mappings, (uchar*)pair_key.ptr(), key_length);
9433 }
9434
show_default_role(THD * thd,ACL_USER * acl_entry,char * buff,size_t buffsize)9435 static bool show_default_role(THD *thd, ACL_USER *acl_entry,
9436 char *buff, size_t buffsize)
9437 {
9438 Protocol *protocol= thd->protocol;
9439 LEX_CSTRING def_rolename= acl_entry->default_rolename;
9440
9441 if (def_rolename.length)
9442 {
9443 String def_str(buff, buffsize, system_charset_info);
9444 def_str.length(0);
9445 def_str.append(STRING_WITH_LEN("SET DEFAULT ROLE "));
9446 append_identifier(thd, &def_str, def_rolename.str, def_rolename.length);
9447 def_str.append(" FOR ");
9448 append_identifier(thd, &def_str, acl_entry->user.str, acl_entry->user.length);
9449 DBUG_ASSERT(!(acl_entry->flags & IS_ROLE));
9450 def_str.append('@');
9451 append_identifier(thd, &def_str, acl_entry->host.hostname,
9452 acl_entry->hostname_length);
9453 protocol->prepare_for_resend();
9454 protocol->store(def_str.ptr(),def_str.length(),def_str.charset());
9455 if (protocol->write())
9456 {
9457 return TRUE;
9458 }
9459 }
9460 return FALSE;
9461 }
9462
show_role_grants(THD * thd,const char * hostname,ACL_USER_BASE * acl_entry,char * buff,size_t buffsize)9463 static bool show_role_grants(THD *thd, const char *hostname,
9464 ACL_USER_BASE *acl_entry,
9465 char *buff, size_t buffsize)
9466 {
9467 uint counter;
9468 Protocol *protocol= thd->protocol;
9469 LEX_CSTRING host= {const_cast<char*>(hostname), strlen(hostname)};
9470
9471 String grant(buff, buffsize, system_charset_info);
9472 for (counter= 0; counter < acl_entry->role_grants.elements; counter++)
9473 {
9474 grant.length(0);
9475 grant.append(STRING_WITH_LEN("GRANT "));
9476 ACL_ROLE *acl_role= *(dynamic_element(&acl_entry->role_grants, counter,
9477 ACL_ROLE**));
9478 append_identifier(thd, &grant, acl_role->user.str, acl_role->user.length);
9479 grant.append(STRING_WITH_LEN(" TO "));
9480 append_identifier(thd, &grant, acl_entry->user.str, acl_entry->user.length);
9481 if (!(acl_entry->flags & IS_ROLE))
9482 {
9483 grant.append('@');
9484 append_identifier(thd, &grant, host.str, host.length);
9485 }
9486
9487 ROLE_GRANT_PAIR *pair=
9488 find_role_grant_pair(&acl_entry->user, &host, &acl_role->user);
9489 DBUG_ASSERT(pair);
9490
9491 if (pair->with_admin)
9492 grant.append(STRING_WITH_LEN(" WITH ADMIN OPTION"));
9493
9494 protocol->prepare_for_resend();
9495 protocol->store(grant.ptr(),grant.length(),grant.charset());
9496 if (protocol->write())
9497 {
9498 return TRUE;
9499 }
9500 }
9501 return FALSE;
9502 }
9503
show_global_privileges(THD * thd,ACL_USER_BASE * acl_entry,bool handle_as_role,char * buff,size_t buffsize)9504 static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
9505 bool handle_as_role,
9506 char *buff, size_t buffsize)
9507 {
9508 uint counter;
9509 privilege_t want_access(NO_ACL);
9510 Protocol *protocol= thd->protocol;
9511
9512 String global(buff, buffsize, system_charset_info);
9513 global.length(0);
9514 global.append(STRING_WITH_LEN("GRANT "));
9515
9516 if (handle_as_role)
9517 want_access= ((ACL_ROLE *)acl_entry)->initial_role_access;
9518 else
9519 want_access= acl_entry->access;
9520 if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
9521 global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
9522 else if (!(want_access & ~GRANT_ACL))
9523 global.append(STRING_WITH_LEN("USAGE"));
9524 else
9525 {
9526 bool found=0;
9527 ulonglong j;
9528 privilege_t test_access(want_access & ~GRANT_ACL);
9529 for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
9530 {
9531 if (test_access & j)
9532 {
9533 if (found)
9534 global.append(STRING_WITH_LEN(", "));
9535 found=1;
9536 global.append(command_array[counter],command_lengths[counter]);
9537 }
9538 }
9539 }
9540 global.append (STRING_WITH_LEN(" ON *.* TO "));
9541 append_identifier(thd, &global, acl_entry->user.str, acl_entry->user.length);
9542
9543 if (!handle_as_role)
9544 add_user_parameters(thd, &global, (ACL_USER *)acl_entry,
9545 (want_access & GRANT_ACL));
9546
9547 else if (want_access & GRANT_ACL)
9548 global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9549 protocol->prepare_for_resend();
9550 protocol->store(global.ptr(),global.length(),global.charset());
9551 if (protocol->write())
9552 return TRUE;
9553
9554 return FALSE;
9555
9556 }
9557
9558
add_to_user(THD * thd,String * result,const char * user,bool is_user,const char * host)9559 static void add_to_user(THD *thd, String *result, const char *user,
9560 bool is_user, const char *host)
9561 {
9562 result->append(STRING_WITH_LEN(" TO "));
9563 append_identifier(thd, result, user, strlen(user));
9564 if (is_user)
9565 {
9566 result->append('@');
9567 // host and lex_user->host are equal except for case
9568 append_identifier(thd, result, host, strlen(host));
9569 }
9570 }
9571
9572
show_database_privileges(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)9573 static bool show_database_privileges(THD *thd, const char *username,
9574 const char *hostname,
9575 char *buff, size_t buffsize)
9576 {
9577 privilege_t want_access(NO_ACL);
9578 Protocol *protocol= thd->protocol;
9579
9580 for (uint i=0 ; i < acl_dbs.elements() ; i++)
9581 {
9582 const char *user, *host;
9583
9584 ACL_DB *acl_db= &acl_dbs.at(i);
9585 user= acl_db->user;
9586 host=acl_db->host.hostname;
9587
9588 /*
9589 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
9590 but make it case-insensitive because that's the way they are
9591 actually applied, and showing fewer privileges than are applied
9592 would be wrong from a security point of view.
9593 */
9594
9595 if (!strcmp(username, user) &&
9596 !my_strcasecmp(system_charset_info, hostname, host))
9597 {
9598 /*
9599 do not print inherited access bits for roles,
9600 the role bits present in the table are what matters
9601 */
9602 if (*hostname) // User
9603 want_access=acl_db->access;
9604 else // Role
9605 want_access=acl_db->initial_access;
9606 if (want_access)
9607 {
9608 String db(buff, buffsize, system_charset_info);
9609 db.length(0);
9610 db.append(STRING_WITH_LEN("GRANT "));
9611
9612 if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
9613 db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
9614 else if (!(want_access & ~GRANT_ACL))
9615 db.append(STRING_WITH_LEN("USAGE"));
9616 else
9617 {
9618 int found=0, cnt;
9619 ulonglong j;
9620 privilege_t test_access(want_access & ~GRANT_ACL);
9621 for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
9622 {
9623 if (test_access & j)
9624 {
9625 if (found)
9626 db.append(STRING_WITH_LEN(", "));
9627 found = 1;
9628 db.append(command_array[cnt],command_lengths[cnt]);
9629 }
9630 }
9631 }
9632 db.append (STRING_WITH_LEN(" ON "));
9633 append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
9634 db.append (STRING_WITH_LEN(".*"));
9635 add_to_user(thd, &db, username, (*hostname), host);
9636 if (want_access & GRANT_ACL)
9637 db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9638 protocol->prepare_for_resend();
9639 protocol->store(db.ptr(),db.length(),db.charset());
9640 if (protocol->write())
9641 {
9642 return TRUE;
9643 }
9644 }
9645 }
9646 }
9647 return FALSE;
9648
9649 }
9650
show_table_and_column_privileges(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)9651 static bool show_table_and_column_privileges(THD *thd, const char *username,
9652 const char *hostname,
9653 char *buff, size_t buffsize)
9654 {
9655 uint counter, index;
9656 Protocol *protocol= thd->protocol;
9657
9658 for (index=0 ; index < column_priv_hash.records ; index++)
9659 {
9660 const char *user, *host;
9661 GRANT_TABLE *grant_table= (GRANT_TABLE*)
9662 my_hash_element(&column_priv_hash, index);
9663
9664 user= grant_table->user;
9665 host= grant_table->host.hostname;
9666
9667 /*
9668 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
9669 but make it case-insensitive because that's the way they are
9670 actually applied, and showing fewer privileges than are applied
9671 would be wrong from a security point of view.
9672 */
9673
9674 if (!strcmp(username,user) &&
9675 !my_strcasecmp(system_charset_info, hostname, host))
9676 {
9677 privilege_t table_access(NO_ACL);
9678 privilege_t cols_access(NO_ACL);
9679 if (*hostname) // User
9680 {
9681 table_access= grant_table->privs;
9682 cols_access= grant_table->cols;
9683 }
9684 else // Role
9685 {
9686 table_access= grant_table->init_privs;
9687 cols_access= grant_table->init_cols;
9688 }
9689
9690 if ((table_access | cols_access) != NO_ACL)
9691 {
9692 String global(buff, sizeof(buff), system_charset_info);
9693 privilege_t test_access= (table_access | cols_access) & ~GRANT_ACL;
9694
9695 global.length(0);
9696 global.append(STRING_WITH_LEN("GRANT "));
9697
9698 if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
9699 global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
9700 else if (!test_access)
9701 global.append(STRING_WITH_LEN("USAGE"));
9702 else
9703 {
9704 /* Add specific column access */
9705 int found= 0;
9706 ulonglong j;
9707
9708 for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
9709 {
9710 if (test_access & j)
9711 {
9712 if (found)
9713 global.append(STRING_WITH_LEN(", "));
9714 found= 1;
9715 global.append(command_array[counter],command_lengths[counter]);
9716
9717 if (grant_table->cols)
9718 {
9719 uint found_col= 0;
9720 HASH *hash_columns;
9721 hash_columns= &grant_table->hash_columns;
9722
9723 for (uint col_index=0 ;
9724 col_index < hash_columns->records ;
9725 col_index++)
9726 {
9727 GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
9728 my_hash_element(hash_columns,col_index);
9729 if (j & (*hostname ? grant_column->rights // User
9730 : grant_column->init_rights)) // Role
9731 {
9732 if (!found_col)
9733 {
9734 found_col= 1;
9735 /*
9736 If we have a duplicated table level privilege, we
9737 must write the access privilege name again.
9738 */
9739 if (table_access & j)
9740 {
9741 global.append(STRING_WITH_LEN(", "));
9742 global.append(command_array[counter],
9743 command_lengths[counter]);
9744 }
9745 global.append(STRING_WITH_LEN(" ("));
9746 }
9747 else
9748 global.append(STRING_WITH_LEN(", "));
9749 global.append(grant_column->column,
9750 grant_column->key_length,
9751 system_charset_info);
9752 }
9753 }
9754 if (found_col)
9755 global.append(')');
9756 }
9757 }
9758 }
9759 }
9760 global.append(STRING_WITH_LEN(" ON "));
9761 append_identifier(thd, &global, grant_table->db,
9762 strlen(grant_table->db));
9763 global.append('.');
9764 append_identifier(thd, &global, grant_table->tname,
9765 strlen(grant_table->tname));
9766 add_to_user(thd, &global, username, (*hostname), host);
9767 if (table_access & GRANT_ACL)
9768 global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9769 protocol->prepare_for_resend();
9770 protocol->store(global.ptr(),global.length(),global.charset());
9771 if (protocol->write())
9772 {
9773 return TRUE;
9774 }
9775 }
9776 }
9777 }
9778 return FALSE;
9779
9780 }
9781
show_routine_grants(THD * thd,const char * username,const char * hostname,const Sp_handler * sph,char * buff,int buffsize)9782 static int show_routine_grants(THD* thd,
9783 const char *username, const char *hostname,
9784 const Sp_handler *sph,
9785 char *buff, int buffsize)
9786 {
9787 uint counter, index;
9788 int error= 0;
9789 Protocol *protocol= thd->protocol;
9790 HASH *hash= sph->get_priv_hash();
9791 /* Add routine access */
9792 for (index=0 ; index < hash->records ; index++)
9793 {
9794 const char *user, *host;
9795 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
9796
9797 user= grant_proc->user;
9798 host= grant_proc->host.hostname;
9799
9800 /*
9801 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
9802 but make it case-insensitive because that's the way they are
9803 actually applied, and showing fewer privileges than are applied
9804 would be wrong from a security point of view.
9805 */
9806
9807 if (!strcmp(username, user) &&
9808 !my_strcasecmp(system_charset_info, hostname, host))
9809 {
9810 privilege_t proc_access(NO_ACL);
9811 if (*hostname) // User
9812 proc_access= grant_proc->privs;
9813 else // Role
9814 proc_access= grant_proc->init_privs;
9815
9816 if (proc_access != NO_ACL)
9817 {
9818 String global(buff, buffsize, system_charset_info);
9819 privilege_t test_access(proc_access & ~GRANT_ACL);
9820
9821 global.length(0);
9822 global.append(STRING_WITH_LEN("GRANT "));
9823
9824 if (!test_access)
9825 global.append(STRING_WITH_LEN("USAGE"));
9826 else
9827 {
9828 /* Add specific procedure access */
9829 int found= 0;
9830 ulonglong j;
9831
9832 for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
9833 {
9834 if (test_access & j)
9835 {
9836 if (found)
9837 global.append(STRING_WITH_LEN(", "));
9838 found= 1;
9839 global.append(command_array[counter],command_lengths[counter]);
9840 }
9841 }
9842 }
9843 global.append(STRING_WITH_LEN(" ON "));
9844 LEX_CSTRING tmp= sph->type_lex_cstring();
9845 global.append(&tmp);
9846 global.append(' ');
9847 append_identifier(thd, &global, grant_proc->db,
9848 strlen(grant_proc->db));
9849 global.append('.');
9850 append_identifier(thd, &global, grant_proc->tname,
9851 strlen(grant_proc->tname));
9852 add_to_user(thd, &global, username, (*hostname), host);
9853 if (proc_access & GRANT_ACL)
9854 global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9855 protocol->prepare_for_resend();
9856 protocol->store(global.ptr(),global.length(),global.charset());
9857 if (protocol->write())
9858 {
9859 error= -1;
9860 break;
9861 }
9862 }
9863 }
9864 }
9865 return error;
9866 }
9867
9868
9869 /*
9870 Make a clear-text version of the requested privilege.
9871 */
9872
get_privilege_desc(char * to,uint max_length,privilege_t access_arg)9873 void get_privilege_desc(char *to, uint max_length, privilege_t access_arg)
9874 {
9875 uint pos;
9876 char *start=to;
9877 DBUG_ASSERT(max_length >= 30); // For end ', ' removal
9878
9879 if (ulonglong access= access_arg)
9880 {
9881 max_length--; // Reserve place for end-zero
9882 for (pos=0 ; access ; pos++, access>>=1)
9883 {
9884 if ((access & 1) &&
9885 command_lengths[pos] + (uint) (to-start) < max_length)
9886 {
9887 to= strmov(to, command_array[pos]);
9888 *to++= ',';
9889 *to++= ' ';
9890 }
9891 }
9892 to--; // Remove end ' '
9893 to--; // Remove end ','
9894 }
9895 *to=0;
9896 }
9897
9898
get_mqh(const char * user,const char * host,USER_CONN * uc)9899 void get_mqh(const char *user, const char *host, USER_CONN *uc)
9900 {
9901 ACL_USER *acl_user;
9902
9903 mysql_mutex_lock(&acl_cache->lock);
9904
9905 if (initialized && (acl_user= find_user_wild(host,user)))
9906 uc->user_resources= acl_user->user_resource;
9907 else
9908 bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
9909
9910 mysql_mutex_unlock(&acl_cache->lock);
9911 }
9912
9913 /*
9914 Modify a privilege table.
9915
9916 SYNOPSIS
9917 modify_grant_table()
9918 table The table to modify.
9919 host_field The host name field.
9920 user_field The user name field.
9921 user_to The new name for the user if to be renamed,
9922 NULL otherwise.
9923
9924 DESCRIPTION
9925 Update user/host in the current record if user_to is not NULL.
9926 Delete the current record if user_to is NULL.
9927
9928 RETURN
9929 0 OK.
9930 != 0 Error.
9931 */
9932
modify_grant_table(TABLE * table,Field * host_field,Field * user_field,LEX_USER * user_to)9933 static int modify_grant_table(TABLE *table, Field *host_field,
9934 Field *user_field, LEX_USER *user_to)
9935 {
9936 int error;
9937 DBUG_ENTER("modify_grant_table");
9938
9939 if (user_to)
9940 {
9941 /* rename */
9942 store_record(table, record[1]);
9943 host_field->store(user_to->host.str, user_to->host.length,
9944 system_charset_info);
9945 user_field->store(user_to->user.str, user_to->user.length,
9946 system_charset_info);
9947 if (unlikely(error= table->file->ha_update_row(table->record[1],
9948 table->record[0])) &&
9949 error != HA_ERR_RECORD_IS_THE_SAME)
9950 table->file->print_error(error, MYF(0));
9951 else
9952 error= 0;
9953 }
9954 else
9955 {
9956 /* delete */
9957 if (unlikely((error=table->file->ha_delete_row(table->record[0]))))
9958 table->file->print_error(error, MYF(0));
9959 }
9960
9961 DBUG_RETURN(error);
9962 }
9963
9964 /*
9965 Handle the roles_mapping privilege table
9966 */
handle_roles_mappings_table(TABLE * table,bool drop,LEX_USER * user_from,LEX_USER * user_to)9967 static int handle_roles_mappings_table(TABLE *table, bool drop,
9968 LEX_USER *user_from, LEX_USER *user_to)
9969 {
9970 /*
9971 All entries (Host, User) that match user_from will be renamed,
9972 as well as all Role entries that match if user_from.host.str == ""
9973
9974 Otherwise, only matching (Host, User) will be renamed.
9975 */
9976 DBUG_ENTER("handle_roles_mappings_table");
9977
9978 int error;
9979 int result= 0;
9980 THD *thd= table->in_use;
9981 const char *host, *user, *role;
9982 Field *host_field= table->field[0];
9983 Field *user_field= table->field[1];
9984 Field *role_field= table->field[2];
9985
9986 DBUG_PRINT("info", ("Rewriting entry in roles_mapping table: %s@%s",
9987 user_from->user.str, user_from->host.str));
9988 table->use_all_columns();
9989
9990 if (unlikely(table->file->ha_rnd_init_with_error(1)))
9991 result= -1;
9992 else
9993 {
9994 while((error= table->file->ha_rnd_next(table->record[0])) !=
9995 HA_ERR_END_OF_FILE)
9996 {
9997 if (error)
9998 {
9999 DBUG_PRINT("info", ("scan error: %d", error));
10000 continue;
10001 }
10002
10003 host= safe_str(get_field(thd->mem_root, host_field));
10004 user= safe_str(get_field(thd->mem_root, user_field));
10005
10006 if (!(strcmp(user_from->user.str, user) ||
10007 my_strcasecmp(system_charset_info, user_from->host.str, host)))
10008 result= ((drop || user_to) &&
10009 modify_grant_table(table, host_field, user_field, user_to)) ?
10010 -1 : result ? result : 1; /* Error or keep result or found. */
10011 else
10012 {
10013 role= safe_str(get_field(thd->mem_root, role_field));
10014
10015 if (!user_from->is_role() || strcmp(user_from->user.str, role))
10016 continue;
10017
10018 error= 0;
10019
10020 if (drop) /* drop if requested */
10021 {
10022 if (unlikely((error= table->file->ha_delete_row(table->record[0]))))
10023 table->file->print_error(error, MYF(0));
10024 }
10025 else if (user_to)
10026 {
10027 store_record(table, record[1]);
10028 role_field->store(user_to->user.str, user_to->user.length,
10029 system_charset_info);
10030 if (unlikely(error= table->file->ha_update_row(table->record[1],
10031 table->record[0])) &&
10032 error != HA_ERR_RECORD_IS_THE_SAME)
10033 table->file->print_error(error, MYF(0));
10034 }
10035
10036 /* Error or keep result or found. */
10037 result= error ? -1 : result ? result : 1;
10038 }
10039 }
10040 table->file->ha_rnd_end();
10041 }
10042 DBUG_RETURN(result);
10043 }
10044
10045 /*
10046 Handle a privilege table.
10047
10048 SYNOPSIS
10049 handle_grant_table()
10050 grant_table An open grant table handle.
10051 which_table Which grant table to handle.
10052 drop If user_from is to be dropped.
10053 user_from The the user to be searched/dropped/renamed.
10054 user_to The new name for the user if to be renamed,
10055 NULL otherwise.
10056
10057 DESCRIPTION
10058 Scan through all records in a grant table and apply the requested
10059 operation. For the "user" table, a single index access is sufficient,
10060 since there is an unique index on (host, user).
10061 Delete from grant table if drop is true.
10062 Update in grant table if drop is false and user_to is not NULL.
10063 Search in grant table if drop is false and user_to is NULL.
10064
10065 RETURN
10066 > 0 At least one record matched.
10067 0 OK, but no record matched.
10068 < 0 Error.
10069
10070 TODO(cvicentiu) refactor handle_grant_table to use
10071 Grant_table_base instead of TABLE directly.
10072 */
10073
handle_grant_table(THD * thd,const Grant_table_base & grant_table,enum enum_acl_tables which_table,bool drop,LEX_USER * user_from,LEX_USER * user_to)10074 static int handle_grant_table(THD *thd, const Grant_table_base& grant_table,
10075 enum enum_acl_tables which_table, bool drop,
10076 LEX_USER *user_from, LEX_USER *user_to)
10077 {
10078 int result= 0;
10079 int error;
10080 TABLE *table= grant_table.table();
10081 Field *host_field= table->field[0];
10082 Field *user_field= table->field[which_table == USER_TABLE ||
10083 which_table == PROXIES_PRIV_TABLE ? 1 : 2];
10084 const char *host_str= user_from->host.str;
10085 const char *user_str= user_from->user.str;
10086 const char *host;
10087 const char *user;
10088 uchar user_key[MAX_KEY_LENGTH];
10089 uint key_prefix_length;
10090 DBUG_ENTER("handle_grant_table");
10091
10092 if (which_table == ROLES_MAPPING_TABLE)
10093 {
10094 result= handle_roles_mappings_table(table, drop, user_from, user_to);
10095 DBUG_RETURN(result);
10096 }
10097
10098 table->use_all_columns();
10099 if (which_table == USER_TABLE) // mysql.user table
10100 {
10101 /*
10102 The 'user' table has an unique index on (host, user).
10103 Thus, we can handle everything with a single index access.
10104 The host- and user fields are consecutive in the user table records.
10105 So we set host- and user fields of table->record[0] and use the
10106 pointer to the host field as key.
10107 index_read_idx() will replace table->record[0] (its first argument)
10108 by the searched record, if it exists.
10109 */
10110 DBUG_PRINT("info",("read table: '%s' search: '%s'@'%s'",
10111 table->s->table_name.str, user_str, host_str));
10112 host_field->store(host_str, user_from->host.length, system_charset_info);
10113 user_field->store(user_str, user_from->user.length, system_charset_info);
10114
10115 key_prefix_length= (table->key_info->key_part[0].store_length +
10116 table->key_info->key_part[1].store_length);
10117 key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
10118
10119 error= table->file->ha_index_read_idx_map(table->record[0], 0,
10120 user_key, (key_part_map)3,
10121 HA_READ_KEY_EXACT);
10122 if (!unlikely(error) && !*host_str)
10123 {
10124 // verify that we got a role or a user, as needed
10125 if (static_cast<const User_table&>(grant_table).get_is_role() !=
10126 user_from->is_role())
10127 error= HA_ERR_KEY_NOT_FOUND;
10128 }
10129 if (unlikely(error))
10130 {
10131 if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
10132 {
10133 table->file->print_error(error, MYF(0));
10134 result= -1;
10135 }
10136 }
10137 else
10138 {
10139 /* If requested, delete or update the record. */
10140 result= ((drop || user_to) &&
10141 modify_grant_table(table, host_field, user_field, user_to)) ?
10142 -1 : 1; /* Error or found. */
10143 }
10144 DBUG_PRINT("info",("read result: %d", result));
10145 }
10146 else
10147 {
10148 /*
10149 The non-'user' table do not have indexes on (host, user).
10150 And their host- and user fields are not consecutive.
10151 Thus, we need to do a table scan to find all matching records.
10152 */
10153 if (unlikely(table->file->ha_rnd_init_with_error(1)))
10154 result= -1;
10155 else
10156 {
10157 #ifdef EXTRA_DEBUG
10158 DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'",
10159 table->s->table_name.str, user_str, host_str));
10160 #endif
10161 while ((error= table->file->ha_rnd_next(table->record[0])) !=
10162 HA_ERR_END_OF_FILE)
10163 {
10164 if (error)
10165 {
10166 /* Most probable 'deleted record'. */
10167 DBUG_PRINT("info",("scan error: %d", error));
10168 continue;
10169 }
10170 host= safe_str(get_field(thd->mem_root, host_field));
10171 user= safe_str(get_field(thd->mem_root, user_field));
10172
10173 #ifdef EXTRA_DEBUG
10174 if (which_table != PROXIES_PRIV_TABLE)
10175 {
10176 DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
10177 user, host,
10178 get_field(thd->mem_root, table->field[1]) /*db*/,
10179 get_field(thd->mem_root, table->field[3]) /*table*/,
10180 get_field(thd->mem_root,
10181 table->field[4]) /*column*/));
10182 }
10183 #endif
10184 if (strcmp(user_str, user) ||
10185 my_strcasecmp(system_charset_info, host_str, host))
10186 continue;
10187
10188 /* If requested, delete or update the record. */
10189 result= ((drop || user_to) &&
10190 modify_grant_table(table, host_field, user_field, user_to)) ?
10191 -1 : result ? result : 1; /* Error or keep result or found. */
10192 /* If search is requested, we do not need to search further. */
10193 if (! drop && ! user_to)
10194 break ;
10195 }
10196 (void) table->file->ha_rnd_end();
10197 DBUG_PRINT("info",("scan result: %d", result));
10198 }
10199 }
10200
10201 DBUG_RETURN(result);
10202 }
10203
10204
10205 /**
10206 Handle an in-memory privilege structure.
10207
10208 @param struct_no The number of the structure to handle (0..6).
10209 @param drop If user_from is to be dropped.
10210 @param user_from The the user to be searched/dropped/renamed.
10211 @param user_to The new name for the user if to be renamed, NULL otherwise.
10212
10213 @note
10214 Scan through all elements in an in-memory grant structure and apply
10215 the requested operation.
10216 Delete from grant structure if drop is true.
10217 Update in grant structure if drop is false and user_to is not NULL.
10218 Search in grant structure if drop is false and user_to is NULL.
10219
10220 @retval > 0 At least one element matched.
10221 @retval 0 OK, but no element matched.
10222 */
10223
handle_grant_struct(enum enum_acl_lists struct_no,bool drop,LEX_USER * user_from,LEX_USER * user_to)10224 static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
10225 LEX_USER *user_from, LEX_USER *user_to)
10226 {
10227 int result= 0;
10228 int elements;
10229 bool restart;
10230 const char *UNINIT_VAR(user);
10231 const char *UNINIT_VAR(host);
10232 ACL_USER *acl_user= NULL;
10233 ACL_ROLE *acl_role= NULL;
10234 ACL_DB *acl_db= NULL;
10235 ACL_PROXY_USER *acl_proxy_user= NULL;
10236 GRANT_NAME *grant_name= NULL;
10237 ROLE_GRANT_PAIR *UNINIT_VAR(role_grant_pair);
10238 HASH *grant_name_hash= NULL;
10239 HASH *roles_mappings_hash= NULL;
10240 DBUG_ENTER("handle_grant_struct");
10241 DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
10242 struct_no, user_from->user.str, user_from->host.str));
10243
10244 mysql_mutex_assert_owner(&acl_cache->lock);
10245
10246 /* No point in querying ROLE ACL if user_from is not a role */
10247 if (struct_no == ROLE_ACL && user_from->host.length)
10248 DBUG_RETURN(0);
10249
10250 /* same. no roles in PROXY_USERS_ACL */
10251 if (struct_no == PROXY_USERS_ACL && user_from->is_role())
10252 DBUG_RETURN(0);
10253
10254 if (struct_no == ROLE_ACL) //no need to scan the structures in this case
10255 {
10256 acl_role= find_acl_role(user_from->user.str);
10257 if (!acl_role)
10258 DBUG_RETURN(0);
10259
10260 if (!drop && !user_to) //role was found
10261 DBUG_RETURN(1);
10262
10263 /* this calls for a role update */
10264 const char *old_key= acl_role->user.str;
10265 size_t old_key_length= acl_role->user.length;
10266 if (drop)
10267 {
10268 /* all grants must be revoked from this role by now. propagate this */
10269 propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL);
10270
10271 // delete the role from cross-reference arrays
10272 for (uint i=0; i < acl_role->role_grants.elements; i++)
10273 {
10274 ACL_ROLE *grant= *dynamic_element(&acl_role->role_grants,
10275 i, ACL_ROLE**);
10276 remove_ptr_from_dynarray(&grant->parent_grantee, acl_role);
10277 }
10278
10279 for (uint i=0; i < acl_role->parent_grantee.elements; i++)
10280 {
10281 ACL_USER_BASE *grantee= *dynamic_element(&acl_role->parent_grantee,
10282 i, ACL_USER_BASE**);
10283 remove_ptr_from_dynarray(&grantee->role_grants, acl_role);
10284 }
10285
10286 my_hash_delete(&acl_roles, (uchar*) acl_role);
10287 DBUG_RETURN(1);
10288 }
10289 acl_role->user= safe_lexcstrdup_root(&acl_memroot, user_to->user);
10290
10291 my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key,
10292 old_key_length);
10293 DBUG_RETURN(1);
10294
10295 }
10296
10297 /* Get the number of elements in the in-memory structure. */
10298 switch (struct_no) {
10299 case USER_ACL:
10300 elements= acl_users.elements;
10301 break;
10302 case DB_ACL:
10303 elements= int(acl_dbs.elements());
10304 break;
10305 case COLUMN_PRIVILEGES_HASH:
10306 grant_name_hash= &column_priv_hash;
10307 elements= grant_name_hash->records;
10308 break;
10309 case PROC_PRIVILEGES_HASH:
10310 grant_name_hash= &proc_priv_hash;
10311 elements= grant_name_hash->records;
10312 break;
10313 case FUNC_PRIVILEGES_HASH:
10314 grant_name_hash= &func_priv_hash;
10315 elements= grant_name_hash->records;
10316 break;
10317 case PACKAGE_SPEC_PRIVILEGES_HASH:
10318 grant_name_hash= &package_spec_priv_hash;
10319 elements= grant_name_hash->records;
10320 break;
10321 case PACKAGE_BODY_PRIVILEGES_HASH:
10322 grant_name_hash= &package_body_priv_hash;
10323 elements= grant_name_hash->records;
10324 break;
10325 case PROXY_USERS_ACL:
10326 elements= acl_proxy_users.elements;
10327 break;
10328 case ROLES_MAPPINGS_HASH:
10329 roles_mappings_hash= &acl_roles_mappings;
10330 elements= roles_mappings_hash->records;
10331 break;
10332 default:
10333 DBUG_ASSERT(0);
10334 DBUG_RETURN(-1);
10335 }
10336
10337
10338 #ifdef EXTRA_DEBUG
10339 DBUG_PRINT("loop",("scan struct: %u search user: '%s' host: '%s'",
10340 struct_no, user_from->user.str, user_from->host.str));
10341 #endif
10342 /* Loop over elements backwards as it may reduce the number of mem-moves
10343 for dynamic arrays.
10344
10345 We restart the loop, if we deleted or updated anything in a hash table
10346 because calling my_hash_delete or my_hash_update shuffles elements indices
10347 and we can miss some if we do only one scan.
10348 */
10349 do {
10350 restart= false;
10351 for (int idx= elements - 1; idx >= 0; idx--)
10352 {
10353 /*
10354 Get a pointer to the element.
10355 */
10356 switch (struct_no) {
10357 case USER_ACL:
10358 acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
10359 user= acl_user->user.str;
10360 host= acl_user->host.hostname;
10361 break;
10362
10363 case DB_ACL:
10364 acl_db= &acl_dbs.at(idx);
10365 user= acl_db->user;
10366 host= acl_db->host.hostname;
10367 break;
10368
10369 case COLUMN_PRIVILEGES_HASH:
10370 case PROC_PRIVILEGES_HASH:
10371 case FUNC_PRIVILEGES_HASH:
10372 case PACKAGE_SPEC_PRIVILEGES_HASH:
10373 case PACKAGE_BODY_PRIVILEGES_HASH:
10374 grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
10375 user= grant_name->user;
10376 host= grant_name->host.hostname;
10377 break;
10378
10379 case PROXY_USERS_ACL:
10380 acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
10381 user= acl_proxy_user->get_user();
10382 host= acl_proxy_user->get_host();
10383 break;
10384
10385 case ROLES_MAPPINGS_HASH:
10386 role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx);
10387 user= role_grant_pair->u_uname;
10388 host= role_grant_pair->u_hname;
10389 break;
10390
10391 default:
10392 DBUG_ASSERT(0);
10393 }
10394 if (! host)
10395 host= "";
10396
10397 #ifdef EXTRA_DEBUG
10398 DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'",
10399 struct_no, idx, user, host));
10400 #endif
10401
10402 if (struct_no == ROLES_MAPPINGS_HASH)
10403 {
10404 const char* role= role_grant_pair->r_uname? role_grant_pair->r_uname: "";
10405 if (user_from->is_role())
10406 {
10407 /* When searching for roles within the ROLES_MAPPINGS_HASH, we have
10408 to check both the user field as well as the role field for a match.
10409
10410 It is possible to have a role granted to a role. If we are going
10411 to modify the mapping entry, it needs to be done on either on the
10412 "user" end (here represented by a role) or the "role" end. At least
10413 one part must match.
10414
10415 If the "user" end has a not-empty host string, it can never match
10416 as we are searching for a role here. A role always has an empty host
10417 string.
10418 */
10419 if ((*host || strcmp(user_from->user.str, user)) &&
10420 strcmp(user_from->user.str, role))
10421 continue;
10422 }
10423 else
10424 {
10425 if (strcmp(user_from->user.str, user) ||
10426 my_strcasecmp(system_charset_info, user_from->host.str, host))
10427 continue;
10428 }
10429 }
10430 else
10431 {
10432 if (strcmp(user_from->user.str, user) ||
10433 my_strcasecmp(system_charset_info, user_from->host.str, host))
10434 continue;
10435 }
10436
10437 result= 1; /* At least one element found. */
10438 if ( drop )
10439 {
10440 elements--;
10441 switch ( struct_no ) {
10442 case USER_ACL:
10443 free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*));
10444 delete_dynamic_element(&acl_users, idx);
10445 break;
10446
10447 case DB_ACL:
10448 acl_dbs.del(idx);
10449 break;
10450
10451 case COLUMN_PRIVILEGES_HASH:
10452 case PROC_PRIVILEGES_HASH:
10453 case FUNC_PRIVILEGES_HASH:
10454 case PACKAGE_SPEC_PRIVILEGES_HASH:
10455 case PACKAGE_BODY_PRIVILEGES_HASH:
10456 my_hash_delete(grant_name_hash, (uchar*) grant_name);
10457 restart= true;
10458 break;
10459
10460 case PROXY_USERS_ACL:
10461 delete_dynamic_element(&acl_proxy_users, idx);
10462 break;
10463
10464 case ROLES_MAPPINGS_HASH:
10465 my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair);
10466 restart= true;
10467 break;
10468
10469 default:
10470 DBUG_ASSERT(0);
10471 break;
10472 }
10473 }
10474 else if ( user_to )
10475 {
10476 switch ( struct_no ) {
10477 case USER_ACL:
10478 acl_user->user= safe_lexcstrdup_root(&acl_memroot, user_to->user);
10479 update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str));
10480 acl_user->hostname_length= strlen(acl_user->host.hostname);
10481 break;
10482
10483 case DB_ACL:
10484 acl_db->user= strdup_root(&acl_memroot, user_to->user.str);
10485 update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str));
10486 break;
10487
10488 case COLUMN_PRIVILEGES_HASH:
10489 case PROC_PRIVILEGES_HASH:
10490 case FUNC_PRIVILEGES_HASH:
10491 case PACKAGE_SPEC_PRIVILEGES_HASH:
10492 case PACKAGE_BODY_PRIVILEGES_HASH:
10493 {
10494 /*
10495 Save old hash key and its length to be able to properly update
10496 element position in hash.
10497 */
10498 char *old_key= grant_name->hash_key;
10499 size_t old_key_length= grant_name->key_length;
10500
10501 /*
10502 Update the grant structure with the new user name and host name.
10503 */
10504 grant_name->set_user_details(user_to->host.str, grant_name->db,
10505 user_to->user.str, grant_name->tname,
10506 TRUE);
10507
10508 /*
10509 Since username is part of the hash key, when the user name
10510 is renamed, the hash key is changed. Update the hash to
10511 ensure that the position matches the new hash key value
10512 */
10513 my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
10514 old_key_length);
10515 restart= true;
10516 break;
10517 }
10518
10519 case PROXY_USERS_ACL:
10520 acl_proxy_user->set_user (&acl_memroot, user_to->user.str);
10521 acl_proxy_user->set_host (&acl_memroot, user_to->host.str);
10522 break;
10523
10524 case ROLES_MAPPINGS_HASH:
10525 {
10526 /*
10527 Save old hash key and its length to be able to properly update
10528 element position in hash.
10529 */
10530 char *old_key= role_grant_pair->hashkey.str;
10531 size_t old_key_length= role_grant_pair->hashkey.length;
10532 bool oom;
10533
10534 if (user_to->is_role())
10535 oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname,
10536 role_grant_pair->u_hname,
10537 user_to->user.str, false);
10538 else
10539 oom= role_grant_pair->init(&acl_memroot, user_to->user.str,
10540 user_to->host.str,
10541 role_grant_pair->r_uname, false);
10542 if (oom)
10543 DBUG_RETURN(-1);
10544
10545 my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair,
10546 (uchar*) old_key, old_key_length);
10547 restart= true;
10548 break;
10549 }
10550
10551 default:
10552 DBUG_ASSERT(0);
10553 break;
10554 }
10555
10556 }
10557 else
10558 {
10559 /* If search is requested, we do not need to search further. */
10560 break;
10561 }
10562 }
10563 } while (restart);
10564 #ifdef EXTRA_DEBUG
10565 DBUG_PRINT("loop",("scan struct: %u result %d", struct_no, result));
10566 #endif
10567
10568 DBUG_RETURN(result);
10569 }
10570
10571
10572 /*
10573 Handle all privilege tables and in-memory privilege structures.
10574
10575 SYNOPSIS
10576 handle_grant_data()
10577 tables The array with the four open tables.
10578 drop If user_from is to be dropped.
10579 user_from The the user to be searched/dropped/renamed.
10580 user_to The new name for the user if to be renamed,
10581 NULL otherwise.
10582
10583 DESCRIPTION
10584 Go through all grant tables and in-memory grant structures and apply
10585 the requested operation.
10586 Delete from grant data if drop is true.
10587 Update in grant data if drop is false and user_to is not NULL.
10588 Search in grant data if drop is false and user_to is NULL.
10589
10590 RETURN
10591 > 0 At least one element matched.
10592 0 OK, but no element matched.
10593 < 0 Error.
10594 */
10595
handle_grant_data(THD * thd,Grant_tables & tables,bool drop,LEX_USER * user_from,LEX_USER * user_to)10596 static int handle_grant_data(THD *thd, Grant_tables& tables, bool drop,
10597 LEX_USER *user_from, LEX_USER *user_to)
10598 {
10599 int result= 0;
10600 int found;
10601 bool handle_as_role= user_from->is_role();
10602 bool search_only= !drop && !user_to;
10603 DBUG_ENTER("handle_grant_data");
10604
10605 if (user_to)
10606 DBUG_ASSERT(handle_as_role == user_to->is_role());
10607
10608 if (search_only)
10609 {
10610 /* quickly search in-memory structures first */
10611 if (handle_as_role && find_acl_role(user_from->user.str))
10612 DBUG_RETURN(1); // found
10613
10614 if (!handle_as_role && find_user_exact(user_from->host.str, user_from->user.str))
10615 DBUG_RETURN(1); // found
10616 }
10617
10618 /* Handle db table. */
10619 if ((found= handle_grant_table(thd, tables.db_table(),
10620 DB_TABLE, drop, user_from,
10621 user_to)) < 0)
10622 {
10623 /* Handle of table failed, don't touch the in-memory array. */
10624 result= -1;
10625 }
10626 else
10627 {
10628 /* Handle db array. */
10629 if ((handle_grant_struct(DB_ACL, drop, user_from, user_to) || found)
10630 && ! result)
10631 {
10632 result= 1; /* At least one record/element found. */
10633 /* If search is requested, we do not need to search further. */
10634 if (search_only)
10635 goto end;
10636 acl_cache->clear(1);
10637 }
10638 }
10639
10640 /* Handle stored routines table. */
10641 if ((found= handle_grant_table(thd, tables.procs_priv_table(),
10642 PROCS_PRIV_TABLE, drop,
10643 user_from, user_to)) < 0)
10644 {
10645 /* Handle of table failed, don't touch in-memory array. */
10646 result= -1;
10647 }
10648 else
10649 {
10650 /* Handle procs array. */
10651 if ((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10652 && ! result)
10653 {
10654 result= 1; /* At least one record/element found. */
10655 /* If search is requested, we do not need to search further. */
10656 if (search_only)
10657 goto end;
10658 }
10659 /* Handle funcs array. */
10660 if ((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10661 && ! result)
10662 {
10663 result= 1; /* At least one record/element found. */
10664 /* If search is requested, we do not need to search further. */
10665 if (search_only)
10666 goto end;
10667 }
10668 /* Handle package spec array. */
10669 if ((handle_grant_struct(PACKAGE_SPEC_PRIVILEGES_HASH,
10670 drop, user_from, user_to) || found)
10671 && ! result)
10672 {
10673 result= 1; /* At least one record/element found. */
10674 /* If search is requested, we do not need to search further. */
10675 if (search_only)
10676 goto end;
10677 }
10678 /* Handle package body array. */
10679 if ((handle_grant_struct(PACKAGE_BODY_PRIVILEGES_HASH,
10680 drop, user_from, user_to) || found)
10681 && ! result)
10682 {
10683 result= 1; /* At least one record/element found. */
10684 /* If search is requested, we do not need to search further. */
10685 if (search_only)
10686 goto end;
10687 }
10688 }
10689
10690 /* Handle tables table. */
10691 if ((found= handle_grant_table(thd, tables.tables_priv_table(),
10692 TABLES_PRIV_TABLE, drop,
10693 user_from, user_to)) < 0)
10694 {
10695 /* Handle of table failed, don't touch columns and in-memory array. */
10696 result= -1;
10697 }
10698 else
10699 {
10700 if (found && ! result)
10701 {
10702 result= 1; /* At least one record found. */
10703 /* If search is requested, we do not need to search further. */
10704 if (search_only)
10705 goto end;
10706 }
10707
10708 /* Handle columns table. */
10709 if ((found= handle_grant_table(thd, tables.columns_priv_table(),
10710 COLUMNS_PRIV_TABLE, drop,
10711 user_from, user_to)) < 0)
10712 {
10713 /* Handle of table failed, don't touch the in-memory array. */
10714 result= -1;
10715 }
10716 else
10717 {
10718 /* Handle columns hash. */
10719 if ((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10720 && ! result)
10721 result= 1; /* At least one record/element found. */
10722 if (search_only)
10723 goto end;
10724 }
10725 }
10726
10727 /* Handle proxies_priv table. */
10728 if (tables.proxies_priv_table().table_exists())
10729 {
10730 if ((found= handle_grant_table(thd, tables.proxies_priv_table(),
10731 PROXIES_PRIV_TABLE, drop,
10732 user_from, user_to)) < 0)
10733 {
10734 /* Handle of table failed, don't touch the in-memory array. */
10735 result= -1;
10736 }
10737 else
10738 {
10739 /* Handle proxies_priv array. */
10740 if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) || found)
10741 && ! result)
10742 result= 1; /* At least one record/element found. */
10743 if (search_only)
10744 goto end;
10745 }
10746 }
10747
10748 /* Handle roles_mapping table. */
10749 if (tables.roles_mapping_table().table_exists())
10750 {
10751 if ((found= handle_grant_table(thd, tables.roles_mapping_table(),
10752 ROLES_MAPPING_TABLE, drop,
10753 user_from, user_to)) < 0)
10754 {
10755 /* Handle of table failed, don't touch the in-memory array. */
10756 result= -1;
10757 }
10758 else
10759 {
10760 /* Handle acl_roles_mappings array */
10761 if ((handle_grant_struct(ROLES_MAPPINGS_HASH, drop, user_from, user_to) || found)
10762 && ! result)
10763 result= 1; /* At least one record/element found */
10764 if (search_only)
10765 goto end;
10766 }
10767 }
10768
10769 /* Handle user table. */
10770 if ((found= handle_grant_table(thd, tables.user_table(), USER_TABLE,
10771 drop, user_from, user_to)) < 0)
10772 {
10773 /* Handle of table failed, don't touch the in-memory array. */
10774 result= -1;
10775 }
10776 else
10777 {
10778 enum enum_acl_lists what= handle_as_role ? ROLE_ACL : USER_ACL;
10779 if (((handle_grant_struct(what, drop, user_from, user_to)) || found) && !result)
10780 {
10781 result= 1; /* At least one record/element found. */
10782 DBUG_ASSERT(! search_only);
10783 }
10784 }
10785
10786 end:
10787 DBUG_RETURN(result);
10788 }
10789
10790 /*
10791 Create a list of users.
10792
10793 SYNOPSIS
10794 mysql_create_user()
10795 thd The current thread.
10796 list The users to create.
10797 handle_as_role Handle the user list as roles if true
10798
10799 RETURN
10800 FALSE OK.
10801 TRUE Error.
10802 */
10803
mysql_create_user(THD * thd,List<LEX_USER> & list,bool handle_as_role)10804 bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
10805 {
10806 int result;
10807 String wrong_users;
10808 LEX_USER *user_name;
10809 List_iterator <LEX_USER> user_list(list);
10810 bool binlog= false;
10811 bool some_users_dropped= false;
10812 DBUG_ENTER("mysql_create_user");
10813 DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
10814
10815 if (handle_as_role && sp_process_definer(thd))
10816 DBUG_RETURN(TRUE);
10817
10818 /* CREATE USER may be skipped on replication client. */
10819 Grant_tables tables;
10820 const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
10821 Table_columns_priv | Table_procs_priv |
10822 Table_proxies_priv | Table_roles_mapping;
10823 if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
10824 DBUG_RETURN(result != 1);
10825
10826 mysql_rwlock_wrlock(&LOCK_grant);
10827 mysql_mutex_lock(&acl_cache->lock);
10828
10829 while ((user_name= user_list++))
10830 {
10831 if (user_name->user.str == current_user.str)
10832 {
10833 append_str(&wrong_users, STRING_WITH_LEN("CURRENT_USER"));
10834 result= TRUE;
10835 continue;
10836 }
10837
10838 if (user_name->user.str == current_role.str)
10839 {
10840 append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
10841 result= TRUE;
10842 continue;
10843 }
10844
10845 if (handle_as_role && is_invalid_role_name(user_name->user.str))
10846 {
10847 append_user(thd, &wrong_users, user_name);
10848 result= TRUE;
10849 continue;
10850 }
10851
10852 if (!user_name->host.str)
10853 user_name->host= host_not_specified;
10854
10855 /*
10856 Search all in-memory structures and grant tables
10857 for a mention of the new user/role name.
10858 */
10859 if (handle_grant_data(thd, tables, 0, user_name, NULL))
10860 {
10861 if (thd->lex->create_info.or_replace())
10862 {
10863 // Drop the existing user
10864 if (handle_grant_data(thd, tables, 1, user_name, NULL) <= 0)
10865 {
10866 // DROP failed
10867 append_user(thd, &wrong_users, user_name);
10868 result= true;
10869 continue;
10870 }
10871 else
10872 some_users_dropped= true;
10873 // Proceed with the creation
10874 }
10875 else if (thd->lex->create_info.if_not_exists())
10876 {
10877 binlog= true;
10878 if (handle_as_role)
10879 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10880 ER_ROLE_CREATE_EXISTS,
10881 ER_THD(thd, ER_ROLE_CREATE_EXISTS),
10882 user_name->user.str);
10883 else
10884 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10885 ER_USER_CREATE_EXISTS,
10886 ER_THD(thd, ER_USER_CREATE_EXISTS),
10887 user_name->user.str, user_name->host.str);
10888 continue;
10889 }
10890 else
10891 {
10892 // "CREATE USER user1" for an existing user
10893 append_user(thd, &wrong_users, user_name);
10894 result= true;
10895 continue;
10896 }
10897 }
10898
10899 if (replace_user_table(thd, tables.user_table(), user_name,
10900 NO_ACL, 0, 1, 0))
10901 {
10902 append_user(thd, &wrong_users, user_name);
10903 result= TRUE;
10904 continue;
10905 }
10906 binlog= true;
10907
10908 // every created role is automatically granted to its creator-admin
10909 if (handle_as_role)
10910 {
10911 ACL_USER_BASE *grantee= find_acl_user_base(thd->lex->definer->user.str,
10912 thd->lex->definer->host.str);
10913 ACL_ROLE *role= find_acl_role(user_name->user.str);
10914
10915 /*
10916 just like with routines, views, triggers, and events we allow
10917 non-existant definers here with a warning (see sp_process_definer())
10918 */
10919 if (grantee)
10920 add_role_user_mapping(grantee, role);
10921
10922 /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
10923 Roles_mapping_table instead of TABLE directly. */
10924 if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
10925 &thd->lex->definer->user,
10926 &thd->lex->definer->host,
10927 &user_name->user, true,
10928 NULL, false))
10929 {
10930 append_user(thd, &wrong_users, user_name);
10931 if (grantee)
10932 undo_add_role_user_mapping(grantee, role);
10933 result= TRUE;
10934 }
10935 else if (grantee)
10936 update_role_mapping(&thd->lex->definer->user,
10937 &thd->lex->definer->host,
10938 &user_name->user, true, NULL, false);
10939 }
10940 }
10941
10942 if (result && some_users_dropped && !handle_as_role)
10943 {
10944 /* Rebuild in-memory structs, since 'acl_users' has been modified */
10945 rebuild_check_host();
10946 rebuild_role_grants();
10947 }
10948
10949 mysql_mutex_unlock(&acl_cache->lock);
10950
10951 if (result)
10952 {
10953 my_error(ER_CANNOT_USER, MYF(0),
10954 (handle_as_role) ? "CREATE ROLE" : "CREATE USER",
10955 wrong_users.c_ptr_safe());
10956 }
10957
10958 if (binlog)
10959 result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10960
10961 mysql_rwlock_unlock(&LOCK_grant);
10962 DBUG_RETURN(result);
10963 }
10964
10965 /*
10966 Drop a list of users and all their privileges.
10967
10968 SYNOPSIS
10969 mysql_drop_user()
10970 thd The current thread.
10971 list The users to drop.
10972
10973 RETURN
10974 FALSE OK.
10975 TRUE Error.
10976 */
10977
mysql_drop_user(THD * thd,List<LEX_USER> & list,bool handle_as_role)10978 bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
10979 {
10980 int result;
10981 String wrong_users;
10982 LEX_USER *user_name, *tmp_user_name;
10983 List_iterator <LEX_USER> user_list(list);
10984 bool binlog= false;
10985 DBUG_ENTER("mysql_drop_user");
10986 DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
10987
10988 /* DROP USER may be skipped on replication client. */
10989 Grant_tables tables;
10990 const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
10991 Table_columns_priv | Table_procs_priv |
10992 Table_proxies_priv | Table_roles_mapping;
10993 if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
10994 DBUG_RETURN(result != 1);
10995
10996 Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);
10997
10998 mysql_rwlock_wrlock(&LOCK_grant);
10999 mysql_mutex_lock(&acl_cache->lock);
11000
11001 while ((tmp_user_name= user_list++))
11002 {
11003 int rc;
11004 user_name= get_current_user(thd, tmp_user_name, false);
11005 if (!user_name)
11006 {
11007 thd->clear_error();
11008 append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
11009 result= TRUE;
11010 continue;
11011 }
11012
11013 if (handle_as_role != user_name->is_role())
11014 {
11015 append_user(thd, &wrong_users, user_name);
11016 result= TRUE;
11017 continue;
11018 }
11019
11020 if ((rc= handle_grant_data(thd, tables, 1, user_name, NULL)) > 0)
11021 {
11022 // The user or role was successfully deleted
11023 binlog= true;
11024 continue;
11025 }
11026
11027 if (rc == 0 && thd->lex->if_exists())
11028 {
11029 // "DROP USER IF EXISTS user1" for a non-existing user or role
11030 if (handle_as_role)
11031 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
11032 ER_ROLE_DROP_EXISTS,
11033 ER_THD(thd, ER_ROLE_DROP_EXISTS),
11034 user_name->user.str);
11035 else
11036 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
11037 ER_USER_DROP_EXISTS,
11038 ER_THD(thd, ER_USER_DROP_EXISTS),
11039 user_name->user.str, user_name->host.str);
11040 binlog= true;
11041 continue;
11042 }
11043 // Internal error, or "DROP USER user1" for a non-existing user
11044 append_user(thd, &wrong_users, user_name);
11045 result= TRUE;
11046 }
11047
11048 if (!handle_as_role)
11049 {
11050 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
11051 rebuild_check_host();
11052
11053 /*
11054 Rebuild every user's role_grants since 'acl_users' has been sorted
11055 and old pointers to ACL_USER elements are no longer valid
11056 */
11057 rebuild_role_grants();
11058 }
11059
11060 mysql_mutex_unlock(&acl_cache->lock);
11061
11062 if (result)
11063 my_error(ER_CANNOT_USER, MYF(0),
11064 (handle_as_role) ? "DROP ROLE" : "DROP USER",
11065 wrong_users.c_ptr_safe());
11066
11067 if (binlog)
11068 result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
11069
11070 mysql_rwlock_unlock(&LOCK_grant);
11071 DBUG_RETURN(result);
11072 }
11073
11074 /*
11075 Rename a user.
11076
11077 SYNOPSIS
11078 mysql_rename_user()
11079 thd The current thread.
11080 list The user name pairs: (from, to).
11081
11082 RETURN
11083 FALSE OK.
11084 TRUE Error.
11085 */
11086
mysql_rename_user(THD * thd,List<LEX_USER> & list)11087 bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
11088 {
11089 int result;
11090 String wrong_users;
11091 LEX_USER *user_from, *tmp_user_from;
11092 LEX_USER *user_to, *tmp_user_to;
11093 List_iterator <LEX_USER> user_list(list);
11094 bool some_users_renamed= FALSE;
11095 DBUG_ENTER("mysql_rename_user");
11096
11097 /* RENAME USER may be skipped on replication client. */
11098 Grant_tables tables;
11099 const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
11100 Table_columns_priv | Table_procs_priv |
11101 Table_proxies_priv | Table_roles_mapping;
11102 if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
11103 DBUG_RETURN(result != 1);
11104
11105 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
11106
11107 mysql_rwlock_wrlock(&LOCK_grant);
11108 mysql_mutex_lock(&acl_cache->lock);
11109
11110 while ((tmp_user_from= user_list++))
11111 {
11112 tmp_user_to= user_list++;
11113 if (!(user_from= get_current_user(thd, tmp_user_from, false)))
11114 {
11115 append_user(thd, &wrong_users, user_from);
11116 result= TRUE;
11117 continue;
11118 }
11119 if (!(user_to= get_current_user(thd, tmp_user_to, false)))
11120 {
11121 append_user(thd, &wrong_users, user_to);
11122 result= TRUE;
11123 continue;
11124 }
11125 DBUG_ASSERT(!user_from->is_role());
11126 DBUG_ASSERT(!user_to->is_role());
11127
11128 /*
11129 Search all in-memory structures and grant tables
11130 for a mention of the new user name.
11131 */
11132 if (handle_grant_data(thd, tables, 0, user_to, NULL) ||
11133 handle_grant_data(thd, tables, 0, user_from, user_to) <= 0)
11134 {
11135 /* NOTE TODO renaming roles is not yet implemented */
11136 append_user(thd, &wrong_users, user_from);
11137 result= TRUE;
11138 continue;
11139 }
11140 some_users_renamed= TRUE;
11141 rebuild_acl_users();
11142 }
11143
11144 /* Rebuild 'acl_dbs' since 'acl_users' has been modified */
11145 rebuild_acl_dbs();
11146
11147 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
11148 rebuild_check_host();
11149
11150 /*
11151 Rebuild every user's role_grants since 'acl_users' has been sorted
11152 and old pointers to ACL_USER elements are no longer valid
11153 */
11154 rebuild_role_grants();
11155
11156 mysql_mutex_unlock(&acl_cache->lock);
11157
11158 if (result)
11159 my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
11160
11161 if (some_users_renamed && mysql_bin_log.is_open())
11162 result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
11163
11164 mysql_rwlock_unlock(&LOCK_grant);
11165 DBUG_RETURN(result);
11166 }
11167
11168 /*
11169 Alter a user's connection and resource settings.
11170
11171 SYNOPSIS
11172 mysql_alter_user()
11173 thd The current thread.
11174 list The users to alter.
11175
11176 RETURN
11177 > 0 Error. Error message already sent.
11178 0 OK.
11179 */
mysql_alter_user(THD * thd,List<LEX_USER> & users_list)11180 int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
11181 {
11182 DBUG_ENTER("mysql_alter_user");
11183 int result= 0;
11184 String wrong_users;
11185 bool some_users_altered= false;
11186
11187 /* The only table we're altering is the user table. */
11188 Grant_tables tables;
11189 if ((result= tables.open_and_lock(thd, Table_user, TL_WRITE)))
11190 DBUG_RETURN(result != 1);
11191
11192 /* Lock ACL data structures until we finish altering all users. */
11193 mysql_rwlock_wrlock(&LOCK_grant);
11194 mysql_mutex_lock(&acl_cache->lock);
11195
11196 LEX_USER *tmp_lex_user;
11197 List_iterator<LEX_USER> users_list_iterator(users_list);
11198
11199 while ((tmp_lex_user= users_list_iterator++))
11200 {
11201 LEX_USER* lex_user= get_current_user(thd, tmp_lex_user, false);
11202 if (!lex_user ||
11203 replace_user_table(thd, tables.user_table(), lex_user, NO_ACL,
11204 false, false, true))
11205 {
11206 thd->clear_error();
11207 append_user(thd, &wrong_users, tmp_lex_user);
11208 result= TRUE;
11209 continue;
11210 }
11211 some_users_altered= true;
11212 }
11213
11214 /* Unlock ACL data structures. */
11215 mysql_mutex_unlock(&acl_cache->lock);
11216 mysql_rwlock_unlock(&LOCK_grant);
11217
11218 if (result)
11219 {
11220 /* 'if exists' flag leads to warnings instead of errors. */
11221 if (thd->lex->create_info.if_exists())
11222 {
11223 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
11224 ER_CANNOT_USER,
11225 ER_THD(thd, ER_CANNOT_USER),
11226 "ALTER USER", wrong_users.c_ptr_safe());
11227 result= FALSE;
11228 }
11229 else
11230 {
11231 my_error(ER_CANNOT_USER, MYF(0),
11232 "ALTER USER",
11233 wrong_users.c_ptr_safe());
11234 }
11235 }
11236
11237 if (some_users_altered)
11238 result|= write_bin_log(thd, FALSE, thd->query(),
11239 thd->query_length());
11240 DBUG_RETURN(result);
11241 }
11242
11243
11244 static bool
mysql_revoke_sp_privs(THD * thd,Grant_tables * tables,const Sp_handler * sph,const LEX_USER * lex_user)11245 mysql_revoke_sp_privs(THD *thd, Grant_tables *tables, const Sp_handler *sph,
11246 const LEX_USER *lex_user)
11247 {
11248 bool rc= false;
11249 uint counter, revoked;
11250 do {
11251 HASH *hash= sph->get_priv_hash();
11252 for (counter= 0, revoked= 0 ; counter < hash->records ; )
11253 {
11254 const char *user,*host;
11255 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
11256 user= grant_proc->user;
11257 host= safe_str(grant_proc->host.hostname);
11258
11259 if (!strcmp(lex_user->user.str, user) &&
11260 !strcmp(lex_user->host.str, host))
11261 {
11262 if (replace_routine_table(thd, grant_proc,
11263 tables->procs_priv_table().table(),
11264 *lex_user,
11265 grant_proc->db, grant_proc->tname,
11266 sph, ALL_KNOWN_ACL, 1) == 0)
11267 {
11268 revoked= 1;
11269 continue;
11270 }
11271 rc= true; // Something went wrong
11272 }
11273 counter++;
11274 }
11275 } while (revoked);
11276 return rc;
11277 }
11278
11279
11280 /*
11281 Revoke all privileges from a list of users.
11282
11283 SYNOPSIS
11284 mysql_revoke_all()
11285 thd The current thread.
11286 list The users to revoke all privileges from.
11287
11288 RETURN
11289 > 0 Error. Error message already sent.
11290 0 OK.
11291 < 0 Error. Error message not yet sent.
11292 */
11293
mysql_revoke_all(THD * thd,List<LEX_USER> & list)11294 bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
11295 {
11296 uint counter, revoked;
11297 int result;
11298 ACL_DB *acl_db;
11299 DBUG_ENTER("mysql_revoke_all");
11300
11301 Grant_tables tables;
11302 const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
11303 Table_columns_priv | Table_procs_priv |
11304 Table_proxies_priv | Table_roles_mapping;
11305 if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
11306 DBUG_RETURN(result != 1);
11307
11308 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
11309
11310 mysql_rwlock_wrlock(&LOCK_grant);
11311 mysql_mutex_lock(&acl_cache->lock);
11312
11313 LEX_USER *lex_user, *tmp_lex_user;
11314 List_iterator <LEX_USER> user_list(list);
11315 while ((tmp_lex_user= user_list++))
11316 {
11317 if (!(lex_user= get_current_user(thd, tmp_lex_user, false)))
11318 {
11319 result= -1;
11320 continue;
11321 }
11322
11323 /* This is not a role and the user could not be found */
11324 if (!lex_user->is_role() &&
11325 !find_user_exact(lex_user->host.str, lex_user->user.str))
11326 {
11327 result= -1;
11328 continue;
11329 }
11330
11331 if (replace_user_table(thd, tables.user_table(), lex_user,
11332 ALL_KNOWN_ACL, 1, 0, 0))
11333 {
11334 result= -1;
11335 continue;
11336 }
11337
11338 /* Remove db access privileges */
11339 /*
11340 Because acl_dbs and column_priv_hash shrink and may re-order
11341 as privileges are removed, removal occurs in a repeated loop
11342 until no more privileges are revoked.
11343 */
11344 do
11345 {
11346 for (counter= 0, revoked= 0 ; counter < acl_dbs.elements() ; )
11347 {
11348 const char *user, *host;
11349
11350 acl_db= &acl_dbs.at(counter);
11351
11352 user= acl_db->user;
11353 host= safe_str(acl_db->host.hostname);
11354
11355 if (!strcmp(lex_user->user.str, user) &&
11356 !strcmp(lex_user->host.str, host))
11357 {
11358 /* TODO(cvicentiu) refactor replace_db_table to use
11359 Db_table instead of TABLE directly. */
11360 if (!replace_db_table(tables.db_table().table(), acl_db->db, *lex_user,
11361 ALL_KNOWN_ACL, 1))
11362 {
11363 /*
11364 Don't increment counter as replace_db_table deleted the
11365 current element in acl_dbs.
11366 */
11367 revoked= 1;
11368 continue;
11369 }
11370 result= -1; // Something went wrong
11371 }
11372 counter++;
11373 }
11374 } while (revoked);
11375
11376 /* Remove column access */
11377 do
11378 {
11379 for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
11380 {
11381 const char *user,*host;
11382 GRANT_TABLE *grant_table=
11383 (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
11384 user= grant_table->user;
11385 host= safe_str(grant_table->host.hostname);
11386
11387 if (!strcmp(lex_user->user.str,user) &&
11388 !strcmp(lex_user->host.str, host))
11389 {
11390 List<LEX_COLUMN> columns;
11391 /* TODO(cvicentiu) refactor to use
11392 Db_table instead of TABLE directly. */
11393 if (replace_column_table(grant_table,
11394 tables.columns_priv_table().table(),
11395 *lex_user, columns,
11396 grant_table->db, grant_table->tname,
11397 ALL_KNOWN_ACL, 1))
11398 result= -1;
11399 if (int res= replace_table_table(thd, grant_table,
11400 tables.tables_priv_table().table(),
11401 *lex_user,
11402 grant_table->db, grant_table->tname,
11403 ALL_KNOWN_ACL, NO_ACL, 1))
11404 {
11405 if (res > 0)
11406 result= -1;
11407 else
11408 {
11409 /*
11410 Entry was deleted. We have to retry the loop as the
11411 hash table has probably been reorganized.
11412 */
11413 revoked= 1;
11414 continue;
11415 }
11416 }
11417 }
11418 counter++;
11419 }
11420 } while (revoked);
11421
11422 /* Remove procedure access */
11423 if (mysql_revoke_sp_privs(thd, &tables, &sp_handler_function, lex_user) ||
11424 mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user) ||
11425 mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_spec, lex_user) ||
11426 mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_body, lex_user))
11427 result= -1;
11428
11429 ACL_USER_BASE *user_or_role;
11430 /* remove role grants */
11431 if (lex_user->is_role())
11432 {
11433 /* this can not fail due to get_current_user already having searched for it */
11434 user_or_role= find_acl_role(lex_user->user.str);
11435 }
11436 else
11437 {
11438 user_or_role= find_user_exact(lex_user->host.str, lex_user->user.str);
11439 }
11440 /*
11441 Find every role grant pair matching the role_grants array and remove it,
11442 both from the acl_roles_mappings and the roles_mapping table
11443 */
11444 for (counter= 0; counter < user_or_role->role_grants.elements; counter++)
11445 {
11446 ACL_ROLE *role_grant= *dynamic_element(&user_or_role->role_grants,
11447 counter, ACL_ROLE**);
11448 ROLE_GRANT_PAIR *pair = find_role_grant_pair(&lex_user->user,
11449 &lex_user->host,
11450 &role_grant->user);
11451 /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
11452 Roles_mapping_table instead of TABLE directly. */
11453 if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
11454 &lex_user->user, &lex_user->host,
11455 &role_grant->user, false, pair, true))
11456 {
11457 result= -1; //Something went wrong
11458 }
11459 update_role_mapping(&lex_user->user, &lex_user->host,
11460 &role_grant->user, false, pair, true);
11461 /*
11462 Delete from the parent_grantee array of the roles granted,
11463 the entry pointing to this user_or_role
11464 */
11465 remove_ptr_from_dynarray(&role_grant->parent_grantee, user_or_role);
11466 }
11467 /* TODO
11468 How to handle an error in the replace_roles_mapping_table, in
11469 regards to the privileges held in memory
11470 */
11471
11472 /* Finally, clear the role_grants array */
11473 if (counter == user_or_role->role_grants.elements)
11474 {
11475 reset_dynamic(&user_or_role->role_grants);
11476 }
11477 /*
11478 If we are revoking from a role, we need to update all the parent grantees
11479 */
11480 if (lex_user->is_role())
11481 {
11482 propagate_role_grants((ACL_ROLE *)user_or_role, PRIVS_TO_MERGE::ALL);
11483 }
11484 }
11485
11486 mysql_mutex_unlock(&acl_cache->lock);
11487
11488 if (result)
11489 my_message(ER_REVOKE_GRANTS, ER_THD(thd, ER_REVOKE_GRANTS), MYF(0));
11490
11491 result= result |
11492 write_bin_log(thd, FALSE, thd->query(), thd->query_length());
11493
11494 mysql_rwlock_unlock(&LOCK_grant);
11495
11496 DBUG_RETURN(result);
11497 }
11498
11499
11500
11501
11502 /**
11503 If the defining user for a routine does not exist, then the ACL lookup
11504 code should raise two errors which we should intercept. We convert the more
11505 descriptive error into a warning, and consume the other.
11506
11507 If any other errors are raised, then we set a flag that should indicate
11508 that there was some failure we should complain at a higher level.
11509 */
11510 class Silence_routine_definer_errors : public Internal_error_handler
11511 {
11512 public:
Silence_routine_definer_errors()11513 Silence_routine_definer_errors()
11514 : is_grave(FALSE)
11515 {}
11516
~Silence_routine_definer_errors()11517 virtual ~Silence_routine_definer_errors()
11518 {}
11519
11520 virtual bool handle_condition(THD *thd,
11521 uint sql_errno,
11522 const char* sqlstate,
11523 Sql_condition::enum_warning_level *level,
11524 const char* msg,
11525 Sql_condition ** cond_hdl);
11526
has_errors()11527 bool has_errors() { return is_grave; }
11528
11529 private:
11530 bool is_grave;
11531 };
11532
11533 bool
handle_condition(THD * thd,uint sql_errno,const char *,Sql_condition::enum_warning_level * level,const char * msg,Sql_condition ** cond_hdl)11534 Silence_routine_definer_errors::handle_condition(
11535 THD *thd,
11536 uint sql_errno,
11537 const char*,
11538 Sql_condition::enum_warning_level *level,
11539 const char* msg,
11540 Sql_condition ** cond_hdl)
11541 {
11542 *cond_hdl= NULL;
11543 if (*level == Sql_condition::WARN_LEVEL_ERROR)
11544 {
11545 switch (sql_errno)
11546 {
11547 case ER_NONEXISTING_PROC_GRANT:
11548 /* Convert the error into a warning. */
11549 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
11550 sql_errno, msg);
11551 return TRUE;
11552 default:
11553 is_grave= TRUE;
11554 }
11555 }
11556
11557 return FALSE;
11558 }
11559
11560
11561 /**
11562 Revoke privileges for all users on a stored procedure. Use an error handler
11563 that converts errors about missing grants into warnings.
11564
11565 @param
11566 thd The current thread.
11567 @param
11568 db DB of the stored procedure
11569 @param
11570 name Name of the stored procedure
11571
11572 @retval
11573 0 OK.
11574 @retval
11575 < 0 Error. Error message not yet sent.
11576 */
11577
sp_revoke_privileges(THD * thd,const char * sp_db,const char * sp_name,const Sp_handler * sph)11578 bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
11579 const Sp_handler *sph)
11580 {
11581 uint counter, revoked;
11582 int result;
11583 HASH *hash= sph->get_priv_hash();
11584 Silence_routine_definer_errors error_handler;
11585 DBUG_ENTER("sp_revoke_privileges");
11586
11587 Grant_tables tables;
11588 const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
11589 Table_columns_priv | Table_procs_priv |
11590 Table_proxies_priv | Table_roles_mapping;
11591 if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
11592 DBUG_RETURN(result != 1);
11593
11594 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
11595
11596 /* Be sure to pop this before exiting this scope! */
11597 thd->push_internal_handler(&error_handler);
11598
11599 mysql_rwlock_wrlock(&LOCK_grant);
11600 mysql_mutex_lock(&acl_cache->lock);
11601
11602 /* Remove procedure access */
11603 do
11604 {
11605 for (counter= 0, revoked= 0 ; counter < hash->records ; )
11606 {
11607 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
11608 if (!my_strcasecmp(&my_charset_utf8mb3_bin, grant_proc->db, sp_db) &&
11609 !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
11610 {
11611 LEX_USER lex_user;
11612 lex_user.user.str= grant_proc->user;
11613 lex_user.user.length= strlen(grant_proc->user);
11614 lex_user.host.str= safe_str(grant_proc->host.hostname);
11615 lex_user.host.length= strlen(lex_user.host.str);
11616 if (replace_routine_table(thd, grant_proc,
11617 tables.procs_priv_table().table(), lex_user,
11618 grant_proc->db, grant_proc->tname,
11619 sph, ALL_KNOWN_ACL, 1) == 0)
11620 {
11621 revoked= 1;
11622 continue;
11623 }
11624 }
11625 counter++;
11626 }
11627 } while (revoked);
11628
11629 mysql_mutex_unlock(&acl_cache->lock);
11630 mysql_rwlock_unlock(&LOCK_grant);
11631
11632 thd->pop_internal_handler();
11633
11634 DBUG_RETURN(error_handler.has_errors());
11635 }
11636
11637
11638 /**
11639 Grant EXECUTE,ALTER privilege for a stored procedure
11640
11641 @param thd The current thread.
11642 @param sp_db
11643 @param sp_name
11644 @param sph
11645
11646 @return
11647 @retval FALSE Success
11648 @retval TRUE An error occurred. Error message not yet sent.
11649 */
11650
sp_grant_privileges(THD * thd,const char * sp_db,const char * sp_name,const Sp_handler * sph)11651 bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
11652 const Sp_handler *sph)
11653 {
11654 Security_context *sctx= thd->security_ctx;
11655 LEX_USER *combo;
11656 TABLE_LIST tables[1];
11657 List<LEX_USER> user_list;
11658 bool result;
11659 ACL_USER *au;
11660 Dummy_error_handler error_handler;
11661 DBUG_ENTER("sp_grant_privileges");
11662
11663 if (!(combo=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
11664 DBUG_RETURN(TRUE);
11665
11666 combo->user.str= (char *) sctx->priv_user;
11667
11668 mysql_mutex_lock(&acl_cache->lock);
11669 if ((au= find_user_exact(combo->host.str= (char *) sctx->priv_host,
11670 combo->user.str)))
11671 goto found_acl;
11672
11673 mysql_mutex_unlock(&acl_cache->lock);
11674 DBUG_RETURN(TRUE);
11675
11676 found_acl:
11677 mysql_mutex_unlock(&acl_cache->lock);
11678
11679 bzero((char*)tables, sizeof(TABLE_LIST));
11680 user_list.empty();
11681
11682 tables->db.str= sp_db;
11683 tables->db.length= sp_db ? strlen(sp_db) : 0;
11684 tables->table_name.str= tables->alias.str= sp_name;
11685 tables->table_name.length= tables->alias.length= sp_name ? strlen(sp_name) : 0;
11686
11687 thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str));
11688 thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str));
11689
11690 combo->auth= NULL;
11691
11692 if (user_list.push_back(combo, thd->mem_root))
11693 DBUG_RETURN(TRUE);
11694
11695 thd->lex->account_options.reset();
11696
11697 /*
11698 Only care about whether the operation failed or succeeded
11699 as all errors will be handled later.
11700 */
11701 thd->push_internal_handler(&error_handler);
11702 result= mysql_routine_grant(thd, tables, sph, user_list,
11703 DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
11704 thd->pop_internal_handler();
11705 DBUG_RETURN(result);
11706 }
11707
11708
11709 /**
11710 Validate if a user can proxy as another user
11711
11712 @thd current thread
11713 @param user the logged in user (proxy user)
11714 @param authenticated_as the effective user a plugin is trying to
11715 impersonate as (proxied user)
11716 @return proxy user definition
11717 @retval NULL proxy user definition not found or not applicable
11718 @retval non-null the proxy user data
11719 */
11720
11721 static ACL_PROXY_USER *
acl_find_proxy_user(const char * user,const char * host,const char * ip,const char * authenticated_as,bool * proxy_used)11722 acl_find_proxy_user(const char *user, const char *host, const char *ip,
11723 const char *authenticated_as, bool *proxy_used)
11724 {
11725 uint i;
11726 /* if the proxied and proxy user are the same return OK */
11727 DBUG_ENTER("acl_find_proxy_user");
11728 DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
11729 user, host, ip, authenticated_as));
11730
11731 if (!strcmp(authenticated_as, user))
11732 {
11733 DBUG_PRINT ("info", ("user is the same as authenticated_as"));
11734 DBUG_RETURN (NULL);
11735 }
11736
11737 *proxy_used= TRUE;
11738 for (i=0; i < acl_proxy_users.elements; i++)
11739 {
11740 ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11741 ACL_PROXY_USER *);
11742 if (proxy->matches(host, user, ip, authenticated_as))
11743 DBUG_RETURN(proxy);
11744 }
11745
11746 DBUG_RETURN(NULL);
11747 }
11748
11749
11750 bool
acl_check_proxy_grant_access(THD * thd,const char * host,const char * user,bool with_grant)11751 acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
11752 bool with_grant)
11753 {
11754 DBUG_ENTER("acl_check_proxy_grant_access");
11755 DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
11756 (int) with_grant));
11757 if (!initialized)
11758 {
11759 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
11760 DBUG_RETURN(1);
11761 }
11762
11763 /* replication slave thread can do anything */
11764 if (thd->slave_thread)
11765 {
11766 DBUG_PRINT("info", ("replication slave"));
11767 DBUG_RETURN(FALSE);
11768 }
11769
11770 /*
11771 one can grant proxy for self to others.
11772 Security context in THD contains two pairs of (user,host):
11773 1. (user,host) pair referring to inbound connection.
11774 2. (priv_user,priv_host) pair obtained from mysql.user table after doing
11775 authentication of incoming connection.
11776 Privileges should be checked wrt (priv_user, priv_host) tuple, because
11777 (user,host) pair obtained from inbound connection may have different
11778 values than what is actually stored in mysql.user table and while granting
11779 or revoking proxy privilege, user is expected to provide entries mentioned
11780 in mysql.user table.
11781 */
11782 if (thd->security_ctx->is_priv_user(user, host))
11783 {
11784 DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
11785 thd->security_ctx->priv_user, user,
11786 host, thd->security_ctx->priv_host));
11787 DBUG_RETURN(FALSE);
11788 }
11789
11790 mysql_mutex_lock(&acl_cache->lock);
11791
11792 /* check for matching WITH PROXY rights */
11793 for (uint i=0; i < acl_proxy_users.elements; i++)
11794 {
11795 ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11796 ACL_PROXY_USER *);
11797 if (proxy->matches(thd->security_ctx->host,
11798 thd->security_ctx->user,
11799 thd->security_ctx->ip,
11800 user) &&
11801 proxy->get_with_grant())
11802 {
11803 DBUG_PRINT("info", ("found"));
11804 mysql_mutex_unlock(&acl_cache->lock);
11805 DBUG_RETURN(FALSE);
11806 }
11807 }
11808
11809 mysql_mutex_unlock(&acl_cache->lock);
11810 my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
11811 thd->security_ctx->user,
11812 thd->security_ctx->host_or_ip);
11813 DBUG_RETURN(TRUE);
11814 }
11815
11816
11817 static bool
show_proxy_grants(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)11818 show_proxy_grants(THD *thd, const char *username, const char *hostname,
11819 char *buff, size_t buffsize)
11820 {
11821 Protocol *protocol= thd->protocol;
11822 int error= 0;
11823
11824 for (uint i=0; i < acl_proxy_users.elements; i++)
11825 {
11826 ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11827 ACL_PROXY_USER *);
11828 if (proxy->granted_on(hostname, username))
11829 {
11830 String global(buff, buffsize, system_charset_info);
11831 global.length(0);
11832 proxy->print_grant(&global);
11833 protocol->prepare_for_resend();
11834 protocol->store(global.ptr(), global.length(), global.charset());
11835 if (protocol->write())
11836 {
11837 error= -1;
11838 break;
11839 }
11840 }
11841 }
11842 return error;
11843 }
11844
enabled_roles_insert(ACL_USER_BASE * role,void * context_data)11845 static int enabled_roles_insert(ACL_USER_BASE *role, void *context_data)
11846 {
11847 TABLE *table= (TABLE*) context_data;
11848 DBUG_ASSERT(role->flags & IS_ROLE);
11849
11850 restore_record(table, s->default_values);
11851 table->field[0]->set_notnull();
11852 table->field[0]->store(role->user.str, role->user.length,
11853 system_charset_info);
11854 if (schema_table_store_record(table->in_use, table))
11855 return -1;
11856 return 0;
11857 }
11858
11859 struct APPLICABLE_ROLES_DATA
11860 {
11861 TABLE *table;
11862 const LEX_CSTRING host;
11863 const LEX_CSTRING user_and_host;
11864 ACL_USER *user;
11865 };
11866
11867 static int
applicable_roles_insert(ACL_USER_BASE * grantee,ACL_ROLE * role,void * ptr)11868 applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr)
11869 {
11870 APPLICABLE_ROLES_DATA *data= (APPLICABLE_ROLES_DATA *)ptr;
11871 CHARSET_INFO *cs= system_charset_info;
11872 TABLE *table= data->table;
11873 bool is_role= grantee != data->user;
11874 const LEX_CSTRING *user_and_host= is_role ? &grantee->user
11875 : &data->user_and_host;
11876 const LEX_CSTRING *host= is_role ? &empty_clex_str : &data->host;
11877
11878 restore_record(table, s->default_values);
11879 table->field[0]->store(user_and_host->str, user_and_host->length, cs);
11880 table->field[1]->store(role->user.str, role->user.length, cs);
11881
11882 ROLE_GRANT_PAIR *pair=
11883 find_role_grant_pair(&grantee->user, host, &role->user);
11884 DBUG_ASSERT(pair);
11885
11886 if (pair->with_admin)
11887 table->field[2]->store(STRING_WITH_LEN("YES"), cs);
11888 else
11889 table->field[2]->store(STRING_WITH_LEN("NO"), cs);
11890
11891 /* Default role is only valid when looking at a role granted to a user. */
11892 if (!is_role)
11893 {
11894 if (data->user->default_rolename.length &&
11895 lex_string_eq(&data->user->default_rolename, &role->user))
11896 table->field[3]->store(STRING_WITH_LEN("YES"), cs);
11897 else
11898 table->field[3]->store(STRING_WITH_LEN("NO"), cs);
11899 table->field[3]->set_notnull();
11900 }
11901
11902 if (schema_table_store_record(table->in_use, table))
11903 return -1;
11904 return 0;
11905 }
11906
11907 /**
11908 Hash iterate function to count the number of total column privileges granted.
11909 */
count_column_grants(void * grant_table,void * current_count)11910 static my_bool count_column_grants(void *grant_table,
11911 void *current_count)
11912 {
11913 HASH hash_columns = ((GRANT_TABLE *)grant_table)->hash_columns;
11914 *(ulong *)current_count+= hash_columns.records;
11915 return 0;
11916 }
11917
11918 /**
11919 SHOW function that computes the number of column grants.
11920
11921 This must be performed under the mutex in order to make sure the
11922 iteration does not fail.
11923 */
show_column_grants(THD * thd,SHOW_VAR * var,char * buff,enum enum_var_type scope)11924 static int show_column_grants(THD *thd, SHOW_VAR *var, char *buff,
11925 enum enum_var_type scope)
11926 {
11927 var->type= SHOW_ULONG;
11928 var->value= buff;
11929 *(ulong *)buff= 0;
11930 if (initialized)
11931 {
11932 mysql_rwlock_rdlock(&LOCK_grant);
11933 mysql_mutex_lock(&acl_cache->lock);
11934 my_hash_iterate(&column_priv_hash, count_column_grants, buff);
11935 mysql_mutex_unlock(&acl_cache->lock);
11936 mysql_rwlock_unlock(&LOCK_grant);
11937 }
11938 return 0;
11939 }
11940
show_database_grants(THD * thd,SHOW_VAR * var,char * buff,enum enum_var_type scope)11941 static int show_database_grants(THD *thd, SHOW_VAR *var, char *buff,
11942 enum enum_var_type scope)
11943 {
11944 var->type= SHOW_UINT;
11945 var->value= buff;
11946 *(uint *)buff= uint(acl_dbs.elements());
11947 return 0;
11948 }
11949
11950 #else
set_user_salt_if_needed(ACL_USER *,int,plugin_ref)11951 static bool set_user_salt_if_needed(ACL_USER *, int, plugin_ref)
11952 { return 0; }
check_grant(THD *,privilege_t,TABLE_LIST *,bool,uint,bool)11953 bool check_grant(THD *, privilege_t, TABLE_LIST *, bool, uint, bool)
11954 { return 0; }
11955 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
11956
11957
11958 #ifdef NO_EMBEDDED_ACCESS_CHECKS
11959
execute(THD * thd)11960 bool Sql_cmd_grant_proxy::execute(THD *thd)
11961 {
11962 my_ok(thd);
11963 return false;
11964 }
11965
execute(THD * thd)11966 bool Sql_cmd_grant_table::execute(THD *thd)
11967 {
11968 my_ok(thd);
11969 return false;
11970 }
11971
11972
execute(THD * thd)11973 bool Sql_cmd_grant_sp::execute(THD *thd)
11974 {
11975 my_ok(thd);
11976 return false;
11977 }
11978
11979 #else // not NO_EMBEDDED_ACCESS_CHECKS
11980
11981
warn_hostname_requires_resolving(THD * thd,List<LEX_USER> & users)11982 void Sql_cmd_grant::warn_hostname_requires_resolving(THD *thd,
11983 List<LEX_USER> &users)
11984 {
11985 LEX_USER *user;
11986 List_iterator <LEX_USER> it(users);
11987 while ((user= it++))
11988 {
11989 if (specialflag & SPECIAL_NO_RESOLVE &&
11990 hostname_requires_resolving(user->host.str))
11991 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
11992 ER_WARN_HOSTNAME_WONT_WORK,
11993 ER_THD(thd, ER_WARN_HOSTNAME_WONT_WORK));
11994 }
11995 }
11996
11997
grant_stage0(THD * thd)11998 void Sql_cmd_grant::grant_stage0(THD *thd)
11999 {
12000 thd->binlog_invoker(false); // Replicate current user as grantor
12001 if (thd->security_ctx->user) // If not replication
12002 warn_hostname_requires_resolving(thd, thd->lex->users_list);
12003 }
12004
12005
user_list_reset_mqh(THD * thd,List<LEX_USER> & users)12006 bool Sql_cmd_grant::user_list_reset_mqh(THD *thd, List<LEX_USER> &users)
12007 {
12008 List_iterator <LEX_USER> it(users);
12009 LEX_USER *user, *tmp_user;
12010 while ((tmp_user= it++))
12011 {
12012 if (!(user= get_current_user(thd, tmp_user)))
12013 return true;
12014 reset_mqh(user, 0);
12015 }
12016 return false;
12017 }
12018
12019
check_access_proxy(THD * thd,List<LEX_USER> & users)12020 bool Sql_cmd_grant_proxy::check_access_proxy(THD *thd, List<LEX_USER> &users)
12021 {
12022 LEX_USER *user;
12023 List_iterator <LEX_USER> it(users);
12024 if ((user= it++))
12025 {
12026 // GRANT/REVOKE PROXY has the target user as a first entry in the list
12027 if (!(user= get_current_user(thd, user)) || !user->host.str)
12028 return true;
12029 if (acl_check_proxy_grant_access(thd, user->host.str, user->user.str,
12030 m_grant_option & GRANT_ACL))
12031 return true;
12032 }
12033 return false;
12034 }
12035
12036
execute(THD * thd)12037 bool Sql_cmd_grant_proxy::execute(THD *thd)
12038 {
12039 LEX *lex= thd->lex;
12040
12041 DBUG_ASSERT(lex->first_select_lex()->table_list.first == NULL);
12042 DBUG_ASSERT((m_grant_option & ~GRANT_ACL) == NO_ACL); // only WITH GRANT OPTION
12043
12044 grant_stage0(thd);
12045
12046 if (thd->security_ctx->user /* If not replication */ &&
12047 check_access_proxy(thd, lex->users_list))
12048 return true;
12049
12050 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
12051 /* Conditionally writes to binlog */
12052 if (mysql_grant(thd, NULL/*db*/, lex->users_list, m_grant_option,
12053 is_revoke(), true/*proxy*/))
12054 return true;
12055
12056 return !is_revoke() && user_list_reset_mqh(thd, lex->users_list);
12057
12058 #ifdef WITH_WSREP
12059 wsrep_error_label:
12060 return true;
12061 #endif // WITH_WSREP
12062 }
12063
12064
grant_stage0_exact_object(THD * thd,TABLE_LIST * table)12065 bool Sql_cmd_grant_object::grant_stage0_exact_object(THD *thd,
12066 TABLE_LIST *table)
12067 {
12068 privilege_t priv= m_object_privilege | m_column_privilege_total | GRANT_ACL;
12069 if (check_access(thd, priv, table->db.str,
12070 &table->grant.privilege, &table->grant.m_internal,
12071 0, 0))
12072 return true;
12073 grant_stage0(thd);
12074 return false;
12075 }
12076
12077
execute_exact_table(THD * thd,TABLE_LIST * table)12078 bool Sql_cmd_grant_table::execute_exact_table(THD *thd, TABLE_LIST *table)
12079 {
12080 LEX *lex= thd->lex;
12081 if (grant_stage0_exact_object(thd, table) ||
12082 check_grant(thd, m_object_privilege | m_column_privilege_total | GRANT_ACL,
12083 lex->query_tables, FALSE, UINT_MAX, FALSE))
12084 return true;
12085 /* Conditionally writes to binlog */
12086 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
12087 return mysql_table_grant(thd, lex->query_tables, lex->users_list,
12088 m_columns, m_object_privilege,
12089 is_revoke());
12090 #ifdef WITH_WSREP
12091 wsrep_error_label:
12092 return true;
12093 #endif // WITH_WSREP
12094 }
12095
12096
execute(THD * thd)12097 bool Sql_cmd_grant_sp::execute(THD *thd)
12098 {
12099 DBUG_ASSERT(!m_columns.elements);
12100 DBUG_ASSERT(!m_column_privilege_total);
12101 LEX *lex= thd->lex;
12102 TABLE_LIST *table= lex->first_select_lex()->table_list.first;
12103 privilege_t grants= m_all_privileges
12104 ? (PROC_ACLS & ~GRANT_ACL) | (m_object_privilege & GRANT_ACL)
12105 : m_object_privilege;
12106
12107 if (!table) // e.g: GRANT EXECUTE ON PROCEDURE *.*
12108 {
12109 my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
12110 MYF(0));
12111 return true;
12112 }
12113
12114 if (grant_stage0_exact_object(thd, table) ||
12115 check_grant_routine(thd, grants|GRANT_ACL, lex->query_tables, &m_sph, 0))
12116 return true;
12117
12118 /* Conditionally writes to binlog */
12119 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
12120 if (mysql_routine_grant(thd, lex->query_tables, &m_sph,
12121 lex->users_list, grants,
12122 is_revoke(), true))
12123 return true;
12124 my_ok(thd);
12125 return false;
12126 #ifdef WITH_WSREP
12127 wsrep_error_label:
12128 return true;
12129 #endif // WITH_WSREP
12130 }
12131
12132
execute_table_mask(THD * thd)12133 bool Sql_cmd_grant_table::execute_table_mask(THD *thd)
12134 {
12135 LEX *lex= thd->lex;
12136 DBUG_ASSERT(lex->first_select_lex()->table_list.first == NULL);
12137
12138 if (check_access(thd, m_object_privilege | m_column_privilege_total | GRANT_ACL,
12139 m_db.str, NULL, NULL, 1, 0))
12140 return true;
12141
12142 grant_stage0(thd);
12143
12144 if (m_columns.elements) // e.g. GRANT SELECT (a) ON *.*
12145 {
12146 my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
12147 MYF(0));
12148 return true;
12149 }
12150
12151 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
12152 /* Conditionally writes to binlog */
12153 if (mysql_grant(thd, m_db.str, lex->users_list, m_object_privilege,
12154 is_revoke(), false/*not proxy*/))
12155 return true;
12156
12157 return !is_revoke() && user_list_reset_mqh(thd, lex->users_list);
12158
12159 #ifdef WITH_WSREP
12160 wsrep_error_label:
12161 return true;
12162 #endif // WITH_WSREP
12163 }
12164
12165
execute(THD * thd)12166 bool Sql_cmd_grant_table::execute(THD *thd)
12167 {
12168 TABLE_LIST *table= thd->lex->first_select_lex()->table_list.first;
12169 return table ? execute_exact_table(thd, table) :
12170 execute_table_mask(thd);
12171 }
12172
12173
12174 #endif // NO_EMBEDDED_ACCESS_CHECKS
12175
12176
12177
12178 SHOW_VAR acl_statistics[] = {
12179 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12180 {"column_grants", (char*)show_column_grants, SHOW_SIMPLE_FUNC},
12181 {"database_grants", (char*)show_database_grants, SHOW_SIMPLE_FUNC},
12182 {"function_grants", (char*)&func_priv_hash.records, SHOW_ULONG},
12183 {"procedure_grants", (char*)&proc_priv_hash.records, SHOW_ULONG},
12184 {"package_spec_grants", (char*)&package_spec_priv_hash.records, SHOW_ULONG},
12185 {"package_body_grants", (char*)&package_body_priv_hash.records, SHOW_ULONG},
12186 {"proxy_users", (char*)&acl_proxy_users.elements, SHOW_UINT},
12187 {"role_grants", (char*)&acl_roles_mappings.records, SHOW_ULONG},
12188 {"roles", (char*)&acl_roles.records, SHOW_ULONG},
12189 {"table_grants", (char*)&column_priv_hash.records, SHOW_ULONG},
12190 {"users", (char*)&acl_users.elements, SHOW_UINT},
12191 #endif
12192 {NullS, NullS, SHOW_LONG},
12193 };
12194
12195 /* Check if a role is granted to a user/role. We traverse the role graph
12196 and return true if we find a match.
12197
12198 hostname == NULL means we are looking for a role as a starting point,
12199 otherwise a user.
12200 */
check_role_is_granted(const char * username,const char * hostname,const char * rolename)12201 bool check_role_is_granted(const char *username,
12202 const char *hostname,
12203 const char *rolename)
12204 {
12205 DBUG_ENTER("check_role_is_granted");
12206 bool result= false;
12207 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12208 ACL_USER_BASE *root;
12209 mysql_mutex_lock(&acl_cache->lock);
12210 if (hostname)
12211 root= find_user_exact(hostname, username);
12212 else
12213 root= find_acl_role(username);
12214
12215 LEX_CSTRING role_lex;
12216 role_lex.str= rolename;
12217 role_lex.length= strlen(rolename);
12218
12219 if (root && /* No grantee, nothing to search. */
12220 traverse_role_graph_down(root, &role_lex, check_role_is_granted_callback,
12221 NULL) == -1)
12222 {
12223 /* We have found the role during our search. */
12224 result= true;
12225 }
12226
12227 /* We haven't found the role or we had no initial grantee to start from. */
12228 mysql_mutex_unlock(&acl_cache->lock);
12229 #endif
12230 DBUG_RETURN(result);
12231 }
12232
fill_schema_enabled_roles(THD * thd,TABLE_LIST * tables,COND * cond)12233 int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond)
12234 {
12235 TABLE *table= tables->table;
12236 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12237 if (thd->security_ctx->priv_role[0])
12238 {
12239 mysql_rwlock_rdlock(&LOCK_grant);
12240 mysql_mutex_lock(&acl_cache->lock);
12241 ACL_ROLE *acl_role= find_acl_role(thd->security_ctx->priv_role);
12242 if (acl_role)
12243 traverse_role_graph_down(acl_role, table, enabled_roles_insert, NULL);
12244 mysql_mutex_unlock(&acl_cache->lock);
12245 mysql_rwlock_unlock(&LOCK_grant);
12246 if (acl_role)
12247 return 0;
12248 }
12249 #endif
12250
12251 restore_record(table, s->default_values);
12252 table->field[0]->set_null();
12253 return schema_table_store_record(table->in_use, table);
12254 }
12255
12256
12257 /*
12258 This shows all roles granted to current user
12259 and recursively all roles granted to those roles
12260 */
fill_schema_applicable_roles(THD * thd,TABLE_LIST * tables,COND * cond)12261 int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond)
12262 {
12263 int res= 0;
12264 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12265 if (initialized)
12266 {
12267 TABLE *table= tables->table;
12268 Security_context *sctx= thd->security_ctx;
12269 mysql_rwlock_rdlock(&LOCK_grant);
12270 mysql_mutex_lock(&acl_cache->lock);
12271 ACL_USER *user= find_user_exact(sctx->priv_host, sctx->priv_user);
12272 if (user)
12273 {
12274 char buff[USER_HOST_BUFF_SIZE+10];
12275 DBUG_ASSERT(user->user.length + user->hostname_length +2 < sizeof(buff));
12276 char *end= strxmov(buff, user->user.str, "@", user->host.hostname, NULL);
12277 APPLICABLE_ROLES_DATA data= { table,
12278 { user->host.hostname, user->hostname_length },
12279 { buff, (size_t)(end - buff) }, user
12280 };
12281
12282 res= traverse_role_graph_down(user, &data, 0, applicable_roles_insert);
12283 }
12284
12285 mysql_mutex_unlock(&acl_cache->lock);
12286 mysql_rwlock_unlock(&LOCK_grant);
12287 }
12288 #endif
12289
12290 return res;
12291 }
12292
12293
wild_case_compare(CHARSET_INFO * cs,const char * str,const char * wildstr)12294 int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
12295 {
12296 int flag;
12297 DBUG_ENTER("wild_case_compare");
12298 DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr));
12299 while (*wildstr)
12300 {
12301 while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
12302 {
12303 if (*wildstr == wild_prefix && wildstr[1])
12304 wildstr++;
12305 if (my_toupper(cs, *wildstr++) !=
12306 my_toupper(cs, *str++)) DBUG_RETURN(1);
12307 }
12308 if (! *wildstr ) DBUG_RETURN (*str != 0);
12309 if (*wildstr++ == wild_one)
12310 {
12311 if (! *str++) DBUG_RETURN (1); /* One char; skip */
12312 }
12313 else
12314 { /* Found '*' */
12315 if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */
12316 flag=(*wildstr != wild_many && *wildstr != wild_one);
12317 do
12318 {
12319 if (flag)
12320 {
12321 char cmp;
12322 if ((cmp= *wildstr) == wild_prefix && wildstr[1])
12323 cmp=wildstr[1];
12324 cmp=my_toupper(cs, cmp);
12325 while (*str && my_toupper(cs, *str) != cmp)
12326 str++;
12327 if (!*str) DBUG_RETURN (1);
12328 }
12329 if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
12330 } while (*str++);
12331 DBUG_RETURN(1);
12332 }
12333 }
12334 DBUG_RETURN (*str != '\0');
12335 }
12336
12337
12338 #ifndef NO_EMBEDDED_ACCESS_CHECKS
update_schema_privilege(THD * thd,TABLE * table,const char * buff,const char * db,const char * t_name,const char * column,uint col_length,const char * priv,uint priv_length,const char * is_grantable)12339 static bool update_schema_privilege(THD *thd, TABLE *table, const char *buff,
12340 const char* db, const char* t_name,
12341 const char* column, uint col_length,
12342 const char *priv, uint priv_length,
12343 const char* is_grantable)
12344 {
12345 int i= 2;
12346 CHARSET_INFO *cs= system_charset_info;
12347 restore_record(table, s->default_values);
12348 table->field[0]->store(buff, (uint) strlen(buff), cs);
12349 table->field[1]->store(STRING_WITH_LEN("def"), cs);
12350 if (db)
12351 table->field[i++]->store(db, (uint) strlen(db), cs);
12352 if (t_name)
12353 table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
12354 if (column)
12355 table->field[i++]->store(column, col_length, cs);
12356 table->field[i++]->store(priv, priv_length, cs);
12357 table->field[i]->store(is_grantable, strlen(is_grantable), cs);
12358 return schema_table_store_record(thd, table);
12359 }
12360 #endif
12361
12362
12363 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12364 class Grantee_str
12365 {
12366 char m_buff[USER_HOST_BUFF_SIZE + 6 /* 4 quotes, @, '\0' */];
12367 public:
Grantee_str(const char * user,const char * host)12368 Grantee_str(const char *user, const char *host)
12369 {
12370 DBUG_ASSERT(strlen(user) + strlen(host) + 6 < sizeof(m_buff));
12371 strxmov(m_buff, "'", user, "'@'", host, "'", NullS);
12372 }
operator const char*() const12373 operator const char *() const { return m_buff; }
12374 };
12375 #endif
12376
12377
fill_schema_user_privileges(THD * thd,TABLE_LIST * tables,COND * cond)12378 int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
12379 {
12380 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12381 int error= 0;
12382 uint counter;
12383 ACL_USER *acl_user;
12384 TABLE *table= tables->table;
12385 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
12386 NULL, NULL, 1, 1);
12387 DBUG_ENTER("fill_schema_user_privileges");
12388
12389 if (!initialized)
12390 DBUG_RETURN(0);
12391 mysql_mutex_lock(&acl_cache->lock);
12392
12393 for (counter=0 ; counter < acl_users.elements ; counter++)
12394 {
12395 const char *user,*host, *is_grantable="YES";
12396 acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
12397 user= acl_user->user.str;
12398 host= safe_str(acl_user->host.hostname);
12399
12400 if (no_global_access &&
12401 !thd->security_ctx->is_priv_user(user, host))
12402 continue;
12403
12404 privilege_t want_access(acl_user->access);
12405 if (!(want_access & GRANT_ACL))
12406 is_grantable= "NO";
12407
12408 Grantee_str grantee(user, host);
12409 if (!(want_access & ~GRANT_ACL))
12410 {
12411 if (update_schema_privilege(thd, table, grantee, 0, 0, 0, 0,
12412 STRING_WITH_LEN("USAGE"), is_grantable))
12413 {
12414 error= 1;
12415 goto err;
12416 }
12417 }
12418 else
12419 {
12420 uint priv_id;
12421 ulonglong j;
12422 privilege_t test_access(want_access & ~GRANT_ACL);
12423 for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
12424 {
12425 if (test_access & j)
12426 {
12427 if (update_schema_privilege(thd, table, grantee, 0, 0, 0, 0,
12428 command_array[priv_id],
12429 command_lengths[priv_id], is_grantable))
12430 {
12431 error= 1;
12432 goto err;
12433 }
12434 }
12435 }
12436 }
12437 }
12438 err:
12439 mysql_mutex_unlock(&acl_cache->lock);
12440
12441 DBUG_RETURN(error);
12442 #else
12443 return(0);
12444 #endif
12445 }
12446
12447
fill_schema_schema_privileges(THD * thd,TABLE_LIST * tables,COND * cond)12448 int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
12449 {
12450 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12451 int error= 0;
12452 uint counter;
12453 ACL_DB *acl_db;
12454 TABLE *table= tables->table;
12455 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
12456 NULL, NULL, 1, 1);
12457 DBUG_ENTER("fill_schema_schema_privileges");
12458
12459 if (!initialized)
12460 DBUG_RETURN(0);
12461 mysql_mutex_lock(&acl_cache->lock);
12462
12463 for (counter=0 ; counter < acl_dbs.elements() ; counter++)
12464 {
12465 const char *user, *host, *is_grantable="YES";
12466
12467 acl_db=&acl_dbs.at(counter);
12468 user= acl_db->user;
12469 host= safe_str(acl_db->host.hostname);
12470
12471 if (no_global_access &&
12472 !thd->security_ctx->is_priv_user(user, host))
12473 continue;
12474
12475 privilege_t want_access(acl_db->access);
12476 if (want_access)
12477 {
12478 if (!(want_access & GRANT_ACL))
12479 {
12480 is_grantable= "NO";
12481 }
12482 Grantee_str grantee(user, host);
12483 if (!(want_access & ~GRANT_ACL))
12484 {
12485 if (update_schema_privilege(thd, table, grantee, acl_db->db, 0, 0,
12486 0, STRING_WITH_LEN("USAGE"), is_grantable))
12487 {
12488 error= 1;
12489 goto err;
12490 }
12491 }
12492 else
12493 {
12494 int cnt;
12495 ulonglong j;
12496 privilege_t test_access(want_access & ~GRANT_ACL);
12497 for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
12498 if (test_access & j)
12499 {
12500 if (update_schema_privilege(thd, table,
12501 grantee, acl_db->db, 0, 0, 0,
12502 command_array[cnt], command_lengths[cnt],
12503 is_grantable))
12504 {
12505 error= 1;
12506 goto err;
12507 }
12508 }
12509 }
12510 }
12511 }
12512 err:
12513 mysql_mutex_unlock(&acl_cache->lock);
12514
12515 DBUG_RETURN(error);
12516 #else
12517 return (0);
12518 #endif
12519 }
12520
12521
fill_schema_table_privileges(THD * thd,TABLE_LIST * tables,COND * cond)12522 int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
12523 {
12524 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12525 int error= 0;
12526 uint index;
12527 TABLE *table= tables->table;
12528 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
12529 NULL, NULL, 1, 1);
12530 DBUG_ENTER("fill_schema_table_privileges");
12531
12532 mysql_rwlock_rdlock(&LOCK_grant);
12533
12534 for (index=0 ; index < column_priv_hash.records ; index++)
12535 {
12536 const char *user, *host, *is_grantable= "YES";
12537 GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
12538 index);
12539 user= grant_table->user;
12540 host= safe_str(grant_table->host.hostname);
12541
12542 if (no_global_access &&
12543 !thd->security_ctx->is_priv_user(user, host))
12544 continue;
12545
12546 privilege_t table_access(grant_table->privs);
12547 if (table_access)
12548 {
12549 privilege_t test_access(table_access & ~GRANT_ACL);
12550 /*
12551 We should skip 'usage' privilege on table if
12552 we have any privileges on column(s) of this table
12553 */
12554 if (!test_access && grant_table->cols)
12555 continue;
12556 if (!(table_access & GRANT_ACL))
12557 is_grantable= "NO";
12558
12559 Grantee_str grantee(user, host);
12560 if (!test_access)
12561 {
12562 if (update_schema_privilege(thd, table,
12563 grantee, grant_table->db,
12564 grant_table->tname, 0, 0,
12565 STRING_WITH_LEN("USAGE"), is_grantable))
12566 {
12567 error= 1;
12568 goto err;
12569 }
12570 }
12571 else
12572 {
12573 ulonglong j;
12574 int cnt;
12575 for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
12576 {
12577 if (test_access & j)
12578 {
12579 if (update_schema_privilege(thd, table,
12580 grantee, grant_table->db,
12581 grant_table->tname, 0, 0,
12582 command_array[cnt],
12583 command_lengths[cnt], is_grantable))
12584 {
12585 error= 1;
12586 goto err;
12587 }
12588 }
12589 }
12590 }
12591 }
12592 }
12593 err:
12594 mysql_rwlock_unlock(&LOCK_grant);
12595
12596 DBUG_RETURN(error);
12597 #else
12598 return (0);
12599 #endif
12600 }
12601
12602
fill_schema_column_privileges(THD * thd,TABLE_LIST * tables,COND * cond)12603 int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
12604 {
12605 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12606 int error= 0;
12607 uint index;
12608 TABLE *table= tables->table;
12609 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
12610 NULL, NULL, 1, 1);
12611 DBUG_ENTER("fill_schema_table_privileges");
12612
12613 mysql_rwlock_rdlock(&LOCK_grant);
12614
12615 for (index=0 ; index < column_priv_hash.records ; index++)
12616 {
12617 const char *user, *host, *is_grantable= "YES";
12618 GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
12619 index);
12620 user= grant_table->user;
12621 host= safe_str(grant_table->host.hostname);
12622
12623 if (no_global_access &&
12624 !thd->security_ctx->is_priv_user(user, host))
12625 continue;
12626
12627 privilege_t table_access(grant_table->cols);
12628 if (table_access != NO_ACL)
12629 {
12630 if (!(grant_table->privs & GRANT_ACL))
12631 is_grantable= "NO";
12632
12633 privilege_t test_access(table_access & ~GRANT_ACL);
12634 Grantee_str grantee(user, host);
12635 if (!test_access)
12636 continue;
12637 else
12638 {
12639 ulonglong j;
12640 int cnt;
12641 for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
12642 {
12643 if (test_access & j)
12644 {
12645 for (uint col_index=0 ;
12646 col_index < grant_table->hash_columns.records ;
12647 col_index++)
12648 {
12649 GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
12650 my_hash_element(&grant_table->hash_columns,col_index);
12651 if ((grant_column->rights & j) && (table_access & j))
12652 {
12653 if (update_schema_privilege(thd, table,
12654 grantee,
12655 grant_table->db,
12656 grant_table->tname,
12657 grant_column->column,
12658 grant_column->key_length,
12659 command_array[cnt],
12660 command_lengths[cnt], is_grantable))
12661 {
12662 error= 1;
12663 goto err;
12664 }
12665 }
12666 }
12667 }
12668 }
12669 }
12670 }
12671 }
12672 err:
12673 mysql_rwlock_unlock(&LOCK_grant);
12674
12675 DBUG_RETURN(error);
12676 #else
12677 return (0);
12678 #endif
12679 }
12680
12681
12682 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12683 /*
12684 fill effective privileges for table
12685
12686 SYNOPSIS
12687 fill_effective_table_privileges()
12688 thd thread handler
12689 grant grants table descriptor
12690 db db name
12691 table table name
12692 */
12693
fill_effective_table_privileges(THD * thd,GRANT_INFO * grant,const char * db,const char * table)12694 void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
12695 const char *db, const char *table)
12696 {
12697 Security_context *sctx= thd->security_ctx;
12698 DBUG_ENTER("fill_effective_table_privileges");
12699 DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
12700 sctx->priv_host, sctx->ip, sctx->priv_user, db, table));
12701 /* --skip-grants */
12702 if (!initialized)
12703 {
12704 DBUG_PRINT("info", ("skip grants"));
12705 grant->privilege= ALL_KNOWN_ACL; // everything is allowed
12706 DBUG_PRINT("info", ("privilege 0x%llx", (longlong) grant->privilege));
12707 DBUG_VOID_RETURN;
12708 }
12709
12710 /* global privileges */
12711 grant->privilege= sctx->master_access;
12712
12713 if (!thd->db.str || strcmp(db, thd->db.str))
12714 {
12715 /* db privileges */
12716 grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
12717 /* db privileges for role */
12718 if (sctx->priv_role[0])
12719 grant->privilege|= acl_get("", "", sctx->priv_role, db, 0);
12720 }
12721 else
12722 {
12723 grant->privilege|= sctx->db_access;
12724 }
12725
12726 /* table privileges */
12727 mysql_rwlock_rdlock(&LOCK_grant);
12728 if (grant->version != grant_version)
12729 {
12730 grant->grant_table_user=
12731 table_hash_search(sctx->host, sctx->ip, db,
12732 sctx->priv_user,
12733 table, 0); /* purecov: inspected */
12734 grant->grant_table_role=
12735 sctx->priv_role[0] ? table_hash_search("", "", db,
12736 sctx->priv_role,
12737 table, TRUE) : NULL;
12738 grant->version= grant_version; /* purecov: inspected */
12739 }
12740 if (grant->grant_table_user != 0)
12741 {
12742 grant->privilege|= grant->grant_table_user->privs;
12743 }
12744 if (grant->grant_table_role != 0)
12745 {
12746 grant->privilege|= grant->grant_table_role->privs;
12747 }
12748 mysql_rwlock_unlock(&LOCK_grant);
12749
12750 DBUG_PRINT("info", ("privilege 0x%llx", (longlong) grant->privilege));
12751 DBUG_VOID_RETURN;
12752 }
12753
12754 #else /* NO_EMBEDDED_ACCESS_CHECKS */
12755
12756 /****************************************************************************
12757 Dummy wrappers when we don't have any access checks
12758 ****************************************************************************/
12759
check_routine_level_acl(THD * thd,const char * db,const char * name,const Sp_handler * sph)12760 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
12761 const Sp_handler *sph)
12762 {
12763 return FALSE;
12764 }
12765
12766 #endif
12767
12768 /**
12769 Return information about user or current user.
12770
12771 @param[in] thd thread handler
12772 @param[in] user user
12773 @param[in] lock whether &acl_cache->lock mutex needs to be locked
12774
12775 @return
12776 - On success, return a valid pointer to initialized
12777 LEX_USER, which contains user information.
12778 - On error, return 0.
12779 */
12780
get_current_user(THD * thd,LEX_USER * user,bool lock)12781 LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock)
12782 {
12783 if (user->user.str == current_user.str) // current_user
12784 return create_default_definer(thd, false);
12785
12786 if (user->user.str == current_role.str) // current_role
12787 return create_default_definer(thd, true);
12788
12789 if (user->host.str == NULL) // Possibly a role
12790 {
12791 // to be reexecution friendly we have to make a copy
12792 LEX_USER *dup= (LEX_USER*) thd->memdup(user, sizeof(*user));
12793 if (!dup)
12794 return 0;
12795
12796 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12797 if (has_auth(user, thd->lex))
12798 {
12799 dup->host= host_not_specified;
12800 return dup;
12801 }
12802
12803 if (is_invalid_role_name(user->user.str))
12804 return 0;
12805
12806 if (lock)
12807 mysql_mutex_lock(&acl_cache->lock);
12808 if (find_acl_role(dup->user.str))
12809 dup->host= empty_clex_str;
12810 else
12811 dup->host= host_not_specified;
12812 if (lock)
12813 mysql_mutex_unlock(&acl_cache->lock);
12814 #endif
12815
12816 return dup;
12817 }
12818
12819 return user;
12820 }
12821
12822 struct ACL_internal_schema_registry_entry
12823 {
12824 const LEX_CSTRING *m_name;
12825 const ACL_internal_schema_access *m_access;
12826 };
12827
12828 /**
12829 Internal schema registered.
12830 Currently, this is only:
12831 - performance_schema
12832 - information_schema,
12833 This can be reused later for:
12834 - mysql
12835 */
12836 static ACL_internal_schema_registry_entry registry_array[2];
12837 static uint m_registry_array_size= 0;
12838
12839 /**
12840 Add an internal schema to the registry.
12841 @param name the schema name
12842 @param access the schema ACL specific rules
12843 */
register_schema(const LEX_CSTRING * name,const ACL_internal_schema_access * access)12844 void ACL_internal_schema_registry::register_schema
12845 (const LEX_CSTRING *name, const ACL_internal_schema_access *access)
12846 {
12847 DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
12848
12849 /* Not thread safe, and does not need to be. */
12850 registry_array[m_registry_array_size].m_name= name;
12851 registry_array[m_registry_array_size].m_access= access;
12852 m_registry_array_size++;
12853 }
12854
12855 /**
12856 Search per internal schema ACL by name.
12857 @param name a schema name
12858 @return per schema rules, or NULL
12859 */
12860 const ACL_internal_schema_access *
lookup(const char * name)12861 ACL_internal_schema_registry::lookup(const char *name)
12862 {
12863 DBUG_ASSERT(name != NULL);
12864
12865 uint i;
12866
12867 for (i= 0; i<m_registry_array_size; i++)
12868 {
12869 if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
12870 name) == 0)
12871 return registry_array[i].m_access;
12872 }
12873 return NULL;
12874 }
12875
12876 /**
12877 Get a cached internal schema access.
12878 @param grant_internal_info the cache
12879 @param schema_name the name of the internal schema
12880 */
12881 const ACL_internal_schema_access *
get_cached_schema_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name)12882 get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
12883 const char *schema_name)
12884 {
12885 if (grant_internal_info)
12886 {
12887 if (! grant_internal_info->m_schema_lookup_done)
12888 {
12889 grant_internal_info->m_schema_access=
12890 ACL_internal_schema_registry::lookup(schema_name);
12891 grant_internal_info->m_schema_lookup_done= TRUE;
12892 }
12893 return grant_internal_info->m_schema_access;
12894 }
12895 return ACL_internal_schema_registry::lookup(schema_name);
12896 }
12897
12898 /**
12899 Get a cached internal table access.
12900 @param grant_internal_info the cache
12901 @param schema_name the name of the internal schema
12902 @param table_name the name of the internal table
12903 */
12904 const ACL_internal_table_access *
get_cached_table_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name,const char * table_name)12905 get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
12906 const char *schema_name,
12907 const char *table_name)
12908 {
12909 DBUG_ASSERT(grant_internal_info);
12910 if (! grant_internal_info->m_table_lookup_done)
12911 {
12912 const ACL_internal_schema_access *schema_access;
12913 schema_access= get_cached_schema_access(grant_internal_info, schema_name);
12914 if (schema_access)
12915 grant_internal_info->m_table_access= schema_access->lookup(table_name);
12916 grant_internal_info->m_table_lookup_done= TRUE;
12917 }
12918 return grant_internal_info->m_table_access;
12919 }
12920
12921
12922 /****************************************************************************
12923 AUTHENTICATION CODE
12924 including initial connect handshake, invoking appropriate plugins,
12925 client-server plugin negotiation, COM_CHANGE_USER, and native
12926 MySQL authentication plugins.
12927 ****************************************************************************/
12928
12929 /* few defines to have less ifdef's in the code below */
12930 #ifdef EMBEDDED_LIBRARY
12931 #undef HAVE_OPENSSL
12932 #ifdef NO_EMBEDDED_ACCESS_CHECKS
12933 #define initialized 0
12934 #define check_for_max_user_connections(X,Y) 0
12935 #define get_or_create_user_conn(A,B,C,D) 0
12936 #endif
12937 #endif
12938 #ifndef HAVE_OPENSSL
12939 #define ssl_acceptor_fd 0
12940 #define sslaccept(A,B,C,D) 1
12941 #endif
12942
12943 /**
12944 The internal version of what plugins know as MYSQL_PLUGIN_VIO,
12945 basically the context of the authentication session
12946 */
12947 struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
12948 {
12949 MYSQL_SERVER_AUTH_INFO auth_info;
12950 ACL_USER *acl_user; ///< a copy, independent from acl_users array
12951 plugin_ref plugin; ///< what plugin we're under
12952 LEX_CSTRING db; ///< db name from the handshake packet
12953 /** when restarting a plugin this caches the last client reply */
12954 struct {
12955 const char *plugin;
12956 char *pkt; ///< pointer into NET::buff
12957 uint pkt_len;
12958 } cached_client_reply;
12959 /** this caches the first plugin packet for restart request on the client */
12960 struct {
12961 char *pkt;
12962 uint pkt_len;
12963 } cached_server_packet;
12964 uint curr_auth; ///< an index in acl_user->auth[]
12965 int packets_read, packets_written; ///< counters for send/received packets
12966 bool make_it_fail;
12967 /** when plugin returns a failure this tells us what really happened */
12968 enum { SUCCESS, FAILURE, RESTART } status;
12969 };
12970
12971 /**
12972 a helper function to report an access denied error in most proper places
12973 */
login_failed_error(THD * thd)12974 static void login_failed_error(THD *thd)
12975 {
12976 my_error(access_denied_error_code(thd->password), MYF(0),
12977 thd->main_security_ctx.user,
12978 thd->main_security_ctx.host_or_ip,
12979 thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12980 general_log_print(thd, COM_CONNECT,
12981 ER_THD(thd, access_denied_error_code(thd->password)),
12982 thd->main_security_ctx.user,
12983 thd->main_security_ctx.host_or_ip,
12984 thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12985 status_var_increment(thd->status_var.access_denied_errors);
12986 /*
12987 Log access denied messages to the error log when log-warnings = 2
12988 so that the overhead of the general query log is not required to track
12989 failed connections.
12990 */
12991 if (global_system_variables.log_warnings > 1)
12992 {
12993 sql_print_warning(ER_THD(thd, access_denied_error_code(thd->password)),
12994 thd->main_security_ctx.user,
12995 thd->main_security_ctx.host_or_ip,
12996 thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12997 }
12998 }
12999
13000 /**
13001 sends a server handshake initialization packet, the very first packet
13002 after the connection was established
13003
13004 Packet format:
13005
13006 Bytes Content
13007 ----- ----
13008 1 protocol version (always 10)
13009 n server version string, \0-terminated
13010 4 thread id
13011 8 first 8 bytes of the plugin provided data (scramble)
13012 1 \0 byte, terminating the first part of a scramble
13013 2 server capabilities (two lower bytes)
13014 1 server character set
13015 2 server status
13016 2 server capabilities (two upper bytes)
13017 1 length of the scramble
13018 10 reserved, always 0
13019 n rest of the plugin provided data (at least 12 bytes)
13020 1 \0 byte, terminating the second part of a scramble
13021
13022 @retval 0 ok
13023 @retval 1 error
13024 */
send_server_handshake_packet(MPVIO_EXT * mpvio,const char * data,uint data_len)13025 static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
13026 const char *data, uint data_len)
13027 {
13028 DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
13029 DBUG_ASSERT(data_len <= 255);
13030
13031 THD *thd= mpvio->auth_info.thd;
13032 char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + 1 + data_len + 64);
13033 char scramble_buf[SCRAMBLE_LENGTH];
13034 char *end= buff;
13035 DBUG_ENTER("send_server_handshake_packet");
13036
13037 *end++= protocol_version;
13038
13039 thd->client_capabilities= CLIENT_BASIC_FLAGS;
13040
13041 if (opt_using_transactions)
13042 thd->client_capabilities|= CLIENT_TRANSACTIONS;
13043
13044 thd->client_capabilities|= CAN_CLIENT_COMPRESS;
13045
13046 if (ssl_acceptor_fd)
13047 {
13048 thd->client_capabilities |= CLIENT_SSL;
13049 thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
13050 }
13051
13052 if (data_len)
13053 {
13054 mpvio->cached_server_packet.pkt= (char*)thd->memdup(data, data_len);
13055 mpvio->cached_server_packet.pkt_len= data_len;
13056 }
13057
13058 if (data_len < SCRAMBLE_LENGTH)
13059 {
13060 if (data_len)
13061 {
13062 /*
13063 the first packet *must* have at least 20 bytes of a scramble.
13064 if a plugin provided less, we pad it to 20 with zeros
13065 */
13066 memcpy(scramble_buf, data, data_len);
13067 bzero(scramble_buf + data_len, SCRAMBLE_LENGTH - data_len);
13068 data= scramble_buf;
13069 }
13070 else
13071 {
13072 /*
13073 if the default plugin does not provide the data for the scramble at
13074 all, we generate a scramble internally anyway, just in case the
13075 user account (that will be known only later) uses a
13076 native_password_plugin (which needs a scramble). If we don't send a
13077 scramble now - wasting 20 bytes in the packet -
13078 native_password_plugin will have to send it in a separate packet,
13079 adding one more round trip.
13080 */
13081 thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
13082 data= thd->scramble;
13083 }
13084 data_len= SCRAMBLE_LENGTH;
13085 }
13086
13087 /* When server version is specified in config file, don't include
13088 the replication hack prefix. */
13089 if (using_custom_server_version)
13090 end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
13091 else
13092 end= strxnmov(end, SERVER_VERSION_LENGTH, RPL_VERSION_HACK, server_version, NullS) + 1;
13093
13094 int4store((uchar*) end, mpvio->auth_info.thd->thread_id);
13095 end+= 4;
13096
13097 /*
13098 Old clients does not understand long scrambles, but can ignore packet
13099 tail: that's why first part of the scramble is placed here, and second
13100 part at the end of packet.
13101 */
13102 end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
13103 end+= SCRAMBLE_LENGTH_323;
13104 *end++= 0;
13105
13106 int2store(end, thd->client_capabilities);
13107 /* write server characteristics: up to 16 bytes allowed */
13108 end[2]= (char) default_charset_info->number;
13109 int2store(end+3, mpvio->auth_info.thd->server_status);
13110 int2store(end+5, thd->client_capabilities >> 16);
13111 end[7]= data_len;
13112 DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
13113 DBUG_EXECUTE_IF("increase_srv_handshake_scramble_len", end[7]= 50;);
13114 bzero(end + 8, 6);
13115 int4store(end + 14, thd->client_capabilities >> 32);
13116 end+= 18;
13117 /* write scramble tail */
13118 end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
13119 data_len - SCRAMBLE_LENGTH_323);
13120 end+= data_len - SCRAMBLE_LENGTH_323;
13121 end= strmake(end, plugin_name(mpvio->plugin)->str,
13122 plugin_name(mpvio->plugin)->length);
13123
13124 int res= my_net_write(&mpvio->auth_info.thd->net, (uchar*) buff,
13125 (size_t) (end - buff + 1)) ||
13126 net_flush(&mpvio->auth_info.thd->net);
13127 my_afree(buff);
13128 DBUG_RETURN (res);
13129 }
13130
secure_auth(THD * thd)13131 static bool secure_auth(THD *thd)
13132 {
13133 if (!opt_secure_auth)
13134 return 0;
13135
13136 /*
13137 If the server is running in secure auth mode, short scrambles are
13138 forbidden. Extra juggling to report the same error as the old code.
13139 */
13140 if (thd->client_capabilities & CLIENT_PROTOCOL_41)
13141 {
13142 my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
13143 thd->security_ctx->user,
13144 thd->security_ctx->host_or_ip);
13145 general_log_print(thd, COM_CONNECT,
13146 ER_THD(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE),
13147 thd->security_ctx->user,
13148 thd->security_ctx->host_or_ip);
13149 }
13150 else
13151 {
13152 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
13153 general_log_print(thd, COM_CONNECT,
13154 ER_THD(thd, ER_NOT_SUPPORTED_AUTH_MODE));
13155 }
13156 return 1;
13157 }
13158
13159 /**
13160 sends a "change plugin" packet, requesting a client to restart authentication
13161 using a different authentication plugin
13162
13163 Packet format:
13164
13165 Bytes Content
13166 ----- ----
13167 1 byte with the value 254
13168 n client plugin to use, \0-terminated
13169 n plugin provided data
13170
13171 In a special case of switching from native_password_plugin to
13172 old_password_plugin, the packet contains only one - the first - byte,
13173 plugin name is omitted, plugin data aren't needed as the scramble was
13174 already sent. This one-byte packet is identical to the "use the short
13175 scramble" packet in the protocol before plugins were introduced.
13176
13177 @retval 0 ok
13178 @retval 1 error
13179 */
send_plugin_request_packet(MPVIO_EXT * mpvio,const uchar * data,uint data_len)13180 static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
13181 const uchar *data, uint data_len)
13182 {
13183 NET *net= &mpvio->auth_info.thd->net;
13184 static uchar switch_plugin_request_buf[]= { 254 };
13185 DBUG_ENTER("send_plugin_request_packet");
13186
13187 const char *client_auth_plugin=
13188 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
13189
13190 DBUG_EXECUTE_IF("auth_disconnect", { DBUG_RETURN(1); });
13191 DBUG_EXECUTE_IF("auth_invalid_plugin", client_auth_plugin="foo/bar"; );
13192 DBUG_ASSERT(client_auth_plugin);
13193
13194 /*
13195 we send an old "short 4.0 scramble request", if we need to request a
13196 client to use 4.0 auth plugin (short scramble) and the scramble was
13197 already sent to the client
13198
13199 below, cached_client_reply.plugin is the plugin name that client has used,
13200 client_auth_plugin is derived from mysql.user table, for the given
13201 user account, it's the plugin that the client need to use to login.
13202 */
13203 bool switch_from_long_to_short_scramble=
13204 client_auth_plugin == old_password_plugin_name.str &&
13205 my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
13206 native_password_plugin_name.str) == 0;
13207
13208 if (switch_from_long_to_short_scramble)
13209 DBUG_RETURN (secure_auth(mpvio->auth_info.thd) ||
13210 my_net_write(net, switch_plugin_request_buf, 1) ||
13211 net_flush(net));
13212
13213 /*
13214 We never request a client to switch from a short to long scramble.
13215 Plugin-aware clients can do that, but traditionally it meant to
13216 ask an old 4.0 client to use the new 4.1 authentication protocol.
13217 */
13218 bool switch_from_short_to_long_scramble=
13219 client_auth_plugin == native_password_plugin_name.str &&
13220 my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
13221 old_password_plugin_name.str) == 0;
13222
13223 if (switch_from_short_to_long_scramble)
13224 {
13225 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
13226 general_log_print(mpvio->auth_info.thd, COM_CONNECT,
13227 ER_THD(mpvio->auth_info.thd, ER_NOT_SUPPORTED_AUTH_MODE));
13228 DBUG_RETURN (1);
13229 }
13230
13231 DBUG_PRINT("info", ("requesting client to use the %s plugin",
13232 client_auth_plugin));
13233 DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
13234 (uchar*) client_auth_plugin,
13235 strlen(client_auth_plugin) + 1,
13236 (uchar*) data, data_len));
13237 }
13238
13239 #ifndef NO_EMBEDDED_ACCESS_CHECKS
13240
13241 /**
13242 Safeguard to avoid blocking the root, when max_password_errors
13243 limit is reached.
13244
13245 Currently, we allow password errors for superuser on localhost.
13246
13247 @return true, if password errors should be ignored, and user should not be locked.
13248 */
ignore_max_password_errors(const ACL_USER * acl_user)13249 static bool ignore_max_password_errors(const ACL_USER *acl_user)
13250 {
13251 const char *host= acl_user->host.hostname;
13252 return (acl_user->access & PRIV_IGNORE_MAX_PASSWORD_ERRORS)
13253 && (!strcasecmp(host, "localhost") ||
13254 !strcmp(host, "127.0.0.1") ||
13255 !strcmp(host, "::1"));
13256 }
13257 /**
13258 Finds acl entry in user database for authentication purposes.
13259
13260 Finds a user and copies it into mpvio. Creates a fake user
13261 if no matching user account is found.
13262
13263 @retval 0 found
13264 @retval 1 error
13265 */
find_mpvio_user(MPVIO_EXT * mpvio)13266 static bool find_mpvio_user(MPVIO_EXT *mpvio)
13267 {
13268 Security_context *sctx= mpvio->auth_info.thd->security_ctx;
13269 DBUG_ENTER("find_mpvio_user");
13270 DBUG_ASSERT(mpvio->acl_user == 0);
13271
13272 mysql_mutex_lock(&acl_cache->lock);
13273
13274 ACL_USER *user= find_user_or_anon(sctx->host, sctx->user, sctx->ip);
13275
13276 if (user)
13277 mpvio->acl_user= user->copy(mpvio->auth_info.thd->mem_root);
13278
13279 mysql_mutex_unlock(&acl_cache->lock);
13280
13281 if (!mpvio->acl_user)
13282 {
13283 /*
13284 A matching user was not found. Fake it. Take any user, make the
13285 authentication fail later.
13286 This way we get a realistically looking failure, with occasional
13287 "change auth plugin" requests even for nonexistent users. The ratio
13288 of "change auth plugin" request will be the same for real and
13289 nonexistent users.
13290 Note, that we cannot pick any user at random, it must always be
13291 the same user account for the incoming sctx->user name.
13292 */
13293 ulong nr1=1, nr2=4;
13294 CHARSET_INFO *cs= &my_charset_latin1;
13295 cs->hash_sort((uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2);
13296
13297 mysql_mutex_lock(&acl_cache->lock);
13298 if (!acl_users.elements)
13299 {
13300 mysql_mutex_unlock(&acl_cache->lock);
13301 login_failed_error(mpvio->auth_info.thd);
13302 DBUG_RETURN(1);
13303 }
13304 uint i= nr1 % acl_users.elements;
13305 ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
13306 mpvio->acl_user= acl_user_tmp->copy(mpvio->auth_info.thd->mem_root);
13307 mysql_mutex_unlock(&acl_cache->lock);
13308
13309 mpvio->make_it_fail= true;
13310 }
13311
13312 if (mpvio->acl_user->password_errors >= max_password_errors &&
13313 !ignore_max_password_errors(mpvio->acl_user))
13314 {
13315 my_error(ER_USER_IS_BLOCKED, MYF(0));
13316 general_log_print(mpvio->auth_info.thd, COM_CONNECT,
13317 ER_THD(mpvio->auth_info.thd, ER_USER_IS_BLOCKED));
13318 DBUG_RETURN(1);
13319 }
13320
13321 /* user account requires non-default plugin and the client is too old */
13322 if (mpvio->acl_user->auth->plugin.str != native_password_plugin_name.str &&
13323 mpvio->acl_user->auth->plugin.str != old_password_plugin_name.str &&
13324 !(mpvio->auth_info.thd->client_capabilities & CLIENT_PLUGIN_AUTH))
13325 {
13326 DBUG_ASSERT(my_strcasecmp(system_charset_info,
13327 mpvio->acl_user->auth->plugin.str, native_password_plugin_name.str));
13328 DBUG_ASSERT(my_strcasecmp(system_charset_info,
13329 mpvio->acl_user->auth->plugin.str, old_password_plugin_name.str));
13330 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
13331 general_log_print(mpvio->auth_info.thd, COM_CONNECT,
13332 ER_THD(mpvio->auth_info.thd, ER_NOT_SUPPORTED_AUTH_MODE));
13333 DBUG_RETURN (1);
13334 }
13335 DBUG_RETURN(0);
13336 }
13337
13338 static bool
read_client_connect_attrs(char ** ptr,char * end,CHARSET_INFO * from_cs)13339 read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs)
13340 {
13341 ulonglong length;
13342 char *ptr_save= *ptr;
13343
13344 /* not enough bytes to hold the length */
13345 if (ptr_save >= end)
13346 return true;
13347
13348 length= safe_net_field_length_ll((uchar **) ptr, end - ptr_save);
13349
13350 /* cannot even read the length */
13351 if (*ptr == NULL)
13352 return true;
13353
13354 /* length says there're more data than can fit into the packet */
13355 if (*ptr + length > end)
13356 return true;
13357
13358 /* impose an artificial length limit of 64k */
13359 if (length > 65535)
13360 return true;
13361
13362 if (PSI_CALL_set_thread_connect_attrs(*ptr, (uint)length, from_cs) &&
13363 current_thd->variables.log_warnings)
13364 sql_print_warning("Connection attributes of length %llu were truncated",
13365 length);
13366 return false;
13367 }
13368
13369 #endif
13370
13371 /* the packet format is described in send_change_user_packet() */
parse_com_change_user_packet(MPVIO_EXT * mpvio,uint packet_length)13372 static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
13373 {
13374 THD *thd= mpvio->auth_info.thd;
13375 NET *net= &thd->net;
13376 Security_context *sctx= thd->security_ctx;
13377
13378 char *user= (char*) net->read_pos;
13379 char *end= user + packet_length;
13380 /* Safe because there is always a trailing \0 at the end of the packet */
13381 char *passwd= strend(user) + 1;
13382 uint user_len= (uint)(passwd - user - 1);
13383 char *db= passwd;
13384 char db_buff[SAFE_NAME_LEN + 1]; // buffer to store db in utf8
13385 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
13386 uint dummy_errors;
13387 DBUG_ENTER ("parse_com_change_user_packet");
13388
13389 if (passwd >= end)
13390 {
13391 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
13392 MYF(0));
13393 DBUG_RETURN (1);
13394 }
13395
13396 /*
13397 Old clients send null-terminated string as password; new clients send
13398 the size (1 byte) + string (not null-terminated). Hence in case of empty
13399 password both send '\0'.
13400
13401 This strlen() can't be easily deleted without changing protocol.
13402
13403 Cast *passwd to an unsigned char, so that it doesn't extend the sign for
13404 *passwd > 127 and become 2**32-127+ after casting to uint.
13405 */
13406 uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
13407 (uchar) (*passwd++) : (uint)strlen(passwd));
13408
13409 db+= passwd_len + 1;
13410 /*
13411 Database name is always NUL-terminated, so in case of empty database
13412 the packet must contain at least the trailing '\0'.
13413 */
13414 if (db >= end)
13415 {
13416 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
13417 MYF(0));
13418 DBUG_RETURN (1);
13419 }
13420
13421 size_t db_len= strlen(db);
13422
13423 char *next_field= db + db_len + 1;
13424
13425 if (next_field + 1 < end)
13426 {
13427 if (thd_init_client_charset(thd, uint2korr(next_field)))
13428 DBUG_RETURN(1);
13429 next_field+= 2;
13430 }
13431
13432 /* Convert database and user names to utf8 */
13433 db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
13434 db, db_len, thd->charset(), &dummy_errors);
13435
13436 user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
13437 system_charset_info, user, user_len,
13438 thd->charset(), &dummy_errors);
13439
13440 if (!(sctx->user= my_strndup(key_memory_MPVIO_EXT_auth_info, user_buff,
13441 user_len, MYF(MY_WME))))
13442 DBUG_RETURN(1);
13443
13444 /* Clear variables that are allocated */
13445 thd->user_connect= 0;
13446 strmake_buf(sctx->priv_user, sctx->user);
13447
13448 if (thd->make_lex_string(&mpvio->db, db_buff, db_len) == 0)
13449 DBUG_RETURN(1); /* The error is set by make_lex_string(). */
13450
13451 /*
13452 Clear thd->db as it points to something, that will be freed when
13453 connection is closed. We don't want to accidentally free a wrong
13454 pointer if connect failed.
13455 */
13456 thd->reset_db(&null_clex_str);
13457
13458 if (!initialized)
13459 {
13460 // if mysqld's been started with --skip-grant-tables option
13461 mpvio->status= MPVIO_EXT::SUCCESS;
13462 DBUG_RETURN(0);
13463 }
13464
13465 #ifndef NO_EMBEDDED_ACCESS_CHECKS
13466 thd->password= passwd_len > 0;
13467 if (find_mpvio_user(mpvio))
13468 DBUG_RETURN(1);
13469
13470 const char *client_plugin;
13471 if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
13472 {
13473 if (next_field >= end)
13474 {
13475 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
13476 MYF(0));
13477 DBUG_RETURN(1);
13478 }
13479 client_plugin= next_field;
13480 next_field+= strlen(next_field) + 1;
13481 }
13482 else
13483 {
13484 if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
13485 client_plugin= native_password_plugin_name.str;
13486 else
13487 {
13488 /*
13489 Normally old clients use old_password_plugin, but for
13490 a passwordless accounts we use native_password_plugin.
13491 See guess_auth_plugin().
13492 */
13493 client_plugin= passwd_len ? old_password_plugin_name.str
13494 : native_password_plugin_name.str;
13495 }
13496 }
13497
13498 if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
13499 read_client_connect_attrs(&next_field, end, thd->charset()))
13500 {
13501 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
13502 MYF(0));
13503 DBUG_RETURN(1);
13504 }
13505
13506 DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
13507 /*
13508 Remember the data part of the packet, to present it to plugin in
13509 read_packet()
13510 */
13511 mpvio->cached_client_reply.pkt= passwd;
13512 mpvio->cached_client_reply.pkt_len= passwd_len;
13513 mpvio->cached_client_reply.plugin= client_plugin;
13514 mpvio->status= MPVIO_EXT::RESTART;
13515 #endif
13516
13517 DBUG_RETURN (0);
13518 }
13519
13520
13521 /* the packet format is described in send_client_reply_packet() */
parse_client_handshake_packet(MPVIO_EXT * mpvio,uchar ** buff,ulong pkt_len)13522 static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
13523 uchar **buff, ulong pkt_len)
13524 {
13525 #ifndef EMBEDDED_LIBRARY
13526 THD *thd= mpvio->auth_info.thd;
13527 NET *net= &thd->net;
13528 char *end;
13529 DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
13530
13531 if (pkt_len < MIN_HANDSHAKE_SIZE)
13532 return packet_error;
13533
13534 /*
13535 Protocol buffer is guaranteed to always end with \0. (see my_net_read())
13536 As the code below depends on this, lets check that.
13537 */
13538 DBUG_ASSERT(net->read_pos[pkt_len] == 0);
13539
13540 ulonglong client_capabilities= uint2korr(net->read_pos);
13541 compile_time_assert(sizeof(client_capabilities) >= 8);
13542 if (client_capabilities & CLIENT_PROTOCOL_41)
13543 {
13544 if (pkt_len < 32)
13545 return packet_error;
13546 client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
13547 if (!(client_capabilities & CLIENT_MYSQL))
13548 {
13549 // it is client with mariadb extensions
13550 ulonglong ext_client_capabilities=
13551 (((ulonglong)uint4korr(net->read_pos + 28)) << 32);
13552 client_capabilities|= ext_client_capabilities;
13553 }
13554 }
13555
13556 /* Disable those bits which are not supported by the client. */
13557 compile_time_assert(sizeof(thd->client_capabilities) >= 8);
13558 thd->client_capabilities&= client_capabilities;
13559
13560 DBUG_PRINT("info", ("client capabilities: %llu", thd->client_capabilities));
13561 if (thd->client_capabilities & CLIENT_SSL)
13562 {
13563 unsigned long errptr __attribute__((unused));
13564
13565 /* Do the SSL layering. */
13566 if (!ssl_acceptor_fd)
13567 return packet_error;
13568
13569 DBUG_PRINT("info", ("IO layer change in progress..."));
13570 mysql_rwlock_rdlock(&LOCK_ssl_refresh);
13571 int ssl_ret = sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr);
13572 mysql_rwlock_unlock(&LOCK_ssl_refresh);
13573 ssl_acceptor_stats_update(ssl_ret);
13574
13575 if(ssl_ret)
13576 {
13577 DBUG_PRINT("error", ("Failed to accept new SSL connection"));
13578 return packet_error;
13579 }
13580
13581 DBUG_PRINT("info", ("Reading user information over SSL layer"));
13582 pkt_len= my_net_read(net);
13583 if (unlikely(pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE))
13584 {
13585 DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
13586 pkt_len));
13587 return packet_error;
13588 }
13589 }
13590
13591 if (client_capabilities & CLIENT_PROTOCOL_41)
13592 {
13593 thd->max_client_packet_length= uint4korr(net->read_pos+4);
13594 DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
13595 if (thd_init_client_charset(thd, (uint) net->read_pos[8]))
13596 return packet_error;
13597 end= (char*) net->read_pos+32;
13598 }
13599 else
13600 {
13601 if (pkt_len < 5)
13602 return packet_error;
13603 thd->max_client_packet_length= uint3korr(net->read_pos+2);
13604 end= (char*) net->read_pos+5;
13605 }
13606
13607 if (end >= (char*) net->read_pos+ pkt_len +2)
13608 return packet_error;
13609
13610 if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
13611 thd->variables.sql_mode|= MODE_IGNORE_SPACE;
13612 if (thd->client_capabilities & CLIENT_INTERACTIVE)
13613 thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
13614
13615 if (end >= (char*) net->read_pos+ pkt_len +2)
13616 return packet_error;
13617
13618 if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
13619 opt_using_transactions)
13620 net->return_status= &thd->server_status;
13621
13622 char *user= end;
13623 char *passwd= strend(user)+1;
13624 size_t user_len= (size_t)(passwd - user - 1), db_len;
13625 char *db= passwd;
13626 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
13627 uint dummy_errors;
13628
13629 /*
13630 Old clients send null-terminated string as password; new clients send
13631 the size (1 byte) + string (not null-terminated). Hence in case of empty
13632 password both send '\0'.
13633
13634 This strlen() can't be easily deleted without changing protocol.
13635
13636 Cast *passwd to an unsigned char, so that it doesn't extend the sign for
13637 *passwd > 127 and become 2**32-127+ after casting to uint.
13638 */
13639 ulonglong len;
13640 size_t passwd_len;
13641
13642 if (!(thd->client_capabilities & CLIENT_SECURE_CONNECTION))
13643 len= strlen(passwd);
13644 else if (!(thd->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA))
13645 len= (uchar)(*passwd++);
13646 else
13647 {
13648 len= safe_net_field_length_ll((uchar**)&passwd,
13649 net->read_pos + pkt_len - (uchar*)passwd);
13650 if (len > pkt_len)
13651 return packet_error;
13652 }
13653
13654 passwd_len= (size_t)len;
13655 db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
13656 db + passwd_len + 1 : 0;
13657
13658 if (passwd == NULL ||
13659 passwd + passwd_len + MY_TEST(db) > (char*) net->read_pos + pkt_len)
13660 return packet_error;
13661
13662 /* strlen() can't be easily deleted without changing protocol */
13663 db_len= safe_strlen(db);
13664
13665 char *next_field;
13666 const char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0);
13667
13668 /*
13669 Since 4.1 all database names are stored in utf8
13670 The cast is ok as copy_with_error will create a new area for db
13671 */
13672 if (unlikely(thd->copy_with_error(system_charset_info,
13673 (LEX_STRING*) &mpvio->db,
13674 thd->charset(), db, db_len)))
13675 return packet_error;
13676
13677 user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
13678 system_charset_info, user, user_len,
13679 thd->charset(), &dummy_errors);
13680 user= user_buff;
13681
13682 /* If username starts and ends in "'", chop them off */
13683 if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
13684 {
13685 user++;
13686 user_len-= 2;
13687 }
13688
13689 /*
13690 Clip username to allowed length in characters (not bytes). This is
13691 mostly for backward compatibility (to truncate long usernames, as
13692 old 5.1 did)
13693 */
13694 user_len= Well_formed_prefix(system_charset_info, user, user_len,
13695 username_char_length).length();
13696 user[user_len]= '\0';
13697
13698 Security_context *sctx= thd->security_ctx;
13699
13700 my_free(const_cast<char*>(sctx->user));
13701 if (!(sctx->user= my_strndup(key_memory_MPVIO_EXT_auth_info, user, user_len, MYF(MY_WME))))
13702 return packet_error; /* The error is set by my_strdup(). */
13703
13704
13705 /*
13706 Clear thd->db as it points to something, that will be freed when
13707 connection is closed. We don't want to accidentally free a wrong
13708 pointer if connect failed.
13709 */
13710 thd->reset_db(&null_clex_str);
13711
13712 if (!initialized)
13713 {
13714 // if mysqld's been started with --skip-grant-tables option
13715 mpvio->status= MPVIO_EXT::SUCCESS;
13716 return packet_error;
13717 }
13718
13719 thd->password= passwd_len > 0;
13720 if (find_mpvio_user(mpvio))
13721 return packet_error;
13722
13723 if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) &&
13724 (client_plugin < (char *)net->read_pos + pkt_len))
13725 {
13726 next_field+= strlen(next_field) + 1;
13727 }
13728 else
13729 {
13730 /* Some clients lie. Sad, but true */
13731 thd->client_capabilities &= ~CLIENT_PLUGIN_AUTH;
13732
13733 if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
13734 client_plugin= native_password_plugin_name.str;
13735 else
13736 {
13737 /*
13738 Normally old clients use old_password_plugin, but for
13739 a passwordless accounts we use native_password_plugin.
13740 See guess_auth_plugin().
13741 */
13742 client_plugin= passwd_len ? old_password_plugin_name.str
13743 : native_password_plugin_name.str;
13744 }
13745 }
13746
13747 if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
13748 read_client_connect_attrs(&next_field, ((char *)net->read_pos) + pkt_len,
13749 mpvio->auth_info.thd->charset()))
13750 return packet_error;
13751
13752 /*
13753 if the acl_user needs a different plugin to authenticate
13754 (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
13755 we need to restart the authentication in the server.
13756 But perhaps the client has already used the correct plugin -
13757 in that case the authentication on the client may not need to be
13758 restarted and a server auth plugin will read the data that the client
13759 has just send. Cache them to return in the next server_mpvio_read_packet().
13760 */
13761 if (!lex_string_eq(&mpvio->acl_user->auth->plugin, plugin_name(mpvio->plugin)))
13762 {
13763 mpvio->cached_client_reply.pkt= passwd;
13764 mpvio->cached_client_reply.pkt_len= (uint)passwd_len;
13765 mpvio->cached_client_reply.plugin= client_plugin;
13766 mpvio->status= MPVIO_EXT::RESTART;
13767 return packet_error;
13768 }
13769
13770 /*
13771 ok, we don't need to restart the authentication on the server.
13772 but if the client used the wrong plugin, we need to restart
13773 the authentication on the client. Do it here, the server plugin
13774 doesn't need to know.
13775 */
13776 const char *client_auth_plugin=
13777 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
13778
13779 if (client_auth_plugin &&
13780 my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
13781 {
13782 mpvio->cached_client_reply.plugin= client_plugin;
13783 if (send_plugin_request_packet(mpvio,
13784 (uchar*) mpvio->cached_server_packet.pkt,
13785 mpvio->cached_server_packet.pkt_len))
13786 return packet_error;
13787
13788 passwd_len= my_net_read(&thd->net);
13789 passwd= (char*)thd->net.read_pos;
13790 }
13791
13792 *buff= (uchar*) passwd;
13793 return (ulong)passwd_len;
13794 #else
13795 return 0;
13796 #endif
13797 }
13798
13799
13800 /**
13801 vio->write_packet() callback method for server authentication plugins
13802
13803 This function is called by a server authentication plugin, when it wants
13804 to send data to the client.
13805
13806 It transparently wraps the data into a handshake packet,
13807 and handles plugin negotiation with the client. If necessary,
13808 it escapes the plugin data, if it starts with a mysql protocol packet byte.
13809 */
server_mpvio_write_packet(MYSQL_PLUGIN_VIO * param,const uchar * packet,int packet_len)13810 static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
13811 const uchar *packet, int packet_len)
13812 {
13813 MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
13814 int res;
13815 DBUG_ENTER("server_mpvio_write_packet");
13816
13817 /* reset cached_client_reply */
13818 mpvio->cached_client_reply.pkt= 0;
13819
13820 /* for the 1st packet we wrap plugin data into the handshake packet */
13821 if (mpvio->packets_written == 0)
13822 res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
13823 else if (mpvio->status == MPVIO_EXT::RESTART)
13824 res= send_plugin_request_packet(mpvio, packet, packet_len);
13825 else if (packet_len > 0 && (*packet == 1 || *packet == 255 || *packet == 254))
13826 {
13827 /*
13828 we cannot allow plugin data packet to start from 255 or 254 -
13829 as the client will treat it as an error or "change plugin" packet.
13830 We'll escape these bytes with \1. Consequently, we
13831 have to escape \1 byte too.
13832 */
13833 res= net_write_command(&mpvio->auth_info.thd->net, 1, (uchar*)"", 0,
13834 packet, packet_len);
13835 }
13836 else
13837 {
13838 res= my_net_write(&mpvio->auth_info.thd->net, packet, packet_len) ||
13839 net_flush(&mpvio->auth_info.thd->net);
13840 }
13841 mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
13842 mpvio->packets_written++;
13843 DBUG_RETURN(res);
13844 }
13845
13846 /**
13847 vio->read_packet() callback method for server authentication plugins
13848
13849 This function is called by a server authentication plugin, when it wants
13850 to read data from the client.
13851
13852 It transparently extracts the client plugin data, if embedded into
13853 a client authentication handshake packet, and handles plugin negotiation
13854 with the client, if necessary.
13855 */
server_mpvio_read_packet(MYSQL_PLUGIN_VIO * param,uchar ** buf)13856 static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
13857 {
13858 MPVIO_EXT * const mpvio= (MPVIO_EXT *) param;
13859 MYSQL_SERVER_AUTH_INFO * const ai= &mpvio->auth_info;
13860 ulong pkt_len;
13861 DBUG_ENTER("server_mpvio_read_packet");
13862 if (mpvio->status == MPVIO_EXT::RESTART)
13863 {
13864 const char *client_auth_plugin=
13865 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
13866 if (client_auth_plugin == 0)
13867 {
13868 mpvio->status= MPVIO_EXT::FAILURE;
13869 pkt_len= 0;
13870 *buf= 0;
13871 goto done;
13872 }
13873
13874 if (mpvio->cached_client_reply.pkt)
13875 {
13876 DBUG_ASSERT(mpvio->packets_read > 0);
13877 /*
13878 if the have the data cached from the last server_mpvio_read_packet
13879 (which can be the case if it's a restarted authentication)
13880 and a client has used the correct plugin, then we can return the
13881 cached data straight away and avoid one round trip.
13882 */
13883 if (my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
13884 client_auth_plugin) == 0)
13885 {
13886 mpvio->status= MPVIO_EXT::FAILURE;
13887 pkt_len= mpvio->cached_client_reply.pkt_len;
13888 *buf= (uchar*) mpvio->cached_client_reply.pkt;
13889 mpvio->packets_read++;
13890 goto done;
13891 }
13892 }
13893
13894 /*
13895 plugin wants to read the data without sending anything first.
13896 send an empty packet to force a server handshake packet to be sent
13897 */
13898 if (server_mpvio_write_packet(mpvio, 0, 0))
13899 pkt_len= packet_error;
13900 else
13901 pkt_len= my_net_read(&ai->thd->net);
13902 }
13903 else
13904 pkt_len= my_net_read(&ai->thd->net);
13905
13906 if (unlikely(pkt_len == packet_error))
13907 goto err;
13908
13909 mpvio->packets_read++;
13910
13911 /*
13912 the 1st packet has the plugin data wrapped into the client authentication
13913 handshake packet
13914 */
13915 if (mpvio->packets_read == 1)
13916 {
13917 pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
13918 if (unlikely(pkt_len == packet_error))
13919 goto err;
13920 }
13921 else
13922 *buf= ai->thd->net.read_pos;
13923
13924 done:
13925 if (set_user_salt_if_needed(mpvio->acl_user, mpvio->curr_auth, mpvio->plugin))
13926 {
13927 ai->thd->clear_error(); // authenticating user should not see these errors
13928 my_error(ER_ACCESS_DENIED_ERROR, MYF(0), ai->thd->security_ctx->user,
13929 ai->thd->security_ctx->host_or_ip, ER_THD(ai->thd, ER_YES));
13930 goto err;
13931 }
13932
13933 ai->user_name= ai->thd->security_ctx->user;
13934 ai->user_name_length= (uint) strlen(ai->user_name);
13935 ai->auth_string= mpvio->acl_user->auth[mpvio->curr_auth].salt.str;
13936 ai->auth_string_length= (ulong) mpvio->acl_user->auth[mpvio->curr_auth].salt.length;
13937 strmake_buf(ai->authenticated_as, mpvio->acl_user->user.str);
13938
13939 DBUG_RETURN((int)pkt_len);
13940
13941 err:
13942 if (mpvio->status == MPVIO_EXT::FAILURE)
13943 {
13944 if (!ai->thd->is_error())
13945 my_error(ER_HANDSHAKE_ERROR, MYF(0));
13946 }
13947 DBUG_RETURN(-1);
13948 }
13949
13950 /**
13951 fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
13952 connection
13953 */
server_mpvio_info(MYSQL_PLUGIN_VIO * vio,MYSQL_PLUGIN_VIO_INFO * info)13954 static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
13955 MYSQL_PLUGIN_VIO_INFO *info)
13956 {
13957 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
13958 mpvio_info(mpvio->auth_info.thd->net.vio, info);
13959 }
13960
acl_check_ssl(THD * thd,const ACL_USER * acl_user)13961 static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
13962 {
13963 Vio *vio= thd->net.vio;
13964 #ifdef HAVE_OPENSSL
13965 SSL *ssl= (SSL *) vio->ssl_arg;
13966 X509 *cert;
13967 #endif
13968
13969 /*
13970 At this point we know that user is allowed to connect
13971 from given host by given username/password pair. Now
13972 we check if SSL is required, if user is using SSL and
13973 if X509 certificate attributes are OK
13974 */
13975 switch (acl_user->ssl_type) {
13976 case SSL_TYPE_NOT_SPECIFIED: // Impossible
13977 case SSL_TYPE_NONE: // SSL is not required
13978 if (opt_require_secure_transport)
13979 {
13980 enum enum_vio_type type= vio_type(vio);
13981 #ifdef HAVE_OPENSSL
13982 return type != VIO_TYPE_SSL &&
13983 #ifndef _WIN32
13984 type != VIO_TYPE_SOCKET;
13985 #else
13986 type != VIO_TYPE_NAMEDPIPE;
13987 #endif
13988 #else
13989 #ifndef _WIN32
13990 return type != VIO_TYPE_SOCKET;
13991 #else
13992 return type != VIO_TYPE_NAMEDPIPE;
13993 #endif
13994 #endif
13995 }
13996 return 0;
13997 #ifdef HAVE_OPENSSL
13998 case SSL_TYPE_ANY: // Any kind of SSL is ok
13999 return vio_type(vio) != VIO_TYPE_SSL;
14000 case SSL_TYPE_X509: /* Client should have any valid certificate. */
14001 /*
14002 Connections with non-valid certificates are dropped already
14003 in sslaccept() anyway, so we do not check validity here.
14004
14005 We need to check for absence of SSL because without SSL
14006 we should reject connection.
14007 */
14008 if (vio_type(vio) == VIO_TYPE_SSL &&
14009 SSL_get_verify_result(ssl) == X509_V_OK &&
14010 (cert= SSL_get_peer_certificate(ssl)))
14011 {
14012 X509_free(cert);
14013 return 0;
14014 }
14015 return 1;
14016 case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
14017 /* If a cipher name is specified, we compare it to actual cipher in use. */
14018 if (vio_type(vio) != VIO_TYPE_SSL ||
14019 SSL_get_verify_result(ssl) != X509_V_OK)
14020 return 1;
14021 if (acl_user->ssl_cipher)
14022 {
14023 const char *ssl_cipher= SSL_get_cipher(ssl);
14024 DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
14025 acl_user->ssl_cipher, ssl_cipher));
14026 if (strcmp(acl_user->ssl_cipher, ssl_cipher))
14027 {
14028 if (global_system_variables.log_warnings)
14029 sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
14030 acl_user->ssl_cipher, ssl_cipher);
14031 return 1;
14032 }
14033 }
14034 if (!acl_user->x509_issuer[0] && !acl_user->x509_subject[0])
14035 return 0; // all done
14036
14037 /* Prepare certificate (if exists) */
14038 if (!(cert= SSL_get_peer_certificate(ssl)))
14039 return 1;
14040 /* If X509 issuer is specified, we check it... */
14041 if (acl_user->x509_issuer[0])
14042 {
14043 char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
14044 DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
14045 acl_user->x509_issuer, ptr));
14046 if (strcmp(acl_user->x509_issuer, ptr))
14047 {
14048 if (global_system_variables.log_warnings)
14049 sql_print_information("X509 issuer mismatch: should be '%s' "
14050 "but is '%s'", acl_user->x509_issuer, ptr);
14051 free(ptr);
14052 X509_free(cert);
14053 return 1;
14054 }
14055 free(ptr);
14056 }
14057 /* X509 subject is specified, we check it .. */
14058 if (acl_user->x509_subject[0])
14059 {
14060 char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
14061 DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
14062 acl_user->x509_subject, ptr));
14063 if (strcmp(acl_user->x509_subject, ptr))
14064 {
14065 if (global_system_variables.log_warnings)
14066 sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
14067 acl_user->x509_subject, ptr);
14068 free(ptr);
14069 X509_free(cert);
14070 return 1;
14071 }
14072 free(ptr);
14073 }
14074 X509_free(cert);
14075 return 0;
14076 #else /* HAVE_OPENSSL */
14077 default:
14078 /*
14079 If we don't have SSL but SSL is required for this user the
14080 authentication should fail.
14081 */
14082 return 1;
14083 #endif /* HAVE_OPENSSL */
14084 }
14085 return 1;
14086 }
14087
14088
do_auth_once(THD * thd,const LEX_CSTRING * auth_plugin_name,MPVIO_EXT * mpvio)14089 static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name,
14090 MPVIO_EXT *mpvio)
14091 {
14092 int res= CR_OK;
14093 bool unlock_plugin= false;
14094 plugin_ref plugin= get_auth_plugin(thd, *auth_plugin_name, &unlock_plugin);
14095
14096 mpvio->plugin= plugin;
14097 mpvio->auth_info.user_name= NULL;
14098
14099 if (plugin)
14100 {
14101 st_mysql_auth *info= (st_mysql_auth *) plugin_decl(plugin)->info;
14102 switch (info->interface_version >> 8) {
14103 case 0x02:
14104 res= info->authenticate_user(mpvio, &mpvio->auth_info);
14105 break;
14106 case 0x01:
14107 {
14108 MYSQL_SERVER_AUTH_INFO_0x0100 compat;
14109 compat.downgrade(&mpvio->auth_info);
14110 res= info->authenticate_user(mpvio, (MYSQL_SERVER_AUTH_INFO *)&compat);
14111 compat.upgrade(&mpvio->auth_info);
14112 }
14113 break;
14114 default: DBUG_ASSERT(0);
14115 }
14116
14117 if (unlock_plugin)
14118 plugin_unlock(thd, plugin);
14119 }
14120 else
14121 {
14122 /* Server cannot load the required plugin. */
14123 Host_errors errors;
14124 errors.m_no_auth_plugin= 1;
14125 inc_host_errors(mpvio->auth_info.thd->security_ctx->ip, &errors);
14126 my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
14127 res= CR_ERROR;
14128 }
14129
14130 return res;
14131 }
14132
14133 enum PASSWD_ERROR_ACTION
14134 {
14135 PASSWD_ERROR_CLEAR,
14136 PASSWD_ERROR_INCREMENT
14137 };
14138
14139 /* Increment, or clear password errors for a user. */
handle_password_errors(const char * user,const char * hostname,PASSWD_ERROR_ACTION action)14140 static void handle_password_errors(const char *user, const char *hostname, PASSWD_ERROR_ACTION action)
14141 {
14142 #ifndef NO_EMBEDDED_ACCESS_CHECKS
14143 mysql_mutex_assert_not_owner(&acl_cache->lock);
14144 mysql_mutex_lock(&acl_cache->lock);
14145 ACL_USER *u = find_user_exact(hostname, user);
14146 if (u)
14147 {
14148 switch(action)
14149 {
14150 case PASSWD_ERROR_INCREMENT:
14151 u->password_errors++;
14152 break;
14153 case PASSWD_ERROR_CLEAR:
14154 u->password_errors= 0;
14155 break;
14156 default:
14157 DBUG_ASSERT(0);
14158 break;
14159 }
14160 }
14161 mysql_mutex_unlock(&acl_cache->lock);
14162 #endif
14163 }
14164
check_password_lifetime(THD * thd,const ACL_USER & acl_user)14165 static bool check_password_lifetime(THD *thd, const ACL_USER &acl_user)
14166 {
14167 /* the password should never expire */
14168 if (!acl_user.password_lifetime)
14169 return false;
14170
14171 longlong interval= acl_user.password_lifetime;
14172 if (interval < 0)
14173 {
14174 interval= default_password_lifetime;
14175
14176 /* default global policy applies, and that is password never expires */
14177 if (!interval)
14178 return false;
14179 }
14180
14181 thd->set_time();
14182
14183 if ((thd->query_start() - acl_user.password_last_changed)/3600/24 >= interval)
14184 return true;
14185
14186 return false;
14187 }
14188
14189 /**
14190 Perform the handshake, authorize the client and update thd sctx variables.
14191
14192 @param thd thread handle
14193 @param com_change_user_pkt_len size of the COM_CHANGE_USER packet
14194 (without the first, command, byte) or 0
14195 if it's not a COM_CHANGE_USER (that is, if
14196 it's a new connection)
14197
14198 @retval 0 success, thd is updated.
14199 @retval 1 error
14200 */
acl_authenticate(THD * thd,uint com_change_user_pkt_len)14201 bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
14202 {
14203 int res= CR_OK;
14204 MPVIO_EXT mpvio;
14205 enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
14206 : COM_CONNECT;
14207 DBUG_ENTER("acl_authenticate");
14208
14209 bzero(&mpvio, sizeof(mpvio));
14210 mpvio.read_packet= server_mpvio_read_packet;
14211 mpvio.write_packet= server_mpvio_write_packet;
14212 mpvio.cached_client_reply.plugin= "";
14213 mpvio.info= server_mpvio_info;
14214 mpvio.status= MPVIO_EXT::RESTART;
14215 mpvio.auth_info.thd= thd;
14216 mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
14217 mpvio.auth_info.host_or_ip_length=
14218 (unsigned int) strlen(thd->security_ctx->host_or_ip);
14219
14220 DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
14221
14222 if (command == COM_CHANGE_USER)
14223 {
14224 mpvio.packets_written++; // pretend that a server handshake packet was sent
14225 mpvio.packets_read++; // take COM_CHANGE_USER packet into account
14226
14227 if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
14228 DBUG_RETURN(1);
14229
14230 res= mpvio.status == MPVIO_EXT::SUCCESS ? CR_OK : CR_ERROR;
14231
14232 DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
14233 mpvio.status == MPVIO_EXT::SUCCESS);
14234 }
14235 else
14236 {
14237 /* mark the thd as having no scramble yet */
14238 thd->scramble[SCRAMBLE_LENGTH]= 1;
14239
14240 /*
14241 perform the first authentication attempt, with the default plugin.
14242 This sends the server handshake packet, reads the client reply
14243 with a user name, and performs the authentication if everyone has used
14244 the correct plugin.
14245 */
14246
14247 res= do_auth_once(thd, default_auth_plugin_name, &mpvio);
14248 }
14249
14250 PSI_CALL_set_connection_type(vio_type(thd->net.vio));
14251
14252 Security_context * const sctx= thd->security_ctx;
14253 const ACL_USER * acl_user= mpvio.acl_user;
14254 if (!acl_user)
14255 statistic_increment(aborted_connects_preauth, &LOCK_status);
14256
14257 if (acl_user)
14258 {
14259 /*
14260 retry the authentication with curr_auth==0 if after receiving the user
14261 name we found that we need to switch to a non-default plugin
14262 */
14263 for (mpvio.curr_auth= mpvio.status != MPVIO_EXT::RESTART;
14264 res != CR_OK && mpvio.curr_auth < acl_user->nauth;
14265 mpvio.curr_auth++)
14266 {
14267 thd->clear_error();
14268 mpvio.status= MPVIO_EXT::RESTART;
14269 res= do_auth_once(thd, &acl_user->auth[mpvio.curr_auth].plugin, &mpvio);
14270 }
14271 }
14272
14273 if (mpvio.make_it_fail && res == CR_OK)
14274 {
14275 mpvio.status= MPVIO_EXT::FAILURE;
14276 res= CR_ERROR;
14277 }
14278
14279 thd->password= mpvio.auth_info.password_used; // remember for error messages
14280
14281 /*
14282 Log the command here so that the user can check the log
14283 for the tried logins and also to detect break-in attempts.
14284
14285 if sctx->user is unset it's protocol failure, bad packet.
14286 */
14287 if (sctx->user)
14288 {
14289 general_log_print(thd, command, (char*) "%s@%s on %s using %s",
14290 sctx->user, sctx->host_or_ip,
14291 safe_str(mpvio.db.str), safe_vio_type_name(thd->net.vio));
14292 }
14293
14294 if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
14295 {
14296 Host_errors errors;
14297 switch (res)
14298 {
14299 case CR_AUTH_PLUGIN_ERROR:
14300 errors.m_auth_plugin= 1;
14301 break;
14302 case CR_AUTH_HANDSHAKE:
14303 errors.m_handshake= 1;
14304 break;
14305 case CR_AUTH_USER_CREDENTIALS:
14306 errors.m_authentication= 1;
14307 if (thd->password && !mpvio.make_it_fail)
14308 handle_password_errors(acl_user->user.str, acl_user->host.hostname, PASSWD_ERROR_INCREMENT);
14309 break;
14310 case CR_ERROR:
14311 default:
14312 /* Unknown of unspecified auth plugin error. */
14313 errors.m_auth_plugin= 1;
14314 break;
14315 }
14316 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
14317 if (!thd->is_error())
14318 login_failed_error(thd);
14319 DBUG_RETURN(1);
14320 }
14321
14322 sctx->proxy_user[0]= 0;
14323 if (thd->password && acl_user->password_errors)
14324 {
14325 /* Login succeeded, clear password errors.*/
14326 handle_password_errors(acl_user->user.str, acl_user->host.hostname, PASSWD_ERROR_CLEAR);
14327 }
14328
14329 if (initialized) // if not --skip-grant-tables
14330 {
14331 /*
14332 OK. Let's check the SSL. Historically it was checked after the password,
14333 as an additional layer, not instead of the password
14334 (in which case it would've been a plugin too).
14335 */
14336 if (acl_check_ssl(thd, acl_user))
14337 {
14338 Host_errors errors;
14339 errors.m_ssl= 1;
14340 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
14341 login_failed_error(thd);
14342 DBUG_RETURN(1);
14343 }
14344
14345 if (acl_user->account_locked) {
14346 status_var_increment(denied_connections);
14347 my_error(ER_ACCOUNT_HAS_BEEN_LOCKED, MYF(0));
14348 DBUG_RETURN(1);
14349 }
14350
14351 bool client_can_handle_exp_pass= thd->client_capabilities &
14352 CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
14353 bool password_expired= thd->password != PASSWORD_USED_NO_MENTION
14354 && (acl_user->password_expired ||
14355 check_password_lifetime(thd, *acl_user));
14356
14357 if (!client_can_handle_exp_pass && disconnect_on_expired_password &&
14358 password_expired)
14359 {
14360 status_var_increment(denied_connections);
14361 my_error(ER_MUST_CHANGE_PASSWORD_LOGIN, MYF(0));
14362 DBUG_RETURN(1);
14363 }
14364
14365 sctx->password_expired= password_expired;
14366
14367 #ifndef NO_EMBEDDED_ACCESS_CHECKS
14368 if (!password_expired)
14369 {
14370 bool is_proxy_user= FALSE;
14371 const char *auth_user = acl_user->user.str;
14372 ACL_PROXY_USER *proxy_user;
14373 /* check if the user is allowed to proxy as another user */
14374 proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
14375 mpvio.auth_info.authenticated_as,
14376 &is_proxy_user);
14377 if (is_proxy_user)
14378 {
14379 ACL_USER *acl_proxy_user;
14380
14381 /* we need to find the proxy user, but there was none */
14382 if (!proxy_user)
14383 {
14384 Host_errors errors;
14385 errors.m_proxy_user= 1;
14386 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
14387 if (!thd->is_error())
14388 login_failed_error(thd);
14389 DBUG_RETURN(1);
14390 }
14391
14392 my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
14393 "'%s'@'%s'", auth_user,
14394 safe_str(acl_user->host.hostname));
14395
14396 /* we're proxying : find the proxy user definition */
14397 mysql_mutex_lock(&acl_cache->lock);
14398 acl_proxy_user= find_user_exact(safe_str(proxy_user->get_proxied_host()),
14399 mpvio.auth_info.authenticated_as);
14400 if (!acl_proxy_user)
14401 {
14402 mysql_mutex_unlock(&acl_cache->lock);
14403
14404 Host_errors errors;
14405 errors.m_proxy_user_acl= 1;
14406 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
14407 if (!thd->is_error())
14408 login_failed_error(thd);
14409 DBUG_RETURN(1);
14410 }
14411 acl_user= acl_proxy_user->copy(thd->mem_root);
14412 mysql_mutex_unlock(&acl_cache->lock);
14413 }
14414 }
14415 #endif
14416
14417 sctx->master_access= acl_user->access;
14418 strmake_buf(sctx->priv_user, acl_user->user.str);
14419
14420 if (acl_user->host.hostname)
14421 strmake_buf(sctx->priv_host, acl_user->host.hostname);
14422 else
14423 *sctx->priv_host= 0;
14424
14425
14426 /*
14427 Don't allow the user to connect if he has done too many queries.
14428 As we are testing max_user_connections == 0 here, it means that we
14429 can't let the user change max_user_connections from 0 in the server
14430 without a restart as it would lead to wrong connect counting.
14431 */
14432 if ((acl_user->user_resource.questions ||
14433 acl_user->user_resource.updates ||
14434 acl_user->user_resource.conn_per_hour ||
14435 acl_user->user_resource.user_conn ||
14436 acl_user->user_resource.max_statement_time != 0.0 ||
14437 max_user_connections_checking) &&
14438 get_or_create_user_conn(thd,
14439 (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
14440 (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
14441 &acl_user->user_resource))
14442 DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
14443
14444 if (acl_user->user_resource.max_statement_time != 0.0)
14445 {
14446 thd->variables.max_statement_time_double=
14447 acl_user->user_resource.max_statement_time;
14448 thd->variables.max_statement_time=
14449 (ulonglong) (thd->variables.max_statement_time_double * 1e6 + 0.1);
14450 }
14451 }
14452 else
14453 sctx->skip_grants();
14454
14455 if (thd->user_connect &&
14456 (thd->user_connect->user_resources.conn_per_hour ||
14457 thd->user_connect->user_resources.user_conn ||
14458 max_user_connections_checking) &&
14459 check_for_max_user_connections(thd, thd->user_connect))
14460 {
14461 /* Ensure we don't decrement thd->user_connections->connections twice */
14462 thd->user_connect= 0;
14463 status_var_increment(denied_connections);
14464 DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
14465 }
14466
14467 DBUG_PRINT("info",
14468 ("Capabilities: %llu packet_length: %ld Host: '%s' "
14469 "Login user: '%s' Priv_user: '%s' Using password: %s "
14470 "Access: %llx db: '%s'",
14471 thd->client_capabilities, thd->max_client_packet_length,
14472 sctx->host_or_ip, sctx->user, sctx->priv_user,
14473 thd->password ? "yes": "no",
14474 (longlong) sctx->master_access, mpvio.db.str));
14475
14476 if (command == COM_CONNECT &&
14477 !(thd->main_security_ctx.master_access & PRIV_IGNORE_MAX_CONNECTIONS))
14478 {
14479 if (*thd->scheduler->connection_count > *thd->scheduler->max_connections)
14480 { // too many connections
14481 my_error(ER_CON_COUNT_ERROR, MYF(0));
14482 DBUG_RETURN(1);
14483 }
14484 }
14485
14486 /*
14487 This is the default access rights for the current database. It's
14488 set to 0 here because we don't have an active database yet (and we
14489 may not have an active database to set.
14490 */
14491 sctx->db_access= NO_ACL;
14492
14493 #ifndef NO_EMBEDDED_ACCESS_CHECKS
14494 /*
14495 In case the user has a default role set, attempt to set that role
14496 */
14497 if (initialized && acl_user->default_rolename.length) {
14498 privilege_t access(NO_ACL);
14499 int result;
14500 result= acl_check_setrole(thd, acl_user->default_rolename.str, &access);
14501 if (!result)
14502 result= acl_setrole(thd, acl_user->default_rolename.str, access);
14503 if (result)
14504 thd->clear_error(); // even if the default role was not granted, do not
14505 // close the connection
14506 }
14507 #endif
14508
14509 /* Change a database if necessary */
14510 if (mpvio.db.length)
14511 {
14512 uint err = mysql_change_db(thd, &mpvio.db, FALSE);
14513 if(err)
14514 {
14515 if (err == ER_DBACCESS_DENIED_ERROR)
14516 {
14517 /*
14518 Got an "access denied" error, which must be handled
14519 other access denied errors (see login_failed_error()).
14520 mysql_change_db() already sent error to client, and
14521 wrote to general log, we only need to increment the counter
14522 and maybe write a warning to error log.
14523 */
14524 status_var_increment(thd->status_var.access_denied_errors);
14525 if (global_system_variables.log_warnings > 1)
14526 {
14527 Security_context* sctx = thd->security_ctx;
14528 sql_print_warning(ER_THD(thd, err),
14529 sctx->priv_user, sctx->priv_host, mpvio.db.str);
14530 }
14531 }
14532 DBUG_RETURN(1);
14533 }
14534 }
14535
14536 thd->net.net_skip_rest_factor= 2; // skip at most 2*max_packet_size
14537
14538 if (mpvio.auth_info.external_user[0])
14539 sctx->external_user= my_strdup(key_memory_MPVIO_EXT_auth_info,
14540 mpvio.auth_info.external_user, MYF(0));
14541
14542 if (res == CR_OK_HANDSHAKE_COMPLETE)
14543 thd->get_stmt_da()->disable_status();
14544 else
14545 my_ok(thd);
14546
14547 PSI_CALL_set_thread_account
14548 (thd->main_security_ctx.user, static_cast<uint>(strlen(thd->main_security_ctx.user)),
14549 thd->main_security_ctx.host_or_ip, static_cast<uint>(strlen(thd->main_security_ctx.host_or_ip)));
14550
14551 /* Ready to handle queries */
14552 DBUG_RETURN(0);
14553 }
14554
14555 /**
14556 MySQL Server Password Authentication Plugin
14557
14558 In the MySQL authentication protocol:
14559 1. the server sends the random scramble to the client
14560 2. client sends the encrypted password back to the server
14561 3. the server checks the password.
14562 */
native_password_authenticate(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)14563 static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
14564 MYSQL_SERVER_AUTH_INFO *info)
14565 {
14566 uchar *pkt;
14567 int pkt_len;
14568 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
14569 THD *thd=info->thd;
14570 DBUG_ENTER("native_password_authenticate");
14571
14572 /* generate the scramble, or reuse the old one */
14573 if (thd->scramble[SCRAMBLE_LENGTH])
14574 thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
14575
14576 /* and send it to the client */
14577 if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
14578 DBUG_RETURN(CR_AUTH_HANDSHAKE);
14579
14580 /* reply and authenticate */
14581
14582 /*
14583 <digression>
14584 This is more complex than it looks.
14585
14586 The plugin (we) may be called right after the client was connected -
14587 and will need to send a scramble, read reply, authenticate.
14588
14589 Or the plugin may be called after another plugin has sent a scramble,
14590 and read the reply. If the client has used the correct client-plugin,
14591 we won't need to read anything here from the client, the client
14592 has already sent a reply with everything we need for authentication.
14593
14594 Or the plugin may be called after another plugin has sent a scramble,
14595 and read the reply, but the client has used the wrong client-plugin.
14596 We'll need to sent a "switch to another plugin" packet to the
14597 client and read the reply. "Use the short scramble" packet is a special
14598 case of "switch to another plugin" packet.
14599
14600 Or, perhaps, the plugin may be called after another plugin has
14601 done the handshake but did not send a useful scramble. We'll need
14602 to send a scramble (and perhaps a "switch to another plugin" packet)
14603 and read the reply.
14604
14605 Besides, a client may be an old one, that doesn't understand plugins.
14606 Or doesn't even understand 4.0 scramble.
14607
14608 And we want to keep the same protocol on the wire unless non-native
14609 plugins are involved.
14610
14611 Anyway, it still looks simple from a plugin point of view:
14612 "send the scramble, read the reply and authenticate".
14613 All the magic is transparently handled by the server.
14614 </digression>
14615 */
14616
14617 /* read the reply with the encrypted password */
14618 if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
14619 DBUG_RETURN(CR_AUTH_HANDSHAKE);
14620 DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
14621
14622 #ifdef NO_EMBEDDED_ACCESS_CHECKS
14623 DBUG_RETURN(CR_OK);
14624 #endif
14625
14626 DBUG_EXECUTE_IF("native_password_bad_reply", { pkt_len= 12; });
14627
14628 if (pkt_len == 0) /* no password */
14629 DBUG_RETURN(info->auth_string_length != 0
14630 ? CR_AUTH_USER_CREDENTIALS : CR_OK);
14631
14632 info->password_used= PASSWORD_USED_YES;
14633 if (pkt_len == SCRAMBLE_LENGTH)
14634 {
14635 if (info->auth_string_length != SCRAMBLE_LENGTH)
14636 DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
14637
14638 if (check_scramble(pkt, thd->scramble, (uchar*)info->auth_string))
14639 DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
14640 else
14641 DBUG_RETURN(CR_OK);
14642 }
14643
14644 my_error(ER_HANDSHAKE_ERROR, MYF(0));
14645 DBUG_RETURN(CR_AUTH_HANDSHAKE);
14646 }
14647
native_password_make_scramble(const char * password,size_t password_length,char * hash,size_t * hash_length)14648 static int native_password_make_scramble(const char *password,
14649 size_t password_length, char *hash, size_t *hash_length)
14650 {
14651 DBUG_ASSERT(*hash_length >= SCRAMBLED_PASSWORD_CHAR_LENGTH);
14652 if (password_length == 0)
14653 *hash_length= 0;
14654 else
14655 {
14656 *hash_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
14657 my_make_scrambled_password(hash, password, password_length);
14658 }
14659 return 0;
14660 }
14661
14662 /* As this contains is a string of not a valid SCRAMBLE_LENGTH */
14663 static const char invalid_password[] = "*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE";
14664
native_password_get_salt(const char * hash,size_t hash_length,unsigned char * out,size_t * out_length)14665 static int native_password_get_salt(const char *hash, size_t hash_length,
14666 unsigned char *out, size_t *out_length)
14667 {
14668 DBUG_ASSERT(sizeof(invalid_password) > SCRAMBLE_LENGTH);
14669 DBUG_ASSERT(*out_length >= SCRAMBLE_LENGTH);
14670 DBUG_ASSERT(*out_length >= sizeof(invalid_password));
14671 if (hash_length == 0)
14672 {
14673 *out_length= 0;
14674 return 0;
14675 }
14676
14677 if (hash_length != SCRAMBLED_PASSWORD_CHAR_LENGTH)
14678 {
14679 if (hash_length == 7 && strcmp(hash, "invalid") == 0)
14680 {
14681 memcpy(out, invalid_password, sizeof(invalid_password));
14682 *out_length= sizeof(invalid_password);
14683 return 0;
14684 }
14685 my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
14686 return 1;
14687 }
14688
14689 for (const char *c= hash + 1; c < (hash + hash_length); c++)
14690 {
14691 /* If any non-hex characters are found, mark the password as invalid. */
14692 if (!(*c >= '0' && *c <= '9') &&
14693 !(*c >= 'A' && *c <= 'F') &&
14694 !(*c >= 'a' && *c <= 'f'))
14695 {
14696 memcpy(out, invalid_password, sizeof(invalid_password));
14697 *out_length= sizeof(invalid_password);
14698 return 0;
14699 }
14700 }
14701
14702 *out_length= SCRAMBLE_LENGTH;
14703 get_salt_from_password(out, hash);
14704 return 0;
14705 }
14706
old_password_authenticate(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)14707 static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
14708 MYSQL_SERVER_AUTH_INFO *info)
14709 {
14710 uchar *pkt;
14711 int pkt_len;
14712 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
14713 THD *thd=info->thd;
14714
14715 /* generate the scramble, or reuse the old one */
14716 if (thd->scramble[SCRAMBLE_LENGTH])
14717 thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
14718 /* and send it to the client */
14719 if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
14720 return CR_AUTH_HANDSHAKE;
14721
14722 /* read the reply and authenticate */
14723 if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
14724 return CR_AUTH_HANDSHAKE;
14725
14726 #ifdef NO_EMBEDDED_ACCESS_CHECKS
14727 return CR_OK;
14728 #endif
14729
14730 /*
14731 legacy: if switch_from_long_to_short_scramble,
14732 the password is sent \0-terminated, the pkt_len is always 9 bytes.
14733 We need to figure out the correct scramble length here.
14734 */
14735 if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
14736 pkt_len= (int)strnlen((char*)pkt, pkt_len);
14737
14738 if (pkt_len == 0) /* no password */
14739 return info->auth_string_length ? CR_AUTH_USER_CREDENTIALS : CR_OK;
14740
14741 if (secure_auth(thd))
14742 return CR_AUTH_HANDSHAKE;
14743
14744 info->password_used= PASSWORD_USED_YES;
14745
14746 if (pkt_len == SCRAMBLE_LENGTH_323)
14747 {
14748 if (!info->auth_string_length)
14749 return CR_AUTH_USER_CREDENTIALS;
14750
14751 return check_scramble_323(pkt, thd->scramble, (ulong *) info->auth_string)
14752 ? CR_AUTH_USER_CREDENTIALS : CR_OK;
14753 }
14754
14755 my_error(ER_HANDSHAKE_ERROR, MYF(0));
14756 return CR_AUTH_HANDSHAKE;
14757 }
14758
old_password_make_scramble(const char * password,size_t password_length,char * hash,size_t * hash_length)14759 static int old_password_make_scramble(const char *password,
14760 size_t password_length, char *hash, size_t *hash_length)
14761 {
14762 DBUG_ASSERT(*hash_length >= SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
14763 if (password_length == 0)
14764 *hash_length= 0;
14765 else
14766 {
14767 *hash_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
14768 my_make_scrambled_password_323(hash, password, password_length);
14769 }
14770 return 0;
14771 }
14772
14773 #define SALT_LENGTH_323 (sizeof(ulong)*2)
old_password_get_salt(const char * hash,size_t hash_length,unsigned char * out,size_t * out_length)14774 static int old_password_get_salt(const char *hash, size_t hash_length,
14775 unsigned char *out, size_t *out_length)
14776 {
14777 DBUG_ASSERT(*out_length >= SALT_LENGTH_323);
14778
14779 if (hash_length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
14780 {
14781 my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
14782 return 1;
14783 }
14784
14785 *out_length= SALT_LENGTH_323;
14786 get_salt_from_password_323((ulong*)out, hash);
14787 return 0;
14788 }
14789
14790 static struct st_mysql_auth native_password_handler=
14791 {
14792 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
14793 native_password_plugin_name.str,
14794 native_password_authenticate,
14795 native_password_make_scramble,
14796 native_password_get_salt
14797 };
14798
14799 static struct st_mysql_auth old_password_handler=
14800 {
14801 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
14802 old_password_plugin_name.str,
14803 old_password_authenticate,
14804 old_password_make_scramble,
14805 old_password_get_salt
14806 };
14807
maria_declare_plugin(mysql_password)14808 maria_declare_plugin(mysql_password)
14809 {
14810 MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
14811 &native_password_handler, /* type descriptor */
14812 native_password_plugin_name.str, /* Name */
14813 "R.J.Silk, Sergei Golubchik", /* Author */
14814 "Native MySQL authentication", /* Description */
14815 PLUGIN_LICENSE_GPL, /* License */
14816 NULL, /* Init function */
14817 NULL, /* Deinit function */
14818 0x0100, /* Version (1.0) */
14819 NULL, /* status variables */
14820 NULL, /* system variables */
14821 "1.0", /* String version */
14822 MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */
14823 },
14824 {
14825 MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
14826 &old_password_handler, /* type descriptor */
14827 old_password_plugin_name.str, /* Name */
14828 "R.J.Silk, Sergei Golubchik", /* Author */
14829 "Old MySQL-4.0 authentication", /* Description */
14830 PLUGIN_LICENSE_GPL, /* License */
14831 NULL, /* Init function */
14832 NULL, /* Deinit function */
14833 0x0100, /* Version (1.0) */
14834 NULL, /* status variables */
14835 NULL, /* system variables */
14836 "1.0", /* String version */
14837 MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */
14838 }
14839 maria_declare_plugin_end;
14840
14841
14842 /*
14843 Exporting functions that allow plugins to do server-style
14844 host/user matching. Used in server_audit2 plugin.
14845 */
maria_compare_hostname(const char * wild_host,long wild_ip,long ip_mask,const char * host,const char * ip)14846 extern "C" int maria_compare_hostname(
14847 const char *wild_host, long wild_ip, long ip_mask,
14848 const char *host, const char *ip)
14849 {
14850 #ifndef NO_EMBEDDED_ACCESS_CHECKS
14851 acl_host_and_ip h;
14852 h.hostname= (char *) wild_host;
14853 h.ip= wild_ip;
14854 h.ip_mask= ip_mask;
14855
14856 return compare_hostname(&h, host, ip);
14857 #else
14858 return 0;
14859 #endif
14860 }
14861
14862
maria_update_hostname(const char ** wild_host,long * wild_ip,long * ip_mask,const char * host)14863 extern "C" void maria_update_hostname(
14864 const char **wild_host, long *wild_ip, long *ip_mask,
14865 const char *host)
14866 {
14867 #ifndef NO_EMBEDDED_ACCESS_CHECKS
14868 acl_host_and_ip h;
14869 update_hostname(&h, host);
14870 *wild_host= h.hostname;
14871 *wild_ip= h.ip;
14872 *ip_mask= h.ip_mask;
14873 #endif
14874 }
14875
14876
14877