1 /* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
2 Copyright (c) 2009, 2020, 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 bool mysql_user_table_is_in_short_password_format= false;
61
62 static LEX_CSTRING native_password_plugin_name= {
63 STRING_WITH_LEN("mysql_native_password")
64 };
65
66 static LEX_CSTRING old_password_plugin_name= {
67 STRING_WITH_LEN("mysql_old_password")
68 };
69
70 /// @todo make it configurable
71 LEX_CSTRING *default_auth_plugin_name= &native_password_plugin_name;
72
73 /*
74 Wildcard host, matches any hostname
75 */
76 LEX_CSTRING host_not_specified= { STRING_WITH_LEN("%") };
77
78 /*
79 Constants, used in the SHOW GRANTS command.
80 Their actual string values are irrelevant, they're always compared
81 as pointers to these string constants.
82 */
83 LEX_CSTRING current_user= { STRING_WITH_LEN("*current_user") };
84 LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role") };
85 LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role") };
86
87 #ifndef NO_EMBEDDED_ACCESS_CHECKS
88 static plugin_ref old_password_plugin;
89 #endif
90 static plugin_ref native_password_plugin;
91
92 /* Classes */
93
94 struct acl_host_and_ip
95 {
96 char *hostname;
97 long ip, ip_mask; // Used with masked ip:s
98 };
99
100 #ifndef NO_EMBEDDED_ACCESS_CHECKS
101 static bool compare_hostname(const acl_host_and_ip *, const char *, const char *);
102 #else
103 #define compare_hostname(X,Y,Z) 0
104 #endif
105
106 class ACL_ACCESS {
107 public:
108 ulong sort;
109 ulong access;
110 };
111
112 /* ACL_HOST is used if no host is specified */
113
114 class ACL_HOST :public ACL_ACCESS
115 {
116 public:
117 acl_host_and_ip host;
118 char *db;
119 };
120
121 class ACL_USER_BASE :public ACL_ACCESS
122 {
123
124 public:
operator new(size_t size,MEM_ROOT * mem_root)125 static void *operator new(size_t size, MEM_ROOT *mem_root)
126 { return (void*) alloc_root(mem_root, size); }
operator delete(void *,MEM_ROOT *)127 static void operator delete(void *, MEM_ROOT *){}
128 uchar flags; // field used to store various state information
129 LEX_CSTRING user;
130 /* list to hold references to granted roles (ACL_ROLE instances) */
131 DYNAMIC_ARRAY role_grants;
132 };
133
134 class ACL_USER :public ACL_USER_BASE
135 {
136 public:
137 acl_host_and_ip host;
138 size_t hostname_length;
139 USER_RESOURCES user_resource;
140 uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form
141 uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
142 enum SSL_type ssl_type;
143 const char *ssl_cipher, *x509_issuer, *x509_subject;
144 LEX_CSTRING plugin;
145 LEX_CSTRING auth_string;
146 LEX_CSTRING default_rolename;
147
copy(MEM_ROOT * root)148 ACL_USER *copy(MEM_ROOT *root)
149 {
150 ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
151 if (!dst)
152 return 0;
153 *dst= *this;
154 dst->user.str= safe_strdup_root(root, user.str);
155 dst->user.length= user.length;
156 dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
157 dst->x509_issuer= safe_strdup_root(root, x509_issuer);
158 dst->x509_subject= safe_strdup_root(root, x509_subject);
159 if (plugin.str == native_password_plugin_name.str ||
160 plugin.str == old_password_plugin_name.str)
161 dst->plugin= plugin;
162 else
163 dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
164 dst->auth_string.str= safe_strdup_root(root, auth_string.str);
165 dst->host.hostname= safe_strdup_root(root, host.hostname);
166 dst->default_rolename.str= safe_strdup_root(root, default_rolename.str);
167 dst->default_rolename.length= default_rolename.length;
168 bzero(&dst->role_grants, sizeof(role_grants));
169 return dst;
170 }
171
cmp(const char * user2,const char * host2)172 int cmp(const char *user2, const char *host2)
173 {
174 CHARSET_INFO *cs= system_charset_info;
175 int res;
176 res= strcmp(safe_str(user.str), safe_str(user2));
177 if (!res)
178 res= my_strcasecmp(cs, host.hostname, host2);
179 return res;
180 }
181
eq(const char * user2,const char * host2)182 bool eq(const char *user2, const char *host2) { return !cmp(user2, host2); }
183
wild_eq(const char * user2,const char * host2,const char * ip2)184 bool wild_eq(const char *user2, const char *host2, const char *ip2)
185 {
186 if (strcmp(safe_str(user.str), safe_str(user2)))
187 return false;
188
189 return compare_hostname(&host, host2, ip2 ? ip2 : host2);
190 }
191 };
192
193 class ACL_ROLE :public ACL_USER_BASE
194 {
195 public:
196 /*
197 In case of granting a role to a role, the access bits are merged together
198 via a bit OR operation and placed in the ACL_USER::access field.
199
200 When rebuilding role_grants via the rebuild_role_grant function,
201 the ACL_USER::access field needs to be reset first. The field
202 initial_role_access holds initial grants, as granted directly to the role
203 */
204 ulong initial_role_access;
205 /*
206 In subgraph traversal, when we need to traverse only a part of the graph
207 (e.g. all direct and indirect grantees of a role X), the counter holds the
208 number of affected neighbour nodes.
209 See also propagate_role_grants()
210 */
211 uint counter;
212 DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted
213
214 ACL_ROLE(ACL_USER * user, MEM_ROOT *mem);
215 ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *mem);
216
217 };
218
219 class ACL_DB :public ACL_ACCESS
220 {
221 public:
222 acl_host_and_ip host;
223 const char *user,*db;
224 ulong initial_access; /* access bits present in the table */
225 };
226
227 #ifndef DBUG_OFF
228 /* status variables, only visible in SHOW STATUS after -#d,role_merge_stats */
229 ulong role_global_merges= 0, role_db_merges= 0, role_table_merges= 0,
230 role_column_merges= 0, role_routine_merges= 0;
231 #endif
232
233 #ifndef NO_EMBEDDED_ACCESS_CHECKS
234 static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd);
235 static void update_hostname(acl_host_and_ip *host, const char *hostname);
236 static ulong get_sort(uint count,...);
237 static bool show_proxy_grants (THD *, const char *, const char *,
238 char *, size_t);
239 static bool show_role_grants(THD *, const char *,
240 ACL_USER_BASE *, char *, size_t);
241 static bool show_default_role(THD *, ACL_USER *, char *, size_t);
242 static bool show_global_privileges(THD *, ACL_USER_BASE *,
243 bool, char *, size_t);
244 static bool show_database_privileges(THD *, const char *, const char *,
245 char *, size_t);
246 static bool show_table_and_column_privileges(THD *, const char *, const char *,
247 char *, size_t);
248 static int show_routine_grants(THD *, const char *, const char *,
249 const Sp_handler *sph, char *, int);
250
251 class Grant_tables;
252 class User_table;
253 class Proxies_priv_table;
254
255 class ACL_PROXY_USER :public ACL_ACCESS
256 {
257 acl_host_and_ip host;
258 const char *user;
259 acl_host_and_ip proxied_host;
260 const char *proxied_user;
261 bool with_grant;
262
263 typedef enum {
264 MYSQL_PROXIES_PRIV_HOST,
265 MYSQL_PROXIES_PRIV_USER,
266 MYSQL_PROXIES_PRIV_PROXIED_HOST,
267 MYSQL_PROXIES_PRIV_PROXIED_USER,
268 MYSQL_PROXIES_PRIV_WITH_GRANT,
269 MYSQL_PROXIES_PRIV_GRANTOR,
270 MYSQL_PROXIES_PRIV_TIMESTAMP } proxy_table_fields;
271 public:
ACL_PROXY_USER()272 ACL_PROXY_USER () {};
273
init(const char * host_arg,const char * user_arg,const char * proxied_host_arg,const char * proxied_user_arg,bool with_grant_arg)274 void init(const char *host_arg, const char *user_arg,
275 const char *proxied_host_arg, const char *proxied_user_arg,
276 bool with_grant_arg)
277 {
278 user= (user_arg && *user_arg) ? user_arg : NULL;
279 update_hostname (&host, (host_arg && *host_arg) ? host_arg : NULL);
280 proxied_user= (proxied_user_arg && *proxied_user_arg) ?
281 proxied_user_arg : NULL;
282 update_hostname (&proxied_host,
283 (proxied_host_arg && *proxied_host_arg) ?
284 proxied_host_arg : NULL);
285 with_grant= with_grant_arg;
286 sort= get_sort(4, host.hostname, user, proxied_host.hostname, proxied_user);
287 }
288
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)289 void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
290 const char *proxied_host_arg, const char *proxied_user_arg,
291 bool with_grant_arg)
292 {
293 init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
294 (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
295 (proxied_host_arg && *proxied_host_arg) ?
296 strdup_root (mem, proxied_host_arg) : NULL,
297 (proxied_user_arg && *proxied_user_arg) ?
298 strdup_root (mem, proxied_user_arg) : NULL,
299 with_grant_arg);
300 }
301
302 void init(const Proxies_priv_table& proxies_priv_table, MEM_ROOT *mem);
303
get_with_grant()304 bool get_with_grant() { return with_grant; }
get_user()305 const char *get_user() { return user; }
get_host()306 const char *get_host() { return host.hostname; }
get_proxied_user()307 const char *get_proxied_user() { return proxied_user; }
get_proxied_host()308 const char *get_proxied_host() { return proxied_host.hostname; }
set_user(MEM_ROOT * mem,const char * user_arg)309 void set_user(MEM_ROOT *mem, const char *user_arg)
310 {
311 user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
312 }
set_host(MEM_ROOT * mem,const char * host_arg)313 void set_host(MEM_ROOT *mem, const char *host_arg)
314 {
315 update_hostname(&host, safe_strdup_root(mem, host_arg));
316 }
317
check_validity(bool check_no_resolve)318 bool check_validity(bool check_no_resolve)
319 {
320 if (check_no_resolve &&
321 (hostname_requires_resolving(host.hostname) ||
322 hostname_requires_resolving(proxied_host.hostname)))
323 {
324 sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
325 "ignored in --skip-name-resolve mode.",
326 safe_str(proxied_user),
327 safe_str(proxied_host.hostname),
328 safe_str(user),
329 safe_str(host.hostname));
330 return TRUE;
331 }
332 return FALSE;
333 }
334
matches(const char * host_arg,const char * user_arg,const char * ip_arg,const char * proxied_user_arg)335 bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
336 const char *proxied_user_arg)
337 {
338 DBUG_ENTER("ACL_PROXY_USER::matches");
339 DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
340 "compare_hostname(%s,%s,%s) &&"
341 "wild_compare (%s,%s) &&"
342 "wild_compare (%s,%s)",
343 host.hostname, host_arg, ip_arg, proxied_host.hostname,
344 host_arg, ip_arg, user_arg, user,
345 proxied_user_arg, proxied_user));
346 DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
347 compare_hostname(&proxied_host, host_arg, ip_arg) &&
348 (!user ||
349 (user_arg && !wild_compare(user_arg, user, TRUE))) &&
350 (!proxied_user ||
351 (proxied_user && !wild_compare(proxied_user_arg,
352 proxied_user, TRUE))));
353 }
354
355
auth_element_equals(const char * a,const char * b)356 inline static bool auth_element_equals(const char *a, const char *b)
357 {
358 return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
359 }
360
361
pk_equals(ACL_PROXY_USER * grant)362 bool pk_equals(ACL_PROXY_USER *grant)
363 {
364 DBUG_ENTER("pk_equals");
365 DBUG_PRINT("info", ("strcmp(%s,%s) &&"
366 "strcmp(%s,%s) &&"
367 "wild_compare (%s,%s) &&"
368 "wild_compare (%s,%s)",
369 user, grant->user, proxied_user, grant->proxied_user,
370 host.hostname, grant->host.hostname,
371 proxied_host.hostname, grant->proxied_host.hostname));
372
373 bool res= auth_element_equals(user, grant->user) &&
374 auth_element_equals(proxied_user, grant->proxied_user) &&
375 auth_element_equals(host.hostname, grant->host.hostname) &&
376 auth_element_equals(proxied_host.hostname,
377 grant->proxied_host.hostname);
378 DBUG_RETURN(res);
379 }
380
381
granted_on(const char * host_arg,const char * user_arg)382 bool granted_on(const char *host_arg, const char *user_arg)
383 {
384 return (((!user && (!user_arg || !user_arg[0])) ||
385 (user && user_arg && !strcmp(user, user_arg))) &&
386 ((!host.hostname && (!host_arg || !host_arg[0])) ||
387 (host.hostname && host_arg && !strcmp(host.hostname, host_arg))));
388 }
389
390
print_grant(String * str)391 void print_grant(String *str)
392 {
393 str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
394 if (proxied_user)
395 str->append(proxied_user, strlen(proxied_user));
396 str->append(STRING_WITH_LEN("'@'"));
397 if (proxied_host.hostname)
398 str->append(proxied_host.hostname, strlen(proxied_host.hostname));
399 str->append(STRING_WITH_LEN("' TO '"));
400 if (user)
401 str->append(user, strlen(user));
402 str->append(STRING_WITH_LEN("'@'"));
403 if (host.hostname)
404 str->append(host.hostname, strlen(host.hostname));
405 str->append(STRING_WITH_LEN("'"));
406 if (with_grant)
407 str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
408 }
409
set_data(ACL_PROXY_USER * grant)410 void set_data(ACL_PROXY_USER *grant)
411 {
412 with_grant= grant->with_grant;
413 }
414
store_pk(TABLE * table,const LEX_CSTRING * host,const LEX_CSTRING * user,const LEX_CSTRING * proxied_host,const LEX_CSTRING * proxied_user)415 static int store_pk(TABLE *table,
416 const LEX_CSTRING *host,
417 const LEX_CSTRING *user,
418 const LEX_CSTRING *proxied_host,
419 const LEX_CSTRING *proxied_user)
420 {
421 DBUG_ENTER("ACL_PROXY_USER::store_pk");
422 DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
423 host->str, user->str,
424 proxied_host->str, proxied_user->str));
425 if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
426 host->length,
427 system_charset_info))
428 DBUG_RETURN(TRUE);
429 if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
430 user->length,
431 system_charset_info))
432 DBUG_RETURN(TRUE);
433 if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str,
434 proxied_host->length,
435 system_charset_info))
436 DBUG_RETURN(TRUE);
437 if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str,
438 proxied_user->length,
439 system_charset_info))
440 DBUG_RETURN(TRUE);
441
442 DBUG_RETURN(FALSE);
443 }
444
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)445 static int store_data_record(TABLE *table,
446 const LEX_CSTRING *host,
447 const LEX_CSTRING *user,
448 const LEX_CSTRING *proxied_host,
449 const LEX_CSTRING *proxied_user,
450 bool with_grant,
451 const char *grantor)
452 {
453 DBUG_ENTER("ACL_PROXY_USER::store_pk");
454 if (store_pk(table, host, user, proxied_host, proxied_user))
455 DBUG_RETURN(TRUE);
456 DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
457 if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
458 TRUE))
459 DBUG_RETURN(TRUE);
460 if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
461 strlen(grantor),
462 system_charset_info))
463 DBUG_RETURN(TRUE);
464
465 DBUG_RETURN(FALSE);
466 }
467 };
468
469 #define FIRST_NON_YN_FIELD 26
470
471 class acl_entry :public hash_filo_element
472 {
473 public:
474 ulong access;
475 uint16 length;
476 char key[1]; // Key will be stored here
477 };
478
479
acl_entry_get_key(acl_entry * entry,size_t * length,my_bool not_used)480 static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
481 my_bool not_used __attribute__((unused)))
482 {
483 *length=(uint) entry->length;
484 return (uchar*) entry->key;
485 }
486
acl_role_get_key(ACL_ROLE * entry,size_t * length,my_bool not_used)487 static uchar* acl_role_get_key(ACL_ROLE *entry, size_t *length,
488 my_bool not_used __attribute__((unused)))
489 {
490 *length=(uint) entry->user.length;
491 return (uchar*) entry->user.str;
492 }
493
494 struct ROLE_GRANT_PAIR : public Sql_alloc
495 {
496 char *u_uname;
497 char *u_hname;
498 char *r_uname;
499 LEX_STRING hashkey;
500 bool with_admin;
501
502 bool init(MEM_ROOT *mem, const char *username, const char *hostname,
503 const char *rolename, bool with_admin_option);
504 };
505
acl_role_map_get_key(ROLE_GRANT_PAIR * entry,size_t * length,my_bool not_used)506 static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length,
507 my_bool not_used __attribute__((unused)))
508 {
509 *length=(uint) entry->hashkey.length;
510 return (uchar*) entry->hashkey.str;
511 }
512
init(MEM_ROOT * mem,const char * username,const char * hostname,const char * rolename,bool with_admin_option)513 bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, const char *username,
514 const char *hostname, const char *rolename,
515 bool with_admin_option)
516 {
517 size_t uname_l = safe_strlen(username);
518 size_t hname_l = safe_strlen(hostname);
519 size_t rname_l = safe_strlen(rolename);
520 /*
521 Create a buffer that holds all 3 NULL terminated strings in succession
522 To save memory space, the same buffer is used as the hashkey
523 */
524 size_t bufflen = uname_l + hname_l + rname_l + 3; //add the '\0' aswell
525 char *buff= (char *)alloc_root(mem, bufflen);
526 if (!buff)
527 return true;
528
529 /*
530 Offsets in the buffer for all 3 strings
531 */
532 char *username_pos= buff;
533 char *hostname_pos= buff + uname_l + 1;
534 char *rolename_pos= buff + uname_l + hname_l + 2;
535
536 if (username) //prevent undefined behaviour
537 memcpy(username_pos, username, uname_l);
538 username_pos[uname_l]= '\0'; //#1 string terminator
539 u_uname= username_pos;
540
541 if (hostname) //prevent undefined behaviour
542 memcpy(hostname_pos, hostname, hname_l);
543 hostname_pos[hname_l]= '\0'; //#2 string terminator
544 u_hname= hostname_pos;
545
546 if (rolename) //prevent undefined behaviour
547 memcpy(rolename_pos, rolename, rname_l);
548 rolename_pos[rname_l]= '\0'; //#3 string terminator
549 r_uname= rolename_pos;
550
551 hashkey.str = buff;
552 hashkey.length = bufflen;
553
554 with_admin= with_admin_option;
555
556 return false;
557 }
558
559 #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
560 #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
561 1 + USERNAME_LENGTH + 1)
562
563 #if defined(HAVE_OPENSSL)
564 /*
565 Without SSL the handshake consists of one packet. This packet
566 has both client capabilities and scrambled password.
567 With SSL the handshake might consist of two packets. If the first
568 packet (client capabilities) has CLIENT_SSL flag set, we have to
569 switch to SSL and read the second packet. The scrambled password
570 is in the second packet and client_capabilities field will be ignored.
571 Maybe it is better to accept flags other than CLIENT_SSL from the
572 second packet?
573 */
574 #define SSL_HANDSHAKE_SIZE 2
575 #define MIN_HANDSHAKE_SIZE 2
576 #else
577 #define MIN_HANDSHAKE_SIZE 6
578 #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
579 #define NORMAL_HANDSHAKE_SIZE 6
580
581 #define ROLE_ASSIGN_COLUMN_IDX 44
582 #define DEFAULT_ROLE_COLUMN_IDX 45
583 #define MAX_STATEMENT_TIME_COLUMN_IDX 46
584
585 /* various flags valid for ACL_USER */
586 #define IS_ROLE (1L << 0)
587 /* Flag to mark that a ROLE is on the recursive DEPTH_FIRST_SEARCH stack */
588 #define ROLE_ON_STACK (1L << 1)
589 /*
590 Flag to mark that a ROLE and all it's neighbours have
591 been visited
592 */
593 #define ROLE_EXPLORED (1L << 2)
594 /* Flag to mark that on_node was already called for this role */
595 #define ROLE_OPENED (1L << 3)
596
597 static DYNAMIC_ARRAY acl_hosts, acl_users, acl_proxy_users;
598 static Dynamic_array<ACL_DB> acl_dbs(0U,50U);
599 typedef Dynamic_array<ACL_DB>::CMP_FUNC acl_dbs_cmp;
600 static HASH acl_roles;
601 /*
602 An hash containing mappings user <--> role
603
604 A hash is used so as to make updates quickly
605 The hashkey used represents all the entries combined
606 */
607 static HASH acl_roles_mappings;
608 static MEM_ROOT acl_memroot, grant_memroot;
609 static bool initialized=0;
610 static bool allow_all_hosts=1;
611 static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
612 static HASH package_spec_priv_hash, package_body_priv_hash;
613 static DYNAMIC_ARRAY acl_wild_hosts;
614 static Hash_filo<acl_entry> *acl_cache;
615 static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
616 static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
617 static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
618 static ulong get_sort(uint count,...);
619 static void init_check_host(void);
620 static void rebuild_check_host(void);
621 static void rebuild_role_grants(void);
622 static ACL_USER *find_user_exact(const char *host, const char *user);
623 static ACL_USER *find_user_wild(const char *host, const char *user, const char *ip= 0);
624 static ACL_ROLE *find_acl_role(const char *user);
625 static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, const LEX_CSTRING *h, const LEX_CSTRING *r);
626 static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host);
627 static bool update_user_table(THD *, const User_table &, const char *, const char *, const
628 char *, size_t new_password_len);
629 static bool acl_load(THD *thd, const Grant_tables& grant_tables);
630 static inline void get_grantor(THD *thd, char* grantor);
631 static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname);
632 static bool get_YN_as_bool(Field *field);
633
634 #define ROLE_CYCLE_FOUND 2
635 static int traverse_role_graph_up(ACL_ROLE *, void *,
636 int (*) (ACL_ROLE *, void *),
637 int (*) (ACL_ROLE *, ACL_ROLE *, void *));
638
639 static int traverse_role_graph_down(ACL_USER_BASE *, void *,
640 int (*) (ACL_USER_BASE *, void *),
641 int (*) (ACL_USER_BASE *, ACL_ROLE *, void *));
642
643
get_priv_hash() const644 HASH *Sp_handler_procedure::get_priv_hash() const
645 {
646 return &proc_priv_hash;
647 }
648
649
get_priv_hash() const650 HASH *Sp_handler_function::get_priv_hash() const
651 {
652 return &func_priv_hash;
653 }
654
655
get_priv_hash() const656 HASH *Sp_handler_package_spec::get_priv_hash() const
657 {
658 return &package_spec_priv_hash;
659 }
660
661
get_priv_hash() const662 HASH *Sp_handler_package_body::get_priv_hash() const
663 {
664 return &package_body_priv_hash;
665 }
666
667
668 /*
669 Enumeration of ACL/GRANT tables in the mysql database
670 */
671 enum enum_acl_tables
672 {
673 USER_TABLE,
674 DB_TABLE,
675 TABLES_PRIV_TABLE,
676 COLUMNS_PRIV_TABLE,
677 #define FIRST_OPTIONAL_TABLE HOST_TABLE
678 HOST_TABLE,
679 PROCS_PRIV_TABLE,
680 PROXIES_PRIV_TABLE,
681 ROLES_MAPPING_TABLE,
682 TABLES_MAX // <== always the last
683 };
684
685 static const int Table_user= 1 << USER_TABLE;
686 static const int Table_db= 1 << DB_TABLE;
687 static const int Table_tables_priv= 1 << TABLES_PRIV_TABLE;
688 static const int Table_columns_priv= 1 << COLUMNS_PRIV_TABLE;
689 static const int Table_host= 1 << HOST_TABLE;
690 static const int Table_procs_priv= 1 << PROCS_PRIV_TABLE;
691 static const int Table_proxies_priv= 1 << PROXIES_PRIV_TABLE;
692 static const int Table_roles_mapping= 1 << ROLES_MAPPING_TABLE;
693
694 /**
695 Base class representing a generic grant table from the mysql database.
696
697 The potential tables that this class can represent are:
698 user, db, columns_priv, tables_priv, host, procs_priv, proxies_priv,
699 roles_mapping
700
701 Objects belonging to this parent class can only be constructed by the
702 Grants_table class. This ensures the correct initialization of the objects.
703 */
704 class Grant_table_base
705 {
706 public:
707 /* Number of fields for this Grant Table. */
num_fields() const708 uint num_fields() const { return tl.table->s->fields; }
709 /* Check if the table exists after an attempt to open it was made.
710 Some tables, such as the host table in MySQL 5.6.7+ are missing. */
table_exists() const711 bool table_exists() const { return tl.table; };
712 /* Initializes the READ_RECORD structure provided as a parameter
713 to read through the whole table, with all columns available. Cleaning up
714 is the caller's job. */
init_read_record(READ_RECORD * info,THD * thd) const715 bool init_read_record(READ_RECORD* info, THD* thd) const
716 {
717 DBUG_ASSERT(tl.table);
718 bool result= ::init_read_record(info, thd, tl.table, NULL, NULL, 1,
719 true, false);
720 if (!result)
721 tl.table->use_all_columns();
722 return result;
723 }
724
725 /* Return the number of privilege columns for this table. */
num_privileges() const726 uint num_privileges() const { return num_privilege_cols; }
727 /* Return a privilege column by index. */
priv_field(uint privilege_idx) const728 Field* priv_field(uint privilege_idx) const
729 {
730 DBUG_ASSERT(privilege_idx < num_privileges());
731 return tl.table->field[start_privilege_column + privilege_idx];
732 }
733
734 /* Fetch the privileges from the table as a set of bits. The first column
735 is represented by the first bit in the result, the second column by the
736 second bit, etc. */
get_access() const737 ulong get_access() const
738 {
739 return get_access(start_privilege_column,
740 start_privilege_column + num_privileges() - 1);
741 }
742
743 /* Return the underlying TABLE handle. */
table() const744 TABLE* table() const
745 {
746 return tl.table;
747 }
748
749 /** Check if the table was opened, issue an error otherwise. */
no_such_table() const750 int no_such_table() const
751 {
752 if (table_exists())
753 return 0;
754
755 my_error(ER_NO_SUCH_TABLE, MYF(0), tl.db.str, tl.alias.str);
756 return 1;
757 }
758
759
760 protected:
761 friend class Grant_tables;
762
Grant_table_base()763 Grant_table_base() : start_privilege_column(0), num_privilege_cols(0)
764 {
765 tl.reset();
766 };
767
768 /* Initialization sequence common for all grant tables. This should be called
769 after all table-specific initialization is performed. */
init(enum thr_lock_type lock_type,bool is_optional)770 void init(enum thr_lock_type lock_type, bool is_optional)
771 {
772 tl.open_type= OT_BASE_ONLY;
773 tl.i_s_requested_object= OPEN_TABLE_ONLY;
774 if (is_optional)
775 tl.open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
776 }
777
778 /*
779 Get all access bits from table between start_field and end_field indices.
780
781 IMPLEMENTATION
782 The record should be already read in table->record[0]. All privileges
783 are specified as an ENUM(Y,N).
784
785 SYNOPSIS
786 get_access()
787 start_field_idx The field index at which the first privilege
788 specification begins.
789 end_field_idx The field index at which the last privilege
790 specification is located.
791
792 RETURN VALUE
793 privilege mask
794 */
get_access(uint start_field_idx,uint end_field_idx) const795 ulong get_access(uint start_field_idx, uint end_field_idx) const
796 {
797 ulong access_bits= 0, bit= 1;
798 for (uint i = start_field_idx; i <= end_field_idx; i++, bit<<=1)
799 {
800 if (get_YN_as_bool(tl.table->field[i]))
801 access_bits|= bit;
802 }
803 return access_bits;
804 }
805
806 /* Compute how many privilege columns this table has. This method
807 can only be called after the table has been opened.
808
809 IMPLEMENTATION
810 A privilege column is of type enum('Y', 'N'). Privilege columns are
811 expected to be one after another.
812 */
compute_num_privilege_cols()813 void compute_num_privilege_cols()
814 {
815 if (!table_exists()) // Table does not exist or not opened.
816 return;
817
818 num_privilege_cols= 0;
819 for (uint i= 0; i < num_fields(); i++)
820 {
821 Field *field= tl.table->field[i];
822 if (num_privilege_cols > 0 && field->real_type() != MYSQL_TYPE_ENUM)
823 return;
824 if (field->real_type() == MYSQL_TYPE_ENUM &&
825 static_cast<Field_enum*>(field)->typelib->count == 2)
826 {
827 num_privilege_cols++;
828 if (num_privilege_cols == 1)
829 start_privilege_column= i;
830 }
831 }
832 }
833
834 /* The index at which privilege columns start. */
835 uint start_privilege_column;
836 /* The number of privilege columns in the table. */
837 uint num_privilege_cols;
838
839 TABLE_LIST tl;
840 };
841
842 class User_table: public Grant_table_base
843 {
844 public:
845 /* Field getters return NULL if the column is not present in the table.
846 This is consistent only if the table is in a supported version. We do
847 not guard against corrupt tables. (yet) */
host() const848 Field* host() const
849 { return get_field(0); }
user() const850 Field* user() const
851 { return get_field(1); }
password() const852 Field* password() const
853 { return have_password() ? NULL : tl.table->field[2]; }
854 /* Columns after privilege columns. */
ssl_type() const855 Field* ssl_type() const
856 { return get_field(start_privilege_column + num_privileges()); }
ssl_cipher() const857 Field* ssl_cipher() const
858 { return get_field(start_privilege_column + num_privileges() + 1); }
x509_issuer() const859 Field* x509_issuer() const
860 { return get_field(start_privilege_column + num_privileges() + 2); }
x509_subject() const861 Field* x509_subject() const
862 { return get_field(start_privilege_column + num_privileges() + 3); }
max_questions() const863 Field* max_questions() const
864 { return get_field(start_privilege_column + num_privileges() + 4); }
max_updates() const865 Field* max_updates() const
866 { return get_field(start_privilege_column + num_privileges() + 5); }
max_connections() const867 Field* max_connections() const
868 { return get_field(start_privilege_column + num_privileges() + 6); }
max_user_connections() const869 Field* max_user_connections() const
870 { return get_field(start_privilege_column + num_privileges() + 7); }
plugin() const871 Field* plugin() const
872 { return get_field(start_privilege_column + num_privileges() + 8); }
authentication_string() const873 Field* authentication_string() const
874 { return get_field(start_privilege_column + num_privileges() + 9); }
password_expired() const875 Field* password_expired() const
876 { return get_field(start_privilege_column + num_privileges() + 10); }
is_role() const877 Field* is_role() const
878 { return get_field(start_privilege_column + num_privileges() + 11); }
default_role() const879 Field* default_role() const
880 { return get_field(start_privilege_column + num_privileges() + 12); }
max_statement_time() const881 Field* max_statement_time() const
882 { return get_field(start_privilege_column + num_privileges() + 13); }
883
884 /*
885 Check if a user entry in the user table is marked as being a role entry
886
887 IMPLEMENTATION
888 Access the coresponding column and check the coresponding ENUM of the form
889 ENUM('N', 'Y')
890
891 SYNOPSIS
892 check_is_role()
893 form an open table to read the entry from.
894 The record should be already read in table->record[0]
895
896 RETURN VALUE
897 TRUE if the user is marked as a role
898 FALSE otherwise
899 */
check_is_role() const900 bool check_is_role() const
901 {
902 /* Table version does not support roles */
903 if (!is_role())
904 return false;
905
906 return get_YN_as_bool(is_role());
907 }
908
909
get_access() const910 ulong get_access() const
911 {
912 ulong access= Grant_table_base::get_access();
913 if ((num_fields() <= 13) && (access & CREATE_ACL))
914 access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
915
916 if (num_fields() <= 18)
917 {
918 access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
919 if (access & FILE_ACL)
920 access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
921 if (access & PROCESS_ACL)
922 access|= SUPER_ACL | EXECUTE_ACL;
923 }
924 /*
925 If it is pre 5.0.1 privilege table then map CREATE privilege on
926 CREATE VIEW & SHOW VIEW privileges.
927 */
928 if (num_fields() <= 31 && (access & CREATE_ACL))
929 access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
930 /*
931 If it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
932 CREATE PROCEDURE & ALTER PROCEDURE privileges.
933 */
934 if (num_fields() <= 33)
935 {
936 if (access & CREATE_ACL)
937 access|= CREATE_PROC_ACL;
938 if (access & ALTER_ACL)
939 access|= ALTER_PROC_ACL;
940 }
941 /*
942 Pre 5.0.3 did not have CREATE_USER_ACL.
943 */
944 if (num_fields() <= 36 && (access & GRANT_ACL))
945 access|= CREATE_USER_ACL;
946 /*
947 If it is pre 5.1.6 privilege table then map CREATE privilege on
948 CREATE|ALTER|DROP|EXECUTE EVENT.
949 */
950 if (num_fields() <= 37 && (access & SUPER_ACL))
951 access|= EVENT_ACL;
952 /*
953 If it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
954 */
955 if (num_fields() <= 38 && (access & SUPER_ACL))
956 access|= TRIGGER_ACL;
957
958 if (num_fields() <= 46 && (access & DELETE_ACL))
959 access|= DELETE_HISTORY_ACL;
960
961 return access & GLOBAL_ACLS;
962 }
963
964 private:
965 friend class Grant_tables;
966
967 /* Only Grant_tables can instantiate this class. */
User_table()968 User_table() {};
969
init(enum thr_lock_type lock_type)970 void init(enum thr_lock_type lock_type)
971 {
972 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
973 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, lock_type);
974 Grant_table_base::init(lock_type, false);
975 }
976
977 /* The user table is a bit different compared to the other Grant tables.
978 Usually, we only add columns to the grant tables when adding functionality.
979 This makes it easy to test which version of the table we are using, by
980 just looking at the number of fields present in the table.
981
982 In MySQL 5.7.6 the Password column was removed. We need to guard for that.
983 The field-fetching methods for the User table return NULL if the field
984 doesn't exist. This simplifies checking of table "version", as we don't
985 have to make use of num_fields() any more.
986 */
get_field(uint field_num) const987 inline Field* get_field(uint field_num) const
988 {
989 if (field_num >= num_fields())
990 return NULL;
991
992 return tl.table->field[field_num];
993 }
994
995 /* Normally password column is the third column in the table. If privileges
996 start on the third column instead, we are missing the password column.
997 This means we are using a MySQL 5.7.6+ data directory. */
have_password() const998 bool have_password() const { return start_privilege_column == 2; }
999
1000 };
1001
1002 class Db_table: public Grant_table_base
1003 {
1004 public:
host() const1005 Field* host() const { return tl.table->field[0]; }
db() const1006 Field* db() const { return tl.table->field[1]; }
user() const1007 Field* user() const { return tl.table->field[2]; }
1008
1009 private:
1010 friend class Grant_tables;
1011
Db_table()1012 Db_table() {};
1013
init(enum thr_lock_type lock_type)1014 void init(enum thr_lock_type lock_type)
1015 {
1016 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1017 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_DB_NAME, NULL, lock_type);
1018 Grant_table_base::init(lock_type, false);
1019 }
1020 };
1021
1022 class Tables_priv_table: public Grant_table_base
1023 {
1024 public:
host() const1025 Field* host() const { return tl.table->field[0]; }
db() const1026 Field* db() const { return tl.table->field[1]; }
user() const1027 Field* user() const { return tl.table->field[2]; }
table_name() const1028 Field* table_name() const { return tl.table->field[3]; }
grantor() const1029 Field* grantor() const { return tl.table->field[4]; }
timestamp() const1030 Field* timestamp() const { return tl.table->field[5]; }
table_priv() const1031 Field* table_priv() const { return tl.table->field[6]; }
column_priv() const1032 Field* column_priv() const { return tl.table->field[7]; }
1033
1034 private:
1035 friend class Grant_tables;
1036
Tables_priv_table()1037 Tables_priv_table() {};
1038
init(enum thr_lock_type lock_type,Grant_table_base * next_table=NULL)1039 void init(enum thr_lock_type lock_type, Grant_table_base *next_table= NULL)
1040 {
1041 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1042 LEX_CSTRING MYSQL_TABLES_PRIV_NAME={STRING_WITH_LEN("tables_priv") };
1043 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLES_PRIV_NAME, NULL, lock_type);
1044 Grant_table_base::init(lock_type, false);
1045 }
1046 };
1047
1048 class Columns_priv_table: public Grant_table_base
1049 {
1050 public:
host() const1051 Field* host() const { return tl.table->field[0]; }
db() const1052 Field* db() const { return tl.table->field[1]; }
user() const1053 Field* user() const { return tl.table->field[2]; }
table_name() const1054 Field* table_name() const { return tl.table->field[3]; }
column_name() const1055 Field* column_name() const { return tl.table->field[4]; }
timestamp() const1056 Field* timestamp() const { return tl.table->field[5]; }
column_priv() const1057 Field* column_priv() const { return tl.table->field[6]; }
1058
1059 private:
1060 friend class Grant_tables;
1061
Columns_priv_table()1062 Columns_priv_table() {};
1063
init(enum thr_lock_type lock_type)1064 void init(enum thr_lock_type lock_type)
1065 {
1066 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1067 LEX_CSTRING MYSQL_COLUMNS_PRIV_NAME={ STRING_WITH_LEN("columns_priv") };
1068 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_COLUMNS_PRIV_NAME, NULL, lock_type);
1069 Grant_table_base::init(lock_type, false);
1070 }
1071 };
1072
1073 class Host_table: public Grant_table_base
1074 {
1075 public:
host() const1076 Field* host() const { return tl.table->field[0]; }
db() const1077 Field* db() const { return tl.table->field[1]; }
1078
1079 private:
1080 friend class Grant_tables;
1081
Host_table()1082 Host_table() {}
1083
init(enum thr_lock_type lock_type)1084 void init(enum thr_lock_type lock_type)
1085 {
1086 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1087 LEX_CSTRING MYSQL_HOST_NAME={STRING_WITH_LEN("host") };
1088 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HOST_NAME, NULL, lock_type);
1089 Grant_table_base::init(lock_type, true);
1090 }
1091 };
1092
1093 class Procs_priv_table: public Grant_table_base
1094 {
1095 public:
host() const1096 Field* host() const { return tl.table->field[0]; }
db() const1097 Field* db() const { return tl.table->field[1]; }
user() const1098 Field* user() const { return tl.table->field[2]; }
routine_name() const1099 Field* routine_name() const { return tl.table->field[3]; }
routine_type() const1100 Field* routine_type() const { return tl.table->field[4]; }
grantor() const1101 Field* grantor() const { return tl.table->field[5]; }
proc_priv() const1102 Field* proc_priv() const { return tl.table->field[6]; }
timestamp() const1103 Field* timestamp() const { return tl.table->field[7]; }
1104
1105 private:
1106 friend class Grant_tables;
1107
Procs_priv_table()1108 Procs_priv_table() {}
1109
init(enum thr_lock_type lock_type)1110 void init(enum thr_lock_type lock_type)
1111 {
1112 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1113 LEX_CSTRING MYSQL_PROCS_PRIV_NAME={STRING_WITH_LEN("procs_priv") };
1114 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROCS_PRIV_NAME, NULL, lock_type);
1115 Grant_table_base::init(lock_type, true);
1116 }
1117 };
1118
1119 class Proxies_priv_table: public Grant_table_base
1120 {
1121 public:
host() const1122 Field* host() const { return tl.table->field[0]; }
user() const1123 Field* user() const { return tl.table->field[1]; }
proxied_host() const1124 Field* proxied_host() const { return tl.table->field[2]; }
proxied_user() const1125 Field* proxied_user() const { return tl.table->field[3]; }
with_grant() const1126 Field* with_grant() const { return tl.table->field[4]; }
grantor() const1127 Field* grantor() const { return tl.table->field[5]; }
timestamp() const1128 Field* timestamp() const { return tl.table->field[6]; }
1129
1130 private:
1131 friend class Grant_tables;
1132
Proxies_priv_table()1133 Proxies_priv_table() {}
1134
init(enum thr_lock_type lock_type)1135 void init(enum thr_lock_type lock_type)
1136 {
1137 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1138 LEX_CSTRING MYSQL_PROXIES_PRIV_NAME={STRING_WITH_LEN("proxies_priv") };
1139 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROXIES_PRIV_NAME, NULL, lock_type);
1140 Grant_table_base::init(lock_type, true);
1141 }
1142 };
1143
1144 class Roles_mapping_table: public Grant_table_base
1145 {
1146 public:
host() const1147 Field* host() const { return tl.table->field[0]; }
user() const1148 Field* user() const { return tl.table->field[1]; }
role() const1149 Field* role() const { return tl.table->field[2]; }
admin_option() const1150 Field* admin_option() const { return tl.table->field[3]; }
1151
1152 private:
1153 friend class Grant_tables;
1154
Roles_mapping_table()1155 Roles_mapping_table() {}
1156
init(enum thr_lock_type lock_type)1157 void init(enum thr_lock_type lock_type)
1158 {
1159 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1160 LEX_CSTRING MYSQL_ROLES_MAPPING_NAME={STRING_WITH_LEN("roles_mapping") };
1161 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_ROLES_MAPPING_NAME, NULL, lock_type);
1162 Grant_table_base::init(lock_type, true);
1163 }
1164 };
1165
1166 /**
1167 Class that represents a collection of grant tables.
1168 */
1169 class Grant_tables
1170 {
1171 public:
1172 /* When constructing the Grant_tables object, we initialize only
1173 the tables which are going to be opened.
1174 @param which_tables Bitmap of which tables to open.
1175 @param lock_type Lock type to use when opening tables.
1176 */
Grant_tables(int which_tables,enum thr_lock_type lock_type)1177 Grant_tables(int which_tables, enum thr_lock_type lock_type)
1178 {
1179 DBUG_ENTER("Grant_tables::Grant_tables");
1180 DBUG_PRINT("info", ("which_tables: %x, lock_type: %u",
1181 which_tables, lock_type));
1182 DBUG_ASSERT(which_tables); /* At least one table must be opened. */
1183 Grant_table_base* prev= NULL;
1184 /* We start from the last table, Table_roles_mapping, such that
1185 the first one in the linked list is Table_user. */
1186 if (which_tables & Table_roles_mapping)
1187 {
1188 m_roles_mapping_table.init(lock_type);
1189 prev= &m_roles_mapping_table;
1190 }
1191 if (which_tables & Table_proxies_priv)
1192 {
1193 m_proxies_priv_table.init(lock_type);
1194 link_tables(&m_proxies_priv_table, prev);
1195 prev= &m_proxies_priv_table;
1196 }
1197 if (which_tables & Table_procs_priv)
1198 {
1199 m_procs_priv_table.init(lock_type);
1200 link_tables(&m_procs_priv_table, prev);
1201 prev= &m_procs_priv_table;
1202 }
1203 if (which_tables & Table_host)
1204 {
1205 m_host_table.init(lock_type);
1206 link_tables(&m_host_table, prev);
1207 prev= &m_host_table;
1208 }
1209 if (which_tables & Table_columns_priv)
1210 {
1211 m_columns_priv_table.init(lock_type);
1212 link_tables(&m_columns_priv_table, prev);
1213 prev= &m_columns_priv_table;
1214 }
1215 if (which_tables & Table_tables_priv)
1216 {
1217 m_tables_priv_table.init(lock_type);
1218 link_tables(&m_tables_priv_table, prev);
1219 prev= &m_tables_priv_table;
1220 }
1221 if (which_tables & Table_db)
1222 {
1223 m_db_table.init(lock_type);
1224 link_tables(&m_db_table, prev);
1225 prev= &m_db_table;
1226 }
1227 if (which_tables & Table_user)
1228 {
1229 m_user_table.init(lock_type);
1230 link_tables(&m_user_table, prev);
1231 prev= &m_user_table;
1232 }
1233
1234 first_table_in_list= prev;
1235 DBUG_VOID_RETURN;
1236 }
1237
1238 /* Before any operation is possible on grant tables, they must be opened.
1239 This opens the tables according to the lock type specified during
1240 construction.
1241
1242 @retval 1 replication filters matched. Abort the operation,
1243 but return OK (!)
1244 @retval 0 tables were opened successfully
1245 @retval -1 error, tables could not be opened
1246 */
open_and_lock(THD * thd)1247 int open_and_lock(THD *thd)
1248 {
1249 DBUG_ENTER("Grant_tables::open_and_lock");
1250 DBUG_ASSERT(first_table_in_list);
1251 #ifdef HAVE_REPLICATION
1252 if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE &&
1253 thd->slave_thread && !thd->spcont)
1254 {
1255 /*
1256 GRANT and REVOKE are applied the slave in/exclusion rules as they are
1257 some kind of updates to the mysql.% tables.
1258 */
1259 Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
1260 if (rpl_filter->is_on() &&
1261 !rpl_filter->tables_ok(0, &first_table_in_list->tl))
1262 DBUG_RETURN(1);
1263 }
1264 #endif
1265 if (open_and_lock_tables(thd, &first_table_in_list->tl, FALSE,
1266 MYSQL_LOCK_IGNORE_TIMEOUT))
1267 DBUG_RETURN(-1);
1268
1269 /*
1270 We can read privilege tables even when !initialized.
1271 This can be acl_load() - server startup or FLUSH PRIVILEGES
1272 */
1273 if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE &&
1274 !initialized)
1275 {
1276 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1277 DBUG_RETURN(-1);
1278 }
1279
1280 /* The privilge columns vary based on MariaDB version. Figure out
1281 how many we have after we've opened the table. */
1282 m_user_table.compute_num_privilege_cols();
1283 m_db_table.compute_num_privilege_cols();
1284 m_tables_priv_table.compute_num_privilege_cols();
1285 m_columns_priv_table.compute_num_privilege_cols();
1286 m_host_table.compute_num_privilege_cols();
1287 m_procs_priv_table.compute_num_privilege_cols();
1288 m_proxies_priv_table.compute_num_privilege_cols();
1289 m_roles_mapping_table.compute_num_privilege_cols();
1290 DBUG_RETURN(0);
1291 }
1292
user_table() const1293 inline const User_table& user_table() const
1294 {
1295 return m_user_table;
1296 }
1297
db_table() const1298 inline const Db_table& db_table() const
1299 {
1300 return m_db_table;
1301 }
1302
1303
tables_priv_table() const1304 inline const Tables_priv_table& tables_priv_table() const
1305 {
1306 return m_tables_priv_table;
1307 }
1308
columns_priv_table() const1309 inline const Columns_priv_table& columns_priv_table() const
1310 {
1311 return m_columns_priv_table;
1312 }
1313
host_table() const1314 inline const Host_table& host_table() const
1315 {
1316 return m_host_table;
1317 }
1318
procs_priv_table() const1319 inline const Procs_priv_table& procs_priv_table() const
1320 {
1321 return m_procs_priv_table;
1322 }
1323
proxies_priv_table() const1324 inline const Proxies_priv_table& proxies_priv_table() const
1325 {
1326 return m_proxies_priv_table;
1327 }
1328
roles_mapping_table() const1329 inline const Roles_mapping_table& roles_mapping_table() const
1330 {
1331 return m_roles_mapping_table;
1332 }
1333
1334 private:
1335 User_table m_user_table;
1336 Db_table m_db_table;
1337 Tables_priv_table m_tables_priv_table;
1338 Columns_priv_table m_columns_priv_table;
1339 Host_table m_host_table;
1340 Procs_priv_table m_procs_priv_table;
1341 Proxies_priv_table m_proxies_priv_table;
1342 Roles_mapping_table m_roles_mapping_table;
1343
1344 /* The grant tables are set-up in a linked list. We keep the head of it. */
1345 Grant_table_base *first_table_in_list;
1346 /**
1347 Chain two grant tables' TABLE_LIST members.
1348 */
link_tables(Grant_table_base * from,Grant_table_base * to)1349 static void link_tables(Grant_table_base *from, Grant_table_base *to)
1350 {
1351 DBUG_ASSERT(from);
1352 if (to)
1353 from->tl.next_local= from->tl.next_global= &to->tl;
1354 else
1355 from->tl.next_local= from->tl.next_global= NULL;
1356 }
1357 };
1358
1359
init(const Proxies_priv_table & proxies_priv_table,MEM_ROOT * mem)1360 void ACL_PROXY_USER::init(const Proxies_priv_table& proxies_priv_table,
1361 MEM_ROOT *mem)
1362 {
1363 init(get_field(mem, proxies_priv_table.host()),
1364 get_field(mem, proxies_priv_table.user()),
1365 get_field(mem, proxies_priv_table.proxied_host()),
1366 get_field(mem, proxies_priv_table.proxied_user()),
1367 proxies_priv_table.with_grant()->val_int() != 0);
1368 }
1369
1370
1371
1372 /*
1373 Enumeration of various ACL's and Hashes used in handle_grant_struct()
1374 */
1375 enum enum_acl_lists
1376 {
1377 USER_ACL= 0,
1378 ROLE_ACL,
1379 DB_ACL,
1380 COLUMN_PRIVILEGES_HASH,
1381 PROC_PRIVILEGES_HASH,
1382 FUNC_PRIVILEGES_HASH,
1383 PACKAGE_SPEC_PRIVILEGES_HASH,
1384 PACKAGE_BODY_PRIVILEGES_HASH,
1385 PROXY_USERS_ACL,
1386 ROLES_MAPPINGS_HASH
1387 };
1388
ACL_ROLE(ACL_USER * user,MEM_ROOT * root)1389 ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) : counter(0)
1390 {
1391
1392 access= user->access;
1393 /* set initial role access the same as the table row privileges */
1394 initial_role_access= user->access;
1395 this->user.str= safe_strdup_root(root, user->user.str);
1396 this->user.length= user->user.length;
1397 bzero(&role_grants, sizeof(role_grants));
1398 bzero(&parent_grantee, sizeof(parent_grantee));
1399 flags= IS_ROLE;
1400 }
1401
ACL_ROLE(const char * rolename,ulong privileges,MEM_ROOT * root)1402 ACL_ROLE::ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *root) :
1403 initial_role_access(privileges), counter(0)
1404 {
1405 this->access= initial_role_access;
1406 this->user.str= safe_strdup_root(root, rolename);
1407 this->user.length= strlen(rolename);
1408 bzero(&role_grants, sizeof(role_grants));
1409 bzero(&parent_grantee, sizeof(parent_grantee));
1410 flags= IS_ROLE;
1411 }
1412
1413
is_invalid_role_name(const char * str)1414 static bool is_invalid_role_name(const char *str)
1415 {
1416 if (*str && strcasecmp(str, "PUBLIC") && strcasecmp(str, "NONE"))
1417 return false;
1418
1419 my_error(ER_INVALID_ROLE, MYF(0), str);
1420 return true;
1421 }
1422
1423
free_acl_user(ACL_USER * user)1424 static void free_acl_user(ACL_USER *user)
1425 {
1426 delete_dynamic(&(user->role_grants));
1427 }
1428
free_acl_role(ACL_ROLE * role)1429 static void free_acl_role(ACL_ROLE *role)
1430 {
1431 delete_dynamic(&(role->role_grants));
1432 delete_dynamic(&(role->parent_grantee));
1433 }
1434
check_if_exists(THD *,plugin_ref,void *)1435 static my_bool check_if_exists(THD *, plugin_ref, void *)
1436 {
1437 return TRUE;
1438 }
1439
has_validation_plugins()1440 static bool has_validation_plugins()
1441 {
1442 return plugin_foreach(NULL, check_if_exists,
1443 MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL);
1444 }
1445
1446 struct validation_data { LEX_CSTRING *user, *password; };
1447
do_validate(THD *,plugin_ref plugin,void * arg)1448 static my_bool do_validate(THD *, plugin_ref plugin, void *arg)
1449 {
1450 struct validation_data *data= (struct validation_data *)arg;
1451 struct st_mariadb_password_validation *handler=
1452 (st_mariadb_password_validation *)plugin_decl(plugin)->info;
1453 return handler->validate_password(data->user, data->password);
1454 }
1455
1456
validate_password(LEX_USER * user,THD * thd)1457 static bool validate_password(LEX_USER *user, THD *thd)
1458 {
1459 if (user->pwtext.length || !user->pwhash.length)
1460 {
1461 struct validation_data data= { &user->user,
1462 user->pwtext.str ? &user->pwtext :
1463 const_cast<LEX_CSTRING *>(&empty_clex_str) };
1464 if (plugin_foreach(NULL, do_validate,
1465 MariaDB_PASSWORD_VALIDATION_PLUGIN, &data))
1466 {
1467 my_error(ER_NOT_VALID_PASSWORD, MYF(0));
1468 return true;
1469 }
1470 }
1471 else
1472 {
1473 if (!thd->slave_thread &&
1474 strict_password_validation && has_validation_plugins()
1475 #ifdef WITH_WSREP
1476 && !thd->wsrep_applier
1477 #endif
1478 )
1479 {
1480 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--strict-password-validation");
1481 return true;
1482 }
1483 }
1484 return false;
1485 }
1486
1487 /**
1488 Convert scrambled password to binary form, according to scramble type,
1489 Binary form is stored in user.salt.
1490
1491 @param acl_user The object where to store the salt
1492 @param password The password hash containing the salt
1493 @param password_len The length of the password hash
1494
1495 Despite the name of the function it is used when loading ACLs from disk
1496 to store the password hash in the ACL_USER object.
1497 */
1498
1499 static void
set_user_salt(ACL_USER * acl_user,const char * password,size_t password_len)1500 set_user_salt(ACL_USER *acl_user, const char *password, size_t password_len)
1501 {
1502 if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
1503 {
1504 get_salt_from_password(acl_user->salt, password);
1505 acl_user->salt_len= SCRAMBLE_LENGTH;
1506 }
1507 else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1508 {
1509 get_salt_from_password_323((ulong *) acl_user->salt, password);
1510 acl_user->salt_len= SCRAMBLE_LENGTH_323;
1511 }
1512 else
1513 acl_user->salt_len= 0;
1514 }
1515
fix_plugin_ptr(const char * name)1516 static const char *fix_plugin_ptr(const char *name)
1517 {
1518 if (my_strcasecmp(system_charset_info, name,
1519 native_password_plugin_name.str) == 0)
1520 return native_password_plugin_name.str;
1521 else
1522 if (my_strcasecmp(system_charset_info, name,
1523 old_password_plugin_name.str) == 0)
1524 return old_password_plugin_name.str;
1525 else
1526 return name;
1527 }
1528
1529 /**
1530 Fix ACL::plugin pointer to point to a hard-coded string, if appropriate
1531
1532 Make sure that if ACL_USER's plugin is a built-in, then it points
1533 to a hard coded string, not to an allocated copy. Run-time, for
1534 authentication, we want to be able to detect built-ins by comparing
1535 pointers, not strings.
1536
1537 Additionally - update the salt if the plugin is built-in.
1538
1539 @retval 0 the pointers were fixed
1540 @retval 1 this ACL_USER uses a not built-in plugin
1541 */
fix_user_plugin_ptr(ACL_USER * user)1542 static bool fix_user_plugin_ptr(ACL_USER *user)
1543 {
1544 if (lex_string_eq(&user->plugin, &native_password_plugin_name))
1545 user->plugin= native_password_plugin_name;
1546 else
1547 if (lex_string_eq(&user->plugin, &old_password_plugin_name))
1548 user->plugin= old_password_plugin_name;
1549 else
1550 return true;
1551
1552 set_user_salt(user, user->auth_string.str, user->auth_string.length);
1553 return false;
1554 }
1555
1556
1557 /*
1558 Validates the password, calculates password hash, transforms
1559 equivalent LEX_USER representations.
1560
1561 Upon entering this function:
1562
1563 - if user->plugin is specified, user->auth is the plugin auth data.
1564 - if user->plugin is mysql_native_password or mysql_old_password,
1565 user->auth is the password hash, and LEX_USER is transformed
1566 to match the next case (that is, user->plugin is cleared).
1567 - if user->plugin is NOT specified, built-in auth is assumed, that is
1568 mysql_native_password or mysql_old_password. In that case,
1569 user->pwhash is the password hash. And user->pwtext is the original
1570 plain-text password. Either one can be set or both.
1571
1572 Upon exiting this function:
1573
1574 - user->pwtext is left untouched
1575 - user->pwhash is the password hash, as the mysql.user.password column
1576 - user->plugin is the plugin name, as the mysql.user.plugin column
1577 - user->auth is the plugin auth data, as the mysql.user.authentication_string column
1578 */
fix_lex_user(THD * thd,LEX_USER * user)1579 static bool fix_lex_user(THD *thd, LEX_USER *user)
1580 {
1581 size_t check_length;
1582
1583 DBUG_ASSERT(user->plugin.length || !user->auth.length);
1584 DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length)));
1585
1586 if (lex_string_eq(&user->plugin, &native_password_plugin_name))
1587 check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
1588 else
1589 if (lex_string_eq(&user->plugin, &old_password_plugin_name))
1590 check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
1591 else
1592 if (user->plugin.length)
1593 return false; // nothing else to do
1594 else if (thd->variables.old_passwords == 1 ||
1595 user->pwhash.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1596 check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
1597 else
1598 check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
1599
1600 if (user->plugin.length)
1601 {
1602 user->pwhash= user->auth;
1603 user->plugin= empty_clex_str;
1604 user->auth= empty_clex_str;
1605 }
1606
1607 if (user->pwhash.length && user->pwhash.length != check_length)
1608 {
1609 my_error(ER_PASSWD_LENGTH, MYF(0), (int) check_length);
1610 return true;
1611 }
1612
1613 if (user->pwtext.length && !user->pwhash.length)
1614 {
1615 size_t scramble_length;
1616 void (*make_scramble)(char *, const char *, size_t);
1617
1618 if (thd->variables.old_passwords == 1)
1619 {
1620 scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
1621 make_scramble= my_make_scrambled_password_323;
1622 }
1623 else
1624 {
1625 scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
1626 make_scramble= my_make_scrambled_password;
1627 }
1628
1629 Query_arena *arena, backup;
1630 arena= thd->activate_stmt_arena_if_needed(&backup);
1631 char *buff= (char *) thd->alloc(scramble_length + 1);
1632 if (arena)
1633 thd->restore_active_arena(arena, &backup);
1634
1635 if (buff == NULL)
1636 return true;
1637 make_scramble(buff, user->pwtext.str, user->pwtext.length);
1638 user->pwhash.str= buff;
1639 user->pwhash.length= scramble_length;
1640 }
1641
1642 return false;
1643 }
1644
1645
get_YN_as_bool(Field * field)1646 static bool get_YN_as_bool(Field *field)
1647 {
1648 char buff[2];
1649 String res(buff,sizeof(buff),&my_charset_latin1);
1650 field->val_str(&res);
1651 return res[0] == 'Y' || res[0] == 'y';
1652 }
1653
1654
1655 /*
1656 Initialize structures responsible for user/db-level privilege checking and
1657 load privilege information for them from tables in the 'mysql' database.
1658
1659 SYNOPSIS
1660 acl_init()
1661 dont_read_acl_tables TRUE if we want to skip loading data from
1662 privilege tables and disable privilege checking.
1663
1664 NOTES
1665 This function is mostly responsible for preparatory steps, main work
1666 on initialization and grants loading is done in acl_reload().
1667
1668 RETURN VALUES
1669 0 ok
1670 1 Could not initialize grant's
1671 */
1672
acl_init(bool dont_read_acl_tables)1673 bool acl_init(bool dont_read_acl_tables)
1674 {
1675 THD *thd;
1676 bool return_val;
1677 DBUG_ENTER("acl_init");
1678
1679 acl_cache= new Hash_filo<acl_entry>(ACL_CACHE_SIZE, 0, 0,
1680 (my_hash_get_key) acl_entry_get_key,
1681 (my_hash_free_key) free,
1682 &my_charset_utf8_bin);
1683
1684 /*
1685 cache built-in native authentication plugins,
1686 to avoid hash searches and a global mutex lock on every connect
1687 */
1688 native_password_plugin= my_plugin_lock_by_name(0,
1689 &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
1690 old_password_plugin= my_plugin_lock_by_name(0,
1691 &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
1692
1693 if (!native_password_plugin || !old_password_plugin)
1694 DBUG_RETURN(1);
1695
1696 if (dont_read_acl_tables)
1697 {
1698 DBUG_RETURN(0); /* purecov: tested */
1699 }
1700
1701 /*
1702 To be able to run this from boot, we allocate a temporary THD
1703 */
1704 if (!(thd=new THD(0)))
1705 DBUG_RETURN(1); /* purecov: inspected */
1706 thd->thread_stack= (char*) &thd;
1707 thd->store_globals();
1708 /*
1709 It is safe to call acl_reload() since acl_* arrays and hashes which
1710 will be freed there are global static objects and thus are initialized
1711 by zeros at startup.
1712 */
1713 return_val= acl_reload(thd);
1714 delete thd;
1715 DBUG_RETURN(return_val);
1716 }
1717
1718 /**
1719 Choose from either native or old password plugins when assigning a password
1720 */
1721
set_user_plugin(ACL_USER * user,size_t password_len)1722 static bool set_user_plugin (ACL_USER *user, size_t password_len)
1723 {
1724 switch (password_len)
1725 {
1726 case 0: /* no password */
1727 case SCRAMBLED_PASSWORD_CHAR_LENGTH:
1728 user->plugin= native_password_plugin_name;
1729 return FALSE;
1730 case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
1731 user->plugin= old_password_plugin_name;
1732 return FALSE;
1733 default:
1734 sql_print_warning("Found invalid password for user: '%s@%s'; "
1735 "Ignoring user", safe_str(user->user.str),
1736 safe_str(user->host.hostname));
1737 return TRUE;
1738 }
1739 }
1740
1741
1742 /*
1743 Initialize structures responsible for user/db-level privilege checking
1744 and load information about grants from open privilege tables.
1745
1746 SYNOPSIS
1747 acl_load()
1748 thd Current thread
1749 tables List containing open "mysql.host", "mysql.user",
1750 "mysql.db", "mysql.proxies_priv" and "mysql.roles_mapping"
1751 tables.
1752
1753 RETURN VALUES
1754 FALSE Success
1755 TRUE Error
1756 */
1757
acl_load(THD * thd,const Grant_tables & tables)1758 static bool acl_load(THD *thd, const Grant_tables& tables)
1759 {
1760 READ_RECORD read_record_info;
1761 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
1762 char tmp_name[SAFE_NAME_LEN+1];
1763 int password_length;
1764 Sql_mode_save old_mode_save(thd);
1765 DBUG_ENTER("acl_load");
1766
1767 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
1768
1769 grant_version++; /* Privileges updated */
1770
1771 const Host_table& host_table= tables.host_table();
1772 init_sql_alloc(&acl_memroot, "ACL", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
1773 if (host_table.table_exists()) // "host" table may not exist (e.g. in MySQL 5.6.7+)
1774 {
1775 if (host_table.init_read_record(&read_record_info, thd))
1776 DBUG_RETURN(true);
1777 while (!(read_record_info.read_record()))
1778 {
1779 ACL_HOST host;
1780 update_hostname(&host.host, get_field(&acl_memroot, host_table.host()));
1781 host.db= get_field(&acl_memroot, host_table.db());
1782 if (lower_case_table_names && host.db)
1783 {
1784 /*
1785 convert db to lower case and give a warning if the db wasn't
1786 already in lower case
1787 */
1788 char *end = strnmov(tmp_name, host.db, sizeof(tmp_name));
1789 if (end >= tmp_name + sizeof(tmp_name))
1790 {
1791 sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), host.db);
1792 continue;
1793 }
1794 my_casedn_str(files_charset_info, host.db);
1795 if (strcmp(host.db, tmp_name) != 0)
1796 sql_print_warning("'host' entry '%s|%s' had database in mixed "
1797 "case that has been forced to lowercase because "
1798 "lower_case_table_names is set. It will not be "
1799 "possible to remove this privilege using REVOKE.",
1800 host.host.hostname, host.db);
1801 }
1802 host.access= host_table.get_access();
1803 host.access= fix_rights_for_db(host.access);
1804 host.sort= get_sort(2, host.host.hostname, host.db);
1805 if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
1806 {
1807 sql_print_warning("'host' entry '%s|%s' "
1808 "ignored in --skip-name-resolve mode.",
1809 safe_str(host.host.hostname),
1810 safe_str(host.db));
1811 continue;
1812 }
1813 #ifndef TO_BE_REMOVED
1814 if (host_table.num_fields() == 8)
1815 { // Without grant
1816 if (host.access & CREATE_ACL)
1817 host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
1818 }
1819 #endif
1820 (void) push_dynamic(&acl_hosts,(uchar*) &host);
1821 }
1822 my_qsort((uchar*) dynamic_element(&acl_hosts, 0, ACL_HOST*),
1823 acl_hosts.elements, sizeof(ACL_HOST),(qsort_cmp) acl_compare);
1824 end_read_record(&read_record_info);
1825 }
1826 freeze_size(&acl_hosts);
1827
1828 const User_table& user_table= tables.user_table();
1829 if (user_table.init_read_record(&read_record_info, thd))
1830 DBUG_RETURN(true);
1831
1832 if (user_table.num_fields() < 13) // number of columns in 3.21
1833 {
1834 sql_print_error("Fatal error: mysql.user table is damaged or in "
1835 "unsupported 3.20 format.");
1836 DBUG_RETURN(true);
1837 }
1838 username_char_length= MY_MIN(user_table.user()->char_length(),
1839 USERNAME_CHAR_LENGTH);
1840 if (user_table.password()) // Password column might be missing. (MySQL 5.7.6+)
1841 {
1842 password_length= user_table.password()->field_length /
1843 user_table.password()->charset()->mbmaxlen;
1844 if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1845 {
1846 sql_print_error("Fatal error: mysql.user table is damaged or in "
1847 "unsupported 3.20 format.");
1848 DBUG_RETURN(TRUE);
1849 }
1850
1851 DBUG_PRINT("info",("user table fields: %d, password length: %d",
1852 user_table.num_fields(), password_length));
1853
1854 mysql_mutex_lock(&LOCK_global_system_variables);
1855 if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
1856 {
1857 if (opt_secure_auth)
1858 {
1859 mysql_mutex_unlock(&LOCK_global_system_variables);
1860 sql_print_error("Fatal error: mysql.user table is in old format, "
1861 "but server started with --secure-auth option.");
1862 DBUG_RETURN(TRUE);
1863 }
1864 mysql_user_table_is_in_short_password_format= true;
1865 if (global_system_variables.old_passwords)
1866 mysql_mutex_unlock(&LOCK_global_system_variables);
1867 else
1868 {
1869 extern sys_var *Sys_old_passwords_ptr;
1870 Sys_old_passwords_ptr->value_origin= sys_var::AUTO;
1871 global_system_variables.old_passwords= 1;
1872 mysql_mutex_unlock(&LOCK_global_system_variables);
1873 sql_print_warning("mysql.user table is not updated to new password format; "
1874 "Disabling new password usage until "
1875 "mysql_fix_privilege_tables is run");
1876 }
1877 thd->variables.old_passwords= 1;
1878 }
1879 else
1880 {
1881 mysql_user_table_is_in_short_password_format= false;
1882 mysql_mutex_unlock(&LOCK_global_system_variables);
1883 }
1884 }
1885
1886 allow_all_hosts=0;
1887 while (!(read_record_info.read_record()))
1888 {
1889 ACL_USER user;
1890 bool is_role= FALSE;
1891 bzero(&user, sizeof(user));
1892 update_hostname(&user.host, get_field(&acl_memroot, user_table.host()));
1893 char *username= get_field(&acl_memroot, user_table.user());
1894 user.user.str= username;
1895 user.user.length= safe_strlen(username);
1896
1897 /*
1898 If the user entry is a role, skip password and hostname checks
1899 A user can not log in with a role so some checks are not necessary
1900 */
1901 is_role= user_table.check_is_role();
1902
1903 if (is_role && is_invalid_role_name(username))
1904 {
1905 thd->clear_error(); // the warning is still issued
1906 continue;
1907 }
1908
1909 if (!is_role && check_no_resolve &&
1910 hostname_requires_resolving(user.host.hostname))
1911 {
1912 sql_print_warning("'user' entry '%s@%s' "
1913 "ignored in --skip-name-resolve mode.",
1914 safe_str(user.user.str),
1915 safe_str(user.host.hostname));
1916 continue;
1917 }
1918
1919 char *password= const_cast<char*>("");
1920 if (user_table.password())
1921 password= get_field(&acl_memroot, user_table.password());
1922 size_t password_len= safe_strlen(password);
1923 user.auth_string.str= safe_str(password);
1924 user.auth_string.length= password_len;
1925 set_user_salt(&user, password, password_len);
1926
1927 if (!is_role && set_user_plugin(&user, password_len))
1928 continue;
1929
1930 {
1931 user.access= user_table.get_access();
1932 user.sort= get_sort(2, user.host.hostname, user.user.str);
1933 user.hostname_length= safe_strlen(user.host.hostname);
1934 user.user_resource.user_conn= 0;
1935 user.user_resource.max_statement_time= 0.0;
1936
1937 /* Starting from 4.0.2 we have more fields */
1938 if (user_table.ssl_type())
1939 {
1940 char *ssl_type=get_field(thd->mem_root, user_table.ssl_type());
1941 if (!ssl_type)
1942 user.ssl_type=SSL_TYPE_NONE;
1943 else if (!strcmp(ssl_type, "ANY"))
1944 user.ssl_type=SSL_TYPE_ANY;
1945 else if (!strcmp(ssl_type, "X509"))
1946 user.ssl_type=SSL_TYPE_X509;
1947 else /* !strcmp(ssl_type, "SPECIFIED") */
1948 user.ssl_type=SSL_TYPE_SPECIFIED;
1949
1950 user.ssl_cipher= get_field(&acl_memroot, user_table.ssl_cipher());
1951 user.x509_issuer= get_field(&acl_memroot, user_table.x509_issuer());
1952 user.x509_subject= get_field(&acl_memroot, user_table.x509_subject());
1953
1954 char *ptr = get_field(thd->mem_root, user_table.max_questions());
1955 user.user_resource.questions=ptr ? atoi(ptr) : 0;
1956 ptr = get_field(thd->mem_root, user_table.max_updates());
1957 user.user_resource.updates=ptr ? atoi(ptr) : 0;
1958 ptr = get_field(thd->mem_root, user_table.max_connections());
1959 user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
1960 if (user.user_resource.questions || user.user_resource.updates ||
1961 user.user_resource.conn_per_hour)
1962 mqh_used=1;
1963
1964 if (user_table.max_user_connections())
1965 {
1966 /* Starting from 5.0.3 we have max_user_connections field */
1967 ptr= get_field(thd->mem_root, user_table.max_user_connections());
1968 user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
1969 }
1970
1971 if (!is_role && user_table.plugin())
1972 {
1973 /* We may have plugin & auth_String fields */
1974 char *tmpstr= get_field(&acl_memroot, user_table.plugin());
1975 if (tmpstr)
1976 {
1977 user.plugin.str= tmpstr;
1978 user.plugin.length= strlen(user.plugin.str);
1979 user.auth_string.str=
1980 safe_str(get_field(&acl_memroot,
1981 user_table.authentication_string()));
1982 user.auth_string.length= strlen(user.auth_string.str);
1983
1984 if (user.auth_string.length && password_len &&
1985 (user.auth_string.length != password_len ||
1986 memcmp(user.auth_string.str, password, password_len)))
1987 {
1988 sql_print_warning("'user' entry '%s@%s' has both a password "
1989 "and an authentication plugin specified. The "
1990 "password will be ignored.",
1991 safe_str(user.user.str),
1992 safe_str(user.host.hostname));
1993 }
1994 else if (password_len)
1995 {
1996 user.auth_string.str= password;
1997 user.auth_string.length= password_len;
1998 }
1999
2000 fix_user_plugin_ptr(&user);
2001 }
2002 }
2003
2004 if (user_table.max_statement_time())
2005 {
2006 /* Starting from 10.1.1 we can have max_statement_time */
2007 ptr= get_field(thd->mem_root,
2008 user_table.max_statement_time());
2009 user.user_resource.max_statement_time= ptr ? atof(ptr) : 0.0;
2010 }
2011 }
2012 else
2013 {
2014 user.ssl_type=SSL_TYPE_NONE;
2015 #ifndef TO_BE_REMOVED
2016 if (user_table.num_fields() <= 13)
2017 { // Without grant
2018 if (user.access & CREATE_ACL)
2019 user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
2020 }
2021 /* Convert old privileges */
2022 user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
2023 if (user.access & FILE_ACL)
2024 user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
2025 if (user.access & PROCESS_ACL)
2026 user.access|= SUPER_ACL | EXECUTE_ACL;
2027 #endif
2028 }
2029
2030 (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *),
2031 8, 8, MYF(0));
2032
2033 /* check default role, if any */
2034 if (!is_role && user_table.default_role())
2035 {
2036 user.default_rolename.str=
2037 get_field(&acl_memroot, user_table.default_role());
2038 user.default_rolename.length= safe_strlen(user.default_rolename.str);
2039 }
2040
2041 if (is_role)
2042 {
2043 DBUG_PRINT("info", ("Found role %s", user.user.str));
2044 ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot);
2045 entry->role_grants = user.role_grants;
2046 (void) my_init_dynamic_array(&entry->parent_grantee,
2047 sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
2048 my_hash_insert(&acl_roles, (uchar *)entry);
2049
2050 continue;
2051 }
2052 else
2053 {
2054 DBUG_PRINT("info", ("Found user %s", user.user.str));
2055 (void) push_dynamic(&acl_users,(uchar*) &user);
2056 }
2057 if (!user.host.hostname ||
2058 (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
2059 allow_all_hosts=1; // Anyone can connect
2060 }
2061 }
2062 my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
2063 sizeof(ACL_USER),(qsort_cmp) acl_compare);
2064 end_read_record(&read_record_info);
2065 freeze_size(&acl_users);
2066
2067 const Db_table& db_table= tables.db_table();
2068 if (db_table.init_read_record(&read_record_info, thd))
2069 DBUG_RETURN(TRUE);
2070 while (!(read_record_info.read_record()))
2071 {
2072 ACL_DB db;
2073 char *db_name;
2074 db.user=get_field(&acl_memroot, db_table.user());
2075 const char *hostname= get_field(&acl_memroot, db_table.host());
2076 if (!hostname && find_acl_role(db.user))
2077 hostname= "";
2078 update_hostname(&db.host, hostname);
2079 db.db= db_name= get_field(&acl_memroot, db_table.db());
2080 if (!db.db)
2081 {
2082 sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
2083 continue;
2084 }
2085 if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
2086 {
2087 sql_print_warning("'db' entry '%s %s@%s' "
2088 "ignored in --skip-name-resolve mode.",
2089 db.db, safe_str(db.user), safe_str(db.host.hostname));
2090 continue;
2091 }
2092 db.access= db_table.get_access();
2093 db.access=fix_rights_for_db(db.access);
2094 db.initial_access= db.access;
2095 if (lower_case_table_names)
2096 {
2097 /*
2098 convert db to lower case and give a warning if the db wasn't
2099 already in lower case
2100 */
2101 char *end = strnmov(tmp_name, db.db, sizeof(tmp_name));
2102 if (end >= tmp_name + sizeof(tmp_name))
2103 {
2104 sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), db.db);
2105 continue;
2106 }
2107 my_casedn_str(files_charset_info, db_name);
2108 if (strcmp(db_name, tmp_name) != 0)
2109 {
2110 sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
2111 "case that has been forced to lowercase because "
2112 "lower_case_table_names is set. It will not be "
2113 "possible to remove this privilege using REVOKE.",
2114 db.db, safe_str(db.user), safe_str(db.host.hostname));
2115 }
2116 }
2117 db.sort=get_sort(3,db.host.hostname,db.db,db.user);
2118 #ifndef TO_BE_REMOVED
2119 if (db_table.num_fields() <= 9)
2120 { // Without grant
2121 if (db.access & CREATE_ACL)
2122 db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
2123 }
2124 #endif
2125 acl_dbs.push(db);
2126 }
2127 end_read_record(&read_record_info);
2128 acl_dbs.sort((acl_dbs_cmp)acl_compare);
2129 acl_dbs.freeze();
2130
2131 const Proxies_priv_table& proxies_priv_table= tables.proxies_priv_table();
2132 if (proxies_priv_table.table_exists())
2133 {
2134 if (proxies_priv_table.init_read_record(&read_record_info, thd))
2135 DBUG_RETURN(TRUE);
2136 while (!(read_record_info.read_record()))
2137 {
2138 ACL_PROXY_USER proxy;
2139 proxy.init(proxies_priv_table, &acl_memroot);
2140 if (proxy.check_validity(check_no_resolve))
2141 continue;
2142 if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
2143 DBUG_RETURN(TRUE);
2144 }
2145 my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
2146 acl_proxy_users.elements,
2147 sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
2148 end_read_record(&read_record_info);
2149 }
2150 else
2151 {
2152 sql_print_error("Missing system table mysql.proxies_priv; "
2153 "please run mysql_upgrade to create it");
2154 }
2155 freeze_size(&acl_proxy_users);
2156
2157 const Roles_mapping_table& roles_mapping_table= tables.roles_mapping_table();
2158 if (roles_mapping_table.table_exists())
2159 {
2160 if (roles_mapping_table.init_read_record(&read_record_info, thd))
2161 DBUG_RETURN(TRUE);
2162
2163 MEM_ROOT temp_root;
2164 init_alloc_root(&temp_root, "ACL_tmp", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
2165 while (!(read_record_info.read_record()))
2166 {
2167 char *hostname= safe_str(get_field(&temp_root, roles_mapping_table.host()));
2168 char *username= safe_str(get_field(&temp_root, roles_mapping_table.user()));
2169 char *rolename= safe_str(get_field(&temp_root, roles_mapping_table.role()));
2170 bool with_grant_option= get_YN_as_bool(roles_mapping_table.admin_option());
2171
2172 if (add_role_user_mapping(username, hostname, rolename)) {
2173 sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'",
2174 username, hostname, rolename);
2175 continue;
2176 }
2177
2178 ROLE_GRANT_PAIR *mapping= new (&acl_memroot) ROLE_GRANT_PAIR;
2179
2180 if (mapping->init(&acl_memroot, username, hostname, rolename, with_grant_option))
2181 continue;
2182
2183 my_hash_insert(&acl_roles_mappings, (uchar*) mapping);
2184 }
2185
2186 free_root(&temp_root, MYF(0));
2187 end_read_record(&read_record_info);
2188 }
2189 else
2190 {
2191 sql_print_error("Missing system table mysql.roles_mapping; "
2192 "please run mysql_upgrade to create it");
2193 }
2194
2195 init_check_host();
2196
2197 initialized=1;
2198 DBUG_RETURN(FALSE);
2199 }
2200
2201
acl_free(bool end)2202 void acl_free(bool end)
2203 {
2204 my_hash_free(&acl_roles);
2205 free_root(&acl_memroot,MYF(0));
2206 delete_dynamic(&acl_hosts);
2207 delete_dynamic_with_callback(&acl_users, (FREE_FUNC) free_acl_user);
2208 acl_dbs.free_memory();
2209 delete_dynamic(&acl_wild_hosts);
2210 delete_dynamic(&acl_proxy_users);
2211 my_hash_free(&acl_check_hosts);
2212 my_hash_free(&acl_roles_mappings);
2213 if (!end)
2214 acl_cache->clear(1); /* purecov: inspected */
2215 else
2216 {
2217 plugin_unlock(0, native_password_plugin);
2218 plugin_unlock(0, old_password_plugin);
2219 delete acl_cache;
2220 acl_cache=0;
2221 }
2222 }
2223
2224
2225 /*
2226 Forget current user/db-level privileges and read new privileges
2227 from the privilege tables.
2228
2229 SYNOPSIS
2230 acl_reload()
2231 thd Current thread
2232
2233 NOTE
2234 All tables of calling thread which were open and locked by LOCK TABLES
2235 statement will be unlocked and closed.
2236 This function is also used for initialization of structures responsible
2237 for user/db-level privilege checking.
2238
2239 RETURN VALUE
2240 FALSE Success
2241 TRUE Failure
2242 */
2243
acl_reload(THD * thd)2244 bool acl_reload(THD *thd)
2245 {
2246 DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_proxy_users;
2247 Dynamic_array<ACL_DB> old_acl_dbs(0U,0U);
2248 HASH old_acl_roles, old_acl_roles_mappings;
2249 MEM_ROOT old_mem;
2250 int result;
2251 DBUG_ENTER("acl_reload");
2252
2253 Grant_tables tables(Table_host | Table_user | Table_db | Table_proxies_priv |
2254 Table_roles_mapping, TL_READ);
2255 /*
2256 To avoid deadlocks we should obtain table locks before
2257 obtaining acl_cache->lock mutex.
2258 */
2259 if (unlikely((result= tables.open_and_lock(thd))))
2260 {
2261 DBUG_ASSERT(result <= 0);
2262 /*
2263 Execution might have been interrupted; only print the error message
2264 if an error condition has been raised.
2265 */
2266 if (thd->get_stmt_da()->is_error())
2267 sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
2268 thd->get_stmt_da()->message());
2269 goto end;
2270 }
2271
2272 acl_cache->clear(0);
2273 mysql_mutex_lock(&acl_cache->lock);
2274
2275 old_acl_hosts= acl_hosts;
2276 old_acl_users= acl_users;
2277 old_acl_roles= acl_roles;
2278 old_acl_roles_mappings= acl_roles_mappings;
2279 old_acl_proxy_users= acl_proxy_users;
2280 old_acl_dbs= acl_dbs;
2281 my_init_dynamic_array(&acl_hosts, sizeof(ACL_HOST), 20, 50, MYF(0));
2282 my_init_dynamic_array(&acl_users, sizeof(ACL_USER), 50, 100, MYF(0));
2283 acl_dbs.init(50, 100);
2284 my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), 50, 100, MYF(0));
2285 my_hash_init2(&acl_roles,50, &my_charset_utf8_bin,
2286 0, 0, 0, (my_hash_get_key) acl_role_get_key, 0,
2287 (void (*)(void *))free_acl_role, 0);
2288 my_hash_init2(&acl_roles_mappings, 50, &my_charset_utf8_bin, 0, 0, 0,
2289 (my_hash_get_key) acl_role_map_get_key, 0, 0, 0);
2290 old_mem= acl_memroot;
2291 delete_dynamic(&acl_wild_hosts);
2292 my_hash_free(&acl_check_hosts);
2293
2294 if ((result= acl_load(thd, tables)))
2295 { // Error. Revert to old list
2296 DBUG_PRINT("error",("Reverting to old privileges"));
2297 acl_free(); /* purecov: inspected */
2298 acl_hosts= old_acl_hosts;
2299 acl_users= old_acl_users;
2300 acl_roles= old_acl_roles;
2301 acl_roles_mappings= old_acl_roles_mappings;
2302 acl_proxy_users= old_acl_proxy_users;
2303 acl_dbs= old_acl_dbs;
2304 old_acl_dbs.init(0,0);
2305 acl_memroot= old_mem;
2306 init_check_host();
2307 }
2308 else
2309 {
2310 my_hash_free(&old_acl_roles);
2311 free_root(&old_mem,MYF(0));
2312 delete_dynamic(&old_acl_hosts);
2313 delete_dynamic_with_callback(&old_acl_users, (FREE_FUNC) free_acl_user);
2314 delete_dynamic(&old_acl_proxy_users);
2315 my_hash_free(&old_acl_roles_mappings);
2316 }
2317 mysql_mutex_unlock(&acl_cache->lock);
2318 end:
2319 close_mysql_tables(thd);
2320 DBUG_RETURN(result);
2321 }
2322
2323 /*
2324 Get all access bits from table after fieldnr
2325
2326 IMPLEMENTATION
2327 We know that the access privileges ends when there is no more fields
2328 or the field is not an enum with two elements.
2329
2330 SYNOPSIS
2331 get_access()
2332 form an open table to read privileges from.
2333 The record should be already read in table->record[0]
2334 fieldnr number of the first privilege (that is ENUM('N','Y') field
2335 next_field on return - number of the field next to the last ENUM
2336 (unless next_field == 0)
2337
2338 RETURN VALUE
2339 privilege mask
2340 */
2341
get_access(TABLE * form,uint fieldnr,uint * next_field)2342 static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
2343 {
2344 ulong access_bits=0,bit;
2345 char buff[2];
2346 String res(buff,sizeof(buff),&my_charset_latin1);
2347 Field **pos;
2348
2349 for (pos=form->field+fieldnr, bit=1;
2350 *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
2351 ((Field_enum*) (*pos))->typelib->count == 2 ;
2352 pos++, fieldnr++, bit<<=1)
2353 {
2354 if (get_YN_as_bool(*pos))
2355 access_bits|= bit;
2356 }
2357 if (next_field)
2358 *next_field=fieldnr;
2359 return access_bits;
2360 }
2361
2362
2363 /*
2364 Return a number which, if sorted 'desc', puts strings in this order:
2365 no wildcards
2366 wildcards
2367 empty string
2368 */
2369
get_sort(uint count,...)2370 static ulong get_sort(uint count,...)
2371 {
2372 va_list args;
2373 va_start(args,count);
2374 ulong sort=0;
2375
2376 /* Should not use this function with more than 4 arguments for compare. */
2377 DBUG_ASSERT(count <= 4);
2378
2379 while (count--)
2380 {
2381 char *start, *str= va_arg(args,char*);
2382 uint chars= 0;
2383 uint wild_pos= 0; /* first wildcard position */
2384
2385 if ((start= str))
2386 {
2387 for (; *str ; str++)
2388 {
2389 if (*str == wild_prefix && str[1])
2390 str++;
2391 else if (*str == wild_many || *str == wild_one)
2392 {
2393 wild_pos= (uint) (str - start) + 1;
2394 break;
2395 }
2396 chars= 128; // Marker that chars existed
2397 }
2398 }
2399 sort= (sort << 8) + (wild_pos ? MY_MIN(wild_pos, 127U) : chars);
2400 }
2401 va_end(args);
2402 return sort;
2403 }
2404
2405
acl_compare(ACL_ACCESS * a,ACL_ACCESS * b)2406 static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
2407 {
2408 if (a->sort > b->sort)
2409 return -1;
2410 if (a->sort < b->sort)
2411 return 1;
2412 return 0;
2413 }
2414
2415
2416 /*
2417 Gets user credentials without authentication and resource limit checks.
2418
2419 SYNOPSIS
2420 acl_getroot()
2421 sctx Context which should be initialized
2422 user user name
2423 host host name
2424 ip IP
2425 db current data base name
2426
2427 RETURN
2428 FALSE OK
2429 TRUE Error
2430 */
2431
acl_getroot(Security_context * sctx,const char * user,const char * host,const char * ip,const char * db)2432 bool acl_getroot(Security_context *sctx, const char *user, const char *host,
2433 const char *ip, const char *db)
2434 {
2435 int res= 1;
2436 uint i;
2437 ACL_USER *acl_user= 0;
2438 DBUG_ENTER("acl_getroot");
2439
2440 DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
2441 host, ip, user, db));
2442 sctx->user= user;
2443 sctx->host= host;
2444 sctx->ip= ip;
2445 sctx->host_or_ip= host ? host : (safe_str(ip));
2446
2447 if (!initialized)
2448 {
2449 /*
2450 here if mysqld's been started with --skip-grant-tables option.
2451 */
2452 sctx->skip_grants();
2453 DBUG_RETURN(FALSE);
2454 }
2455
2456 mysql_mutex_lock(&acl_cache->lock);
2457
2458 sctx->master_access= 0;
2459 sctx->db_access= 0;
2460 *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0;
2461
2462 if (host[0]) // User, not Role
2463 {
2464 acl_user= find_user_wild(host, user, ip);
2465
2466 if (acl_user)
2467 {
2468 res= 0;
2469 for (i=0 ; i < acl_dbs.elements() ; i++)
2470 {
2471 ACL_DB *acl_db= &acl_dbs.at(i);
2472 if (!acl_db->user ||
2473 (user && user[0] && !strcmp(user, acl_db->user)))
2474 {
2475 if (compare_hostname(&acl_db->host, host, ip))
2476 {
2477 if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
2478 {
2479 sctx->db_access= acl_db->access;
2480 break;
2481 }
2482 }
2483 }
2484 }
2485 sctx->master_access= acl_user->access;
2486
2487 if (acl_user->user.str)
2488 strmake_buf(sctx->priv_user, user);
2489
2490 if (acl_user->host.hostname)
2491 strmake_buf(sctx->priv_host, acl_user->host.hostname);
2492 }
2493 }
2494 else // Role, not User
2495 {
2496 ACL_ROLE *acl_role= find_acl_role(user);
2497 if (acl_role)
2498 {
2499 res= 0;
2500 for (i=0 ; i < acl_dbs.elements() ; i++)
2501 {
2502 ACL_DB *acl_db= &acl_dbs.at(i);
2503 if (!acl_db->user ||
2504 (user && user[0] && !strcmp(user, acl_db->user)))
2505 {
2506 if (compare_hostname(&acl_db->host, "", ""))
2507 {
2508 if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
2509 {
2510 sctx->db_access= acl_db->access;
2511 break;
2512 }
2513 }
2514 }
2515 }
2516 sctx->master_access= acl_role->access;
2517
2518 if (acl_role->user.str)
2519 strmake_buf(sctx->priv_role, user);
2520 }
2521 }
2522
2523 mysql_mutex_unlock(&acl_cache->lock);
2524 DBUG_RETURN(res);
2525 }
2526
check_role_is_granted_callback(ACL_USER_BASE * grantee,void * data)2527 static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data)
2528 {
2529 LEX_CSTRING *rolename= static_cast<LEX_CSTRING *>(data);
2530 if (rolename->length == grantee->user.length &&
2531 !strcmp(rolename->str, grantee->user.str))
2532 return -1; // End search, we've found our role.
2533
2534 /* Keep looking, we haven't found our role yet. */
2535 return 0;
2536 }
2537
2538 /*
2539 unlike find_user_exact and find_user_wild,
2540 this function finds anonymous users too, it's when a
2541 user is not empty, but priv_user (acl_user->user) is empty.
2542 */
find_user_or_anon(const char * host,const char * user,const char * ip)2543 static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip)
2544 {
2545 ACL_USER *result= NULL;
2546 mysql_mutex_assert_owner(&acl_cache->lock);
2547 for (uint i=0; i < acl_users.elements; i++)
2548 {
2549 ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
2550 if ((!acl_user_tmp->user.str ||
2551 !strcmp(user, acl_user_tmp->user.str)) &&
2552 compare_hostname(&acl_user_tmp->host, host, ip))
2553 {
2554 result= acl_user_tmp;
2555 break;
2556 }
2557 }
2558 return result;
2559 }
2560
check_user_can_set_role(THD * thd,const char * user,const char * host,const char * ip,const char * rolename,ulonglong * access)2561 static int check_user_can_set_role(THD *thd, const char *user, const char *host,
2562 const char *ip, const char *rolename,
2563 ulonglong *access)
2564 {
2565 ACL_ROLE *role;
2566 ACL_USER_BASE *acl_user_base;
2567 ACL_USER *UNINIT_VAR(acl_user);
2568 bool is_granted= FALSE;
2569 int result= 0;
2570
2571 /* clear role privileges */
2572 mysql_mutex_lock(&acl_cache->lock);
2573
2574 if (!strcasecmp(rolename, "NONE"))
2575 {
2576 /* have to clear the privileges */
2577 /* get the current user */
2578 acl_user= find_user_wild(host, user, ip);
2579 if (acl_user == NULL)
2580 result= ER_INVALID_CURRENT_USER;
2581 else if (access)
2582 *access= acl_user->access;
2583
2584 goto end;
2585 }
2586
2587 role= find_acl_role(rolename);
2588
2589 /* According to SQL standard, the same error message must be presented */
2590 if (role == NULL)
2591 {
2592 result= ER_INVALID_ROLE;
2593 goto end;
2594 }
2595
2596 for (uint i=0 ; i < role->parent_grantee.elements ; i++)
2597 {
2598 acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**));
2599 if (acl_user_base->flags & IS_ROLE)
2600 continue;
2601
2602 acl_user= (ACL_USER *)acl_user_base;
2603 if (acl_user->wild_eq(user, host, ip))
2604 {
2605 is_granted= TRUE;
2606 break;
2607 }
2608 }
2609
2610 /* According to SQL standard, the same error message must be presented */
2611 if (!is_granted)
2612 {
2613 result= 1;
2614 goto end;
2615 }
2616
2617 if (access)
2618 {
2619 *access = acl_user->access | role->access;
2620 }
2621
2622 end:
2623 mysql_mutex_unlock(&acl_cache->lock);
2624
2625 /* We present different error messages depending if the user has sufficient
2626 privileges to know if the INVALID_ROLE exists. */
2627 switch (result)
2628 {
2629 case ER_INVALID_CURRENT_USER:
2630 my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename);
2631 break;
2632 case ER_INVALID_ROLE:
2633 /* Role doesn't exist at all */
2634 my_error(ER_INVALID_ROLE, MYF(0), rolename);
2635 break;
2636 case 1:
2637 LEX_CSTRING role_lex;
2638 /* First, check if current user can see mysql database. */
2639 bool read_access= !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 1);
2640
2641 role_lex.str= rolename;
2642 role_lex.length= strlen(rolename);
2643 mysql_mutex_lock(&acl_cache->lock);
2644 ACL_USER *cur_user= find_user_or_anon(thd->security_ctx->priv_host,
2645 thd->security_ctx->priv_user,
2646 thd->security_ctx->ip);
2647
2648 /* If the current user does not have select priv to mysql database,
2649 see if the current user can discover the role if it was granted to him.
2650 */
2651 if (cur_user && (read_access ||
2652 traverse_role_graph_down(cur_user, &role_lex,
2653 check_role_is_granted_callback,
2654 NULL) == -1))
2655 {
2656 /* Role is not granted but current user can see the role */
2657 my_printf_error(ER_INVALID_ROLE, "User %`s@%`s has not been granted role %`s",
2658 MYF(0), thd->security_ctx->priv_user,
2659 thd->security_ctx->priv_host, rolename);
2660 }
2661 else
2662 {
2663 /* Role is not granted and current user cannot see the role */
2664 my_error(ER_INVALID_ROLE, MYF(0), rolename);
2665 }
2666 mysql_mutex_unlock(&acl_cache->lock);
2667 break;
2668 }
2669
2670 return result;
2671 }
2672
2673
acl_check_setrole(THD * thd,const char * rolename,ulonglong * access)2674 int acl_check_setrole(THD *thd, const char *rolename, ulonglong *access)
2675 {
2676 if (!initialized)
2677 {
2678 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2679 return 1;
2680 }
2681
2682 return check_user_can_set_role(thd, thd->security_ctx->priv_user,
2683 thd->security_ctx->host, thd->security_ctx->ip, rolename, access);
2684 }
2685
2686
acl_setrole(THD * thd,const char * rolename,ulonglong access)2687 int acl_setrole(THD *thd, const char *rolename, ulonglong access)
2688 {
2689 /* merge the privileges */
2690 Security_context *sctx= thd->security_ctx;
2691 sctx->master_access= static_cast<ulong>(access);
2692 if (thd->db.str)
2693 sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db.str, FALSE);
2694
2695 if (!strcasecmp(rolename, "NONE"))
2696 {
2697 thd->security_ctx->priv_role[0]= 0;
2698 }
2699 else
2700 {
2701 if (thd->db.str)
2702 sctx->db_access|= acl_get("", "", rolename, thd->db.str, FALSE);
2703 /* mark the current role */
2704 strmake_buf(thd->security_ctx->priv_role, rolename);
2705 }
2706 return 0;
2707 }
2708
check_get_key(ACL_USER * buff,size_t * length,my_bool not_used)2709 static uchar* check_get_key(ACL_USER *buff, size_t *length,
2710 my_bool not_used __attribute__((unused)))
2711 {
2712 *length=buff->hostname_length;
2713 return (uchar*) buff->host.hostname;
2714 }
2715
2716
acl_update_role(const char * rolename,ulong privileges)2717 static void acl_update_role(const char *rolename, ulong privileges)
2718 {
2719 ACL_ROLE *role= find_acl_role(rolename);
2720 if (role)
2721 role->initial_role_access= role->access= privileges;
2722 }
2723
2724
acl_update_user(const char * user,const char * host,const char * password,size_t password_len,enum SSL_type ssl_type,const char * ssl_cipher,const char * x509_issuer,const char * x509_subject,USER_RESOURCES * mqh,ulong privileges,const LEX_CSTRING * plugin,const LEX_CSTRING * auth)2725 static void acl_update_user(const char *user, const char *host,
2726 const char *password, size_t password_len,
2727 enum SSL_type ssl_type,
2728 const char *ssl_cipher,
2729 const char *x509_issuer,
2730 const char *x509_subject,
2731 USER_RESOURCES *mqh,
2732 ulong privileges,
2733 const LEX_CSTRING *plugin,
2734 const LEX_CSTRING *auth)
2735 {
2736 mysql_mutex_assert_owner(&acl_cache->lock);
2737
2738 for (uint i=0 ; i < acl_users.elements ; i++)
2739 {
2740 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
2741 if (acl_user->eq(user, host))
2742 {
2743 if (plugin->str[0])
2744 {
2745 acl_user->plugin= *plugin;
2746 acl_user->auth_string.str= auth->str ?
2747 strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
2748 acl_user->auth_string.length= auth->length;
2749 if (fix_user_plugin_ptr(acl_user))
2750 acl_user->plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
2751 }
2752 else
2753 if (password[0])
2754 {
2755 acl_user->auth_string.str= strmake_root(&acl_memroot, password, password_len);
2756 acl_user->auth_string.length= password_len;
2757 set_user_salt(acl_user, password, password_len);
2758 set_user_plugin(acl_user, password_len);
2759 }
2760 acl_user->access=privileges;
2761 if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
2762 acl_user->user_resource.questions=mqh->questions;
2763 if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
2764 acl_user->user_resource.updates=mqh->updates;
2765 if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
2766 acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
2767 if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
2768 acl_user->user_resource.user_conn= mqh->user_conn;
2769 if (mqh->specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
2770 acl_user->user_resource.max_statement_time= mqh->max_statement_time;
2771 if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
2772 {
2773 acl_user->ssl_type= ssl_type;
2774 acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) :
2775 0);
2776 acl_user->x509_issuer= (x509_issuer ? strdup_root(&acl_memroot,x509_issuer) :
2777 0);
2778 acl_user->x509_subject= (x509_subject ?
2779 strdup_root(&acl_memroot,x509_subject) : 0);
2780 }
2781 /* search complete: */
2782 break;
2783 }
2784 }
2785 }
2786
2787
acl_insert_role(const char * rolename,ulong privileges)2788 static void acl_insert_role(const char *rolename, ulong privileges)
2789 {
2790 ACL_ROLE *entry;
2791
2792 mysql_mutex_assert_owner(&acl_cache->lock);
2793 entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot);
2794 (void) my_init_dynamic_array(&entry->parent_grantee,
2795 sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
2796 (void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *),
2797 8, 8, MYF(0));
2798
2799 my_hash_insert(&acl_roles, (uchar *)entry);
2800 }
2801
2802
acl_insert_user(const char * user,const char * host,const char * password,size_t password_len,enum SSL_type ssl_type,const char * ssl_cipher,const char * x509_issuer,const char * x509_subject,USER_RESOURCES * mqh,ulong privileges,const LEX_CSTRING * plugin,const LEX_CSTRING * auth)2803 static void acl_insert_user(const char *user, const char *host,
2804 const char *password, size_t password_len,
2805 enum SSL_type ssl_type,
2806 const char *ssl_cipher,
2807 const char *x509_issuer,
2808 const char *x509_subject,
2809 USER_RESOURCES *mqh,
2810 ulong privileges,
2811 const LEX_CSTRING *plugin,
2812 const LEX_CSTRING *auth)
2813 {
2814 ACL_USER acl_user;
2815
2816 mysql_mutex_assert_owner(&acl_cache->lock);
2817
2818 bzero(&acl_user, sizeof(acl_user));
2819 acl_user.user.str=*user ? strdup_root(&acl_memroot,user) : 0;
2820 acl_user.user.length= strlen(user);
2821 update_hostname(&acl_user.host, safe_strdup_root(&acl_memroot, host));
2822 if (plugin->str[0])
2823 {
2824 acl_user.plugin= *plugin;
2825 acl_user.auth_string.str= auth->str ?
2826 strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
2827 acl_user.auth_string.length= auth->length;
2828 if (fix_user_plugin_ptr(&acl_user))
2829 acl_user.plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
2830 }
2831 else
2832 {
2833 acl_user.auth_string.str= strmake_root(&acl_memroot, password, password_len);
2834 acl_user.auth_string.length= password_len;
2835 set_user_salt(&acl_user, password, password_len);
2836 set_user_plugin(&acl_user, password_len);
2837 }
2838
2839 acl_user.flags= 0;
2840 acl_user.access=privileges;
2841 acl_user.user_resource = *mqh;
2842 acl_user.sort=get_sort(2, acl_user.host.hostname, acl_user.user.str);
2843 acl_user.hostname_length=(uint) strlen(host);
2844 acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ?
2845 ssl_type : SSL_TYPE_NONE);
2846 acl_user.ssl_cipher= ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) : 0;
2847 acl_user.x509_issuer= x509_issuer ? strdup_root(&acl_memroot,x509_issuer) : 0;
2848 acl_user.x509_subject=x509_subject ? strdup_root(&acl_memroot,x509_subject) : 0;
2849 (void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *),
2850 8, 8, MYF(0));
2851
2852 (void) push_dynamic(&acl_users,(uchar*) &acl_user);
2853 if (!acl_user.host.hostname ||
2854 (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
2855 allow_all_hosts=1; // Anyone can connect /* purecov: tested */
2856 my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
2857 sizeof(ACL_USER),(qsort_cmp) acl_compare);
2858
2859 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
2860 rebuild_check_host();
2861
2862 /*
2863 Rebuild every user's role_grants since 'acl_users' has been sorted
2864 and old pointers to ACL_USER elements are no longer valid
2865 */
2866 rebuild_role_grants();
2867 }
2868
2869
acl_update_db(const char * user,const char * host,const char * db,ulong privileges)2870 static bool acl_update_db(const char *user, const char *host, const char *db,
2871 ulong privileges)
2872 {
2873 mysql_mutex_assert_owner(&acl_cache->lock);
2874
2875 bool updated= false;
2876
2877 for (uint i=0 ; i < acl_dbs.elements() ; i++)
2878 {
2879 ACL_DB *acl_db= &acl_dbs.at(i);
2880 if ((!acl_db->user && !user[0]) ||
2881 (acl_db->user &&
2882 !strcmp(user,acl_db->user)))
2883 {
2884 if ((!acl_db->host.hostname && !host[0]) ||
2885 (acl_db->host.hostname &&
2886 !strcmp(host, acl_db->host.hostname)))
2887 {
2888 if ((!acl_db->db && !db[0]) ||
2889 (acl_db->db && !strcmp(db,acl_db->db)))
2890
2891 {
2892 if (privileges)
2893 {
2894 acl_db->access= privileges;
2895 acl_db->initial_access= acl_db->access;
2896 }
2897 else
2898 acl_dbs.del(i);
2899 updated= true;
2900 }
2901 }
2902 }
2903 }
2904
2905 return updated;
2906 }
2907
2908
2909 /*
2910 Insert a user/db/host combination into the global acl_cache
2911
2912 SYNOPSIS
2913 acl_insert_db()
2914 user User name
2915 host Host name
2916 db Database name
2917 privileges Bitmap of privileges
2918
2919 NOTES
2920 acl_cache->lock must be locked when calling this
2921 */
2922
acl_insert_db(const char * user,const char * host,const char * db,ulong privileges)2923 static void acl_insert_db(const char *user, const char *host, const char *db,
2924 ulong privileges)
2925 {
2926 ACL_DB acl_db;
2927 mysql_mutex_assert_owner(&acl_cache->lock);
2928 acl_db.user=strdup_root(&acl_memroot,user);
2929 update_hostname(&acl_db.host, safe_strdup_root(&acl_memroot, host));
2930 acl_db.db=strdup_root(&acl_memroot,db);
2931 acl_db.initial_access= acl_db.access= privileges;
2932 acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
2933 acl_dbs.push(acl_db);
2934 acl_dbs.sort((acl_dbs_cmp)acl_compare);
2935 }
2936
2937
2938 /*
2939 Get privilege for a host, user and db combination
2940
2941 as db_is_pattern changes the semantics of comparison,
2942 acl_cache is not used if db_is_pattern is set.
2943 */
2944
acl_get(const char * host,const char * ip,const char * user,const char * db,my_bool db_is_pattern)2945 ulong acl_get(const char *host, const char *ip,
2946 const char *user, const char *db, my_bool db_is_pattern)
2947 {
2948 ulong host_access= ~(ulong)0, db_access= 0;
2949 uint i;
2950 size_t key_length;
2951 char key[ACL_KEY_LENGTH],*tmp_db,*end;
2952 acl_entry *entry;
2953 DBUG_ENTER("acl_get");
2954
2955 tmp_db= strmov(strmov(key, safe_str(ip)) + 1, user) + 1;
2956 end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db);
2957
2958 if (end >= key + sizeof(key)) // db name was truncated
2959 DBUG_RETURN(0); // no privileges for an invalid db name
2960
2961 if (lower_case_table_names)
2962 {
2963 my_casedn_str(files_charset_info, tmp_db);
2964 db=tmp_db;
2965 }
2966 key_length= (size_t) (end-key);
2967
2968 mysql_mutex_lock(&acl_cache->lock);
2969 if (!db_is_pattern && (entry=acl_cache->search((uchar*) key, key_length)))
2970 {
2971 db_access=entry->access;
2972 mysql_mutex_unlock(&acl_cache->lock);
2973 DBUG_PRINT("exit", ("access: 0x%lx", db_access));
2974 DBUG_RETURN(db_access);
2975 }
2976
2977 /*
2978 Check if there are some access rights for database and user
2979 */
2980 for (i=0 ; i < acl_dbs.elements() ; i++)
2981 {
2982 ACL_DB *acl_db= &acl_dbs.at(i);
2983 if (!acl_db->user || !strcmp(user,acl_db->user))
2984 {
2985 if (compare_hostname(&acl_db->host,host,ip))
2986 {
2987 if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
2988 {
2989 db_access=acl_db->access;
2990 if (acl_db->host.hostname)
2991 goto exit; // Fully specified. Take it
2992 /* the host table is not used for roles */
2993 if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user))
2994 goto exit;
2995 break; /* purecov: tested */
2996 }
2997 }
2998 }
2999 }
3000 if (!db_access)
3001 goto exit; // Can't be better
3002
3003 /*
3004 No host specified for user. Get hostdata from host table
3005 */
3006 host_access=0; // Host must be found
3007 for (i=0 ; i < acl_hosts.elements ; i++)
3008 {
3009 ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
3010 if (compare_hostname(&acl_host->host,host,ip))
3011 {
3012 if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
3013 {
3014 host_access=acl_host->access; // Fully specified. Take it
3015 break;
3016 }
3017 }
3018 }
3019 exit:
3020 /* Save entry in cache for quick retrieval */
3021 if (!db_is_pattern &&
3022 (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
3023 {
3024 entry->access=(db_access & host_access);
3025 DBUG_ASSERT(key_length < 0xffff);
3026 entry->length=(uint16)key_length;
3027 memcpy((uchar*) entry->key,key,key_length);
3028 acl_cache->add(entry);
3029 }
3030 mysql_mutex_unlock(&acl_cache->lock);
3031 DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
3032 DBUG_RETURN(db_access & host_access);
3033 }
3034
3035 /*
3036 Check if there are any possible matching entries for this host
3037
3038 NOTES
3039 All host names without wild cards are stored in a hash table,
3040 entries with wildcards are stored in a dynamic array
3041 */
3042
init_check_host(void)3043 static void init_check_host(void)
3044 {
3045 DBUG_ENTER("init_check_host");
3046 (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
3047 acl_users.elements, 1, MYF(0));
3048 (void) my_hash_init(&acl_check_hosts,system_charset_info,
3049 acl_users.elements, 0, 0,
3050 (my_hash_get_key) check_get_key, 0, 0);
3051 if (!allow_all_hosts)
3052 {
3053 for (uint i=0 ; i < acl_users.elements ; i++)
3054 {
3055 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
3056 if (strchr(acl_user->host.hostname,wild_many) ||
3057 strchr(acl_user->host.hostname,wild_one) ||
3058 acl_user->host.ip_mask)
3059 { // Has wildcard
3060 uint j;
3061 for (j=0 ; j < acl_wild_hosts.elements ; j++)
3062 { // Check if host already exists
3063 acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j,
3064 acl_host_and_ip *);
3065 if (!my_strcasecmp(system_charset_info,
3066 acl_user->host.hostname, acl->hostname))
3067 break; // already stored
3068 }
3069 if (j == acl_wild_hosts.elements) // If new
3070 (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
3071 }
3072 else if (!my_hash_search(&acl_check_hosts,(uchar*)
3073 acl_user->host.hostname,
3074 strlen(acl_user->host.hostname)))
3075 {
3076 if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
3077 { // End of memory
3078 allow_all_hosts=1; // Should never happen
3079 DBUG_VOID_RETURN;
3080 }
3081 }
3082 }
3083 }
3084 freeze_size(&acl_wild_hosts);
3085 freeze_size(&acl_check_hosts.array);
3086 DBUG_VOID_RETURN;
3087 }
3088
3089
3090 /*
3091 Rebuild lists used for checking of allowed hosts
3092
3093 We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
3094 dropping or renaming user, since they contain pointers to elements of
3095 'acl_user' array, which are invalidated by drop operation, and use
3096 ACL_USER::host::hostname as a key, which is changed by rename.
3097 */
rebuild_check_host(void)3098 static void rebuild_check_host(void)
3099 {
3100 delete_dynamic(&acl_wild_hosts);
3101 my_hash_free(&acl_check_hosts);
3102 init_check_host();
3103 }
3104
3105 /*
3106 Reset a role role_grants dynamic array.
3107 Also, the role's access bits are reset to the ones present in the table.
3108 */
acl_role_reset_role_arrays(void * ptr,void * not_used)3109 static my_bool acl_role_reset_role_arrays(void *ptr,
3110 void * not_used __attribute__((unused)))
3111 {
3112 ACL_ROLE *role= (ACL_ROLE *)ptr;
3113 reset_dynamic(&role->role_grants);
3114 reset_dynamic(&role->parent_grantee);
3115 role->counter= 0;
3116 return 0;
3117 }
3118
3119 /*
3120 Add a the coresponding pointers present in the mapping to the entries in
3121 acl_users and acl_roles
3122 */
add_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role)3123 static bool add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
3124 {
3125 return push_dynamic(&grantee->role_grants, (uchar*) &role)
3126 || push_dynamic(&role->parent_grantee, (uchar*) &grantee);
3127
3128 }
3129
3130 /*
3131 Revert the last add_role_user_mapping() action
3132 */
undo_add_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role)3133 static void undo_add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
3134 {
3135 void *pop __attribute__((unused));
3136
3137 pop= pop_dynamic(&grantee->role_grants);
3138 DBUG_ASSERT(role == *(ACL_ROLE**)pop);
3139
3140 pop= pop_dynamic(&role->parent_grantee);
3141 DBUG_ASSERT(grantee == *(ACL_USER_BASE**)pop);
3142 }
3143
3144 /*
3145 this helper is used when building role_grants and parent_grantee arrays
3146 from scratch.
3147
3148 this happens either on initial loading of data from tables, in acl_load().
3149 or in rebuild_role_grants after acl_role_reset_role_arrays().
3150 */
add_role_user_mapping(const char * uname,const char * hname,const char * rname)3151 static bool add_role_user_mapping(const char *uname, const char *hname,
3152 const char *rname)
3153 {
3154 ACL_USER_BASE *grantee= find_acl_user_base(uname, hname);
3155 ACL_ROLE *role= find_acl_role(rname);
3156
3157 if (grantee == NULL || role == NULL)
3158 return 1;
3159
3160 /*
3161 because all arrays are rebuilt completely, and counters were also reset,
3162 we can increment them here, and after the rebuild all counters will
3163 have correct values (equal to the number of roles granted).
3164 */
3165 if (grantee->flags & IS_ROLE)
3166 ((ACL_ROLE*)grantee)->counter++;
3167 return add_role_user_mapping(grantee, role);
3168 }
3169
3170 /*
3171 This helper function is used to removes roles and grantees
3172 from the corresponding cross-reference arrays. see remove_role_user_mapping().
3173 as such, it asserts that an element to delete is present in the array,
3174 and is present only once.
3175 */
remove_ptr_from_dynarray(DYNAMIC_ARRAY * array,void * ptr)3176 static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr)
3177 {
3178 bool found __attribute__((unused))= false;
3179 for (uint i= 0; i < array->elements; i++)
3180 {
3181 if (ptr == *dynamic_element(array, i, void**))
3182 {
3183 DBUG_ASSERT(!found);
3184 delete_dynamic_element(array, i);
3185 IF_DBUG_ASSERT(found= true, break);
3186 }
3187 }
3188 DBUG_ASSERT(found);
3189 }
3190
remove_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role,int grantee_idx=-1,int role_idx=-1)3191 static void remove_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role,
3192 int grantee_idx=-1, int role_idx=-1)
3193 {
3194 remove_ptr_from_dynarray(&grantee->role_grants, role);
3195 remove_ptr_from_dynarray(&role->parent_grantee, grantee);
3196 }
3197
3198
add_role_user_mapping_action(void * ptr,void * unused)3199 static my_bool add_role_user_mapping_action(void *ptr, void *unused __attribute__((unused)))
3200 {
3201 ROLE_GRANT_PAIR *pair= (ROLE_GRANT_PAIR*)ptr;
3202 bool status __attribute__((unused));
3203 status= add_role_user_mapping(pair->u_uname, pair->u_hname, pair->r_uname);
3204 /*
3205 The invariant chosen is that acl_roles_mappings should _always_
3206 only contain valid entries, referencing correct user and role grants.
3207 If add_role_user_mapping detects an invalid entry, it will not add
3208 the mapping into the ACL_USER::role_grants array.
3209 */
3210 DBUG_ASSERT(status == 0);
3211 return 0;
3212 }
3213
3214
3215 /*
3216 Rebuild the role grants every time the acl_users is modified
3217
3218 The role grants in the ACL_USER class need to be rebuilt, as they contain
3219 pointers to elements of the acl_users array.
3220 */
3221
rebuild_role_grants(void)3222 static void rebuild_role_grants(void)
3223 {
3224 DBUG_ENTER("rebuild_role_grants");
3225 /*
3226 Reset every user's and role's role_grants array
3227 */
3228 for (uint i=0; i < acl_users.elements; i++) {
3229 ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *);
3230 reset_dynamic(&user->role_grants);
3231 }
3232 my_hash_iterate(&acl_roles, acl_role_reset_role_arrays, NULL);
3233
3234 /* Rebuild the direct links between users and roles in ACL_USER::role_grants */
3235 my_hash_iterate(&acl_roles_mappings, add_role_user_mapping_action, NULL);
3236
3237 DBUG_VOID_RETURN;
3238 }
3239
3240
3241 /* Return true if there is no users that can match the given host */
acl_check_host(const char * host,const char * ip)3242 bool acl_check_host(const char *host, const char *ip)
3243 {
3244 if (allow_all_hosts)
3245 return 0;
3246 mysql_mutex_lock(&acl_cache->lock);
3247
3248 if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
3249 (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
3250 {
3251 mysql_mutex_unlock(&acl_cache->lock);
3252 return 0; // Found host
3253 }
3254 for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
3255 {
3256 acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
3257 if (compare_hostname(acl, host, ip))
3258 {
3259 mysql_mutex_unlock(&acl_cache->lock);
3260 return 0; // Host ok
3261 }
3262 }
3263 mysql_mutex_unlock(&acl_cache->lock);
3264 if (ip != NULL)
3265 {
3266 /* Increment HOST_CACHE.COUNT_HOST_ACL_ERRORS. */
3267 Host_errors errors;
3268 errors.m_host_acl= 1;
3269 inc_host_errors(ip, &errors);
3270 }
3271 return 1; // Host is not allowed
3272 }
3273
3274 /**
3275 Check if the user is allowed to alter the mysql.user table
3276
3277 @param thd THD
3278 @param host Hostname for the user
3279 @param user User name
3280
3281 @return Error status
3282 @retval 0 OK
3283 @retval 1 Error
3284 */
3285
check_alter_user(THD * thd,const char * host,const char * user)3286 static int check_alter_user(THD *thd, const char *host, const char *user)
3287 {
3288 int error = 1;
3289 if (!initialized)
3290 {
3291 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3292 goto end;
3293 }
3294
3295 if (IF_WSREP((!WSREP(thd) || !thd->wsrep_applier), 1) &&
3296 !thd->slave_thread && !thd->security_ctx->priv_user[0] &&
3297 !in_bootstrap)
3298 {
3299 my_message(ER_PASSWORD_ANONYMOUS_USER,
3300 ER_THD(thd, ER_PASSWORD_ANONYMOUS_USER),
3301 MYF(0));
3302 goto end;
3303 }
3304 if (!host) // Role
3305 {
3306 my_error(ER_PASSWORD_NO_MATCH, MYF(0));
3307 goto end;
3308 }
3309
3310 if (!thd->slave_thread &&
3311 IF_WSREP((!WSREP(thd) || !thd->wsrep_applier),1) &&
3312 (strcmp(thd->security_ctx->priv_user, user) ||
3313 my_strcasecmp(system_charset_info, host,
3314 thd->security_ctx->priv_host)))
3315 {
3316 if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
3317 goto end;
3318 }
3319
3320 error = 0;
3321
3322 end:
3323 return error;
3324 }
3325 /**
3326 Check if the user is allowed to change password
3327
3328 @param thd THD
3329 @param user User, hostname, new password or password hash
3330
3331 @return Error status
3332 @retval 0 OK
3333 @retval 1 ERROR; In this case the error is sent to the client.
3334 */
3335
check_change_password(THD * thd,LEX_USER * user)3336 bool check_change_password(THD *thd, LEX_USER *user)
3337 {
3338 LEX_USER *real_user= get_current_user(thd, user);
3339
3340 if (fix_and_copy_user(real_user, user, thd) ||
3341 validate_password(real_user, thd))
3342 return true;
3343
3344 *user= *real_user;
3345
3346 return check_alter_user(thd, user->host.str, user->user.str);
3347 }
3348
3349
3350 /**
3351 Change a password for a user.
3352
3353 @param thd THD
3354 @param user User, hostname, new password hash
3355
3356 @return Error code
3357 @retval 0 ok
3358 @retval 1 ERROR; In this case the error is sent to the client.
3359 */
change_password(THD * thd,LEX_USER * user)3360 bool change_password(THD *thd, LEX_USER *user)
3361 {
3362 Grant_tables tables(Table_user, TL_WRITE);
3363 /* Buffer should be extended when password length is extended. */
3364 char buff[512];
3365 ulong query_length= 0;
3366 enum_binlog_format save_binlog_format;
3367 int result=0;
3368 const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
3369 DBUG_ENTER("change_password");
3370 DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
3371 user->host.str, user->user.str, user->pwhash.str));
3372 DBUG_ASSERT(user->host.str != 0); // Ensured by parent
3373
3374 /*
3375 This statement will be replicated as a statement, even when using
3376 row-based replication. The flag will be reset at the end of the
3377 statement.
3378 This has to be handled here as it's called by set_var.cc, which is
3379 not automaticly handled by sql_parse.cc
3380 */
3381 save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
3382
3383 if (mysql_bin_log.is_open() ||
3384 (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)))
3385 {
3386 query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
3387 safe_str(user->user.str), safe_str(user->host.str),
3388 safe_str(user->pwhash.str));
3389 }
3390
3391 if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
3392 {
3393 thd->set_query(buff, query_length, system_charset_info);
3394 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL);
3395 }
3396
3397 if ((result= tables.open_and_lock(thd)))
3398 DBUG_RETURN(result != 1);
3399
3400 result= 1;
3401
3402 mysql_mutex_lock(&acl_cache->lock);
3403 ACL_USER *acl_user;
3404 if (!(acl_user= find_user_exact(user->host.str, user->user.str)))
3405 {
3406 mysql_mutex_unlock(&acl_cache->lock);
3407 my_message(ER_PASSWORD_NO_MATCH,
3408 ER_THD(thd, ER_PASSWORD_NO_MATCH), MYF(0));
3409 goto end;
3410 }
3411
3412 /* update loaded acl entry: */
3413 if (acl_user->plugin.str == native_password_plugin_name.str ||
3414 acl_user->plugin.str == old_password_plugin_name.str)
3415 {
3416 acl_user->auth_string.str= strmake_root(&acl_memroot, user->pwhash.str, user->pwhash.length);
3417 acl_user->auth_string.length= user->pwhash.length;
3418 set_user_salt(acl_user, user->pwhash.str, user->pwhash.length);
3419
3420 set_user_plugin(acl_user, user->pwhash.length);
3421 }
3422 else
3423 push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
3424 ER_SET_PASSWORD_AUTH_PLUGIN,
3425 ER_THD(thd, ER_SET_PASSWORD_AUTH_PLUGIN));
3426
3427 if (update_user_table(thd, tables.user_table(),
3428 safe_str(acl_user->host.hostname),
3429 safe_str(acl_user->user.str),
3430 user->pwhash.str, user->pwhash.length))
3431 {
3432 mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
3433 goto end;
3434 }
3435
3436 acl_cache->clear(1); // Clear locked hostname cache
3437 mysql_mutex_unlock(&acl_cache->lock);
3438 result= 0;
3439 if (mysql_bin_log.is_open())
3440 {
3441 DBUG_ASSERT(query_length);
3442 thd->clear_error();
3443 result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
3444 FALSE, FALSE, FALSE, 0) > 0;
3445 }
3446 end:
3447 close_mysql_tables(thd);
3448
3449 #ifdef WITH_WSREP
3450 WSREP_ERROR_LABEL:
3451 if (WSREP(thd) && !thd->wsrep_applier)
3452 {
3453 WSREP_TO_ISOLATION_END;
3454
3455 thd->set_query(query_save);
3456 thd->wsrep_exec_mode = LOCAL_STATE;
3457 }
3458 #endif /* WITH_WSREP */
3459 thd->restore_stmt_binlog_format(save_binlog_format);
3460
3461 DBUG_RETURN(result);
3462 }
3463
acl_check_set_default_role(THD * thd,const char * host,const char * user,const char * role)3464 int acl_check_set_default_role(THD *thd, const char *host, const char *user,
3465 const char *role)
3466 {
3467 DBUG_ENTER("acl_check_set_default_role");
3468 DBUG_RETURN(check_alter_user(thd, host, user) ||
3469 check_user_can_set_role(thd, user, host, NULL, role, NULL));
3470 }
3471
acl_set_default_role(THD * thd,const char * host,const char * user,const char * rolename)3472 int acl_set_default_role(THD *thd, const char *host, const char *user,
3473 const char *rolename)
3474 {
3475 Grant_tables tables(Table_user, TL_WRITE);
3476 char user_key[MAX_KEY_LENGTH];
3477 int result= 1;
3478 int error;
3479 ulong query_length= 0;
3480 bool clear_role= FALSE;
3481 char buff[512];
3482 enum_binlog_format save_binlog_format=
3483 thd->get_current_stmt_binlog_format();
3484 const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
3485
3486 DBUG_ENTER("acl_set_default_role");
3487 DBUG_PRINT("enter",("host: '%s' user: '%s' rolename: '%s'",
3488 safe_str(user), safe_str(host), safe_str(rolename)));
3489
3490 if (!strcasecmp(rolename, "NONE"))
3491 clear_role= TRUE;
3492
3493 if (mysql_bin_log.is_open() ||
3494 (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)))
3495 {
3496 query_length=
3497 sprintf(buff,"SET DEFAULT ROLE '%-.120s' FOR '%-.120s'@'%-.120s'",
3498 safe_str(rolename), safe_str(user), safe_str(host));
3499 }
3500
3501 /*
3502 This statement will be replicated as a statement, even when using
3503 row-based replication. The flag will be reset at the end of the
3504 statement.
3505 This has to be handled here as it's called by set_var.cc, which is
3506 not automaticly handled by sql_parse.cc
3507 */
3508 save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
3509
3510 if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
3511 {
3512 thd->set_query(buff, query_length, system_charset_info);
3513 // Attention!!! here is implicit goto error;
3514 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL);
3515 }
3516
3517 /*
3518 Extra block due to WSREP_TO_ISOLATION_BEGIN using goto.
3519 TODO(cvicentiu) Should move this block out in a new function.
3520 */
3521 {
3522 if ((result= tables.open_and_lock(thd)))
3523 DBUG_RETURN(result != 1);
3524
3525 const User_table& user_table= tables.user_table();
3526 TABLE *table= user_table.table();
3527
3528 result= 1;
3529
3530 mysql_mutex_lock(&acl_cache->lock);
3531 ACL_USER *acl_user;
3532 if (!(acl_user= find_user_exact(host, user)))
3533 {
3534 mysql_mutex_unlock(&acl_cache->lock);
3535 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
3536 MYF(0));
3537 goto end;
3538 }
3539
3540 if (!clear_role)
3541 {
3542 /* set new default_rolename */
3543 acl_user->default_rolename.str= safe_strdup_root(&acl_memroot, rolename);
3544 acl_user->default_rolename.length= strlen(rolename);
3545 }
3546 else
3547 {
3548 /* clear the default_rolename */
3549 acl_user->default_rolename.str = NULL;
3550 acl_user->default_rolename.length = 0;
3551 }
3552
3553 /* update the mysql.user table with the new default role */
3554 tables.user_table().table()->use_all_columns();
3555 if (!tables.user_table().default_role())
3556 {
3557 my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
3558 table->alias.c_ptr(), DEFAULT_ROLE_COLUMN_IDX + 1,
3559 tables.user_table().num_fields(),
3560 static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
3561 mysql_mutex_unlock(&acl_cache->lock);
3562 goto end;
3563 }
3564 user_table.host()->store(host,(uint) strlen(host), system_charset_info);
3565 user_table.user()->store(user,(uint) strlen(user), system_charset_info);
3566 key_copy((uchar *) user_key, table->record[0], table->key_info,
3567 table->key_info->key_length);
3568
3569 if (table->file->ha_index_read_idx_map(table->record[0], 0,
3570 (uchar *) user_key, HA_WHOLE_KEY,
3571 HA_READ_KEY_EXACT))
3572 {
3573 mysql_mutex_unlock(&acl_cache->lock);
3574 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
3575 MYF(0));
3576 goto end;
3577 }
3578 store_record(table, record[1]);
3579 user_table.default_role()->store(acl_user->default_rolename.str,
3580 acl_user->default_rolename.length,
3581 system_charset_info);
3582 if (unlikely(error= table->file->ha_update_row(table->record[1],
3583 table->record[0])) &&
3584 error != HA_ERR_RECORD_IS_THE_SAME)
3585 {
3586 mysql_mutex_unlock(&acl_cache->lock);
3587 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
3588 goto end;
3589 }
3590
3591 acl_cache->clear(1);
3592 mysql_mutex_unlock(&acl_cache->lock);
3593 result= 0;
3594 if (mysql_bin_log.is_open())
3595 {
3596 DBUG_ASSERT(query_length);
3597 thd->clear_error();
3598 result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
3599 FALSE, FALSE, FALSE, 0) > 0;
3600 }
3601 end:
3602 close_mysql_tables(thd);
3603 }
3604
3605 #ifdef WITH_WSREP
3606 WSREP_ERROR_LABEL:
3607 if (WSREP(thd) && !thd->wsrep_applier)
3608 {
3609 WSREP_TO_ISOLATION_END;
3610
3611 thd->set_query(query_save);
3612 thd->wsrep_exec_mode = LOCAL_STATE;
3613 }
3614 #endif /* WITH_WSREP */
3615
3616 thd->restore_stmt_binlog_format(save_binlog_format);
3617
3618 DBUG_RETURN(result);
3619 }
3620
3621
3622 /*
3623 Find user in ACL
3624
3625 SYNOPSIS
3626 is_acl_user()
3627 host host name
3628 user user name
3629
3630 RETURN
3631 FALSE user not fond
3632 TRUE there is such user
3633 */
3634
is_acl_user(const char * host,const char * user)3635 bool is_acl_user(const char *host, const char *user)
3636 {
3637 bool res;
3638
3639 /* --skip-grants */
3640 if (!initialized)
3641 return TRUE;
3642
3643 mysql_mutex_lock(&acl_cache->lock);
3644
3645 if (*host) // User
3646 res= find_user_exact(host, user) != NULL;
3647 else // Role
3648 res= find_acl_role(user) != NULL;
3649
3650 mysql_mutex_unlock(&acl_cache->lock);
3651 return res;
3652 }
3653
3654 /*
3655 Find first entry that matches the specified user@host pair
3656 */
find_user_exact(const char * host,const char * user)3657 static ACL_USER * find_user_exact(const char *host, const char *user)
3658 {
3659 mysql_mutex_assert_owner(&acl_cache->lock);
3660
3661 for (uint i=0 ; i < acl_users.elements ; i++)
3662 {
3663 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
3664 if (acl_user->eq(user, host))
3665 return acl_user;
3666 }
3667 return 0;
3668 }
3669
3670 /*
3671 Find first entry that matches the specified user@host pair
3672 */
find_user_wild(const char * host,const char * user,const char * ip)3673 static ACL_USER * find_user_wild(const char *host, const char *user, const char *ip)
3674 {
3675 mysql_mutex_assert_owner(&acl_cache->lock);
3676
3677 for (uint i=0 ; i < acl_users.elements ; i++)
3678 {
3679 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
3680 if (acl_user->wild_eq(user, host, ip))
3681 return acl_user;
3682 }
3683 return 0;
3684 }
3685
3686 /*
3687 Find a role with the specified name
3688 */
find_acl_role(const char * role)3689 static ACL_ROLE *find_acl_role(const char *role)
3690 {
3691 DBUG_ENTER("find_acl_role");
3692 DBUG_PRINT("enter",("role: '%s'", role));
3693 DBUG_PRINT("info", ("Hash elements: %ld", acl_roles.records));
3694
3695 mysql_mutex_assert_owner(&acl_cache->lock);
3696
3697 ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role,
3698 safe_strlen(role));
3699 DBUG_RETURN(r);
3700 }
3701
3702
find_acl_user_base(const char * user,const char * host)3703 static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host)
3704 {
3705 if (*host)
3706 return find_user_exact(host, user);
3707
3708 return find_acl_role(user);
3709 }
3710
3711
3712 /*
3713 Comparing of hostnames
3714
3715 NOTES
3716 A hostname may be of type:
3717 hostname (May include wildcards); monty.pp.sci.fi
3718 ip (May include wildcards); 192.168.0.0
3719 ip/netmask 192.168.0.0/255.255.255.0
3720
3721 A net mask of 0.0.0.0 is not allowed.
3722 */
3723
calc_ip(const char * ip,long * val,char end)3724 static const char *calc_ip(const char *ip, long *val, char end)
3725 {
3726 long ip_val,tmp;
3727 if (!(ip=str2int(ip,10,0,255,&ip_val)) || *ip != '.')
3728 return 0;
3729 ip_val<<=24;
3730 if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
3731 return 0;
3732 ip_val+=tmp<<16;
3733 if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
3734 return 0;
3735 ip_val+=tmp<<8;
3736 if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != end)
3737 return 0;
3738 *val=ip_val+tmp;
3739 return ip;
3740 }
3741
3742
update_hostname(acl_host_and_ip * host,const char * hostname)3743 static void update_hostname(acl_host_and_ip *host, const char *hostname)
3744 {
3745 // fix historical undocumented convention that empty host is the same as '%'
3746 hostname=const_cast<char*>(hostname ? hostname : host_not_specified.str);
3747 host->hostname=(char*) hostname; // This will not be modified!
3748 if (!(hostname= calc_ip(hostname,&host->ip,'/')) ||
3749 !(hostname= calc_ip(hostname+1,&host->ip_mask,'\0')))
3750 {
3751 host->ip= host->ip_mask=0; // Not a masked ip
3752 }
3753 }
3754
3755
compare_hostname(const acl_host_and_ip * host,const char * hostname,const char * ip)3756 static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
3757 const char *ip)
3758 {
3759 long tmp;
3760 if (host->ip_mask && ip && calc_ip(ip,&tmp,'\0'))
3761 {
3762 return (tmp & host->ip_mask) == host->ip;
3763 }
3764 return (!host->hostname ||
3765 (hostname && !wild_case_compare(system_charset_info,
3766 hostname, host->hostname)) ||
3767 (ip && !wild_compare(ip, host->hostname, 0)));
3768 }
3769
3770 /**
3771 Check if the given host name needs to be resolved or not.
3772 Host name has to be resolved if it actually contains *name*.
3773
3774 For example:
3775 192.168.1.1 --> FALSE
3776 192.168.1.0/255.255.255.0 --> FALSE
3777 % --> FALSE
3778 192.168.1.% --> FALSE
3779 AB% --> FALSE
3780
3781 AAAAFFFF --> TRUE (Hostname)
3782 AAAA:FFFF:1234:5678 --> FALSE
3783 ::1 --> FALSE
3784
3785 This function does not check if the given string is a valid host name or
3786 not. It assumes that the argument is a valid host name.
3787
3788 @param hostname the string to check.
3789
3790 @return a flag telling if the argument needs to be resolved or not.
3791 @retval TRUE the argument is a host name and needs to be resolved.
3792 @retval FALSE the argument is either an IP address, or a patter and
3793 should not be resolved.
3794 */
3795
hostname_requires_resolving(const char * hostname)3796 bool hostname_requires_resolving(const char *hostname)
3797 {
3798 if (!hostname)
3799 return FALSE;
3800
3801 /* Check if hostname is the localhost. */
3802
3803 size_t hostname_len= strlen(hostname);
3804 size_t localhost_len= strlen(my_localhost);
3805
3806 if (hostname == my_localhost ||
3807 (hostname_len == localhost_len &&
3808 !my_strnncoll(system_charset_info,
3809 (const uchar *) hostname, hostname_len,
3810 (const uchar *) my_localhost, strlen(my_localhost))))
3811 {
3812 return FALSE;
3813 }
3814
3815 /*
3816 If the string contains any of {':', '%', '_', '/'}, it is definitely
3817 not a host name:
3818 - ':' means that the string is an IPv6 address;
3819 - '%' or '_' means that the string is a pattern;
3820 - '/' means that the string is an IPv4 network address;
3821 */
3822
3823 for (const char *p= hostname; *p; ++p)
3824 {
3825 switch (*p) {
3826 case ':':
3827 case '%':
3828 case '_':
3829 case '/':
3830 return FALSE;
3831 }
3832 }
3833
3834 /*
3835 Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
3836 (12.34.56.78). The assumption is that if the string contains only
3837 digits and dots, it is an IPv4 address. Otherwise -- a host name.
3838 */
3839
3840 for (const char *p= hostname; *p; ++p)
3841 {
3842 if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
3843 return TRUE; /* a "letter" has been found. */
3844 }
3845
3846 return FALSE; /* all characters are either dots or digits. */
3847 }
3848
3849
set_authentication_plugin_from_password(const User_table & user_table,const char * password,size_t password_length)3850 void set_authentication_plugin_from_password(const User_table& user_table,
3851 const char* password, size_t password_length)
3852 {
3853 if (password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH ||
3854 password_length == 0)
3855 {
3856 user_table.plugin()->store(native_password_plugin_name.str,
3857 native_password_plugin_name.length,
3858 system_charset_info);
3859 }
3860 else
3861 {
3862 DBUG_ASSERT(password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
3863 user_table.plugin()->store(old_password_plugin_name.str,
3864 old_password_plugin_name.length,
3865 system_charset_info);
3866 }
3867 user_table.authentication_string()->store(password,
3868 password_length,
3869 system_charset_info);
3870 }
3871 /**
3872 Update record for user in mysql.user privilege table with new password.
3873
3874 @param thd THD
3875 @param table Pointer to TABLE object for open mysql.user table
3876 @param host Hostname
3877 @param user Username
3878 @param new_password New password hash
3879 @param new_password_len Length of new password hash
3880
3881 @see change_password
3882 */
3883
update_user_table(THD * thd,const User_table & user_table,const char * host,const char * user,const char * new_password,size_t new_password_len)3884 static bool update_user_table(THD *thd, const User_table& user_table,
3885 const char *host, const char *user,
3886 const char *new_password, size_t new_password_len)
3887 {
3888 char user_key[MAX_KEY_LENGTH];
3889 int error;
3890 DBUG_ENTER("update_user_table");
3891 DBUG_PRINT("enter",("user: %s host: %s",user,host));
3892
3893 TABLE *table= user_table.table();
3894 table->use_all_columns();
3895 user_table.host()->store(host,(uint) strlen(host), system_charset_info);
3896 user_table.user()->store(user,(uint) strlen(user), system_charset_info);
3897 key_copy((uchar *) user_key, table->record[0], table->key_info,
3898 table->key_info->key_length);
3899
3900 if (table->file->ha_index_read_idx_map(table->record[0], 0,
3901 (uchar *) user_key, HA_WHOLE_KEY,
3902 HA_READ_KEY_EXACT))
3903 {
3904 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
3905 MYF(0)); /* purecov: deadcode */
3906 DBUG_RETURN(1); /* purecov: deadcode */
3907 }
3908 store_record(table,record[1]);
3909
3910 if (user_table.plugin())
3911 {
3912 set_authentication_plugin_from_password(user_table, new_password,
3913 new_password_len);
3914 }
3915
3916 if (user_table.password())
3917 user_table.password()->store(new_password, new_password_len, system_charset_info);
3918
3919
3920 if (unlikely(error= table->file->ha_update_row(table->record[1],
3921 table->record[0])) &&
3922 error != HA_ERR_RECORD_IS_THE_SAME)
3923 {
3924 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
3925 DBUG_RETURN(1);
3926 }
3927 DBUG_RETURN(0);
3928 }
3929
3930
3931 /*
3932 Return 1 if we are allowed to create new users
3933 the logic here is: INSERT_ACL is sufficient.
3934 It's also a requirement in opt_safe_user_create,
3935 otherwise CREATE_USER_ACL is enough.
3936 */
3937
test_if_create_new_users(THD * thd)3938 static bool test_if_create_new_users(THD *thd)
3939 {
3940 Security_context *sctx= thd->security_ctx;
3941 bool create_new_users= MY_TEST(sctx->master_access & INSERT_ACL) ||
3942 (!opt_safe_user_create &&
3943 MY_TEST(sctx->master_access & CREATE_USER_ACL));
3944 if (!create_new_users)
3945 {
3946 TABLE_LIST tl;
3947 ulong db_access;
3948 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, TL_WRITE);
3949 create_new_users= 1;
3950
3951 db_access=acl_get(sctx->host, sctx->ip,
3952 sctx->priv_user, tl.db.str, 0);
3953 if (sctx->priv_role[0])
3954 db_access|= acl_get("", "", sctx->priv_role, tl.db.str, 0);
3955 if (!(db_access & INSERT_ACL))
3956 {
3957 if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
3958 create_new_users=0;
3959 }
3960 }
3961 return create_new_users;
3962 }
3963
3964
3965 /****************************************************************************
3966 Handle GRANT commands
3967 ****************************************************************************/
3968
replace_user_table(THD * thd,const User_table & user_table,LEX_USER & combo,ulong rights,bool revoke_grant,bool can_create_user,bool no_auto_create)3969 static int replace_user_table(THD *thd, const User_table &user_table,
3970 LEX_USER &combo,
3971 ulong rights, bool revoke_grant,
3972 bool can_create_user, bool no_auto_create)
3973 {
3974 int error = -1;
3975 bool old_row_exists=0;
3976 char what= (revoke_grant) ? 'N' : 'Y';
3977 uchar user_key[MAX_KEY_LENGTH];
3978 bool handle_as_role= combo.is_role();
3979 LEX *lex= thd->lex;
3980 TABLE *table= user_table.table();
3981 DBUG_ENTER("replace_user_table");
3982
3983 mysql_mutex_assert_owner(&acl_cache->lock);
3984
3985 if (combo.pwhash.str && combo.pwhash.str[0])
3986 {
3987 if (combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
3988 combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
3989 {
3990 DBUG_ASSERT(0);
3991 my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
3992 DBUG_RETURN(-1);
3993 }
3994 }
3995 else
3996 combo.pwhash= empty_clex_str;
3997
3998 /* if the user table is not up to date, we can't handle role updates */
3999 if (!user_table.is_role() && handle_as_role)
4000 {
4001 my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
4002 "user", ROLE_ASSIGN_COLUMN_IDX + 1, user_table.num_fields(),
4003 static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
4004 DBUG_RETURN(-1);
4005 }
4006
4007 table->use_all_columns();
4008 user_table.host()->store(combo.host.str,combo.host.length,
4009 system_charset_info);
4010 user_table.user()->store(combo.user.str,combo.user.length,
4011 system_charset_info);
4012 key_copy(user_key, table->record[0], table->key_info,
4013 table->key_info->key_length);
4014
4015 if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
4016 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
4017 {
4018 /* what == 'N' means revoke */
4019 if (what == 'N')
4020 {
4021 if (combo.host.length)
4022 my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
4023 else
4024 my_error(ER_INVALID_ROLE, MYF(0), combo.user.str);
4025 goto end;
4026 }
4027 /*
4028 There are four options which affect the process of creation of
4029 a new user (mysqld option --safe-create-user, 'insert' privilege
4030 on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
4031 SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
4032 how it should work.
4033 if (safe-user-create && ! INSERT_priv) => reject
4034 else if (identified_by) => create
4035 else if (no_auto_create_user) => reject
4036 else create
4037
4038 see also test_if_create_new_users()
4039 */
4040 else if (!combo.pwhash.length && !combo.plugin.length && no_auto_create)
4041 {
4042 my_error(ER_PASSWORD_NO_MATCH, MYF(0));
4043 goto end;
4044 }
4045 else if (!can_create_user)
4046 {
4047 my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
4048 goto end;
4049 }
4050 else if (combo.plugin.str[0])
4051 {
4052 if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN))
4053 {
4054 my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str);
4055 goto end;
4056 }
4057 }
4058
4059 old_row_exists = 0;
4060 restore_record(table,s->default_values);
4061 user_table.host()->store(combo.host.str,combo.host.length,
4062 system_charset_info);
4063 user_table.user()->store(combo.user.str,combo.user.length,
4064 system_charset_info);
4065 }
4066 else
4067 {
4068 old_row_exists = 1;
4069 store_record(table,record[1]); // Save copy for update
4070 }
4071
4072 if (!old_row_exists || combo.pwtext.length || combo.pwhash.length)
4073 if (!handle_as_role && validate_password(&combo, thd))
4074 goto end;
4075
4076 /* Update table columns with new privileges */
4077
4078 ulong priv;
4079 priv = SELECT_ACL;
4080 for (uint i= 0; i < user_table.num_privileges(); i++, priv <<= 1)
4081 {
4082 if (priv & rights)
4083 user_table.priv_field(i)->store(&what, 1, &my_charset_latin1);
4084 }
4085
4086 rights= user_table.get_access();
4087
4088 DBUG_PRINT("info",("table fields: %d", user_table.num_fields()));
4089 /* If we don't have a password column, we'll use the authentication_string
4090 column later. */
4091 if (combo.pwhash.str[0] && user_table.password())
4092 user_table.password()->store(combo.pwhash.str, combo.pwhash.length,
4093 system_charset_info);
4094 /* We either have the password column, the plugin column, or both. Otherwise
4095 we have a corrupt user table. */
4096 DBUG_ASSERT(user_table.password() || user_table.plugin());
4097 if (user_table.ssl_type()) /* From 4.0.0 we have more fields */
4098 {
4099 /* We write down SSL related ACL stuff */
4100 switch (lex->ssl_type) {
4101 case SSL_TYPE_ANY:
4102 user_table.ssl_type()->store(STRING_WITH_LEN("ANY"),
4103 &my_charset_latin1);
4104 user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4105 user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4106 user_table.x509_subject()->store("", 0, &my_charset_latin1);
4107 break;
4108 case SSL_TYPE_X509:
4109 user_table.ssl_type()->store(STRING_WITH_LEN("X509"),
4110 &my_charset_latin1);
4111 user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4112 user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4113 user_table.x509_subject()->store("", 0, &my_charset_latin1);
4114 break;
4115 case SSL_TYPE_SPECIFIED:
4116 user_table.ssl_type()->store(STRING_WITH_LEN("SPECIFIED"),
4117 &my_charset_latin1);
4118 user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4119 user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4120 user_table.x509_subject()->store("", 0, &my_charset_latin1);
4121 if (lex->ssl_cipher)
4122 user_table.ssl_cipher()->store(lex->ssl_cipher,
4123 strlen(lex->ssl_cipher),
4124 system_charset_info);
4125 if (lex->x509_issuer)
4126 user_table.x509_issuer()->store(lex->x509_issuer,
4127 strlen(lex->x509_issuer),
4128 system_charset_info);
4129 if (lex->x509_subject)
4130 user_table.x509_subject()->store(lex->x509_subject,
4131 strlen(lex->x509_subject),
4132 system_charset_info);
4133 break;
4134 case SSL_TYPE_NOT_SPECIFIED:
4135 break;
4136 case SSL_TYPE_NONE:
4137 user_table.ssl_type()->store("", 0, &my_charset_latin1);
4138 user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4139 user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4140 user_table.x509_subject()->store("", 0, &my_charset_latin1);
4141 break;
4142 }
4143
4144 USER_RESOURCES mqh= lex->mqh;
4145 if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
4146 user_table.max_questions()->store((longlong) mqh.questions, TRUE);
4147 if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
4148 user_table.max_updates()->store((longlong) mqh.updates, TRUE);
4149 if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
4150 user_table.max_connections()->store((longlong) mqh.conn_per_hour, TRUE);
4151 if (user_table.max_user_connections() &&
4152 (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
4153 user_table.max_user_connections()->store((longlong) mqh.user_conn, FALSE);
4154 if (user_table.plugin())
4155 {
4156 user_table.plugin()->set_notnull();
4157 user_table.authentication_string()->set_notnull();
4158 if (combo.plugin.str[0])
4159 {
4160 DBUG_ASSERT(combo.pwhash.str[0] == 0);
4161 if (user_table.password())
4162 user_table.password()->reset();
4163 user_table.plugin()->store(combo.plugin.str, combo.plugin.length,
4164 system_charset_info);
4165 user_table.authentication_string()->store(combo.auth.str, combo.auth.length,
4166 system_charset_info);
4167 }
4168 if (combo.pwhash.str[0])
4169 {
4170 DBUG_ASSERT(combo.plugin.str[0] == 0);
4171 /* We have Password column. */
4172 if (user_table.password())
4173 {
4174 user_table.plugin()->reset();
4175 user_table.authentication_string()->reset();
4176 }
4177 else
4178 {
4179 /* We do not have Password column. Use PLUGIN && Authentication_string
4180 columns instead. */
4181 set_authentication_plugin_from_password(user_table,
4182 combo.pwhash.str,
4183 combo.pwhash.length);
4184 }
4185 }
4186
4187 if (user_table.max_statement_time())
4188 {
4189 if (mqh.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
4190 user_table.max_statement_time()->store(mqh.max_statement_time);
4191 }
4192 }
4193 mqh_used= (mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour ||
4194 mqh.user_conn || mqh.max_statement_time != 0.0);
4195
4196 /* table format checked earlier */
4197 if (handle_as_role)
4198 {
4199 if (old_row_exists && !user_table.check_is_role())
4200 {
4201 goto end;
4202 }
4203 user_table.is_role()->store("Y", 1, system_charset_info);
4204 }
4205 }
4206
4207 if (old_row_exists)
4208 {
4209 /*
4210 We should NEVER delete from the user table, as a uses can still
4211 use mysqld even if he doesn't have any privileges in the user table!
4212 */
4213 if (cmp_record(table, record[1]))
4214 {
4215 if (unlikely(error= table->file->ha_update_row(table->record[1],
4216 table->record[0])) &&
4217 error != HA_ERR_RECORD_IS_THE_SAME)
4218 { // This should never happen
4219 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4220 error= -1; /* purecov: deadcode */
4221 goto end; /* purecov: deadcode */
4222 }
4223 else
4224 error= 0;
4225 }
4226 }
4227 else if (unlikely(error=table->file->ha_write_row(table->record[0])))
4228 {
4229 // This should never happen
4230 if (table->file->is_fatal_error(error, HA_CHECK_DUP))
4231 {
4232 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4233 error= -1; /* purecov: deadcode */
4234 goto end; /* purecov: deadcode */
4235 }
4236 }
4237 error=0; // Privileges granted / revoked
4238
4239 end:
4240 if (likely(!error))
4241 {
4242 acl_cache->clear(1); // Clear privilege cache
4243 if (old_row_exists)
4244 {
4245 if (handle_as_role)
4246 acl_update_role(combo.user.str, rights);
4247 else
4248 acl_update_user(combo.user.str, combo.host.str,
4249 combo.pwhash.str, combo.pwhash.length,
4250 lex->ssl_type,
4251 lex->ssl_cipher,
4252 lex->x509_issuer,
4253 lex->x509_subject,
4254 &lex->mqh,
4255 rights,
4256 &combo.plugin,
4257 &combo.auth);
4258 }
4259 else
4260 {
4261 if (handle_as_role)
4262 acl_insert_role(combo.user.str, rights);
4263 else
4264 acl_insert_user(combo.user.str, combo.host.str,
4265 combo.pwhash.str, combo.pwhash.length,
4266 lex->ssl_type,
4267 lex->ssl_cipher,
4268 lex->x509_issuer,
4269 lex->x509_subject,
4270 &lex->mqh,
4271 rights,
4272 &combo.plugin,
4273 &combo.auth);
4274 }
4275 }
4276 DBUG_RETURN(error);
4277 }
4278
4279
4280 /*
4281 change grants in the mysql.db table
4282 */
4283
replace_db_table(TABLE * table,const char * db,const LEX_USER & combo,ulong rights,bool revoke_grant)4284 static int replace_db_table(TABLE *table, const char *db,
4285 const LEX_USER &combo,
4286 ulong rights, bool revoke_grant)
4287 {
4288 uint i;
4289 ulong priv,store_rights;
4290 bool old_row_exists=0;
4291 int error;
4292 char what= (revoke_grant) ? 'N' : 'Y';
4293 uchar user_key[MAX_KEY_LENGTH];
4294 DBUG_ENTER("replace_db_table");
4295
4296 /* Check if there is such a user in user table in memory? */
4297 if (!find_user_wild(combo.host.str,combo.user.str))
4298 {
4299 /* The user could be a role, check if the user is registered as a role */
4300 if (!combo.host.length && !find_acl_role(combo.user.str))
4301 {
4302 my_message(ER_PASSWORD_NO_MATCH, ER_THD(table->in_use,
4303 ER_PASSWORD_NO_MATCH), MYF(0));
4304 DBUG_RETURN(-1);
4305 }
4306 }
4307
4308 table->use_all_columns();
4309 table->field[0]->store(combo.host.str,combo.host.length,
4310 system_charset_info);
4311 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4312 table->field[2]->store(combo.user.str,combo.user.length,
4313 system_charset_info);
4314 key_copy(user_key, table->record[0], table->key_info,
4315 table->key_info->key_length);
4316
4317 if (table->file->ha_index_read_idx_map(table->record[0],0, user_key,
4318 HA_WHOLE_KEY,
4319 HA_READ_KEY_EXACT))
4320 {
4321 if (what == 'N')
4322 { // no row, no revoke
4323 my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
4324 goto abort;
4325 }
4326 old_row_exists = 0;
4327 restore_record(table, s->default_values);
4328 table->field[0]->store(combo.host.str,combo.host.length,
4329 system_charset_info);
4330 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4331 table->field[2]->store(combo.user.str,combo.user.length,
4332 system_charset_info);
4333 }
4334 else
4335 {
4336 old_row_exists = 1;
4337 store_record(table,record[1]);
4338 }
4339
4340 store_rights=get_rights_for_db(rights);
4341 for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
4342 {
4343 if (priv & store_rights) // do it if priv is chosen
4344 table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
4345 }
4346 rights=get_access(table,3);
4347 rights=fix_rights_for_db(rights);
4348
4349 if (old_row_exists)
4350 {
4351 /* update old existing row */
4352 if (rights)
4353 {
4354 if (unlikely((error= table->file->ha_update_row(table->record[1],
4355 table->record[0]))) &&
4356 error != HA_ERR_RECORD_IS_THE_SAME)
4357 goto table_error; /* purecov: deadcode */
4358 }
4359 else /* must have been a revoke of all privileges */
4360 {
4361 if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4362 goto table_error; /* purecov: deadcode */
4363 }
4364 }
4365 else if (rights &&
4366 (unlikely(error= table->file->ha_write_row(table->record[0]))))
4367 {
4368 if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4369 goto table_error; /* purecov: deadcode */
4370 }
4371
4372 acl_cache->clear(1); // Clear privilege cache
4373 if (old_row_exists)
4374 acl_update_db(combo.user.str,combo.host.str,db,rights);
4375 else if (rights)
4376 {
4377 /*
4378 If we did not have an already existing row, for users, we must always
4379 insert an ACL_DB entry. For roles however, it is possible that one was
4380 already created when DB privileges were propagated from other granted
4381 roles onto the current role. For this case, first try to update the
4382 existing entry, otherwise insert a new one.
4383 */
4384 if (!combo.is_role() ||
4385 !acl_update_db(combo.user.str, combo.host.str, db, rights))
4386 {
4387 acl_insert_db(combo.user.str,combo.host.str,db,rights);
4388 }
4389 }
4390 DBUG_RETURN(0);
4391
4392 /* This could only happen if the grant tables got corrupted */
4393 table_error:
4394 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4395
4396 abort:
4397 DBUG_RETURN(-1);
4398 }
4399
4400 /**
4401 Updates the mysql.roles_mapping table
4402
4403 @param table TABLE to update
4404 @param user user name of the grantee
4405 @param host host name of the grantee
4406 @param role role name to grant
4407 @param with_admin WITH ADMIN OPTION flag
4408 @param existing the entry in the acl_roles_mappings hash or NULL.
4409 it is never NULL if revoke_grant is true.
4410 it is NULL when a new pair is added, it's not NULL
4411 when an existing pair is updated.
4412 @param revoke_grant true for REVOKE, false for GRANT
4413 */
4414 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)4415 replace_roles_mapping_table(TABLE *table, LEX_CSTRING *user, LEX_CSTRING *host,
4416 LEX_CSTRING *role, bool with_admin,
4417 ROLE_GRANT_PAIR *existing, bool revoke_grant)
4418 {
4419 DBUG_ENTER("replace_roles_mapping_table");
4420
4421 uchar row_key[MAX_KEY_LENGTH];
4422 int error;
4423 table->use_all_columns();
4424 restore_record(table, s->default_values);
4425 table->field[0]->store(host->str, host->length, system_charset_info);
4426 table->field[1]->store(user->str, user->length, system_charset_info);
4427 table->field[2]->store(role->str, role->length, system_charset_info);
4428
4429 DBUG_ASSERT(!revoke_grant || existing);
4430
4431 if (existing) // delete or update
4432 {
4433 key_copy(row_key, table->record[0], table->key_info,
4434 table->key_info->key_length);
4435 if (table->file->ha_index_read_idx_map(table->record[1], 0, row_key,
4436 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
4437 {
4438 /* No match */
4439 DBUG_RETURN(1);
4440 }
4441 if (revoke_grant && !with_admin)
4442 {
4443 if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4444 {
4445 DBUG_PRINT("info", ("error deleting row '%s' '%s' '%s'",
4446 host->str, user->str, role->str));
4447 goto table_error;
4448 }
4449 }
4450 else if (with_admin)
4451 {
4452 table->field[3]->store(!revoke_grant + 1);
4453
4454 if (unlikely((error= table->file->ha_update_row(table->record[1],
4455 table->record[0]))))
4456 {
4457 DBUG_PRINT("info", ("error updating row '%s' '%s' '%s'",
4458 host->str, user->str, role->str));
4459 goto table_error;
4460 }
4461 }
4462 DBUG_RETURN(0);
4463 }
4464
4465 table->field[3]->store(with_admin + 1);
4466
4467 if (unlikely((error= table->file->ha_write_row(table->record[0]))))
4468 {
4469 DBUG_PRINT("info", ("error inserting row '%s' '%s' '%s'",
4470 host->str, user->str, role->str));
4471 goto table_error;
4472 }
4473
4474 /* all ok */
4475 DBUG_RETURN(0);
4476
4477 table_error:
4478 DBUG_PRINT("info", ("table error"));
4479 table->file->print_error(error, MYF(0));
4480 DBUG_RETURN(1);
4481 }
4482
4483
4484 /**
4485 Updates the acl_roles_mappings hash
4486
4487 @param user user name of the grantee
4488 @param host host name of the grantee
4489 @param role role name to grant
4490 @param with_admin WITH ADMIN OPTION flag
4491 @param existing the entry in the acl_roles_mappings hash or NULL.
4492 it is never NULL if revoke_grant is true.
4493 it is NULL when a new pair is added, it's not NULL
4494 when an existing pair is updated.
4495 @param revoke_grant true for REVOKE, false for GRANT
4496 */
4497 static int
update_role_mapping(LEX_CSTRING * user,LEX_CSTRING * host,LEX_CSTRING * role,bool with_admin,ROLE_GRANT_PAIR * existing,bool revoke_grant)4498 update_role_mapping(LEX_CSTRING *user, LEX_CSTRING *host, LEX_CSTRING *role,
4499 bool with_admin, ROLE_GRANT_PAIR *existing, bool revoke_grant)
4500 {
4501 if (revoke_grant)
4502 {
4503 if (with_admin)
4504 {
4505 existing->with_admin= false;
4506 return 0;
4507 }
4508 return my_hash_delete(&acl_roles_mappings, (uchar*)existing);
4509 }
4510
4511 if (existing)
4512 {
4513 existing->with_admin|= with_admin;
4514 return 0;
4515 }
4516
4517 /* allocate a new entry that will go in the hash */
4518 ROLE_GRANT_PAIR *hash_entry= new (&acl_memroot) ROLE_GRANT_PAIR;
4519 if (hash_entry->init(&acl_memroot, user->str, host->str,
4520 role->str, with_admin))
4521 return 1;
4522 return my_hash_insert(&acl_roles_mappings, (uchar*) hash_entry);
4523 }
4524
4525 static void
acl_update_proxy_user(ACL_PROXY_USER * new_value,bool is_revoke)4526 acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
4527 {
4528 mysql_mutex_assert_owner(&acl_cache->lock);
4529
4530 DBUG_ENTER("acl_update_proxy_user");
4531 for (uint i= 0; i < acl_proxy_users.elements; i++)
4532 {
4533 ACL_PROXY_USER *acl_user=
4534 dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
4535
4536 if (acl_user->pk_equals(new_value))
4537 {
4538 if (is_revoke)
4539 {
4540 DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
4541 delete_dynamic_element(&acl_proxy_users, i);
4542 }
4543 else
4544 {
4545 DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
4546 acl_user->set_data(new_value);
4547 }
4548 break;
4549 }
4550 }
4551 DBUG_VOID_RETURN;
4552 }
4553
4554
4555 static void
acl_insert_proxy_user(ACL_PROXY_USER * new_value)4556 acl_insert_proxy_user(ACL_PROXY_USER *new_value)
4557 {
4558 DBUG_ENTER("acl_insert_proxy_user");
4559 mysql_mutex_assert_owner(&acl_cache->lock);
4560 (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
4561 my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
4562 acl_proxy_users.elements,
4563 sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
4564 DBUG_VOID_RETURN;
4565 }
4566
4567
4568 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)4569 replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
4570 const LEX_USER *proxied_user, bool with_grant_arg,
4571 bool revoke_grant)
4572 {
4573 bool old_row_exists= 0;
4574 int error;
4575 uchar user_key[MAX_KEY_LENGTH];
4576 ACL_PROXY_USER new_grant;
4577 char grantor[USER_HOST_BUFF_SIZE];
4578
4579 DBUG_ENTER("replace_proxies_priv_table");
4580
4581 /* Check if there is such a user in user table in memory? */
4582 if (!find_user_wild(user->host.str,user->user.str))
4583 {
4584 my_message(ER_PASSWORD_NO_MATCH,
4585 ER_THD(thd, ER_PASSWORD_NO_MATCH), MYF(0));
4586 DBUG_RETURN(-1);
4587 }
4588
4589 table->use_all_columns();
4590 ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
4591 &proxied_user->host, &proxied_user->user);
4592
4593 key_copy(user_key, table->record[0], table->key_info,
4594 table->key_info->key_length);
4595
4596 get_grantor(thd, grantor);
4597
4598 if (unlikely((error= table->file->ha_index_init(0, 1))))
4599 {
4600 table->file->print_error(error, MYF(0));
4601 DBUG_PRINT("info", ("ha_index_init error"));
4602 DBUG_RETURN(-1);
4603 }
4604
4605 if (table->file->ha_index_read_map(table->record[0], user_key,
4606 HA_WHOLE_KEY,
4607 HA_READ_KEY_EXACT))
4608 {
4609 DBUG_PRINT ("info", ("Row not found"));
4610 if (revoke_grant)
4611 { // no row, no revoke
4612 my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
4613 goto abort;
4614 }
4615 old_row_exists= 0;
4616 restore_record(table, s->default_values);
4617 ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
4618 &proxied_user->host,
4619 &proxied_user->user,
4620 with_grant_arg,
4621 grantor);
4622 }
4623 else
4624 {
4625 DBUG_PRINT("info", ("Row found"));
4626 old_row_exists= 1;
4627 store_record(table, record[1]);
4628 }
4629
4630 if (old_row_exists)
4631 {
4632 /* update old existing row */
4633 if (!revoke_grant)
4634 {
4635 if (unlikely(error= table->file->ha_update_row(table->record[1],
4636 table->record[0])) &&
4637 error != HA_ERR_RECORD_IS_THE_SAME)
4638 goto table_error; /* purecov: inspected */
4639 }
4640 else
4641 {
4642 if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4643 goto table_error; /* purecov: inspected */
4644 }
4645 }
4646 else if (unlikely((error= table->file->ha_write_row(table->record[0]))))
4647 {
4648 DBUG_PRINT("info", ("error inserting the row"));
4649 if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4650 goto table_error; /* purecov: inspected */
4651 }
4652
4653 acl_cache->clear(1); // Clear privilege cache
4654 if (old_row_exists)
4655 {
4656 new_grant.init(user->host.str, user->user.str,
4657 proxied_user->host.str, proxied_user->user.str,
4658 with_grant_arg);
4659 acl_update_proxy_user(&new_grant, revoke_grant);
4660 }
4661 else
4662 {
4663 new_grant.init(&acl_memroot, user->host.str, user->user.str,
4664 proxied_user->host.str, proxied_user->user.str,
4665 with_grant_arg);
4666 acl_insert_proxy_user(&new_grant);
4667 }
4668
4669 table->file->ha_index_end();
4670 DBUG_RETURN(0);
4671
4672 /* This could only happen if the grant tables got corrupted */
4673 table_error:
4674 DBUG_PRINT("info", ("table error"));
4675 table->file->print_error(error, MYF(0)); /* purecov: inspected */
4676
4677 abort:
4678 DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
4679 table->file->ha_index_end();
4680 DBUG_RETURN(-1);
4681 }
4682
4683
4684 class GRANT_COLUMN :public Sql_alloc
4685 {
4686 public:
4687 char *column;
4688 ulong rights;
4689 ulong init_rights;
4690 uint key_length;
GRANT_COLUMN(String & c,ulong y)4691 GRANT_COLUMN(String &c, ulong y) :rights (y), init_rights(y)
4692 {
4693 column= (char*) memdup_root(&grant_memroot,c.ptr(), key_length=c.length());
4694 }
4695
4696 /* this constructor assumes thas source->column is allocated in grant_memroot */
GRANT_COLUMN(GRANT_COLUMN * source)4697 GRANT_COLUMN(GRANT_COLUMN *source) : column(source->column),
4698 rights (source->rights), init_rights(0), key_length(source->key_length) { }
4699 };
4700
4701
get_key_column(GRANT_COLUMN * buff,size_t * length,my_bool not_used)4702 static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
4703 my_bool not_used __attribute__((unused)))
4704 {
4705 *length=buff->key_length;
4706 return (uchar*) buff->column;
4707 }
4708
4709 class GRANT_NAME :public Sql_alloc
4710 {
4711 public:
4712 acl_host_and_ip host;
4713 char *db, *user, *tname, *hash_key;
4714 ulong privs;
4715 ulong init_privs; /* privileges found in physical table */
4716 ulong sort;
4717 size_t key_length;
4718 GRANT_NAME(const char *h, const char *d,const char *u,
4719 const char *t, ulong p, bool is_routine);
4720 GRANT_NAME (TABLE *form, bool is_routine);
~GRANT_NAME()4721 virtual ~GRANT_NAME() {};
ok()4722 virtual bool ok() { return privs != 0; }
4723 void set_user_details(const char *h, const char *d,
4724 const char *u, const char *t,
4725 bool is_routine);
4726 };
4727
4728
4729 class GRANT_TABLE :public GRANT_NAME
4730 {
4731 public:
4732 ulong cols;
4733 ulong init_cols; /* privileges found in physical table */
4734 HASH hash_columns;
4735
4736 GRANT_TABLE(const char *h, const char *d,const char *u,
4737 const char *t, ulong p, ulong c);
4738 GRANT_TABLE (TABLE *form, TABLE *col_privs);
4739 ~GRANT_TABLE();
ok()4740 bool ok() { return privs != 0 || cols != 0; }
init_hash()4741 void init_hash()
4742 {
4743 my_hash_init2(&hash_columns, 4, system_charset_info, 0, 0, 0,
4744 (my_hash_get_key) get_key_column, 0, 0, 0);
4745 }
4746 };
4747
4748
set_user_details(const char * h,const char * d,const char * u,const char * t,bool is_routine)4749 void GRANT_NAME::set_user_details(const char *h, const char *d,
4750 const char *u, const char *t,
4751 bool is_routine)
4752 {
4753 /* Host given by user */
4754 update_hostname(&host, strdup_root(&grant_memroot, h));
4755 if (db != d)
4756 {
4757 db= strdup_root(&grant_memroot, d);
4758 if (lower_case_table_names)
4759 my_casedn_str(files_charset_info, db);
4760 }
4761 user = strdup_root(&grant_memroot,u);
4762 sort= get_sort(3,host.hostname,db,user);
4763 if (tname != t)
4764 {
4765 tname= strdup_root(&grant_memroot, t);
4766 if (lower_case_table_names || is_routine)
4767 my_casedn_str(files_charset_info, tname);
4768 }
4769 key_length= strlen(d) + strlen(u)+ strlen(t)+3;
4770 hash_key= (char*) alloc_root(&grant_memroot,key_length);
4771 strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
4772 }
4773
GRANT_NAME(const char * h,const char * d,const char * u,const char * t,ulong p,bool is_routine)4774 GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
4775 const char *t, ulong p, bool is_routine)
4776 :db(0), tname(0), privs(p), init_privs(p)
4777 {
4778 set_user_details(h, d, u, t, is_routine);
4779 }
4780
GRANT_TABLE(const char * h,const char * d,const char * u,const char * t,ulong p,ulong c)4781 GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
4782 const char *t, ulong p, ulong c)
4783 :GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
4784 {
4785 init_hash();
4786 }
4787
4788 /*
4789 create a new GRANT_TABLE entry for role inheritance. init_* fields are set
4790 to 0
4791 */
GRANT_NAME(TABLE * form,bool is_routine)4792 GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
4793 {
4794 user= safe_str(get_field(&grant_memroot,form->field[2]));
4795
4796 const char *hostname= get_field(&grant_memroot, form->field[0]);
4797 mysql_mutex_lock(&acl_cache->lock);
4798 if (!hostname && find_acl_role(user))
4799 hostname= "";
4800 mysql_mutex_unlock(&acl_cache->lock);
4801 update_hostname(&host, hostname);
4802
4803 db= get_field(&grant_memroot,form->field[1]);
4804 sort= get_sort(3, host.hostname, db, user);
4805 tname= get_field(&grant_memroot,form->field[3]);
4806 if (!db || !tname)
4807 {
4808 /* Wrong table row; Ignore it */
4809 privs= 0;
4810 return; /* purecov: inspected */
4811 }
4812 if (lower_case_table_names)
4813 {
4814 my_casedn_str(files_charset_info, db);
4815 }
4816 if (lower_case_table_names || is_routine)
4817 {
4818 my_casedn_str(files_charset_info, tname);
4819 }
4820 key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
4821 hash_key= (char*) alloc_root(&grant_memroot, key_length);
4822 strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
4823 privs = (ulong) form->field[6]->val_int();
4824 privs = fix_rights_for_table(privs);
4825 init_privs= privs;
4826 }
4827
4828
GRANT_TABLE(TABLE * form,TABLE * col_privs)4829 GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
4830 :GRANT_NAME(form, FALSE)
4831 {
4832 uchar key[MAX_KEY_LENGTH];
4833
4834 if (!db || !tname)
4835 {
4836 /* Wrong table row; Ignore it */
4837 my_hash_clear(&hash_columns); /* allow for destruction */
4838 cols= 0;
4839 return;
4840 }
4841 cols= (ulong) form->field[7]->val_int();
4842 cols= fix_rights_for_column(cols);
4843 /*
4844 Initial columns privileges are the same as column privileges on creation.
4845 In case of roles, the cols privilege bits can get inherited and thus
4846 cause the cols field to change. The init_cols field is always the same
4847 as the physical table entry
4848 */
4849 init_cols= cols;
4850
4851 init_hash();
4852
4853 if (cols)
4854 {
4855 uint key_prefix_len;
4856 KEY_PART_INFO *key_part= col_privs->key_info->key_part;
4857 col_privs->field[0]->store(host.hostname,
4858 (uint) safe_strlen(host.hostname),
4859 system_charset_info);
4860 col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
4861 col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
4862 col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
4863
4864 key_prefix_len= (key_part[0].store_length +
4865 key_part[1].store_length +
4866 key_part[2].store_length +
4867 key_part[3].store_length);
4868 key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
4869 col_privs->field[4]->store("",0, &my_charset_latin1);
4870
4871 if (col_privs->file->ha_index_init(0, 1))
4872 {
4873 cols= 0;
4874 init_cols= 0;
4875 return;
4876 }
4877
4878 if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
4879 (key_part_map)15,
4880 HA_READ_KEY_EXACT))
4881 {
4882 cols= 0; /* purecov: deadcode */
4883 init_cols= 0;
4884 col_privs->file->ha_index_end();
4885 return;
4886 }
4887 do
4888 {
4889 String *res,column_name;
4890 GRANT_COLUMN *mem_check;
4891 /* As column name is a string, we don't have to supply a buffer */
4892 res=col_privs->field[4]->val_str(&column_name);
4893 ulong priv= (ulong) col_privs->field[6]->val_int();
4894 if (!(mem_check = new GRANT_COLUMN(*res,
4895 fix_rights_for_column(priv))))
4896 {
4897 /* Don't use this entry */
4898 privs= cols= init_privs= init_cols=0; /* purecov: deadcode */
4899 return; /* purecov: deadcode */
4900 }
4901 if (my_hash_insert(&hash_columns, (uchar *) mem_check))
4902 {
4903 /* Invalidate this entry */
4904 privs= cols= init_privs= init_cols=0;
4905 return;
4906 }
4907 } while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
4908 !key_cmp_if_same(col_privs,key,0,key_prefix_len));
4909 col_privs->file->ha_index_end();
4910 }
4911 }
4912
4913
~GRANT_TABLE()4914 GRANT_TABLE::~GRANT_TABLE()
4915 {
4916 my_hash_free(&hash_columns);
4917 }
4918
4919
get_grant_table(GRANT_NAME * buff,size_t * length,my_bool not_used)4920 static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
4921 my_bool not_used __attribute__((unused)))
4922 {
4923 *length=buff->key_length;
4924 return (uchar*) buff->hash_key;
4925 }
4926
4927
free_grant_table(GRANT_TABLE * grant_table)4928 static void free_grant_table(GRANT_TABLE *grant_table)
4929 {
4930 grant_table->~GRANT_TABLE();
4931 }
4932
4933
4934 /* Search after a matching grant. Prefer exact grants before not exact ones */
4935
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)4936 static GRANT_NAME *name_hash_search(HASH *name_hash,
4937 const char *host,const char* ip,
4938 const char *db,
4939 const char *user, const char *tname,
4940 bool exact, bool name_tolower)
4941 {
4942 char helping[SAFE_NAME_LEN*2+USERNAME_LENGTH+3];
4943 char *hend = helping + sizeof(helping);
4944 uint len;
4945 GRANT_NAME *grant_name,*found=0;
4946 HASH_SEARCH_STATE state;
4947
4948 char *db_ptr= strmov(helping, user) + 1;
4949 char *tname_ptr= strnmov(db_ptr, db, hend - db_ptr) + 1;
4950 if (tname_ptr > hend)
4951 return 0; // invalid name = not found
4952 char *end= strnmov(tname_ptr, tname, hend - tname_ptr) + 1;
4953 if (end > hend)
4954 return 0; // invalid name = not found
4955
4956 len = (uint) (end - helping);
4957 if (name_tolower)
4958 my_casedn_str(files_charset_info, tname_ptr);
4959 for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
4960 len, &state);
4961 grant_name ;
4962 grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
4963 len, &state))
4964 {
4965 if (exact)
4966 {
4967 if (!grant_name->host.hostname ||
4968 (host &&
4969 !my_strcasecmp(system_charset_info, host,
4970 grant_name->host.hostname)) ||
4971 (ip && !strcmp(ip, grant_name->host.hostname)))
4972 return grant_name;
4973 }
4974 else
4975 {
4976 if (compare_hostname(&grant_name->host, host, ip) &&
4977 (!found || found->sort < grant_name->sort))
4978 found=grant_name; // Host ok
4979 }
4980 }
4981 return found;
4982 }
4983
4984
4985 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)4986 routine_hash_search(const char *host, const char *ip, const char *db,
4987 const char *user, const char *tname, const Sp_handler *sph,
4988 bool exact)
4989 {
4990 return (GRANT_TABLE*)
4991 name_hash_search(sph->get_priv_hash(),
4992 host, ip, db, user, tname, exact, TRUE);
4993 }
4994
4995
4996 static GRANT_TABLE *
table_hash_search(const char * host,const char * ip,const char * db,const char * user,const char * tname,bool exact)4997 table_hash_search(const char *host, const char *ip, const char *db,
4998 const char *user, const char *tname, bool exact)
4999 {
5000 return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
5001 user, tname, exact, FALSE);
5002 }
5003
5004
5005 static GRANT_COLUMN *
column_hash_search(GRANT_TABLE * t,const char * cname,size_t length)5006 column_hash_search(GRANT_TABLE *t, const char *cname, size_t length)
5007 {
5008 if (!my_hash_inited(&t->hash_columns))
5009 return (GRANT_COLUMN*) 0;
5010 return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
5011 (uchar*) cname, length);
5012 }
5013
5014
replace_column_table(GRANT_TABLE * g_t,TABLE * table,const LEX_USER & combo,List<LEX_COLUMN> & columns,const char * db,const char * table_name,ulong rights,bool revoke_grant)5015 static int replace_column_table(GRANT_TABLE *g_t,
5016 TABLE *table, const LEX_USER &combo,
5017 List <LEX_COLUMN> &columns,
5018 const char *db, const char *table_name,
5019 ulong rights, bool revoke_grant)
5020 {
5021 int result=0;
5022 uchar key[MAX_KEY_LENGTH];
5023 uint key_prefix_length;
5024 KEY_PART_INFO *key_part= table->key_info->key_part;
5025 DBUG_ENTER("replace_column_table");
5026
5027 table->use_all_columns();
5028 table->field[0]->store(combo.host.str,combo.host.length,
5029 system_charset_info);
5030 table->field[1]->store(db,(uint) strlen(db),
5031 system_charset_info);
5032 table->field[2]->store(combo.user.str,combo.user.length,
5033 system_charset_info);
5034 table->field[3]->store(table_name,(uint) strlen(table_name),
5035 system_charset_info);
5036
5037 /* Get length of 4 first key parts */
5038 key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
5039 key_part[2].store_length + key_part[3].store_length);
5040 key_copy(key, table->record[0], table->key_info, key_prefix_length);
5041
5042 rights&= COL_ACLS; // Only ACL for columns
5043
5044 /* first fix privileges for all columns in column list */
5045
5046 List_iterator <LEX_COLUMN> iter(columns);
5047 class LEX_COLUMN *column;
5048 int error= table->file->ha_index_init(0, 1);
5049 if (unlikely(error))
5050 {
5051 table->file->print_error(error, MYF(0));
5052 DBUG_RETURN(-1);
5053 }
5054
5055 while ((column= iter++))
5056 {
5057 ulong privileges= column->rights;
5058 bool old_row_exists=0;
5059 uchar user_key[MAX_KEY_LENGTH];
5060
5061 key_restore(table->record[0],key,table->key_info,
5062 key_prefix_length);
5063 table->field[4]->store(column->column.ptr(), column->column.length(),
5064 system_charset_info);
5065 /* Get key for the first 4 columns */
5066 key_copy(user_key, table->record[0], table->key_info,
5067 table->key_info->key_length);
5068
5069 if (table->file->ha_index_read_map(table->record[0], user_key,
5070 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
5071 {
5072 if (revoke_grant)
5073 {
5074 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
5075 combo.user.str, combo.host.str,
5076 table_name); /* purecov: inspected */
5077 result= -1; /* purecov: inspected */
5078 continue; /* purecov: inspected */
5079 }
5080 old_row_exists = 0;
5081 restore_record(table, s->default_values); // Get empty record
5082 key_restore(table->record[0],key,table->key_info,
5083 key_prefix_length);
5084 table->field[4]->store(column->column.ptr(),column->column.length(),
5085 system_charset_info);
5086 }
5087 else
5088 {
5089 ulong tmp= (ulong) table->field[6]->val_int();
5090 tmp=fix_rights_for_column(tmp);
5091
5092 if (revoke_grant)
5093 privileges = tmp & ~(privileges | rights);
5094 else
5095 privileges |= tmp;
5096 old_row_exists = 1;
5097 store_record(table,record[1]); // copy original row
5098 }
5099
5100 table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
5101
5102 if (old_row_exists)
5103 {
5104 GRANT_COLUMN *grant_column;
5105 if (privileges)
5106 error=table->file->ha_update_row(table->record[1],table->record[0]);
5107 else
5108 error=table->file->ha_delete_row(table->record[1]);
5109 if (unlikely(error) && error != HA_ERR_RECORD_IS_THE_SAME)
5110 {
5111 table->file->print_error(error,MYF(0)); /* purecov: inspected */
5112 result= -1; /* purecov: inspected */
5113 goto end; /* purecov: inspected */
5114 }
5115 else
5116 error= 0;
5117 grant_column= column_hash_search(g_t, column->column.ptr(),
5118 column->column.length());
5119 if (grant_column) // Should always be true
5120 grant_column->rights= privileges; // Update hash
5121 }
5122 else // new grant
5123 {
5124 GRANT_COLUMN *grant_column;
5125 if (unlikely((error=table->file->ha_write_row(table->record[0]))))
5126 {
5127 table->file->print_error(error,MYF(0)); /* purecov: inspected */
5128 result= -1; /* purecov: inspected */
5129 goto end; /* purecov: inspected */
5130 }
5131 grant_column= new GRANT_COLUMN(column->column,privileges);
5132 if (my_hash_insert(&g_t->hash_columns,(uchar*) grant_column))
5133 {
5134 result= -1;
5135 goto end;
5136 }
5137 }
5138 }
5139
5140 /*
5141 If revoke of privileges on the table level, remove all such privileges
5142 for all columns
5143 */
5144
5145 if (revoke_grant)
5146 {
5147 uchar user_key[MAX_KEY_LENGTH];
5148 key_copy(user_key, table->record[0], table->key_info,
5149 key_prefix_length);
5150
5151 if (table->file->ha_index_read_map(table->record[0], user_key,
5152 (key_part_map)15,
5153 HA_READ_KEY_EXACT))
5154 goto end;
5155
5156 /* Scan through all rows with the same host,db,user and table */
5157 do
5158 {
5159 ulong privileges = (ulong) table->field[6]->val_int();
5160 privileges=fix_rights_for_column(privileges);
5161 store_record(table,record[1]);
5162
5163 if (privileges & rights) // is in this record the priv to be revoked ??
5164 {
5165 GRANT_COLUMN *grant_column = NULL;
5166 char colum_name_buf[HOSTNAME_LENGTH+1];
5167 String column_name(colum_name_buf,sizeof(colum_name_buf),
5168 system_charset_info);
5169
5170 privileges&= ~rights;
5171 table->field[6]->store((longlong)
5172 get_rights_for_column(privileges), TRUE);
5173 table->field[4]->val_str(&column_name);
5174 grant_column = column_hash_search(g_t,
5175 column_name.ptr(),
5176 column_name.length());
5177 if (privileges)
5178 {
5179 int tmp_error;
5180 if (unlikely(tmp_error=
5181 table->file->ha_update_row(table->record[1],
5182 table->record[0])) &&
5183 tmp_error != HA_ERR_RECORD_IS_THE_SAME)
5184 { /* purecov: deadcode */
5185 table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
5186 result= -1; /* purecov: deadcode */
5187 goto end; /* purecov: deadcode */
5188 }
5189 if (grant_column)
5190 {
5191 grant_column->rights = privileges; // Update hash
5192 grant_column->init_rights = privileges;
5193 }
5194 }
5195 else
5196 {
5197 int tmp_error;
5198 if (unlikely((tmp_error=
5199 table->file->ha_delete_row(table->record[1]))))
5200 { /* purecov: deadcode */
5201 table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
5202 result= -1; /* purecov: deadcode */
5203 goto end; /* purecov: deadcode */
5204 }
5205 if (grant_column)
5206 my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
5207 }
5208 }
5209 } while (!table->file->ha_index_next(table->record[0]) &&
5210 !key_cmp_if_same(table, key, 0, key_prefix_length));
5211 }
5212
5213 end:
5214 table->file->ha_index_end();
5215 DBUG_RETURN(result);
5216 }
5217
get_grantor(THD * thd,char * grantor)5218 static inline void get_grantor(THD *thd, char *grantor)
5219 {
5220 const char *user= thd->security_ctx->user;
5221 const char *host= thd->security_ctx->host_or_ip;
5222
5223 #if defined(HAVE_REPLICATION)
5224 if (thd->slave_thread && thd->has_invoker())
5225 {
5226 user= thd->get_invoker_user().str;
5227 host= thd->get_invoker_host().str;
5228 }
5229 #endif
5230 strxmov(grantor, user, "@", host, NullS);
5231 }
5232
replace_table_table(THD * thd,GRANT_TABLE * grant_table,TABLE * table,const LEX_USER & combo,const char * db,const char * table_name,ulong rights,ulong col_rights,bool revoke_grant)5233 static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
5234 TABLE *table, const LEX_USER &combo,
5235 const char *db, const char *table_name,
5236 ulong rights, ulong col_rights,
5237 bool revoke_grant)
5238 {
5239 char grantor[USER_HOST_BUFF_SIZE];
5240 int old_row_exists = 1;
5241 int error=0;
5242 ulong store_table_rights, store_col_rights;
5243 uchar user_key[MAX_KEY_LENGTH];
5244 DBUG_ENTER("replace_table_table");
5245
5246 get_grantor(thd, grantor);
5247 /*
5248 The following should always succeed as new users are created before
5249 this function is called!
5250 */
5251 if (!find_user_wild(combo.host.str,combo.user.str))
5252 {
5253 if (!combo.host.length && !find_acl_role(combo.user.str))
5254 {
5255 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
5256 MYF(0)); /* purecov: deadcode */
5257 DBUG_RETURN(-1); /* purecov: deadcode */
5258 }
5259 }
5260
5261 table->use_all_columns();
5262 restore_record(table, s->default_values); // Get empty record
5263 table->field[0]->store(combo.host.str,combo.host.length,
5264 system_charset_info);
5265 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
5266 table->field[2]->store(combo.user.str,combo.user.length,
5267 system_charset_info);
5268 table->field[3]->store(table_name,(uint) strlen(table_name),
5269 system_charset_info);
5270 store_record(table,record[1]); // store at pos 1
5271 key_copy(user_key, table->record[0], table->key_info,
5272 table->key_info->key_length);
5273
5274 if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
5275 HA_WHOLE_KEY,
5276 HA_READ_KEY_EXACT))
5277 {
5278 /*
5279 The following should never happen as we first check the in memory
5280 grant tables for the user. There is however always a small change that
5281 the user has modified the grant tables directly.
5282 */
5283 if (revoke_grant)
5284 { // no row, no revoke
5285 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
5286 combo.user.str, combo.host.str,
5287 table_name); /* purecov: deadcode */
5288 DBUG_RETURN(-1); /* purecov: deadcode */
5289 }
5290 old_row_exists = 0;
5291 restore_record(table,record[1]); // Get saved record
5292 }
5293
5294 store_table_rights= get_rights_for_table(rights);
5295 store_col_rights= get_rights_for_column(col_rights);
5296 if (old_row_exists)
5297 {
5298 ulong j,k;
5299 store_record(table,record[1]);
5300 j = (ulong) table->field[6]->val_int();
5301 k = (ulong) table->field[7]->val_int();
5302
5303 if (revoke_grant)
5304 {
5305 /* column rights are already fixed in mysql_table_grant */
5306 store_table_rights=j & ~store_table_rights;
5307 }
5308 else
5309 {
5310 store_table_rights|= j;
5311 store_col_rights|= k;
5312 }
5313 }
5314
5315 table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
5316 table->field[6]->store((longlong) store_table_rights, TRUE);
5317 table->field[7]->store((longlong) store_col_rights, TRUE);
5318 rights=fix_rights_for_table(store_table_rights);
5319 col_rights=fix_rights_for_column(store_col_rights);
5320
5321 if (old_row_exists)
5322 {
5323 if (store_table_rights || store_col_rights)
5324 {
5325 if (unlikely(error=table->file->ha_update_row(table->record[1],
5326 table->record[0])) &&
5327 error != HA_ERR_RECORD_IS_THE_SAME)
5328 goto table_error; /* purecov: deadcode */
5329 }
5330 else if (unlikely((error = table->file->ha_delete_row(table->record[1]))))
5331 goto table_error; /* purecov: deadcode */
5332 }
5333 else
5334 {
5335 error=table->file->ha_write_row(table->record[0]);
5336 if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
5337 goto table_error; /* purecov: deadcode */
5338 }
5339
5340 if (rights | col_rights)
5341 {
5342 grant_table->init_privs= rights;
5343 grant_table->init_cols= col_rights;
5344
5345 grant_table->privs= rights;
5346 grant_table->cols= col_rights;
5347 }
5348 else
5349 {
5350 my_hash_delete(&column_priv_hash,(uchar*) grant_table);
5351 }
5352 DBUG_RETURN(0);
5353
5354 /* This should never happen */
5355 table_error:
5356 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
5357 DBUG_RETURN(-1); /* purecov: deadcode */
5358 }
5359
5360
5361 /**
5362 @retval 0 success
5363 @retval -1 error
5364 */
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,ulong rights,bool revoke_grant)5365 static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
5366 TABLE *table, const LEX_USER &combo,
5367 const char *db, const char *routine_name,
5368 const Sp_handler *sph,
5369 ulong rights, bool revoke_grant)
5370 {
5371 char grantor[USER_HOST_BUFF_SIZE];
5372 int old_row_exists= 1;
5373 int error=0;
5374 ulong store_proc_rights;
5375 HASH *hash= sph->get_priv_hash();
5376 DBUG_ENTER("replace_routine_table");
5377
5378 if (revoke_grant && !grant_name->init_privs) // only inherited role privs
5379 {
5380 my_hash_delete(hash, (uchar*) grant_name);
5381 DBUG_RETURN(0);
5382 }
5383
5384 get_grantor(thd, grantor);
5385 /*
5386 New users are created before this function is called.
5387
5388 There may be some cases where a routine's definer is removed but the
5389 routine remains.
5390 */
5391
5392 table->use_all_columns();
5393 restore_record(table, s->default_values); // Get empty record
5394 table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
5395 table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
5396 table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
5397 table->field[3]->store(routine_name,(uint) strlen(routine_name),
5398 &my_charset_latin1);
5399 table->field[4]->store((longlong) sph->type(), true);
5400 store_record(table,record[1]); // store at pos 1
5401
5402 if (table->file->ha_index_read_idx_map(table->record[0], 0,
5403 (uchar*) table->field[0]->ptr,
5404 HA_WHOLE_KEY,
5405 HA_READ_KEY_EXACT))
5406 {
5407 /*
5408 The following should never happen as we first check the in memory
5409 grant tables for the user. There is however always a small change that
5410 the user has modified the grant tables directly.
5411
5412 Also, there is also a second posibility that this routine entry
5413 is created for a role by being inherited from a granted role.
5414 */
5415 if (revoke_grant)
5416 { // no row, no revoke
5417 my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
5418 combo.user.str, combo.host.str, routine_name);
5419 DBUG_RETURN(-1);
5420 }
5421 old_row_exists= 0;
5422 restore_record(table,record[1]); // Get saved record
5423 }
5424
5425 store_proc_rights= get_rights_for_procedure(rights);
5426 if (old_row_exists)
5427 {
5428 ulong j;
5429 store_record(table,record[1]);
5430 j= (ulong) table->field[6]->val_int();
5431
5432 if (revoke_grant)
5433 {
5434 /* column rights are already fixed in mysql_table_grant */
5435 store_proc_rights=j & ~store_proc_rights;
5436 }
5437 else
5438 {
5439 store_proc_rights|= j;
5440 }
5441 }
5442
5443 table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
5444 table->field[6]->store((longlong) store_proc_rights, TRUE);
5445 rights=fix_rights_for_procedure(store_proc_rights);
5446
5447 if (old_row_exists)
5448 {
5449 if (store_proc_rights)
5450 {
5451 if (unlikely(error=table->file->ha_update_row(table->record[1],
5452 table->record[0])) &&
5453 error != HA_ERR_RECORD_IS_THE_SAME)
5454 goto table_error;
5455 }
5456 else if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
5457 goto table_error;
5458 }
5459 else
5460 {
5461 error=table->file->ha_write_row(table->record[0]);
5462 if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
5463 goto table_error;
5464 }
5465
5466 if (rights)
5467 {
5468 grant_name->init_privs= rights;
5469 grant_name->privs= rights;
5470 }
5471 else
5472 {
5473 my_hash_delete(hash, (uchar*) grant_name);
5474 }
5475 DBUG_RETURN(0);
5476
5477 /* This should never happen */
5478 table_error:
5479 table->file->print_error(error,MYF(0));
5480 DBUG_RETURN(-1);
5481 }
5482
5483
5484 /*****************************************************************
5485 Role privilege propagation and graph traversal functionality
5486
5487 According to the SQL standard, a role can be granted to a role,
5488 thus role grants can create an arbitrarily complex directed acyclic
5489 graph (a standard explicitly specifies that cycles are not allowed).
5490
5491 When a privilege is granted to a role, it becomes available to all grantees.
5492 The code below recursively traverses a DAG of role grants, propagating
5493 privilege changes.
5494
5495 The traversal function can work both ways, from roles to grantees or
5496 from grantees to roles. The first is used for privilege propagation,
5497 the second - for SHOW GRANTS and I_S.APPLICABLE_ROLES
5498
5499 The role propagation code is smart enough to propagate only privilege
5500 changes to one specific database, table, or routine, if only they
5501 were changed (like in GRANT ... ON ... TO ...) or it can propagate
5502 everything (on startup or after FLUSH PRIVILEGES).
5503
5504 It traverses only a subgraph that's accessible from the modified role,
5505 only visiting roles that can be possibly affected by the GRANT statement.
5506
5507 Additionally, it stops traversal early, if this particular GRANT statement
5508 didn't result in any changes of privileges (e.g. both role1 and role2
5509 are granted to the role3, both role1 and role2 have SELECT privilege.
5510 if SELECT is revoked from role1 it won't change role3 privileges,
5511 so we won't traverse from role3 to its grantees).
5512 ******************************************************************/
5513 struct PRIVS_TO_MERGE
5514 {
5515 enum what
5516 {
5517 ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC, PACKAGE_SPEC, PACKAGE_BODY
5518 } what;
5519 const char *db, *name;
5520 };
5521
5522
sp_privs_to_merge(stored_procedure_type type)5523 static enum PRIVS_TO_MERGE::what sp_privs_to_merge(stored_procedure_type type)
5524 {
5525 switch (type) {
5526 case TYPE_ENUM_FUNCTION:
5527 return PRIVS_TO_MERGE::FUNC;
5528 case TYPE_ENUM_PROCEDURE:
5529 return PRIVS_TO_MERGE::PROC;
5530 case TYPE_ENUM_PACKAGE:
5531 return PRIVS_TO_MERGE::PACKAGE_SPEC;
5532 case TYPE_ENUM_PACKAGE_BODY:
5533 return PRIVS_TO_MERGE::PACKAGE_BODY;
5534 case TYPE_ENUM_TRIGGER:
5535 case TYPE_ENUM_PROXY:
5536 break;
5537 }
5538 DBUG_ASSERT(0);
5539 return PRIVS_TO_MERGE::PROC;
5540 }
5541
5542
init_role_for_merging(ACL_ROLE * role,void * context)5543 static int init_role_for_merging(ACL_ROLE *role, void *context)
5544 {
5545 role->counter= 0;
5546 return 0;
5547 }
5548
count_subgraph_nodes(ACL_ROLE * role,ACL_ROLE * grantee,void * context)5549 static int count_subgraph_nodes(ACL_ROLE *role, ACL_ROLE *grantee, void *context)
5550 {
5551 grantee->counter++;
5552 return 0;
5553 }
5554
5555 static int merge_role_privileges(ACL_ROLE *, ACL_ROLE *, void *);
5556
5557 /**
5558 rebuild privileges of all affected roles
5559
5560 entry point into role privilege propagation. after privileges of the
5561 'role' were changed, this function rebuilds privileges of all affected roles
5562 as necessary.
5563 */
propagate_role_grants(ACL_ROLE * role,enum PRIVS_TO_MERGE::what what,const char * db=0,const char * name=0)5564 static void propagate_role_grants(ACL_ROLE *role,
5565 enum PRIVS_TO_MERGE::what what,
5566 const char *db= 0, const char *name= 0)
5567 {
5568 if (!role)
5569 return;
5570
5571 mysql_mutex_assert_owner(&acl_cache->lock);
5572 PRIVS_TO_MERGE data= { what, db, name };
5573
5574 /*
5575 Changing privileges of a role causes all other roles that had
5576 this role granted to them to have their rights invalidated.
5577
5578 We need to rebuild all roles' related access bits.
5579
5580 This cannot be a simple depth-first search, instead we have to merge
5581 privieges for all roles granted to a specific grantee, *before*
5582 merging privileges for this grantee. In other words, we must visit all
5583 parent nodes of a specific node, before descencing into this node.
5584
5585 For example, if role1 is granted to role2 and role3, and role3 is
5586 granted to role2, after "GRANT ... role1", we cannot merge privileges
5587 for role2, until role3 is merged. The counter will be 0 for role1, 2
5588 for role2, 1 for role3. Traversal will start from role1, go to role2,
5589 decrement the counter, backtrack, go to role3, merge it, go to role2
5590 again, merge it.
5591
5592 And the counter is not just "all parent nodes", but only parent nodes
5593 that are part of the subgraph we're interested in. For example, if
5594 both roleA and roleB are granted to roleC, then roleC has two parent
5595 nodes. But when granting a privilege to roleA, we're only looking at a
5596 subgraph that includes roleA and roleC (roleB cannot be possibly
5597 affected by that grant statement). In this subgraph roleC has only one
5598 parent.
5599
5600 (on the other hand, in acl_load we want to update all roles, and
5601 the counter is exactly equal to the number of all parent nodes)
5602
5603 Thus, we do two graph traversals here. First we only count parents
5604 that are part of the subgraph. On the second traversal we decrement
5605 the counter and actually merge privileges for a node when a counter
5606 drops to zero.
5607 */
5608 traverse_role_graph_up(role, &data, init_role_for_merging, count_subgraph_nodes);
5609 traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
5610 }
5611
5612
5613 // State of a node during a Depth First Search exploration
5614 struct NODE_STATE
5615 {
5616 ACL_USER_BASE *node_data; /* pointer to the node data */
5617 uint neigh_idx; /* the neighbour that needs to be evaluated next */
5618 };
5619
5620 /**
5621 Traverse the role grant graph and invoke callbacks at the specified points.
5622
5623 @param user user or role to start traversal from
5624 @param context opaque parameter to pass to callbacks
5625 @param offset offset to ACL_ROLE::parent_grantee or to
5626 ACL_USER_BASE::role_grants. Depending on this value,
5627 traversal will go from roles to grantees or from
5628 grantees to roles.
5629 @param on_node called when a node is visited for the first time.
5630 Returning a value <0 will abort the traversal.
5631 @param on_edge called for every edge in the graph, when traversal
5632 goes from a node to a neighbour node.
5633 Returning <0 will abort the traversal. Returning >0
5634 will make the traversal not to follow this edge.
5635
5636 @note
5637 The traverse method is a DEPTH FIRST SEARCH, but callbacks can influence
5638 that (on_edge returning >0 value).
5639
5640 @note
5641 This function should not be called directly, use
5642 traverse_role_graph_up() and traverse_role_graph_down() instead.
5643
5644 @retval 0 traversal finished successfully
5645 @retval ROLE_CYCLE_FOUND traversal aborted, cycle detected
5646 @retval <0 traversal was aborted, because a callback returned
5647 this error code
5648 */
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))5649 static int traverse_role_graph_impl(ACL_USER_BASE *user, void *context,
5650 off_t offset,
5651 int (*on_node) (ACL_USER_BASE *role, void *context),
5652 int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
5653 {
5654 DBUG_ENTER("traverse_role_graph_impl");
5655 DBUG_ASSERT(user);
5656 DBUG_PRINT("enter",("role: '%s'", user->user.str));
5657 /*
5658 The search operation should always leave the ROLE_ON_STACK and
5659 ROLE_EXPLORED flags clean for all nodes involved in the search
5660 */
5661 DBUG_ASSERT(!(user->flags & ROLE_ON_STACK));
5662 DBUG_ASSERT(!(user->flags & ROLE_EXPLORED));
5663 mysql_mutex_assert_owner(&acl_cache->lock);
5664
5665 /*
5666 Stack used to simulate the recursive calls of DFS.
5667 It uses a Dynamic_array to reduce the number of
5668 malloc calls to a minimum
5669 */
5670 Dynamic_array<NODE_STATE> stack(20,50);
5671 Dynamic_array<ACL_USER_BASE *> to_clear(20,50);
5672 NODE_STATE state; /* variable used to insert elements in the stack */
5673 int result= 0;
5674
5675 state.neigh_idx= 0;
5676 state.node_data= user;
5677 user->flags|= ROLE_ON_STACK;
5678
5679 stack.push(state);
5680 to_clear.push(user);
5681
5682 user->flags|= ROLE_OPENED;
5683 if (on_node && ((result= on_node(user, context)) < 0))
5684 goto end;
5685
5686 while (stack.elements())
5687 {
5688 NODE_STATE *curr_state= stack.back();
5689
5690 DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
5691
5692 ACL_USER_BASE *current= curr_state->node_data;
5693 ACL_USER_BASE *neighbour= NULL;
5694 DBUG_PRINT("info", ("Examining role %s", current->user.str));
5695 /*
5696 Iterate through the neighbours until a first valid jump-to
5697 neighbour is found
5698 */
5699 bool found= FALSE;
5700 uint i;
5701 DYNAMIC_ARRAY *array= (DYNAMIC_ARRAY *)(((char*)current) + offset);
5702
5703 DBUG_ASSERT(array == ¤t->role_grants || current->flags & IS_ROLE);
5704 for (i= curr_state->neigh_idx; i < array->elements; i++)
5705 {
5706 neighbour= *(dynamic_element(array, i, ACL_ROLE**));
5707 if (!(neighbour->flags & IS_ROLE))
5708 continue;
5709
5710 DBUG_PRINT("info", ("Examining neighbour role %s", neighbour->user.str));
5711
5712 /* check if it forms a cycle */
5713 if (neighbour->flags & ROLE_ON_STACK)
5714 {
5715 DBUG_PRINT("info", ("Found cycle"));
5716 result= ROLE_CYCLE_FOUND;
5717 goto end;
5718 }
5719
5720 if (!(neighbour->flags & ROLE_OPENED))
5721 {
5722 neighbour->flags|= ROLE_OPENED;
5723 to_clear.push(neighbour);
5724 if (on_node && ((result= on_node(neighbour, context)) < 0))
5725 goto end;
5726 }
5727
5728 if (on_edge)
5729 {
5730 result= on_edge(current, (ACL_ROLE*)neighbour, context);
5731 if (result < 0)
5732 goto end;
5733 if (result > 0)
5734 continue;
5735 }
5736
5737 /* Check if it was already explored, in that case, move on */
5738 if (neighbour->flags & ROLE_EXPLORED)
5739 continue;
5740
5741 found= TRUE;
5742 break;
5743 }
5744
5745 /* found states that we have found a node to jump next into */
5746 if (found)
5747 {
5748 curr_state->neigh_idx= i + 1;
5749
5750 /* some sanity checks */
5751 DBUG_ASSERT(!(neighbour->flags & ROLE_ON_STACK));
5752
5753 /* add the neighbour on the stack */
5754 neighbour->flags|= ROLE_ON_STACK;
5755 state.neigh_idx= 0;
5756 state.node_data= neighbour;
5757 stack.push(state);
5758 }
5759 else
5760 {
5761 /* Make sure we got a correct node */
5762 DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
5763 /* Finished with exploring the current node, pop it off the stack */
5764 curr_state= &stack.pop();
5765 curr_state->node_data->flags&= ~ROLE_ON_STACK; /* clear the on-stack bit */
5766 curr_state->node_data->flags|= ROLE_EXPLORED;
5767 }
5768 }
5769
5770 end:
5771 /* Cleanup */
5772 for (uint i= 0; i < to_clear.elements(); i++)
5773 {
5774 ACL_USER_BASE *current= to_clear.at(i);
5775 DBUG_ASSERT(current->flags & (ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED));
5776 current->flags&= ~(ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED);
5777 }
5778 DBUG_RETURN(result);
5779 }
5780
5781 /**
5782 Traverse the role grant graph, going from a role to its grantees.
5783
5784 This is used to propagate changes in privileges, for example,
5785 when GRANT or REVOKE is issued for a role.
5786 */
5787
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))5788 static int traverse_role_graph_up(ACL_ROLE *role, void *context,
5789 int (*on_node) (ACL_ROLE *role, void *context),
5790 int (*on_edge) (ACL_ROLE *current, ACL_ROLE *neighbour, void *context))
5791 {
5792 return traverse_role_graph_impl(role, context,
5793 my_offsetof(ACL_ROLE, parent_grantee),
5794 (int (*)(ACL_USER_BASE *, void *))on_node,
5795 (int (*)(ACL_USER_BASE *, ACL_ROLE *, void *))on_edge);
5796 }
5797
5798 /**
5799 Traverse the role grant graph, going from a user or a role to granted roles.
5800
5801 This is used, for example, to print all grants available to a user or a role
5802 (as in SHOW GRANTS).
5803 */
5804
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))5805 static int traverse_role_graph_down(ACL_USER_BASE *user, void *context,
5806 int (*on_node) (ACL_USER_BASE *role, void *context),
5807 int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
5808 {
5809 return traverse_role_graph_impl(user, context,
5810 my_offsetof(ACL_USER_BASE, role_grants),
5811 on_node, on_edge);
5812 }
5813
5814 /*
5815 To find all db/table/routine privilege for a specific role
5816 we need to scan the array of privileges. It can be big.
5817 But the set of privileges granted to a role in question (or
5818 to roles directly granted to the role in question) is supposedly
5819 much smaller.
5820
5821 We put a role and all roles directly granted to it in a hash, and iterate
5822 the (suposedly long) array of privileges, filtering out "interesting"
5823 entries using the role hash. We put all these "interesting"
5824 entries in a (suposedly small) dynamic array and them use it for merging.
5825 */
role_key(const ACL_ROLE * role,size_t * klen,my_bool)5826 static uchar* role_key(const ACL_ROLE *role, size_t *klen, my_bool)
5827 {
5828 *klen= role->user.length;
5829 return (uchar*) role->user.str;
5830 }
5831 typedef Hash_set<ACL_ROLE> role_hash_t;
5832
merge_role_global_privileges(ACL_ROLE * grantee)5833 static bool merge_role_global_privileges(ACL_ROLE *grantee)
5834 {
5835 ulong old= grantee->access;
5836 grantee->access= grantee->initial_role_access;
5837
5838 DBUG_EXECUTE_IF("role_merge_stats", role_global_merges++;);
5839
5840 for (uint i= 0; i < grantee->role_grants.elements; i++)
5841 {
5842 ACL_ROLE *r= *dynamic_element(&grantee->role_grants, i, ACL_ROLE**);
5843 grantee->access|= r->access;
5844 }
5845 return old != grantee->access;
5846 }
5847
db_name_sort(const int * db1,const int * db2)5848 static int db_name_sort(const int *db1, const int *db2)
5849 {
5850 return strcmp(acl_dbs.at(*db1).db, acl_dbs.at(*db2).db);
5851 }
5852
5853 /**
5854 update ACL_DB for given database and a given role with merged privileges
5855
5856 @param merged ACL_DB of the role in question (or -1 if it wasn't found)
5857 @param first first ACL_DB in an array for the database in question
5858 @param access new privileges for the given role on the gived database
5859 @param role the name of the given role
5860
5861 @return a bitmap of
5862 1 - privileges were changed
5863 2 - ACL_DB was added
5864 4 - ACL_DB was deleted
5865 */
update_role_db(int merged,int first,ulong access,const char * role)5866 static int update_role_db(int merged, int first, ulong access,
5867 const char *role)
5868 {
5869 if (first < 0)
5870 return 0;
5871
5872 DBUG_EXECUTE_IF("role_merge_stats", role_db_merges++;);
5873
5874 if (merged < 0)
5875 {
5876 /*
5877 there's no ACL_DB for this role (all db grants come from granted roles)
5878 we need to create it
5879
5880 Note that we cannot use acl_insert_db() now:
5881 1. it'll sort elements in the acl_dbs, so the pointers will become invalid
5882 2. we may need many of them, no need to sort every time
5883 */
5884 DBUG_ASSERT(access);
5885 ACL_DB acl_db;
5886 acl_db.user= role;
5887 acl_db.host.hostname= const_cast<char*>("");
5888 acl_db.host.ip= acl_db.host.ip_mask= 0;
5889 acl_db.db= acl_dbs.at(first).db;
5890 acl_db.access= access;
5891 acl_db.initial_access= 0;
5892 acl_db.sort=get_sort(3, "", acl_db.db, role);
5893 acl_dbs.push(acl_db);
5894 return 2;
5895 }
5896 else if (access == 0)
5897 {
5898 /*
5899 there is ACL_DB but the role has no db privileges granted
5900 (all privileges were coming from granted roles, and now those roles
5901 were dropped or had their privileges revoked).
5902 we need to remove this ACL_DB entry
5903
5904 Note, that we cannot delete now:
5905 1. it'll shift elements in the acl_dbs, so the pointers will become invalid
5906 2. it's O(N) operation, and we may need many of them
5907 so we only mark elements deleted and will delete later.
5908 */
5909 acl_dbs.at(merged).sort= 0; // lower than any valid ACL_DB sort value, will be sorted last
5910 return 4;
5911 }
5912 else if (acl_dbs.at(merged).access != access)
5913 {
5914 /* this is easy */
5915 acl_dbs.at(merged).access= access;
5916 return 1;
5917 }
5918 return 0;
5919 }
5920
5921 /**
5922 merges db privileges from roles granted to the role 'grantee'.
5923
5924 @return true if database privileges of the 'grantee' were changed
5925
5926 */
merge_role_db_privileges(ACL_ROLE * grantee,const char * dbname,role_hash_t * rhash)5927 static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname,
5928 role_hash_t *rhash)
5929 {
5930 Dynamic_array<int> dbs;
5931
5932 /*
5933 Supposedly acl_dbs can be huge, but only a handful of db grants
5934 apply to grantee or roles directly granted to grantee.
5935
5936 Collect these applicable db grants.
5937 */
5938 for (uint i=0 ; i < acl_dbs.elements() ; i++)
5939 {
5940 ACL_DB *db= &acl_dbs.at(i);
5941 if (db->host.hostname[0])
5942 continue;
5943 if (dbname && strcmp(db->db, dbname))
5944 continue;
5945 ACL_ROLE *r= rhash->find(db->user, strlen(db->user));
5946 if (!r)
5947 continue;
5948 dbs.append(i);
5949 }
5950 dbs.sort(db_name_sort);
5951
5952 /*
5953 Because dbs array is sorted by the db name, all grants for the same db
5954 (that should be merged) are sorted together. The grantee's ACL_DB element
5955 is not necessarily the first and may be not present at all.
5956 */
5957 int first= -1, merged= -1;
5958 ulong access= 0, update_flags= 0;
5959 for (int *p= dbs.front(); p <= dbs.back(); p++)
5960 {
5961 if (first<0 || (!dbname && strcmp(acl_dbs.at(p[0]).db, acl_dbs.at(p[-1]).db)))
5962 { // new db name series
5963 update_flags|= update_role_db(merged, first, access, grantee->user.str);
5964 merged= -1;
5965 access= 0;
5966 first= *p;
5967 }
5968 if (strcmp(acl_dbs.at(*p).user, grantee->user.str) == 0)
5969 access|= acl_dbs.at(merged= *p).initial_access;
5970 else
5971 access|= acl_dbs.at(*p).access;
5972 }
5973 update_flags|= update_role_db(merged, first, access, grantee->user.str);
5974
5975 /*
5976 to make this code a bit simpler, we sort on deletes, to move
5977 deleted elements to the end of the array. strictly speaking it's
5978 unnecessary, it'd be faster to remove them in one O(N) array scan.
5979
5980 on the other hand, qsort on almost sorted array is pretty fast anyway...
5981 */
5982 if (update_flags & (2|4))
5983 { // inserted or deleted, need to sort
5984 acl_dbs.sort((acl_dbs_cmp)acl_compare);
5985 }
5986 if (update_flags & 4)
5987 { // deleted, trim the end
5988 while (acl_dbs.elements() && acl_dbs.back()->sort == 0)
5989 acl_dbs.pop();
5990 }
5991 return update_flags;
5992 }
5993
table_name_sort(GRANT_TABLE * const * tbl1,GRANT_TABLE * const * tbl2)5994 static int table_name_sort(GRANT_TABLE * const *tbl1, GRANT_TABLE * const *tbl2)
5995 {
5996 int res = strcmp((*tbl1)->db, (*tbl2)->db);
5997 if (res) return res;
5998 return strcmp((*tbl1)->tname, (*tbl2)->tname);
5999 }
6000
6001 /**
6002 merges column privileges for the entry 'merged'
6003
6004 @param merged GRANT_TABLE to merge the privileges into
6005 @param cur first entry in the array of GRANT_TABLE's for a given table
6006 @param last last entry in the array of GRANT_TABLE's for a given table,
6007 all entries between cur and last correspond to the *same* table
6008
6009 @return 1 if the _set of columns_ in 'merged' was changed
6010 (not if the _set of privileges_ was changed).
6011 */
update_role_columns(GRANT_TABLE * merged,GRANT_TABLE ** cur,GRANT_TABLE ** last)6012 static int update_role_columns(GRANT_TABLE *merged,
6013 GRANT_TABLE **cur, GRANT_TABLE **last)
6014
6015 {
6016 ulong rights __attribute__((unused))= 0;
6017 int changed= 0;
6018 if (!merged->cols)
6019 {
6020 changed= merged->hash_columns.records > 0;
6021 my_hash_reset(&merged->hash_columns);
6022 return changed;
6023 }
6024
6025 DBUG_EXECUTE_IF("role_merge_stats", role_column_merges++;);
6026
6027 HASH *mh= &merged->hash_columns;
6028 for (uint i=0 ; i < mh->records ; i++)
6029 {
6030 GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
6031 col->rights= col->init_rights;
6032 }
6033
6034 for (; cur < last; cur++)
6035 {
6036 if (*cur == merged)
6037 continue;
6038 HASH *ch= &cur[0]->hash_columns;
6039 for (uint i=0 ; i < ch->records ; i++)
6040 {
6041 GRANT_COLUMN *ccol = (GRANT_COLUMN *)my_hash_element(ch, i);
6042 GRANT_COLUMN *mcol = (GRANT_COLUMN *)my_hash_search(mh,
6043 (uchar *)ccol->column, ccol->key_length);
6044 if (mcol)
6045 mcol->rights|= ccol->rights;
6046 else
6047 {
6048 changed= 1;
6049 my_hash_insert(mh, (uchar*)new (&grant_memroot) GRANT_COLUMN(ccol));
6050 }
6051 }
6052 }
6053
6054 for (uint i=0 ; i < mh->records ; i++)
6055 {
6056 GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
6057 rights|= col->rights;
6058 if (!col->rights)
6059 {
6060 changed= 1;
6061 my_hash_delete(mh, (uchar*)col);
6062 }
6063 }
6064 DBUG_ASSERT(rights == merged->cols);
6065 return changed;
6066 }
6067
6068 /**
6069 update GRANT_TABLE for a given table and a given role with merged privileges
6070
6071 @param merged GRANT_TABLE of the role in question (or NULL if it wasn't found)
6072 @param first first GRANT_TABLE in an array for the table in question
6073 @param last last entry in the array of GRANT_TABLE's for a given table,
6074 all entries between first and last correspond to the *same* table
6075 @param privs new table-level privileges for 'merged'
6076 @param cols new OR-ed column-level privileges for 'merged'
6077 @param role the name of the given role
6078
6079 @return a bitmap of
6080 1 - privileges were changed
6081 2 - GRANT_TABLE was added
6082 4 - GRANT_TABLE was deleted
6083 */
update_role_table_columns(GRANT_TABLE * merged,GRANT_TABLE ** first,GRANT_TABLE ** last,ulong privs,ulong cols,const char * role)6084 static int update_role_table_columns(GRANT_TABLE *merged,
6085 GRANT_TABLE **first, GRANT_TABLE **last,
6086 ulong privs, ulong cols, const char *role)
6087 {
6088 if (!first)
6089 return 0;
6090
6091 DBUG_EXECUTE_IF("role_merge_stats", role_table_merges++;);
6092
6093 if (merged == NULL)
6094 {
6095 /*
6096 there's no GRANT_TABLE for this role (all table grants come from granted
6097 roles) we need to create it
6098 */
6099 DBUG_ASSERT(privs | cols);
6100 merged= new (&grant_memroot) GRANT_TABLE("", first[0]->db, role, first[0]->tname,
6101 privs, cols);
6102 merged->init_privs= merged->init_cols= 0;
6103 update_role_columns(merged, first, last);
6104 my_hash_insert(&column_priv_hash,(uchar*) merged);
6105 return 2;
6106 }
6107 else if ((privs | cols) == 0)
6108 {
6109 /*
6110 there is GRANT_TABLE object but the role has no table or column
6111 privileges granted (all privileges were coming from granted roles, and
6112 now those roles were dropped or had their privileges revoked).
6113 we need to remove this GRANT_TABLE
6114 */
6115 DBUG_EXECUTE_IF("role_merge_stats",
6116 role_column_merges+= MY_TEST(merged->cols););
6117 my_hash_delete(&column_priv_hash,(uchar*) merged);
6118 return 4;
6119 }
6120 else
6121 {
6122 bool changed= merged->cols != cols || merged->privs != privs;
6123 merged->cols= cols;
6124 merged->privs= privs;
6125 if (update_role_columns(merged, first, last))
6126 changed= true;
6127 return changed;
6128 }
6129 }
6130
6131 /**
6132 merges table privileges from roles granted to the role 'grantee'.
6133
6134 @return true if table privileges of the 'grantee' were changed
6135
6136 */
merge_role_table_and_column_privileges(ACL_ROLE * grantee,const char * db,const char * tname,role_hash_t * rhash)6137 static bool merge_role_table_and_column_privileges(ACL_ROLE *grantee,
6138 const char *db, const char *tname, role_hash_t *rhash)
6139 {
6140 Dynamic_array<GRANT_TABLE *> grants;
6141 DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
6142
6143 /*
6144 first, collect table/column privileges granted to
6145 roles in question.
6146 */
6147 for (uint i=0 ; i < column_priv_hash.records ; i++)
6148 {
6149 GRANT_TABLE *grant= (GRANT_TABLE *) my_hash_element(&column_priv_hash, i);
6150 if (grant->host.hostname[0])
6151 continue;
6152 if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
6153 continue;
6154 ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
6155 if (!r)
6156 continue;
6157 grants.append(grant);
6158 }
6159 grants.sort(table_name_sort);
6160
6161 GRANT_TABLE **first= NULL, *merged= NULL, **cur;
6162 ulong privs= 0, cols= 0, update_flags= 0;
6163 for (cur= grants.front(); cur <= grants.back(); cur++)
6164 {
6165 if (!first ||
6166 (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
6167 strcmp(cur[0]->tname, cur[-1]->tname))))
6168 { // new db.tname series
6169 update_flags|= update_role_table_columns(merged, first, cur,
6170 privs, cols, grantee->user.str);
6171 merged= NULL;
6172 privs= cols= 0;
6173 first= cur;
6174 }
6175 if (strcmp(cur[0]->user, grantee->user.str) == 0)
6176 {
6177 merged= cur[0];
6178 cols|= cur[0]->init_cols;
6179 privs|= cur[0]->init_privs;
6180 }
6181 else
6182 {
6183 cols|= cur[0]->cols;
6184 privs|= cur[0]->privs;
6185 }
6186 }
6187 update_flags|= update_role_table_columns(merged, first, cur,
6188 privs, cols, grantee->user.str);
6189
6190 return update_flags;
6191 }
6192
routine_name_sort(GRANT_NAME * const * r1,GRANT_NAME * const * r2)6193 static int routine_name_sort(GRANT_NAME * const *r1, GRANT_NAME * const *r2)
6194 {
6195 int res= strcmp((*r1)->db, (*r2)->db);
6196 if (res) return res;
6197 return strcmp((*r1)->tname, (*r2)->tname);
6198 }
6199
6200 /**
6201 update GRANT_NAME for a given routine and a given role with merged privileges
6202
6203 @param merged GRANT_NAME of the role in question (or NULL if it wasn't found)
6204 @param first first GRANT_NAME in an array for the routine in question
6205 @param privs new routine-level privileges for 'merged'
6206 @param role the name of the given role
6207 @param hash proc_priv_hash or func_priv_hash
6208
6209 @return a bitmap of
6210 1 - privileges were changed
6211 2 - GRANT_NAME was added
6212 4 - GRANT_NAME was deleted
6213 */
update_role_routines(GRANT_NAME * merged,GRANT_NAME ** first,ulong privs,const char * role,HASH * hash)6214 static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first,
6215 ulong privs, const char *role, HASH *hash)
6216 {
6217 if (!first)
6218 return 0;
6219
6220 DBUG_EXECUTE_IF("role_merge_stats", role_routine_merges++;);
6221
6222 if (merged == NULL)
6223 {
6224 /*
6225 there's no GRANT_NAME for this role (all routine grants come from granted
6226 roles) we need to create it
6227 */
6228 DBUG_ASSERT(privs);
6229 merged= new (&grant_memroot) GRANT_NAME("", first[0]->db, role, first[0]->tname,
6230 privs, true);
6231 merged->init_privs= 0; // all privs are inherited
6232 my_hash_insert(hash, (uchar *)merged);
6233 return 2;
6234 }
6235 else if (privs == 0)
6236 {
6237 /*
6238 there is GRANT_NAME but the role has no privileges granted
6239 (all privileges were coming from granted roles, and now those roles
6240 were dropped or had their privileges revoked).
6241 we need to remove this entry
6242 */
6243 my_hash_delete(hash, (uchar*)merged);
6244 return 4;
6245 }
6246 else if (merged->privs != privs)
6247 {
6248 /* this is easy */
6249 merged->privs= privs;
6250 return 1;
6251 }
6252 return 0;
6253 }
6254
6255 /**
6256 merges routine privileges from roles granted to the role 'grantee'.
6257
6258 @return true if routine privileges of the 'grantee' were changed
6259
6260 */
merge_role_routine_grant_privileges(ACL_ROLE * grantee,const char * db,const char * tname,role_hash_t * rhash,HASH * hash)6261 static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee,
6262 const char *db, const char *tname, role_hash_t *rhash, HASH *hash)
6263 {
6264 ulong update_flags= 0;
6265
6266 DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
6267
6268 Dynamic_array<GRANT_NAME *> grants;
6269
6270 /* first, collect routine privileges granted to roles in question */
6271 for (uint i=0 ; i < hash->records ; i++)
6272 {
6273 GRANT_NAME *grant= (GRANT_NAME *) my_hash_element(hash, i);
6274 if (grant->host.hostname[0])
6275 continue;
6276 if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
6277 continue;
6278 ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
6279 if (!r)
6280 continue;
6281 grants.append(grant);
6282 }
6283 grants.sort(routine_name_sort);
6284
6285 GRANT_NAME **first= NULL, *merged= NULL;
6286 ulong privs= 0 ;
6287 for (GRANT_NAME **cur= grants.front(); cur <= grants.back(); cur++)
6288 {
6289 if (!first ||
6290 (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
6291 strcmp(cur[0]->tname, cur[-1]->tname))))
6292 { // new db.tname series
6293 update_flags|= update_role_routines(merged, first, privs,
6294 grantee->user.str, hash);
6295 merged= NULL;
6296 privs= 0;
6297 first= cur;
6298 }
6299 if (strcmp(cur[0]->user, grantee->user.str) == 0)
6300 {
6301 merged= cur[0];
6302 privs|= cur[0]->init_privs;
6303 }
6304 else
6305 {
6306 privs|= cur[0]->privs;
6307 }
6308 }
6309 update_flags|= update_role_routines(merged, first, privs,
6310 grantee->user.str, hash);
6311 return update_flags;
6312 }
6313
6314 /**
6315 update privileges of the 'grantee' from all roles, granted to it
6316 */
merge_role_privileges(ACL_ROLE * role,ACL_ROLE * grantee,void * context)6317 static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
6318 ACL_ROLE *grantee, void *context)
6319 {
6320 PRIVS_TO_MERGE *data= (PRIVS_TO_MERGE *)context;
6321
6322 DBUG_ASSERT(grantee->counter > 0);
6323 if (--grantee->counter)
6324 return 1; // don't recurse into grantee just yet
6325
6326 grantee->counter= 1; // Mark the grantee as merged.
6327
6328 /* if we'll do db/table/routine privileges, create a hash of role names */
6329 role_hash_t role_hash(role_key);
6330 if (data->what != PRIVS_TO_MERGE::GLOBAL)
6331 {
6332 role_hash.insert(grantee);
6333 for (uint i= 0; i < grantee->role_grants.elements; i++)
6334 role_hash.insert(*dynamic_element(&grantee->role_grants, i, ACL_ROLE**));
6335 }
6336
6337 bool all= data->what == PRIVS_TO_MERGE::ALL;
6338 bool changed= false;
6339 if (all || data->what == PRIVS_TO_MERGE::GLOBAL)
6340 changed|= merge_role_global_privileges(grantee);
6341 if (all || data->what == PRIVS_TO_MERGE::DB)
6342 changed|= merge_role_db_privileges(grantee, data->db, &role_hash);
6343 if (all || data->what == PRIVS_TO_MERGE::TABLE_COLUMN)
6344 changed|= merge_role_table_and_column_privileges(grantee,
6345 data->db, data->name, &role_hash);
6346 if (all || data->what == PRIVS_TO_MERGE::PROC)
6347 changed|= merge_role_routine_grant_privileges(grantee,
6348 data->db, data->name, &role_hash, &proc_priv_hash);
6349 if (all || data->what == PRIVS_TO_MERGE::FUNC)
6350 changed|= merge_role_routine_grant_privileges(grantee,
6351 data->db, data->name, &role_hash, &func_priv_hash);
6352 if (all || data->what == PRIVS_TO_MERGE::PACKAGE_SPEC)
6353 changed|= merge_role_routine_grant_privileges(grantee,
6354 data->db, data->name, &role_hash,
6355 &package_spec_priv_hash);
6356 if (all || data->what == PRIVS_TO_MERGE::PACKAGE_BODY)
6357 changed|= merge_role_routine_grant_privileges(grantee,
6358 data->db, data->name, &role_hash,
6359 &package_body_priv_hash);
6360 return !changed; // don't recurse into the subgraph if privs didn't change
6361 }
6362
merge_one_role_privileges(ACL_ROLE * grantee)6363 static bool merge_one_role_privileges(ACL_ROLE *grantee)
6364 {
6365 PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
6366 grantee->counter= 1;
6367 return merge_role_privileges(0, grantee, &data);
6368 }
6369
6370 /*****************************************************************
6371 End of the role privilege propagation and graph traversal code
6372 ******************************************************************/
6373
has_auth(LEX_USER * user,LEX * lex)6374 static bool has_auth(LEX_USER *user, LEX *lex)
6375 {
6376 return user->pwtext.length || user->pwhash.length || user->plugin.length || user->auth.length ||
6377 lex->ssl_type != SSL_TYPE_NOT_SPECIFIED || lex->ssl_cipher ||
6378 lex->x509_issuer || lex->x509_subject ||
6379 lex->mqh.specified_limits;
6380 }
6381
fix_and_copy_user(LEX_USER * to,LEX_USER * from,THD * thd)6382 static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd)
6383 {
6384 if (to != from)
6385 {
6386 /* preserve authentication information, if LEX_USER was reallocated */
6387 to->pwtext= from->pwtext;
6388 to->pwhash= from->pwhash;
6389 to->plugin= from->plugin;
6390 to->auth= from->auth;
6391 }
6392
6393 if (fix_lex_user(thd, to))
6394 return true;
6395
6396 return false;
6397 }
6398
copy_and_check_auth(LEX_USER * to,LEX_USER * from,THD * thd)6399 static bool copy_and_check_auth(LEX_USER *to, LEX_USER *from, THD *thd)
6400 {
6401 if (fix_and_copy_user(to, from, thd))
6402 return true;
6403
6404 // if changing auth for an existing user
6405 if (has_auth(to, thd->lex) && find_user_exact(to->host.str, to->user.str))
6406 {
6407 mysql_mutex_unlock(&acl_cache->lock);
6408 bool res= check_alter_user(thd, to->host.str, to->user.str);
6409 mysql_mutex_lock(&acl_cache->lock);
6410 return res;
6411 }
6412
6413 return false;
6414 }
6415
6416
6417 /*
6418 Store table level and column level grants in the privilege tables
6419
6420 SYNOPSIS
6421 mysql_table_grant()
6422 thd Thread handle
6423 table_list List of tables to give grant
6424 user_list List of users to give grant
6425 columns List of columns to give grant
6426 rights Table level grant
6427 revoke_grant Set to 1 if this is a REVOKE command
6428
6429 RETURN
6430 FALSE ok
6431 TRUE error
6432 */
6433
mysql_table_grant(THD * thd,TABLE_LIST * table_list,List<LEX_USER> & user_list,List<LEX_COLUMN> & columns,ulong rights,bool revoke_grant)6434 int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
6435 List <LEX_USER> &user_list,
6436 List <LEX_COLUMN> &columns, ulong rights,
6437 bool revoke_grant)
6438 {
6439 ulong column_priv= 0;
6440 int result;
6441 List_iterator <LEX_USER> str_list (user_list);
6442 LEX_USER *Str, *tmp_Str;
6443 bool create_new_users=0;
6444 const char *db_name, *table_name;
6445 DBUG_ENTER("mysql_table_grant");
6446
6447 if (rights & ~TABLE_ACLS)
6448 {
6449 my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
6450 ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
6451 MYF(0));
6452 DBUG_RETURN(TRUE);
6453 }
6454
6455 if (!revoke_grant)
6456 {
6457 if (columns.elements)
6458 {
6459 class LEX_COLUMN *column;
6460 List_iterator <LEX_COLUMN> column_iter(columns);
6461
6462 if (open_normal_and_derived_tables(thd, table_list, 0, DT_PREPARE))
6463 DBUG_RETURN(TRUE);
6464
6465 while ((column = column_iter++))
6466 {
6467 uint unused_field_idx= NO_CACHED_FIELD_INDEX;
6468 TABLE_LIST *dummy;
6469 Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
6470 column->column.length(),
6471 column->column.ptr(), NULL, NULL,
6472 NULL, TRUE, FALSE,
6473 &unused_field_idx, FALSE, &dummy);
6474 if (unlikely(f == (Field*)0))
6475 {
6476 my_error(ER_BAD_FIELD_ERROR, MYF(0),
6477 column->column.c_ptr(), table_list->alias.str);
6478 DBUG_RETURN(TRUE);
6479 }
6480 if (unlikely(f == (Field *)-1))
6481 DBUG_RETURN(TRUE);
6482 column_priv|= column->rights;
6483 }
6484 close_mysql_tables(thd);
6485 }
6486 else
6487 {
6488 if (!(rights & CREATE_ACL))
6489 {
6490 if (!ha_table_exists(thd, &table_list->db, &table_list->table_name))
6491 {
6492 my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db.str,
6493 table_list->alias.str);
6494 DBUG_RETURN(TRUE);
6495 }
6496 }
6497 if (table_list->grant.want_privilege)
6498 {
6499 char command[128];
6500 get_privilege_desc(command, sizeof(command),
6501 table_list->grant.want_privilege);
6502 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
6503 command, thd->security_ctx->priv_user,
6504 thd->security_ctx->host_or_ip, table_list->alias.str);
6505 DBUG_RETURN(-1);
6506 }
6507 }
6508 }
6509
6510 /*
6511 Open the mysql.user and mysql.tables_priv tables.
6512 Don't open column table if we don't need it !
6513 */
6514 int maybe_columns_priv= 0;
6515 if (column_priv ||
6516 (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
6517 maybe_columns_priv= Table_columns_priv;
6518
6519 /*
6520 The lock api is depending on the thd->lex variable which needs to be
6521 re-initialized.
6522 */
6523 Query_tables_list backup;
6524 thd->lex->reset_n_backup_query_tables_list(&backup);
6525 /*
6526 Restore Query_tables_list::sql_command value, which was reset
6527 above, as the code writing query to the binary log assumes that
6528 this value corresponds to the statement being executed.
6529 */
6530 thd->lex->sql_command= backup.sql_command;
6531
6532 Grant_tables tables(Table_user | Table_tables_priv | maybe_columns_priv,
6533 TL_WRITE);
6534 if ((result= tables.open_and_lock(thd)))
6535 {
6536 thd->lex->restore_backup_query_tables_list(&backup);
6537 DBUG_RETURN(result != 1);
6538 }
6539
6540 if (!revoke_grant)
6541 create_new_users= test_if_create_new_users(thd);
6542 mysql_rwlock_wrlock(&LOCK_grant);
6543 mysql_mutex_lock(&acl_cache->lock);
6544 MEM_ROOT *old_root= thd->mem_root;
6545 thd->mem_root= &grant_memroot;
6546 grant_version++;
6547
6548 while ((tmp_Str = str_list++))
6549 {
6550 int error;
6551 GRANT_TABLE *grant_table;
6552 if (!(Str= get_current_user(thd, tmp_Str, false)))
6553 {
6554 result= TRUE;
6555 continue;
6556 }
6557 /* Create user if needed */
6558 error= copy_and_check_auth(Str, tmp_Str, thd) ||
6559 replace_user_table(thd, tables.user_table(), *Str,
6560 0, revoke_grant, create_new_users,
6561 MY_TEST(thd->variables.sql_mode &
6562 MODE_NO_AUTO_CREATE_USER));
6563 if (unlikely(error))
6564 {
6565 result= TRUE; // Remember error
6566 continue; // Add next user
6567 }
6568
6569 db_name= table_list->get_db_name();
6570 table_name= table_list->get_table_name();
6571
6572 /* Find/create cached table grant */
6573 grant_table= table_hash_search(Str->host.str, NullS, db_name,
6574 Str->user.str, table_name, 1);
6575 if (!grant_table)
6576 {
6577 if (revoke_grant)
6578 {
6579 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
6580 Str->user.str, Str->host.str, table_list->table_name.str);
6581 result= TRUE;
6582 continue;
6583 }
6584 grant_table = new GRANT_TABLE (Str->host.str, db_name,
6585 Str->user.str, table_name,
6586 rights,
6587 column_priv);
6588 if (!grant_table ||
6589 my_hash_insert(&column_priv_hash,(uchar*) grant_table))
6590 {
6591 result= TRUE; /* purecov: deadcode */
6592 continue; /* purecov: deadcode */
6593 }
6594 }
6595
6596 /* If revoke_grant, calculate the new column privilege for tables_priv */
6597 if (revoke_grant)
6598 {
6599 class LEX_COLUMN *column;
6600 List_iterator <LEX_COLUMN> column_iter(columns);
6601 GRANT_COLUMN *grant_column;
6602
6603 /* Fix old grants */
6604 while ((column = column_iter++))
6605 {
6606 grant_column = column_hash_search(grant_table,
6607 column->column.ptr(),
6608 column->column.length());
6609 if (grant_column)
6610 grant_column->rights&= ~(column->rights | rights);
6611 }
6612 /* scan trough all columns to get new column grant */
6613 column_priv= 0;
6614 for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
6615 {
6616 grant_column= (GRANT_COLUMN*)
6617 my_hash_element(&grant_table->hash_columns, idx);
6618 grant_column->rights&= ~rights; // Fix other columns
6619 column_priv|= grant_column->rights;
6620 }
6621 }
6622 else
6623 {
6624 column_priv|= grant_table->cols;
6625 }
6626
6627
6628 /* update table and columns */
6629
6630 /* TODO(cvicentiu) refactor replace_table_table to use Tables_priv_table
6631 instead of TABLE directly. */
6632 if (replace_table_table(thd, grant_table, tables.tables_priv_table().table(),
6633 *Str, db_name, table_name,
6634 rights, column_priv, revoke_grant))
6635 {
6636 /* Should only happen if table is crashed */
6637 result= TRUE; /* purecov: deadcode */
6638 }
6639 else if (tables.columns_priv_table().table_exists())
6640 {
6641 /* TODO(cvicentiu) refactor replace_column_table to use Columns_priv_table
6642 instead of TABLE directly. */
6643 if (replace_column_table(grant_table, tables.columns_priv_table().table(),
6644 *Str, columns, db_name, table_name, rights,
6645 revoke_grant))
6646 {
6647 result= TRUE;
6648 }
6649 }
6650 if (Str->is_role())
6651 propagate_role_grants(find_acl_role(Str->user.str),
6652 PRIVS_TO_MERGE::TABLE_COLUMN, db_name, table_name);
6653 }
6654
6655 thd->mem_root= old_root;
6656 mysql_mutex_unlock(&acl_cache->lock);
6657
6658 if (!result) /* success */
6659 {
6660 result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
6661 }
6662
6663 mysql_rwlock_unlock(&LOCK_grant);
6664
6665 if (!result) /* success */
6666 my_ok(thd);
6667
6668 thd->lex->restore_backup_query_tables_list(&backup);
6669 DBUG_RETURN(result);
6670 }
6671
6672
6673 /**
6674 Store routine level grants in the privilege tables
6675
6676 @param thd Thread handle
6677 @param table_list List of routines to give grant
6678 @param sph SP handler
6679 @param user_list List of users to give grant
6680 @param rights Table level grant
6681 @param revoke_grant Is this is a REVOKE command?
6682
6683 @return
6684 @retval FALSE Success.
6685 @retval TRUE An error occurred.
6686 */
6687
mysql_routine_grant(THD * thd,TABLE_LIST * table_list,const Sp_handler * sph,List<LEX_USER> & user_list,ulong rights,bool revoke_grant,bool write_to_binlog)6688 bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list,
6689 const Sp_handler *sph,
6690 List <LEX_USER> &user_list, ulong rights,
6691 bool revoke_grant, bool write_to_binlog)
6692 {
6693 List_iterator <LEX_USER> str_list (user_list);
6694 LEX_USER *Str, *tmp_Str;
6695 bool create_new_users= 0;
6696 int result;
6697 const char *db_name, *table_name;
6698 DBUG_ENTER("mysql_routine_grant");
6699
6700 if (rights & ~PROC_ACLS)
6701 {
6702 my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
6703 ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
6704 MYF(0));
6705 DBUG_RETURN(TRUE);
6706 }
6707
6708 if (!revoke_grant)
6709 {
6710 if (sph->sp_exist_routines(thd, table_list))
6711 DBUG_RETURN(TRUE);
6712 }
6713
6714 Grant_tables tables(Table_user | Table_procs_priv, TL_WRITE);
6715 if ((result= tables.open_and_lock(thd)))
6716 DBUG_RETURN(result != 1);
6717
6718 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6719
6720 if (!revoke_grant)
6721 create_new_users= test_if_create_new_users(thd);
6722 mysql_rwlock_wrlock(&LOCK_grant);
6723 mysql_mutex_lock(&acl_cache->lock);
6724 MEM_ROOT *old_root= thd->mem_root;
6725 thd->mem_root= &grant_memroot;
6726
6727 DBUG_PRINT("info",("now time to iterate and add users"));
6728
6729 while ((tmp_Str= str_list++))
6730 {
6731 GRANT_NAME *grant_name;
6732 if (!(Str= get_current_user(thd, tmp_Str, false)))
6733 {
6734 result= TRUE;
6735 continue;
6736 }
6737 /* Create user if needed */
6738 if (copy_and_check_auth(Str, tmp_Str, thd) ||
6739 replace_user_table(thd, tables.user_table(), *Str,
6740 0, revoke_grant, create_new_users,
6741 MY_TEST(thd->variables.sql_mode &
6742 MODE_NO_AUTO_CREATE_USER)))
6743 {
6744 result= TRUE;
6745 continue;
6746 }
6747
6748 db_name= table_list->db.str;
6749 table_name= table_list->table_name.str;
6750 grant_name= routine_hash_search(Str->host.str, NullS, db_name,
6751 Str->user.str, table_name, sph, 1);
6752 if (!grant_name || !grant_name->init_privs)
6753 {
6754 if (revoke_grant)
6755 {
6756 my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
6757 Str->user.str, Str->host.str, table_name);
6758 result= TRUE;
6759 continue;
6760 }
6761 grant_name= new GRANT_NAME(Str->host.str, db_name,
6762 Str->user.str, table_name,
6763 rights, TRUE);
6764 if (!grant_name ||
6765 my_hash_insert(sph->get_priv_hash(), (uchar*) grant_name))
6766 {
6767 result= TRUE;
6768 continue;
6769 }
6770 }
6771
6772 /* TODO(cvicentiu) refactor replace_routine_table to use Tables_procs_priv
6773 instead of TABLE directly. */
6774 if (tables.procs_priv_table().no_such_table() ||
6775 replace_routine_table(thd, grant_name, tables.procs_priv_table().table(),
6776 *Str, db_name, table_name, sph, rights,
6777 revoke_grant) != 0)
6778 {
6779 result= TRUE;
6780 continue;
6781 }
6782 if (Str->is_role())
6783 propagate_role_grants(find_acl_role(Str->user.str),
6784 sp_privs_to_merge(sph->type()),
6785 db_name, table_name);
6786 }
6787 thd->mem_root= old_root;
6788 mysql_mutex_unlock(&acl_cache->lock);
6789
6790 if (write_to_binlog)
6791 {
6792 if (write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
6793 result= TRUE;
6794 }
6795
6796 mysql_rwlock_unlock(&LOCK_grant);
6797
6798 /* Tables are automatically closed */
6799 DBUG_RETURN(result);
6800 }
6801
6802 /**
6803 append a user or role name to a buffer that will be later used as an error message
6804 */
append_user(THD * thd,String * str,const LEX_CSTRING * u,const LEX_CSTRING * h)6805 static void append_user(THD *thd, String *str,
6806 const LEX_CSTRING *u, const LEX_CSTRING *h)
6807 {
6808 if (str->length())
6809 str->append(',');
6810 append_query_string(system_charset_info, str, u->str, u->length,
6811 thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
6812 /* hostname part is not relevant for roles, it is always empty */
6813 if (u->length == 0 || h->length != 0)
6814 {
6815 str->append('@');
6816 append_query_string(system_charset_info, str, h->str, h->length,
6817 thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
6818 }
6819 }
6820
append_user(THD * thd,String * str,LEX_USER * user)6821 static void append_user(THD *thd, String *str, LEX_USER *user)
6822 {
6823 append_user(thd, str, & user->user, & user->host);
6824 }
6825
6826 /**
6827 append a string to a buffer that will be later used as an error message
6828
6829 @note
6830 a string can be either CURRENT_USER or CURRENT_ROLE or NONE, it should be
6831 neither quoted nor escaped.
6832 */
append_str(String * str,const char * s,size_t l)6833 static void append_str(String *str, const char *s, size_t l)
6834 {
6835 if (str->length())
6836 str->append(',');
6837 str->append(s, l);
6838 }
6839
can_grant_role_callback(ACL_USER_BASE * grantee,ACL_ROLE * role,void * data)6840 static int can_grant_role_callback(ACL_USER_BASE *grantee,
6841 ACL_ROLE *role, void *data)
6842 {
6843 ROLE_GRANT_PAIR *pair;
6844
6845 if (role != (ACL_ROLE*)data)
6846 return 0; // keep searching
6847
6848 if (grantee->flags & IS_ROLE)
6849 pair= find_role_grant_pair(&grantee->user, &empty_clex_str, &role->user);
6850 else
6851 {
6852 ACL_USER *user= (ACL_USER *)grantee;
6853 LEX_CSTRING host= { user->host.hostname, user->hostname_length };
6854 pair= find_role_grant_pair(&user->user, &host, &role->user);
6855 }
6856 if (!pair->with_admin)
6857 return 0; // keep searching
6858
6859 return -1; // abort the traversal
6860 }
6861
6862
6863 /*
6864 One can only grant a role if SELECT * FROM I_S.APPLICABLE_ROLES shows this
6865 role as grantable.
6866
6867 What this really means - we need to traverse role graph for the current user
6868 looking for our role being granted with the admin option.
6869 */
can_grant_role(THD * thd,ACL_ROLE * role)6870 static bool can_grant_role(THD *thd, ACL_ROLE *role)
6871 {
6872 Security_context *sctx= thd->security_ctx;
6873
6874 if (!sctx->user) // replication
6875 return true;
6876
6877 ACL_USER *grantee= find_user_exact(sctx->priv_host, sctx->priv_user);
6878 if (!grantee)
6879 return false;
6880
6881 return traverse_role_graph_down(grantee, role, NULL,
6882 can_grant_role_callback) == -1;
6883 }
6884
6885
mysql_grant_role(THD * thd,List<LEX_USER> & list,bool revoke)6886 bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
6887 {
6888 DBUG_ENTER("mysql_grant_role");
6889 /*
6890 The first entry in the list is the granted role. Need at least two
6891 entries for the command to be valid
6892 */
6893 DBUG_ASSERT(list.elements >= 2);
6894 int result;
6895 bool create_new_user, no_auto_create_user;
6896 String wrong_users;
6897 LEX_USER *user, *granted_role;
6898 LEX_CSTRING rolename;
6899 LEX_CSTRING username;
6900 LEX_CSTRING hostname;
6901 ACL_ROLE *role, *role_as_user;
6902
6903 List_iterator <LEX_USER> user_list(list);
6904 granted_role= user_list++;
6905 if (!(granted_role= get_current_user(thd, granted_role)))
6906 DBUG_RETURN(TRUE);
6907
6908 DBUG_ASSERT(granted_role->is_role());
6909 rolename= granted_role->user;
6910
6911 create_new_user= test_if_create_new_users(thd);
6912 no_auto_create_user= MY_TEST(thd->variables.sql_mode &
6913 MODE_NO_AUTO_CREATE_USER);
6914
6915 Grant_tables tables(Table_user | Table_roles_mapping, TL_WRITE);
6916 if ((result= tables.open_and_lock(thd)))
6917 DBUG_RETURN(result != 1);
6918
6919 mysql_rwlock_wrlock(&LOCK_grant);
6920 mysql_mutex_lock(&acl_cache->lock);
6921 if (!(role= find_acl_role(rolename.str)))
6922 {
6923 mysql_mutex_unlock(&acl_cache->lock);
6924 mysql_rwlock_unlock(&LOCK_grant);
6925 my_error(ER_INVALID_ROLE, MYF(0), rolename.str);
6926 DBUG_RETURN(TRUE);
6927 }
6928
6929 if (!can_grant_role(thd, role))
6930 {
6931 mysql_mutex_unlock(&acl_cache->lock);
6932 mysql_rwlock_unlock(&LOCK_grant);
6933 my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
6934 thd->security_ctx->priv_user, thd->security_ctx->priv_host);
6935 DBUG_RETURN(TRUE);
6936 }
6937
6938 while ((user= user_list++))
6939 {
6940 role_as_user= NULL;
6941 /* current_role is treated slightly different */
6942 if (user->user.str == current_role.str)
6943 {
6944 /* current_role is NONE */
6945 if (!thd->security_ctx->priv_role[0])
6946 {
6947 my_error(ER_INVALID_ROLE, MYF(0), "NONE");
6948 append_str(&wrong_users, STRING_WITH_LEN("NONE"));
6949 result= 1;
6950 continue;
6951 }
6952 if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role)))
6953 {
6954 LEX_CSTRING ls= { thd->security_ctx->priv_role,
6955 strlen(thd->security_ctx->priv_role) };
6956 append_user(thd, &wrong_users, &ls, &empty_clex_str);
6957 result= 1;
6958 continue;
6959 }
6960
6961 /* can not grant current_role to current_role */
6962 if (granted_role->user.str == current_role.str)
6963 {
6964 append_user(thd, &wrong_users, &role_as_user->user, &empty_clex_str);
6965 result= 1;
6966 continue;
6967 }
6968 username.str= thd->security_ctx->priv_role;
6969 username.length= strlen(username.str);
6970 hostname= empty_clex_str;
6971 }
6972 else if (user->user.str == current_user.str)
6973 {
6974 username.str= thd->security_ctx->priv_user;
6975 username.length= strlen(username.str);
6976 hostname.str= thd->security_ctx->priv_host;
6977 hostname.length= strlen(hostname.str);
6978 }
6979 else
6980 {
6981 username= user->user;
6982 if (user->host.str)
6983 hostname= user->host;
6984 else
6985 if ((role_as_user= find_acl_role(user->user.str)))
6986 hostname= empty_clex_str;
6987 else
6988 {
6989 if (is_invalid_role_name(username.str))
6990 {
6991 append_user(thd, &wrong_users, &username, &empty_clex_str);
6992 result= 1;
6993 continue;
6994 }
6995 hostname= host_not_specified;
6996 }
6997 }
6998
6999 ROLE_GRANT_PAIR *hash_entry= find_role_grant_pair(&username, &hostname,
7000 &rolename);
7001 ACL_USER_BASE *grantee= role_as_user;
7002
7003 if (has_auth(user, thd->lex))
7004 DBUG_ASSERT(!grantee);
7005 else if (!grantee)
7006 grantee= find_user_exact(hostname.str, username.str);
7007
7008 if (!grantee && !revoke)
7009 {
7010 LEX_USER user_combo = *user;
7011 user_combo.host = hostname;
7012 user_combo.user = username;
7013
7014 if (copy_and_check_auth(&user_combo, &user_combo, thd) ||
7015 replace_user_table(thd, tables.user_table(), user_combo, 0,
7016 false, create_new_user,
7017 no_auto_create_user))
7018 {
7019 append_user(thd, &wrong_users, &username, &hostname);
7020 result= 1;
7021 continue;
7022 }
7023 grantee= find_user_exact(hostname.str, username.str);
7024
7025 /* either replace_user_table failed, or we've added the user */
7026 DBUG_ASSERT(grantee);
7027 }
7028
7029 if (!grantee)
7030 {
7031 append_user(thd, &wrong_users, &username, &hostname);
7032 result= 1;
7033 continue;
7034 }
7035
7036 if (!revoke)
7037 {
7038 if (hash_entry)
7039 {
7040 // perhaps, updating an existing grant, adding WITH ADMIN OPTION
7041 }
7042 else
7043 {
7044 add_role_user_mapping(grantee, role);
7045
7046 /*
7047 Check if this grant would cause a cycle. It only needs to be run
7048 if we're granting a role to a role
7049 */
7050 if (role_as_user &&
7051 traverse_role_graph_down(role, 0, 0, 0) == ROLE_CYCLE_FOUND)
7052 {
7053 append_user(thd, &wrong_users, &username, &empty_clex_str);
7054 result= 1;
7055 undo_add_role_user_mapping(grantee, role);
7056 continue;
7057 }
7058 }
7059 }
7060 else
7061 {
7062 /* grant was already removed or never existed */
7063 if (!hash_entry)
7064 {
7065 append_user(thd, &wrong_users, &username, &hostname);
7066 result= 1;
7067 continue;
7068 }
7069 if (thd->lex->with_admin_option)
7070 {
7071 // only revoking an admin option, not the complete grant
7072 }
7073 else
7074 {
7075 /* revoke a role grant */
7076 remove_role_user_mapping(grantee, role);
7077 }
7078 }
7079
7080 /* write into the roles_mapping table */
7081 /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
7082 Roles_mapping_table instead of TABLE directly. */
7083 if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
7084 &username, &hostname, &rolename,
7085 thd->lex->with_admin_option,
7086 hash_entry, revoke))
7087 {
7088 append_user(thd, &wrong_users, &username, &empty_clex_str);
7089 result= 1;
7090 if (!revoke)
7091 {
7092 /* need to remove the mapping added previously */
7093 undo_add_role_user_mapping(grantee, role);
7094 }
7095 else
7096 {
7097 /* need to restore the mapping deleted previously */
7098 add_role_user_mapping(grantee, role);
7099 }
7100 continue;
7101 }
7102 update_role_mapping(&username, &hostname, &rolename,
7103 thd->lex->with_admin_option, hash_entry, revoke);
7104
7105 /*
7106 Only need to propagate grants when granting/revoking a role to/from
7107 a role
7108 */
7109 if (role_as_user && merge_one_role_privileges(role_as_user) == 0)
7110 propagate_role_grants(role_as_user, PRIVS_TO_MERGE::ALL);
7111 }
7112
7113 mysql_mutex_unlock(&acl_cache->lock);
7114
7115 if (result)
7116 my_error(revoke ? ER_CANNOT_REVOKE_ROLE : ER_CANNOT_GRANT_ROLE, MYF(0),
7117 rolename.str, wrong_users.c_ptr_safe());
7118 else
7119 result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7120
7121 mysql_rwlock_unlock(&LOCK_grant);
7122
7123 DBUG_RETURN(result);
7124 }
7125
7126
mysql_grant(THD * thd,const char * db,List<LEX_USER> & list,ulong rights,bool revoke_grant,bool is_proxy)7127 bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
7128 ulong rights, bool revoke_grant, bool is_proxy)
7129 {
7130 List_iterator <LEX_USER> str_list (list);
7131 LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
7132 char tmp_db[SAFE_NAME_LEN+1];
7133 bool create_new_users=0;
7134 int result;
7135 DBUG_ENTER("mysql_grant");
7136
7137 if (lower_case_table_names && db)
7138 {
7139 char *end= strnmov(tmp_db,db, sizeof(tmp_db));
7140 if (end >= tmp_db + sizeof(tmp_db))
7141 {
7142 my_error(ER_WRONG_DB_NAME ,MYF(0), db);
7143 DBUG_RETURN(TRUE);
7144 }
7145 my_casedn_str(files_charset_info, tmp_db);
7146 db=tmp_db;
7147 }
7148
7149 if (is_proxy)
7150 {
7151 DBUG_ASSERT(!db);
7152 proxied_user= str_list++;
7153 }
7154
7155 Grant_tables tables(Table_user | (is_proxy ? Table_proxies_priv : Table_db),
7156 TL_WRITE);
7157 if ((result= tables.open_and_lock(thd)))
7158 DBUG_RETURN(result != 1);
7159
7160 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7161
7162 if (!revoke_grant)
7163 create_new_users= test_if_create_new_users(thd);
7164
7165 /* go through users in user_list */
7166 mysql_rwlock_wrlock(&LOCK_grant);
7167 mysql_mutex_lock(&acl_cache->lock);
7168 grant_version++;
7169
7170 if (proxied_user)
7171 {
7172 if (!(proxied_user= get_current_user(thd, proxied_user, false)))
7173 DBUG_RETURN(TRUE);
7174 DBUG_ASSERT(proxied_user->host.length); // not a Role
7175 }
7176
7177 while ((tmp_Str = str_list++))
7178 {
7179 if (!(Str= get_current_user(thd, tmp_Str, false)))
7180 {
7181 result= true;
7182 continue;
7183 }
7184
7185 if (copy_and_check_auth(Str, tmp_Str, thd) ||
7186 replace_user_table(thd, tables.user_table(), *Str,
7187 (!db ? rights : 0), revoke_grant, create_new_users,
7188 MY_TEST(thd->variables.sql_mode &
7189 MODE_NO_AUTO_CREATE_USER)))
7190 result= true;
7191 else if (db)
7192 {
7193 ulong db_rights= rights & DB_ACLS;
7194 if (db_rights == rights)
7195 {
7196 if (replace_db_table(tables.db_table().table(), db, *Str, db_rights,
7197 revoke_grant))
7198 result= true;
7199 }
7200 else
7201 {
7202 my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
7203 result= true;
7204 }
7205 }
7206 else if (is_proxy)
7207 {
7208 /* TODO(cvicentiu) refactor replace_proxies_priv_table to use
7209 Proxies_priv_table instead of TABLE directly. */
7210 if (tables.proxies_priv_table().no_such_table() ||
7211 replace_proxies_priv_table (thd, tables.proxies_priv_table().table(),
7212 Str, proxied_user,
7213 rights & GRANT_ACL ? TRUE : FALSE,
7214 revoke_grant))
7215 result= true;
7216 }
7217 if (Str->is_role())
7218 propagate_role_grants(find_acl_role(Str->user.str),
7219 db ? PRIVS_TO_MERGE::DB : PRIVS_TO_MERGE::GLOBAL,
7220 db);
7221 }
7222 mysql_mutex_unlock(&acl_cache->lock);
7223
7224 if (!result)
7225 {
7226 result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7227 }
7228
7229 mysql_rwlock_unlock(&LOCK_grant);
7230
7231 if (!result)
7232 my_ok(thd);
7233
7234 DBUG_RETURN(result);
7235 }
7236
7237
7238 /* Free grant array if possible */
7239
grant_free(void)7240 void grant_free(void)
7241 {
7242 DBUG_ENTER("grant_free");
7243 my_hash_free(&column_priv_hash);
7244 my_hash_free(&proc_priv_hash);
7245 my_hash_free(&func_priv_hash);
7246 my_hash_free(&package_spec_priv_hash);
7247 my_hash_free(&package_body_priv_hash);
7248 free_root(&grant_memroot,MYF(0));
7249 DBUG_VOID_RETURN;
7250 }
7251
7252
7253 /**
7254 @brief Initialize structures responsible for table/column-level privilege
7255 checking and load information for them from tables in the 'mysql' database.
7256
7257 @return Error status
7258 @retval 0 OK
7259 @retval 1 Could not initialize grant subsystem.
7260 */
7261
grant_init()7262 bool grant_init()
7263 {
7264 THD *thd;
7265 bool return_val;
7266 DBUG_ENTER("grant_init");
7267
7268 if (!(thd= new THD(0)))
7269 DBUG_RETURN(1); /* purecov: deadcode */
7270 thd->thread_stack= (char*) &thd;
7271 thd->store_globals();
7272 return_val= grant_reload(thd);
7273 delete thd;
7274 DBUG_RETURN(return_val);
7275 }
7276
7277
7278 /**
7279 @brief Initialize structures responsible for table/column-level privilege
7280 checking and load information about grants from open privilege tables.
7281
7282 @param thd Current thread
7283 @param tables List containing open "mysql.tables_priv" and
7284 "mysql.columns_priv" tables.
7285
7286 @see grant_reload
7287
7288 @return Error state
7289 @retval FALSE Success
7290 @retval TRUE Error
7291 */
7292
grant_load(THD * thd,const Tables_priv_table & tables_priv,const Columns_priv_table & columns_priv,const Procs_priv_table & procs_priv)7293 static bool grant_load(THD *thd,
7294 const Tables_priv_table& tables_priv,
7295 const Columns_priv_table& columns_priv,
7296 const Procs_priv_table& procs_priv)
7297 {
7298 bool return_val= 1;
7299 TABLE *t_table, *c_table, *p_table;
7300 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
7301 MEM_ROOT *save_mem_root= thd->mem_root;
7302 sql_mode_t old_sql_mode= thd->variables.sql_mode;
7303 DBUG_ENTER("grant_load");
7304
7305 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
7306
7307 (void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
7308 0,0,0, (my_hash_get_key) get_grant_table,
7309 (my_hash_free_key) free_grant_table,0);
7310 (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
7311 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7312 (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
7313 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7314 (void) my_hash_init(&package_spec_priv_hash, &my_charset_utf8_bin,
7315 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7316 (void) my_hash_init(&package_body_priv_hash, &my_charset_utf8_bin,
7317 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7318 init_sql_alloc(&grant_memroot, "GRANT", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
7319
7320 t_table= tables_priv.table();
7321 c_table= columns_priv.table();
7322 p_table= procs_priv.table(); // this can be NULL
7323
7324 if (t_table->file->ha_index_init(0, 1))
7325 goto end_index_init;
7326
7327 t_table->use_all_columns();
7328 c_table->use_all_columns();
7329
7330 thd->mem_root= &grant_memroot;
7331
7332 if (!t_table->file->ha_index_first(t_table->record[0]))
7333 {
7334 do
7335 {
7336 GRANT_TABLE *mem_check;
7337 /* TODO(cvicentiu) convert this to use tables_priv and columns_priv. */
7338 if (!(mem_check= new (&grant_memroot) GRANT_TABLE(t_table, c_table)))
7339 {
7340 /* This could only happen if we are out memory */
7341 goto end_unlock;
7342 }
7343
7344 if (check_no_resolve)
7345 {
7346 if (hostname_requires_resolving(mem_check->host.hostname))
7347 {
7348 sql_print_warning("'tables_priv' entry '%s %s@%s' "
7349 "ignored in --skip-name-resolve mode.",
7350 mem_check->tname,
7351 safe_str(mem_check->user),
7352 safe_str(mem_check->host.hostname));
7353 continue;
7354 }
7355 }
7356
7357 if (! mem_check->ok())
7358 delete mem_check;
7359 else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
7360 {
7361 delete mem_check;
7362 goto end_unlock;
7363 }
7364 }
7365 while (!t_table->file->ha_index_next(t_table->record[0]));
7366 }
7367
7368 return_val= 0;
7369
7370 if (p_table)
7371 {
7372 if (p_table->file->ha_index_init(0, 1))
7373 goto end_unlock;
7374
7375 p_table->use_all_columns();
7376
7377 if (!p_table->file->ha_index_first(p_table->record[0]))
7378 {
7379 do
7380 {
7381 GRANT_NAME *mem_check;
7382 HASH *hash;
7383 if (!(mem_check= new (&grant_memroot) GRANT_NAME(p_table, TRUE)))
7384 {
7385 /* This could only happen if we are out memory */
7386 goto end_unlock_p;
7387 }
7388
7389 if (check_no_resolve)
7390 {
7391 if (hostname_requires_resolving(mem_check->host.hostname))
7392 {
7393 sql_print_warning("'procs_priv' entry '%s %s@%s' "
7394 "ignored in --skip-name-resolve mode.",
7395 mem_check->tname, mem_check->user,
7396 safe_str(mem_check->host.hostname));
7397 continue;
7398 }
7399 }
7400 stored_procedure_type type= (stored_procedure_type)procs_priv.routine_type()->val_int();
7401 const Sp_handler *sph= Sp_handler::handler(type);
7402 if (!sph || !(hash= sph->get_priv_hash()))
7403 {
7404 sql_print_warning("'procs_priv' entry '%s' "
7405 "ignored, bad routine type",
7406 mem_check->tname);
7407 continue;
7408 }
7409
7410 mem_check->privs= fix_rights_for_procedure(mem_check->privs);
7411 mem_check->init_privs= mem_check->privs;
7412 if (! mem_check->ok())
7413 delete mem_check;
7414 else if (my_hash_insert(hash, (uchar*) mem_check))
7415 {
7416 delete mem_check;
7417 goto end_unlock_p;
7418 }
7419 }
7420 while (!p_table->file->ha_index_next(p_table->record[0]));
7421 }
7422 }
7423
7424 end_unlock_p:
7425 if (p_table)
7426 p_table->file->ha_index_end();
7427 end_unlock:
7428 t_table->file->ha_index_end();
7429 thd->mem_root= save_mem_root;
7430 end_index_init:
7431 thd->variables.sql_mode= old_sql_mode;
7432 DBUG_RETURN(return_val);
7433 }
7434
propagate_role_grants_action(void * role_ptr,void * ptr)7435 static my_bool propagate_role_grants_action(void *role_ptr,
7436 void *ptr __attribute__((unused)))
7437 {
7438 ACL_ROLE *role= static_cast<ACL_ROLE *>(role_ptr);
7439 if (role->counter)
7440 return 0;
7441
7442 mysql_mutex_assert_owner(&acl_cache->lock);
7443 PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
7444 traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
7445 return 0;
7446 }
7447
7448
7449 /**
7450 @brief Reload information about table and column level privileges if possible
7451
7452 @param thd Current thread
7453
7454 Locked tables are checked by acl_reload() and doesn't have to be checked
7455 in this call.
7456 This function is also used for initialization of structures responsible
7457 for table/column-level privilege checking.
7458
7459 @return Error state
7460 @retval FALSE Success
7461 @retval TRUE Error
7462 */
7463
grant_reload(THD * thd)7464 bool grant_reload(THD *thd)
7465 {
7466 HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
7467 HASH old_package_spec_priv_hash, old_package_body_priv_hash;
7468 MEM_ROOT old_mem;
7469 int result;
7470 DBUG_ENTER("grant_reload");
7471
7472 /*
7473 To avoid deadlocks we should obtain table locks before
7474 obtaining LOCK_grant rwlock.
7475 */
7476
7477 Grant_tables tables(Table_tables_priv | Table_columns_priv| Table_procs_priv,
7478 TL_READ);
7479 if ((result= tables.open_and_lock(thd)))
7480 DBUG_RETURN(result != 1);
7481
7482 mysql_rwlock_wrlock(&LOCK_grant);
7483 grant_version++;
7484 old_column_priv_hash= column_priv_hash;
7485 old_proc_priv_hash= proc_priv_hash;
7486 old_func_priv_hash= func_priv_hash;
7487 old_package_spec_priv_hash= package_spec_priv_hash;
7488 old_package_body_priv_hash= package_body_priv_hash;
7489
7490 /*
7491 Create a new memory pool but save the current memory pool to make an undo
7492 opertion possible in case of failure.
7493 */
7494 old_mem= grant_memroot;
7495
7496 if ((result= grant_load(thd,
7497 tables.tables_priv_table(),
7498 tables.columns_priv_table(),
7499 tables.procs_priv_table())))
7500 { // Error. Revert to old hash
7501 DBUG_PRINT("error",("Reverting to old privileges"));
7502 grant_free(); /* purecov: deadcode */
7503 column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
7504 proc_priv_hash= old_proc_priv_hash;
7505 func_priv_hash= old_func_priv_hash;
7506 package_spec_priv_hash= old_package_spec_priv_hash;
7507 package_body_priv_hash= old_package_body_priv_hash;
7508 grant_memroot= old_mem; /* purecov: deadcode */
7509 }
7510 else
7511 {
7512 my_hash_free(&old_column_priv_hash);
7513 my_hash_free(&old_proc_priv_hash);
7514 my_hash_free(&old_func_priv_hash);
7515 my_hash_free(&old_package_spec_priv_hash);
7516 my_hash_free(&old_package_body_priv_hash);
7517 free_root(&old_mem,MYF(0));
7518 }
7519
7520 mysql_mutex_lock(&acl_cache->lock);
7521 my_hash_iterate(&acl_roles, propagate_role_grants_action, NULL);
7522 mysql_mutex_unlock(&acl_cache->lock);
7523
7524 mysql_rwlock_unlock(&LOCK_grant);
7525
7526 close_mysql_tables(thd);
7527
7528 DBUG_RETURN(result);
7529 }
7530
7531
7532 /**
7533 @brief Check table level grants
7534
7535 @param thd Thread handler
7536 @param want_access Bits of privileges user needs to have.
7537 @param tables List of tables to check. The user should have
7538 'want_access' to all tables in list.
7539 @param any_combination_will_do TRUE if it's enough to have any privilege for
7540 any combination of the table columns.
7541 @param number Check at most this number of tables.
7542 @param no_errors TRUE if no error should be sent directly to the client.
7543
7544 If table->grant.want_privilege != 0 then the requested privileges where
7545 in the set of COL_ACLS but access was not granted on the table level. As
7546 a consequence an extra check of column privileges is required.
7547
7548 Specifically if this function returns FALSE the user has some kind of
7549 privilege on a combination of columns in each table.
7550
7551 This function is usually preceeded by check_access which establish the
7552 User-, Db- and Host access rights.
7553
7554 @see check_access
7555 @see check_table_access
7556
7557 @note
7558 This functions assumes that either number of tables to be inspected
7559 by it is limited explicitly (i.e. is is not UINT_MAX) or table list
7560 used and thd->lex->query_tables_own_last value correspond to each
7561 other (the latter should be either 0 or point to next_global member
7562 of one of elements of this table list).
7563
7564 We delay locking of LOCK_grant until we really need it as we assume that
7565 most privileges be resolved with user or db level accesses.
7566
7567 @return Access status
7568 @retval FALSE Access granted; But column privileges might need to be
7569 checked.
7570 @retval TRUE The user did not have the requested privileges on any of the
7571 tables.
7572
7573 */
7574
check_grant(THD * thd,ulong want_access,TABLE_LIST * tables,bool any_combination_will_do,uint number,bool no_errors)7575 bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
7576 bool any_combination_will_do, uint number, bool no_errors)
7577 {
7578 TABLE_LIST *tl;
7579 TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
7580 Security_context *sctx= thd->security_ctx;
7581 uint i;
7582 ulong original_want_access= want_access;
7583 bool locked= 0;
7584 GRANT_TABLE *grant_table;
7585 GRANT_TABLE *grant_table_role= NULL;
7586 DBUG_ENTER("check_grant");
7587 DBUG_ASSERT(number > 0);
7588
7589 /*
7590 Walk through the list of tables that belong to the query and save the
7591 requested access (orig_want_privilege) to be able to use it when
7592 checking access rights to the underlying tables of a view. Our grant
7593 system gradually eliminates checked bits from want_privilege and thus
7594 after all checks are done we can no longer use it.
7595 The check that first_not_own_table is not reached is for the case when
7596 the given table list refers to the list for prelocking (contains tables
7597 of other queries). For simple queries first_not_own_table is 0.
7598 */
7599 for (i= 0, tl= tables;
7600 i < number && tl != first_not_own_table;
7601 tl= tl->next_global, i++)
7602 {
7603 /*
7604 Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
7605 It will be checked during making view.
7606 */
7607 tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
7608 }
7609 number= i;
7610
7611 for (tl= tables; number-- ; tl= tl->next_global)
7612 {
7613 TABLE_LIST *const t_ref=
7614 tl->correspondent_table ? tl->correspondent_table : tl;
7615 sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx;
7616 ulong orig_want_access= original_want_access;
7617
7618 /*
7619 If sequence is used as part of NEXT VALUE, PREVIOUS VALUE or SELECT,
7620 we need to modify the requested access rights depending on how the
7621 sequence is used.
7622 */
7623 if (t_ref->sequence &&
7624 !(want_access & ~(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL)))
7625 {
7626 /*
7627 We want to have either SELECT or INSERT rights to sequences depending
7628 on how they are accessed
7629 */
7630 orig_want_access= ((t_ref->lock_type == TL_WRITE_ALLOW_WRITE) ?
7631 INSERT_ACL : SELECT_ACL);
7632 }
7633
7634 if (tl->with || !tl->db.str ||
7635 (tl->select_lex &&
7636 (tl->with= tl->select_lex->find_table_def_in_with_clauses(tl))))
7637 continue;
7638
7639 const ACL_internal_table_access *access=
7640 get_cached_table_access(&t_ref->grant.m_internal,
7641 t_ref->get_db_name(),
7642 t_ref->get_table_name());
7643
7644 if (access)
7645 {
7646 switch(access->check(orig_want_access, &t_ref->grant.privilege))
7647 {
7648 case ACL_INTERNAL_ACCESS_GRANTED:
7649 /*
7650 Currently,
7651 - the information_schema does not subclass ACL_internal_table_access,
7652 there are no per table privilege checks for I_S,
7653 - the performance schema does use per tables checks, but at most
7654 returns 'CHECK_GRANT', and never 'ACCESS_GRANTED'.
7655 so this branch is not used.
7656 */
7657 DBUG_ASSERT(0);
7658 case ACL_INTERNAL_ACCESS_DENIED:
7659 goto err;
7660 case ACL_INTERNAL_ACCESS_CHECK_GRANT:
7661 break;
7662 }
7663 }
7664
7665 want_access= orig_want_access;
7666 want_access&= ~sctx->master_access;
7667 if (!want_access)
7668 continue; // ok
7669
7670 if (!(~t_ref->grant.privilege & want_access) ||
7671 t_ref->is_anonymous_derived_table() || t_ref->schema_table)
7672 {
7673 /*
7674 It is subquery in the FROM clause. VIEW set t_ref->derived after
7675 table opening, but this function always called before table opening.
7676
7677 NOTE: is_derived() can't be used here because subquery in this case
7678 the FROM clase (derived tables) can be not be marked yet.
7679 */
7680 if (t_ref->is_anonymous_derived_table() || t_ref->schema_table)
7681 {
7682 /*
7683 If it's a temporary table created for a subquery in the FROM
7684 clause, or an INFORMATION_SCHEMA table, drop the request for
7685 a privilege.
7686 */
7687 t_ref->grant.want_privilege= 0;
7688 }
7689 continue;
7690 }
7691
7692 if (is_temporary_table(t_ref))
7693 {
7694 /*
7695 If this table list element corresponds to a pre-opened temporary
7696 table skip checking of all relevant table-level privileges for it.
7697 Note that during creation of temporary table we still need to check
7698 if user has CREATE_TMP_ACL.
7699 */
7700 t_ref->grant.privilege|= TMP_TABLE_ACLS;
7701 t_ref->grant.want_privilege= 0;
7702 continue;
7703 }
7704
7705 if (!locked)
7706 {
7707 locked= 1;
7708 mysql_rwlock_rdlock(&LOCK_grant);
7709 }
7710
7711 grant_table= table_hash_search(sctx->host, sctx->ip,
7712 t_ref->get_db_name(),
7713 sctx->priv_user,
7714 t_ref->get_table_name(),
7715 FALSE);
7716 if (sctx->priv_role[0])
7717 grant_table_role= table_hash_search("", NULL, t_ref->get_db_name(),
7718 sctx->priv_role,
7719 t_ref->get_table_name(),
7720 TRUE);
7721
7722 if (!grant_table && !grant_table_role)
7723 {
7724 want_access&= ~t_ref->grant.privilege;
7725 goto err; // No grants
7726 }
7727
7728 /*
7729 For SHOW COLUMNS, SHOW INDEX it is enough to have some
7730 privileges on any column combination on the table.
7731 */
7732 if (any_combination_will_do)
7733 continue;
7734
7735 t_ref->grant.grant_table_user= grant_table; // Remember for column test
7736 t_ref->grant.grant_table_role= grant_table_role;
7737 t_ref->grant.version= grant_version;
7738 t_ref->grant.privilege|= grant_table ? grant_table->privs : 0;
7739 t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : 0;
7740 t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege);
7741
7742 if (!(~t_ref->grant.privilege & want_access))
7743 continue;
7744
7745 if ((want_access&= ~((grant_table ? grant_table->cols : 0) |
7746 (grant_table_role ? grant_table_role->cols : 0) |
7747 t_ref->grant.privilege)))
7748 {
7749 goto err; // impossible
7750 }
7751 }
7752 if (locked)
7753 mysql_rwlock_unlock(&LOCK_grant);
7754 DBUG_RETURN(FALSE);
7755
7756 err:
7757 if (locked)
7758 mysql_rwlock_unlock(&LOCK_grant);
7759 if (!no_errors) // Not a silent skip of table
7760 {
7761 char command[128];
7762 get_privilege_desc(command, sizeof(command), want_access);
7763 status_var_increment(thd->status_var.access_denied_errors);
7764
7765 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
7766 command,
7767 sctx->priv_user,
7768 sctx->host_or_ip,
7769 tl ? tl->get_table_name() : "unknown");
7770 }
7771 DBUG_RETURN(TRUE);
7772 }
7773
7774
check_grant_column_int(GRANT_TABLE * grant_table,const char * name,uint length,ulong * want_access)7775 static void check_grant_column_int(GRANT_TABLE *grant_table, const char *name,
7776 uint length, ulong *want_access)
7777 {
7778 if (grant_table)
7779 {
7780 *want_access&= ~grant_table->privs;
7781 if (*want_access & grant_table->cols)
7782 {
7783 GRANT_COLUMN *grant_column= column_hash_search(grant_table, name, length);
7784 if (grant_column)
7785 *want_access&= ~grant_column->rights;
7786 }
7787 }
7788 }
7789
7790 /*
7791 Check column rights in given security context
7792
7793 SYNOPSIS
7794 check_grant_column()
7795 thd thread handler
7796 grant grant information structure
7797 db_name db name
7798 table_name table name
7799 name column name
7800 length column name length
7801 sctx security context
7802
7803 RETURN
7804 FALSE OK
7805 TRUE access denied
7806 */
7807
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)7808 bool check_grant_column(THD *thd, GRANT_INFO *grant,
7809 const char *db_name, const char *table_name,
7810 const char *name, size_t length, Security_context *sctx)
7811 {
7812 ulong want_access= grant->want_privilege & ~grant->privilege;
7813 DBUG_ENTER("check_grant_column");
7814 DBUG_PRINT("enter", ("table: %s want_access: %lu", table_name, want_access));
7815
7816 if (!want_access)
7817 DBUG_RETURN(0); // Already checked
7818
7819 mysql_rwlock_rdlock(&LOCK_grant);
7820
7821 /* reload table if someone has modified any grants */
7822
7823 if (grant->version != grant_version)
7824 {
7825 grant->grant_table_user=
7826 table_hash_search(sctx->host, sctx->ip, db_name,
7827 sctx->priv_user,
7828 table_name, 0); /* purecov: inspected */
7829 grant->grant_table_role=
7830 sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
7831 sctx->priv_role,
7832 table_name, TRUE) : NULL;
7833 grant->version= grant_version; /* purecov: inspected */
7834 }
7835
7836 check_grant_column_int(grant->grant_table_user, name, (uint)length,
7837 &want_access);
7838 check_grant_column_int(grant->grant_table_role, name, (uint)length,
7839 &want_access);
7840
7841 mysql_rwlock_unlock(&LOCK_grant);
7842 if (!want_access)
7843 DBUG_RETURN(0);
7844
7845 char command[128];
7846 get_privilege_desc(command, sizeof(command), want_access);
7847 /* TODO perhaps error should print current rolename aswell */
7848 my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), command, sctx->priv_user,
7849 sctx->host_or_ip, name, table_name);
7850 DBUG_RETURN(1);
7851 }
7852
7853
7854 /*
7855 Check the access right to a column depending on the type of table.
7856
7857 SYNOPSIS
7858 check_column_grant_in_table_ref()
7859 thd thread handler
7860 table_ref table reference where to check the field
7861 name name of field to check
7862 length length of name
7863 fld use fld object to check invisibility when it is
7864 not 0, not_found_field, view_ref_found
7865
7866 DESCRIPTION
7867 Check the access rights to a column depending on the type of table
7868 reference where the column is checked. The function provides a
7869 generic interface to check column access rights that hides the
7870 heterogeneity of the column representation - whether it is a view
7871 or a stored table colum.
7872
7873 RETURN
7874 FALSE OK
7875 TRUE access denied
7876 */
7877
check_column_grant_in_table_ref(THD * thd,TABLE_LIST * table_ref,const char * name,size_t length,Field * fld)7878 bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
7879 const char *name, size_t length,
7880 Field *fld)
7881 {
7882 GRANT_INFO *grant;
7883 const char *db_name;
7884 const char *table_name;
7885 Security_context *sctx= table_ref->security_ctx ?
7886 table_ref->security_ctx : thd->security_ctx;
7887 if (fld && fld != not_found_field && fld != view_ref_found
7888 && fld->invisible >= INVISIBLE_SYSTEM)
7889 return false;
7890
7891 if (table_ref->view || table_ref->field_translation)
7892 {
7893 /* View or derived information schema table. */
7894 ulong view_privs;
7895 grant= &(table_ref->grant);
7896 db_name= table_ref->view_db.str;
7897 table_name= table_ref->view_name.str;
7898 if (table_ref->belong_to_view &&
7899 thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
7900 {
7901 view_privs= get_column_grant(thd, grant, db_name, table_name, name);
7902 if (view_privs & VIEW_ANY_ACL)
7903 {
7904 table_ref->belong_to_view->allowed_show= TRUE;
7905 return FALSE;
7906 }
7907 table_ref->belong_to_view->allowed_show= FALSE;
7908 my_message(ER_VIEW_NO_EXPLAIN, ER_THD(thd, ER_VIEW_NO_EXPLAIN), MYF(0));
7909 return TRUE;
7910 }
7911 }
7912 else
7913 {
7914 /* Normal or temporary table. */
7915 TABLE *table= table_ref->table;
7916 grant= &(table->grant);
7917 db_name= table->s->db.str;
7918 table_name= table->s->table_name.str;
7919 }
7920
7921 if (grant->want_privilege)
7922 return check_grant_column(thd, grant, db_name, table_name, name,
7923 length, sctx);
7924 else
7925 return FALSE;
7926
7927 }
7928
7929
7930 /**
7931 @brief check if a query can access a set of columns
7932
7933 @param thd the current thread
7934 @param want_access_arg the privileges requested
7935 @param fields an iterator over the fields of a table reference.
7936 @return Operation status
7937 @retval 0 Success
7938 @retval 1 Falure
7939 @details This function walks over the columns of a table reference
7940 The columns may originate from different tables, depending on the kind of
7941 table reference, e.g. join, view.
7942 For each table it will retrieve the grant information and will use it
7943 to check the required access privileges for the fields requested from it.
7944 */
check_grant_all_columns(THD * thd,ulong want_access_arg,Field_iterator_table_ref * fields)7945 bool check_grant_all_columns(THD *thd, ulong want_access_arg,
7946 Field_iterator_table_ref *fields)
7947 {
7948 Security_context *sctx= thd->security_ctx;
7949 ulong UNINIT_VAR(want_access);
7950 const char *table_name= NULL;
7951 const char* db_name;
7952 GRANT_INFO *grant;
7953 GRANT_TABLE *UNINIT_VAR(grant_table);
7954 GRANT_TABLE *UNINIT_VAR(grant_table_role);
7955 /*
7956 Flag that gets set if privilege checking has to be performed on column
7957 level.
7958 */
7959 bool using_column_privileges= FALSE;
7960
7961 mysql_rwlock_rdlock(&LOCK_grant);
7962
7963 for (; !fields->end_of_fields(); fields->next())
7964 {
7965 if (fields->field() &&
7966 fields->field()->invisible >= INVISIBLE_SYSTEM)
7967 continue;
7968 LEX_CSTRING *field_name= fields->name();
7969
7970 if (table_name != fields->get_table_name())
7971 {
7972 table_name= fields->get_table_name();
7973 db_name= fields->get_db_name();
7974 grant= fields->grant();
7975 /* get a fresh one for each table */
7976 want_access= want_access_arg & ~grant->privilege;
7977 if (want_access)
7978 {
7979 /* reload table if someone has modified any grants */
7980 if (grant->version != grant_version)
7981 {
7982 grant->grant_table_user=
7983 table_hash_search(sctx->host, sctx->ip, db_name,
7984 sctx->priv_user,
7985 table_name, 0); /* purecov: inspected */
7986 grant->grant_table_role=
7987 sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
7988 sctx->priv_role,
7989 table_name, TRUE) : NULL;
7990 grant->version= grant_version; /* purecov: inspected */
7991 }
7992
7993 grant_table= grant->grant_table_user;
7994 grant_table_role= grant->grant_table_role;
7995 DBUG_ASSERT (grant_table || grant_table_role);
7996 }
7997 }
7998
7999 if (want_access)
8000 {
8001 ulong have_access= 0;
8002 if (grant_table)
8003 {
8004 GRANT_COLUMN *grant_column=
8005 column_hash_search(grant_table, field_name->str, field_name->length);
8006 if (grant_column)
8007 have_access= grant_column->rights;
8008 }
8009 if (grant_table_role)
8010 {
8011 GRANT_COLUMN *grant_column=
8012 column_hash_search(grant_table_role, field_name->str,
8013 field_name->length);
8014 if (grant_column)
8015 have_access|= grant_column->rights;
8016 }
8017
8018 if (have_access)
8019 using_column_privileges= TRUE;
8020 if (want_access & ~have_access)
8021 goto err;
8022 }
8023 }
8024 mysql_rwlock_unlock(&LOCK_grant);
8025 return 0;
8026
8027 err:
8028 mysql_rwlock_unlock(&LOCK_grant);
8029
8030 char command[128];
8031 get_privilege_desc(command, sizeof(command), want_access);
8032 /*
8033 Do not give an error message listing a column name unless the user has
8034 privilege to see all columns.
8035 */
8036 if (using_column_privileges)
8037 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
8038 command, sctx->priv_user,
8039 sctx->host_or_ip, table_name);
8040 else
8041 my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
8042 command,
8043 sctx->priv_user,
8044 sctx->host_or_ip,
8045 fields->name()->str,
8046 table_name);
8047 return 1;
8048 }
8049
8050
check_grant_db_routine(THD * thd,const char * db,HASH * hash)8051 static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
8052 {
8053 Security_context *sctx= thd->security_ctx;
8054
8055 for (uint idx= 0; idx < hash->records; ++idx)
8056 {
8057 GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
8058
8059 if (strcmp(item->user, sctx->priv_user) == 0 &&
8060 strcmp(item->db, db) == 0 &&
8061 compare_hostname(&item->host, sctx->host, sctx->ip))
8062 {
8063 return FALSE;
8064 }
8065 if (sctx->priv_role[0] && strcmp(item->user, sctx->priv_role) == 0 &&
8066 strcmp(item->db, db) == 0 &&
8067 (!item->host.hostname || !item->host.hostname[0]))
8068 {
8069 return FALSE; /* Found current role match */
8070 }
8071 }
8072
8073 return TRUE;
8074 }
8075
8076
8077 /*
8078 Check if a user has the right to access a database
8079 Access is accepted if he has a grant for any table/routine in the database
8080 Return 1 if access is denied
8081 */
8082
check_grant_db(THD * thd,const char * db)8083 bool check_grant_db(THD *thd, const char *db)
8084 {
8085 Security_context *sctx= thd->security_ctx;
8086 char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end;
8087 char helping2 [SAFE_NAME_LEN + USERNAME_LENGTH+2], *tmp_db;
8088 uint len, UNINIT_VAR(len2);
8089 bool error= TRUE;
8090
8091 tmp_db= strmov(helping, sctx->priv_user) + 1;
8092 end= strnmov(tmp_db, db, helping + sizeof(helping) - tmp_db);
8093
8094 if (end >= helping + sizeof(helping)) // db name was truncated
8095 return 1; // no privileges for an invalid db name
8096
8097 if (lower_case_table_names)
8098 {
8099 end = tmp_db + my_casedn_str(files_charset_info, tmp_db);
8100 db=tmp_db;
8101 }
8102
8103 len= (uint) (end - helping) + 1;
8104
8105 /*
8106 If a role is set, we need to check for privileges here as well.
8107 */
8108 if (sctx->priv_role[0])
8109 {
8110 end= strmov(helping2, sctx->priv_role) + 1;
8111 end= strnmov(end, db, helping2 + sizeof(helping2) - end);
8112 len2= (uint) (end - helping2) + 1;
8113 }
8114
8115
8116 mysql_rwlock_rdlock(&LOCK_grant);
8117
8118 for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
8119 {
8120 GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
8121 idx);
8122 if (len < grant_table->key_length &&
8123 !memcmp(grant_table->hash_key, helping, len) &&
8124 compare_hostname(&grant_table->host, sctx->host, sctx->ip))
8125 {
8126 error= FALSE; /* Found match. */
8127 break;
8128 }
8129 if (sctx->priv_role[0] &&
8130 len2 < grant_table->key_length &&
8131 !memcmp(grant_table->hash_key, helping2, len2) &&
8132 (!grant_table->host.hostname || !grant_table->host.hostname[0]))
8133 {
8134 error= FALSE; /* Found role match */
8135 break;
8136 }
8137 }
8138
8139 if (error)
8140 error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
8141 check_grant_db_routine(thd, db, &func_priv_hash) &&
8142 check_grant_db_routine(thd, db, &package_spec_priv_hash) &&
8143 check_grant_db_routine(thd, db, &package_body_priv_hash);
8144
8145 mysql_rwlock_unlock(&LOCK_grant);
8146
8147 return error;
8148 }
8149
8150
8151 /****************************************************************************
8152 Check routine level grants
8153
8154 SYNPOSIS
8155 bool check_grant_routine()
8156 thd Thread handler
8157 want_access Bits of privileges user needs to have
8158 procs List of routines to check. The user should have 'want_access'
8159 sph SP handler
8160 no_errors If 0 then we write an error. The error is sent directly to
8161 the client
8162
8163 RETURN
8164 0 ok
8165 1 Error: User did not have the requested privielges
8166 ****************************************************************************/
8167
check_grant_routine(THD * thd,ulong want_access,TABLE_LIST * procs,const Sp_handler * sph,bool no_errors)8168 bool check_grant_routine(THD *thd, ulong want_access,
8169 TABLE_LIST *procs, const Sp_handler *sph,
8170 bool no_errors)
8171 {
8172 TABLE_LIST *table;
8173 Security_context *sctx= thd->security_ctx;
8174 char *user= sctx->priv_user;
8175 char *host= sctx->priv_host;
8176 char *role= sctx->priv_role;
8177 DBUG_ENTER("check_grant_routine");
8178
8179 want_access&= ~sctx->master_access;
8180 if (!want_access)
8181 DBUG_RETURN(0); // ok
8182
8183 mysql_rwlock_rdlock(&LOCK_grant);
8184 for (table= procs; table; table= table->next_global)
8185 {
8186 GRANT_NAME *grant_proc;
8187 if ((grant_proc= routine_hash_search(host, sctx->ip, table->db.str, user,
8188 table->table_name.str, sph, 0)))
8189 table->grant.privilege|= grant_proc->privs;
8190 if (role[0]) /* current role set check */
8191 {
8192 if ((grant_proc= routine_hash_search("", NULL, table->db.str, role,
8193 table->table_name.str, sph, 0)))
8194 table->grant.privilege|= grant_proc->privs;
8195 }
8196
8197 if (want_access & ~table->grant.privilege)
8198 {
8199 want_access &= ~table->grant.privilege;
8200 goto err;
8201 }
8202 }
8203 mysql_rwlock_unlock(&LOCK_grant);
8204 DBUG_RETURN(0);
8205 err:
8206 mysql_rwlock_unlock(&LOCK_grant);
8207 if (!no_errors)
8208 {
8209 char buff[1024];
8210 const char *command="";
8211 if (table)
8212 strxmov(buff, table->db.str, ".", table->table_name.str, NullS);
8213 if (want_access & EXECUTE_ACL)
8214 command= "execute";
8215 else if (want_access & ALTER_PROC_ACL)
8216 command= "alter routine";
8217 else if (want_access & GRANT_ACL)
8218 command= "grant";
8219 my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
8220 command, user, host, table ? buff : "unknown");
8221 }
8222 DBUG_RETURN(1);
8223 }
8224
8225
8226 /*
8227 Check if routine has any of the
8228 routine level grants
8229
8230 SYNPOSIS
8231 bool check_routine_level_acl()
8232 thd Thread handler
8233 db Database name
8234 name Routine name
8235
8236 RETURN
8237 0 Ok
8238 1 error
8239 */
8240
check_routine_level_acl(THD * thd,const char * db,const char * name,const Sp_handler * sph)8241 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
8242 const Sp_handler *sph)
8243 {
8244 bool no_routine_acl= 1;
8245 GRANT_NAME *grant_proc;
8246 Security_context *sctx= thd->security_ctx;
8247 mysql_rwlock_rdlock(&LOCK_grant);
8248 if ((grant_proc= routine_hash_search(sctx->priv_host,
8249 sctx->ip, db,
8250 sctx->priv_user,
8251 name, sph, 0)))
8252 no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
8253
8254 if (no_routine_acl && sctx->priv_role[0]) /* current set role check */
8255 {
8256 if ((grant_proc= routine_hash_search("",
8257 NULL, db,
8258 sctx->priv_role,
8259 name, sph, 0)))
8260 no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
8261 }
8262 mysql_rwlock_unlock(&LOCK_grant);
8263 return no_routine_acl;
8264 }
8265
8266
8267 /*****************************************************************************
8268 Functions to retrieve the grant for a table/column (for SHOW functions)
8269 *****************************************************************************/
8270
get_table_grant(THD * thd,TABLE_LIST * table)8271 ulong get_table_grant(THD *thd, TABLE_LIST *table)
8272 {
8273 ulong privilege;
8274 Security_context *sctx= thd->security_ctx;
8275 const char *db = table->db.str ? table->db.str : thd->db.str;
8276 GRANT_TABLE *grant_table;
8277 GRANT_TABLE *grant_table_role= NULL;
8278
8279 mysql_rwlock_rdlock(&LOCK_grant);
8280 #ifdef EMBEDDED_LIBRARY
8281 grant_table= NULL;
8282 grant_table_role= NULL;
8283 #else
8284 grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
8285 table->table_name.str, 0);
8286 if (sctx->priv_role[0])
8287 grant_table_role= table_hash_search("", "", db, sctx->priv_role,
8288 table->table_name.str, 0);
8289 #endif
8290 table->grant.grant_table_user= grant_table; // Remember for column test
8291 table->grant.grant_table_role= grant_table_role;
8292 table->grant.version=grant_version;
8293 if (grant_table)
8294 table->grant.privilege|= grant_table->privs;
8295 if (grant_table_role)
8296 table->grant.privilege|= grant_table_role->privs;
8297 privilege= table->grant.privilege;
8298 mysql_rwlock_unlock(&LOCK_grant);
8299 return privilege;
8300 }
8301
8302
8303 /*
8304 Determine the access priviliges for a field.
8305
8306 SYNOPSIS
8307 get_column_grant()
8308 thd thread handler
8309 grant grants table descriptor
8310 db_name name of database that the field belongs to
8311 table_name name of table that the field belongs to
8312 field_name name of field
8313
8314 DESCRIPTION
8315 The procedure may also modify: grant->grant_table and grant->version.
8316
8317 RETURN
8318 The access priviliges for the field db_name.table_name.field_name
8319 */
8320
get_column_grant(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * field_name)8321 ulong get_column_grant(THD *thd, GRANT_INFO *grant,
8322 const char *db_name, const char *table_name,
8323 const char *field_name)
8324 {
8325 GRANT_TABLE *grant_table;
8326 GRANT_TABLE *grant_table_role;
8327 GRANT_COLUMN *grant_column;
8328 ulong priv= 0;
8329
8330 mysql_rwlock_rdlock(&LOCK_grant);
8331 /* reload table if someone has modified any grants */
8332 if (grant->version != grant_version)
8333 {
8334 Security_context *sctx= thd->security_ctx;
8335 grant->grant_table_user=
8336 table_hash_search(sctx->host, sctx->ip,
8337 db_name, sctx->priv_user,
8338 table_name, 0); /* purecov: inspected */
8339 grant->grant_table_role=
8340 sctx->priv_role[0] ? table_hash_search("", "", db_name,
8341 sctx->priv_role,
8342 table_name, TRUE) : NULL;
8343 grant->version= grant_version; /* purecov: inspected */
8344 }
8345
8346 grant_table= grant->grant_table_user;
8347 grant_table_role= grant->grant_table_role;
8348
8349 if (!grant_table && !grant_table_role)
8350 priv= grant->privilege;
8351 else
8352 {
8353 if (grant_table)
8354 {
8355 grant_column= column_hash_search(grant_table, field_name,
8356 (uint) strlen(field_name));
8357 if (!grant_column)
8358 priv= (grant->privilege | grant_table->privs);
8359 else
8360 priv= (grant->privilege | grant_table->privs | grant_column->rights);
8361 }
8362
8363 if (grant_table_role)
8364 {
8365 grant_column= column_hash_search(grant_table_role, field_name,
8366 (uint) strlen(field_name));
8367 if (!grant_column)
8368 priv|= (grant->privilege | grant_table_role->privs);
8369 else
8370 priv|= (grant->privilege | grant_table_role->privs |
8371 grant_column->rights);
8372 }
8373 }
8374 mysql_rwlock_unlock(&LOCK_grant);
8375 return priv;
8376 }
8377
8378
8379 /* Help function for mysql_show_grants */
8380
add_user_option(String * grant,long value,const char * name,bool is_signed)8381 static void add_user_option(String *grant, long value, const char *name,
8382 bool is_signed)
8383 {
8384 if (value)
8385 {
8386 char buff[22], *p; // just as in int2str
8387 grant->append(' ');
8388 grant->append(name, strlen(name));
8389 grant->append(' ');
8390 p=int10_to_str(value, buff, is_signed ? -10 : 10);
8391 grant->append(buff,p-buff);
8392 }
8393 }
8394
8395
add_user_option(String * grant,double value,const char * name)8396 static void add_user_option(String *grant, double value, const char *name)
8397 {
8398 if (value != 0.0 )
8399 {
8400 char buff[FLOATING_POINT_BUFFER];
8401 size_t len;
8402 grant->append(' ');
8403 grant->append(name, strlen(name));
8404 grant->append(' ');
8405 len= my_fcvt(value, 6, buff, NULL);
8406 grant->append(buff, len);
8407 }
8408 }
8409
add_user_parameters(THD * thd,String * result,ACL_USER * acl_user,bool with_grant)8410 static void add_user_parameters(THD *thd, String *result, ACL_USER* acl_user,
8411 bool with_grant)
8412 {
8413 result->append('@');
8414 append_identifier(thd, result, acl_user->host.hostname,
8415 acl_user->hostname_length);
8416
8417 if (acl_user->plugin.str == native_password_plugin_name.str ||
8418 acl_user->plugin.str == old_password_plugin_name.str)
8419 {
8420 if (acl_user->auth_string.length)
8421 {
8422 DBUG_ASSERT(acl_user->salt_len);
8423 result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
8424 result->append(&acl_user->auth_string);
8425 result->append('\'');
8426 }
8427 }
8428 else
8429 {
8430 result->append(STRING_WITH_LEN(" IDENTIFIED VIA "));
8431 result->append(&acl_user->plugin);
8432 if (acl_user->auth_string.length)
8433 {
8434 result->append(STRING_WITH_LEN(" USING '"));
8435 result->append(&acl_user->auth_string);
8436 result->append('\'');
8437 }
8438 }
8439 /* "show grants" SSL related stuff */
8440 if (acl_user->ssl_type == SSL_TYPE_ANY)
8441 result->append(STRING_WITH_LEN(" REQUIRE SSL"));
8442 else if (acl_user->ssl_type == SSL_TYPE_X509)
8443 result->append(STRING_WITH_LEN(" REQUIRE X509"));
8444 else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
8445 {
8446 int ssl_options = 0;
8447 result->append(STRING_WITH_LEN(" REQUIRE "));
8448 if (acl_user->x509_issuer)
8449 {
8450 ssl_options++;
8451 result->append(STRING_WITH_LEN("ISSUER \'"));
8452 result->append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
8453 result->append('\'');
8454 }
8455 if (acl_user->x509_subject)
8456 {
8457 if (ssl_options++)
8458 result->append(' ');
8459 result->append(STRING_WITH_LEN("SUBJECT \'"));
8460 result->append(acl_user->x509_subject,strlen(acl_user->x509_subject),
8461 system_charset_info);
8462 result->append('\'');
8463 }
8464 if (acl_user->ssl_cipher)
8465 {
8466 if (ssl_options++)
8467 result->append(' ');
8468 result->append(STRING_WITH_LEN("CIPHER '"));
8469 result->append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
8470 system_charset_info);
8471 result->append('\'');
8472 }
8473 }
8474 if (with_grant ||
8475 (acl_user->user_resource.questions ||
8476 acl_user->user_resource.updates ||
8477 acl_user->user_resource.conn_per_hour ||
8478 acl_user->user_resource.user_conn ||
8479 acl_user->user_resource.max_statement_time != 0.0))
8480 {
8481 result->append(STRING_WITH_LEN(" WITH"));
8482 if (with_grant)
8483 result->append(STRING_WITH_LEN(" GRANT OPTION"));
8484 add_user_option(result, acl_user->user_resource.questions,
8485 "MAX_QUERIES_PER_HOUR", false);
8486 add_user_option(result, acl_user->user_resource.updates,
8487 "MAX_UPDATES_PER_HOUR", false);
8488 add_user_option(result, acl_user->user_resource.conn_per_hour,
8489 "MAX_CONNECTIONS_PER_HOUR", false);
8490 add_user_option(result, acl_user->user_resource.user_conn,
8491 "MAX_USER_CONNECTIONS", true);
8492 add_user_option(result, acl_user->user_resource.max_statement_time,
8493 "MAX_STATEMENT_TIME");
8494 }
8495 }
8496
8497 static const char *command_array[]=
8498 {
8499 "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
8500 "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
8501 "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
8502 "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
8503 "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
8504 "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE",
8505 "DELETE HISTORY"
8506 };
8507
8508 static uint command_lengths[]=
8509 {
8510 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
8511 14, 13, 11, 5, 7, 17, 14,
8512 };
8513
8514
print_grants_for_role(THD * thd,ACL_ROLE * role)8515 static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
8516 {
8517 char buff[1024];
8518
8519 if (show_role_grants(thd, "", role, buff, sizeof(buff)))
8520 return TRUE;
8521
8522 if (show_global_privileges(thd, role, TRUE, buff, sizeof(buff)))
8523 return TRUE;
8524
8525 if (show_database_privileges(thd, role->user.str, "", buff, sizeof(buff)))
8526 return TRUE;
8527
8528 if (show_table_and_column_privileges(thd, role->user.str, "", buff, sizeof(buff)))
8529 return TRUE;
8530
8531 if (show_routine_grants(thd, role->user.str, "", &sp_handler_procedure,
8532 buff, sizeof(buff)))
8533 return TRUE;
8534
8535 if (show_routine_grants(thd, role->user.str, "", &sp_handler_function,
8536 buff, sizeof(buff)))
8537 return TRUE;
8538
8539 if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_spec,
8540 buff, sizeof(buff)))
8541 return TRUE;
8542
8543 if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_body,
8544 buff, sizeof(buff)))
8545 return TRUE;
8546
8547 return FALSE;
8548
8549 }
8550
8551
mysql_show_create_user(THD * thd,LEX_USER * lex_user)8552 bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
8553 {
8554 const char *username= NULL, *hostname= NULL;
8555 char buff[1024]; //Show create user should not take more than 1024 bytes.
8556 Protocol *protocol= thd->protocol;
8557 bool error= false;
8558 ACL_USER *acl_user;
8559 uint head_length;
8560 DBUG_ENTER("mysql_show_create_user");
8561
8562 if (get_show_user(thd, lex_user, &username, &hostname, NULL))
8563 DBUG_RETURN(TRUE);
8564
8565 List<Item> field_list;
8566 head_length= (uint) (strxmov(buff, "CREATE USER for ", username, "@",
8567 hostname, NullS) - buff);
8568 Item_string *field = new (thd->mem_root) Item_string_ascii(thd, "", 0);
8569 if (!field)
8570 DBUG_RETURN(true); // Error given my my_alloc()
8571
8572 field->name.str= buff;
8573 field->name.length= head_length;
8574 field->max_length= sizeof(buff);
8575 field_list.push_back(field, thd->mem_root);
8576 if (protocol->send_result_set_metadata(&field_list,
8577 Protocol::SEND_NUM_ROWS |
8578 Protocol::SEND_EOF))
8579 DBUG_RETURN(true);
8580
8581 String result(buff, sizeof(buff), system_charset_info);
8582 result.length(0);
8583 mysql_rwlock_rdlock(&LOCK_grant);
8584 mysql_mutex_lock(&acl_cache->lock);
8585
8586 acl_user= find_user_exact(hostname, username);
8587
8588 // User not found in the internal data structures.
8589 if (!acl_user)
8590 {
8591 my_error(ER_PASSWORD_NO_MATCH, MYF(0));
8592 error= true;
8593 goto end;
8594 }
8595
8596 result.append("CREATE USER ");
8597 append_identifier(thd, &result, username, strlen(username));
8598 add_user_parameters(thd, &result, acl_user, false);
8599
8600 protocol->prepare_for_resend();
8601 protocol->store(result.ptr(), result.length(), result.charset());
8602 if (protocol->write())
8603 {
8604 error= true;
8605 }
8606 my_eof(thd);
8607
8608 end:
8609 mysql_rwlock_unlock(&LOCK_grant);
8610 mysql_mutex_unlock(&acl_cache->lock);
8611
8612 DBUG_RETURN(error);
8613 }
8614
8615
show_grants_callback(ACL_USER_BASE * role,void * data)8616 static int show_grants_callback(ACL_USER_BASE *role, void *data)
8617 {
8618 THD *thd= (THD *)data;
8619 DBUG_ASSERT(role->flags & IS_ROLE);
8620 if (print_grants_for_role(thd, (ACL_ROLE *)role))
8621 return -1;
8622 return 0;
8623 }
8624
mysql_show_grants_get_fields(THD * thd,List<Item> * fields,const char * name,size_t length)8625 void mysql_show_grants_get_fields(THD *thd, List<Item> *fields,
8626 const char *name, size_t length)
8627 {
8628 Item_string *field=new (thd->mem_root) Item_string_ascii(thd, "", 0);
8629 /* Set name explicit to avoid character set conversions */
8630 field->name.str= name;
8631 field->name.length= length;
8632 field->max_length=1024;
8633 fields->push_back(field, thd->mem_root);
8634 }
8635
8636 /** checks privileges for SHOW GRANTS and SHOW CREATE USER
8637
8638 @note that in case of SHOW CREATE USER the parser guarantees
8639 that a role can never happen here, so *rolename will never
8640 be assigned to
8641 */
get_show_user(THD * thd,LEX_USER * lex_user,const char ** username,const char ** hostname,const char ** rolename)8642 bool get_show_user(THD *thd, LEX_USER *lex_user, const char **username,
8643 const char **hostname, const char **rolename)
8644 {
8645 if (lex_user->user.str == current_user.str)
8646 {
8647 *username= thd->security_ctx->priv_user;
8648 *hostname= thd->security_ctx->priv_host;
8649 return 0;
8650 }
8651 if (lex_user->user.str == current_role.str)
8652 {
8653 *rolename= thd->security_ctx->priv_role;
8654 return 0;
8655 }
8656 if (lex_user->user.str == current_user_and_current_role.str)
8657 {
8658 *username= thd->security_ctx->priv_user;
8659 *hostname= thd->security_ctx->priv_host;
8660 *rolename= thd->security_ctx->priv_role;
8661 return 0;
8662 }
8663
8664 Security_context *sctx= thd->security_ctx;
8665 bool do_check_access;
8666
8667 if (!(lex_user= get_current_user(thd, lex_user)))
8668 return 1;
8669
8670 if (lex_user->is_role())
8671 {
8672 *rolename= lex_user->user.str;
8673 do_check_access= strcmp(*rolename, sctx->priv_role);
8674 }
8675 else
8676 {
8677 *username= lex_user->user.str;
8678 *hostname= lex_user->host.str;
8679 do_check_access= strcmp(*username, sctx->priv_user) ||
8680 strcmp(*hostname, sctx->priv_host);
8681 }
8682
8683 if (do_check_access && check_access(thd, SELECT_ACL, "mysql", 0, 0, 1, 0))
8684 return 1;
8685 return 0;
8686 }
8687
8688 /*
8689 SHOW GRANTS; Send grants for a user to the client
8690
8691 IMPLEMENTATION
8692 Send to client grant-like strings depicting user@host privileges
8693 */
8694
mysql_show_grants(THD * thd,LEX_USER * lex_user)8695 bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
8696 {
8697 int error = -1;
8698 ACL_USER *UNINIT_VAR(acl_user);
8699 ACL_ROLE *acl_role= NULL;
8700 char buff[1024];
8701 Protocol *protocol= thd->protocol;
8702 const char *username= NULL, *hostname= NULL, *rolename= NULL, *end;
8703 DBUG_ENTER("mysql_show_grants");
8704
8705 if (!initialized)
8706 {
8707 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
8708 DBUG_RETURN(TRUE);
8709 }
8710
8711 if (get_show_user(thd, lex_user, &username, &hostname, &rolename))
8712 DBUG_RETURN(TRUE);
8713
8714 DBUG_ASSERT(rolename || username);
8715
8716 List<Item> field_list;
8717 if (username)
8718 end= strxmov(buff,"Grants for ",username,"@",hostname, NullS);
8719 else
8720 end= strxmov(buff,"Grants for ",rolename, NullS);
8721
8722 mysql_show_grants_get_fields(thd, &field_list, buff, (uint) (end-buff));
8723
8724 if (protocol->send_result_set_metadata(&field_list,
8725 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
8726 DBUG_RETURN(TRUE);
8727
8728 mysql_rwlock_rdlock(&LOCK_grant);
8729 mysql_mutex_lock(&acl_cache->lock);
8730
8731 if (username)
8732 {
8733 acl_user= find_user_exact(hostname, username);
8734 if (!acl_user)
8735 {
8736 mysql_mutex_unlock(&acl_cache->lock);
8737 mysql_rwlock_unlock(&LOCK_grant);
8738
8739 my_error(ER_NONEXISTING_GRANT, MYF(0),
8740 username, hostname);
8741 DBUG_RETURN(TRUE);
8742 }
8743
8744 /* Show granted roles to acl_user */
8745 if (show_role_grants(thd, hostname, acl_user, buff, sizeof(buff)))
8746 goto end;
8747
8748 /* Add first global access grants */
8749 if (show_global_privileges(thd, acl_user, FALSE, buff, sizeof(buff)))
8750 goto end;
8751
8752 /* Add database access */
8753 if (show_database_privileges(thd, username, hostname, buff, sizeof(buff)))
8754 goto end;
8755
8756 /* Add table & column access */
8757 if (show_table_and_column_privileges(thd, username, hostname, buff, sizeof(buff)))
8758 goto end;
8759
8760 if (show_routine_grants(thd, username, hostname, &sp_handler_procedure,
8761 buff, sizeof(buff)))
8762 goto end;
8763
8764 if (show_routine_grants(thd, username, hostname, &sp_handler_function,
8765 buff, sizeof(buff)))
8766 goto end;
8767
8768 if (show_routine_grants(thd, username, hostname, &sp_handler_package_spec,
8769 buff, sizeof(buff)))
8770 goto end;
8771
8772 if (show_routine_grants(thd, username, hostname, &sp_handler_package_body,
8773 buff, sizeof(buff)))
8774 goto end;
8775
8776 if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff)))
8777 goto end;
8778 }
8779
8780 if (rolename)
8781 {
8782 acl_role= find_acl_role(rolename);
8783 if (acl_role)
8784 {
8785 /* get a list of all inherited roles */
8786 traverse_role_graph_down(acl_role, thd, show_grants_callback, NULL);
8787 }
8788 else
8789 {
8790 if (lex_user->user.str == current_role.str)
8791 {
8792 mysql_mutex_unlock(&acl_cache->lock);
8793 mysql_rwlock_unlock(&LOCK_grant);
8794 my_error(ER_NONEXISTING_GRANT, MYF(0),
8795 thd->security_ctx->priv_user,
8796 thd->security_ctx->priv_host);
8797 DBUG_RETURN(TRUE);
8798 }
8799 }
8800 }
8801
8802 if (username)
8803 {
8804 /* Show default role to acl_user */
8805 if (show_default_role(thd, acl_user, buff, sizeof(buff)))
8806 goto end;
8807 }
8808
8809
8810 error= 0;
8811 end:
8812 mysql_mutex_unlock(&acl_cache->lock);
8813 mysql_rwlock_unlock(&LOCK_grant);
8814
8815 my_eof(thd);
8816 DBUG_RETURN(error);
8817 }
8818
find_role_grant_pair(const LEX_CSTRING * u,const LEX_CSTRING * h,const LEX_CSTRING * r)8819 static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u,
8820 const LEX_CSTRING *h,
8821 const LEX_CSTRING *r)
8822 {
8823 char buf[1024];
8824 String pair_key(buf, sizeof(buf), &my_charset_bin);
8825
8826 size_t key_length= u->length + h->length + r->length + 3;
8827 pair_key.alloc(key_length);
8828
8829 strmov(strmov(strmov(const_cast<char*>(pair_key.ptr()),
8830 safe_str(u->str)) + 1, h->str) + 1, r->str);
8831
8832 return (ROLE_GRANT_PAIR *)
8833 my_hash_search(&acl_roles_mappings, (uchar*)pair_key.ptr(), key_length);
8834 }
8835
show_default_role(THD * thd,ACL_USER * acl_entry,char * buff,size_t buffsize)8836 static bool show_default_role(THD *thd, ACL_USER *acl_entry,
8837 char *buff, size_t buffsize)
8838 {
8839 Protocol *protocol= thd->protocol;
8840 LEX_CSTRING def_rolename= acl_entry->default_rolename;
8841
8842 if (def_rolename.length)
8843 {
8844 String def_str(buff, buffsize, system_charset_info);
8845 def_str.length(0);
8846 def_str.append(STRING_WITH_LEN("SET DEFAULT ROLE "));
8847 append_identifier(thd, &def_str, def_rolename.str, def_rolename.length);
8848 def_str.append(" FOR ");
8849 append_identifier(thd, &def_str, acl_entry->user.str, acl_entry->user.length);
8850 DBUG_ASSERT(!(acl_entry->flags & IS_ROLE));
8851 def_str.append('@');
8852 append_identifier(thd, &def_str, acl_entry->host.hostname,
8853 acl_entry->hostname_length);
8854 protocol->prepare_for_resend();
8855 protocol->store(def_str.ptr(),def_str.length(),def_str.charset());
8856 if (protocol->write())
8857 {
8858 return TRUE;
8859 }
8860 }
8861 return FALSE;
8862 }
8863
show_role_grants(THD * thd,const char * hostname,ACL_USER_BASE * acl_entry,char * buff,size_t buffsize)8864 static bool show_role_grants(THD *thd, const char *hostname,
8865 ACL_USER_BASE *acl_entry,
8866 char *buff, size_t buffsize)
8867 {
8868 uint counter;
8869 Protocol *protocol= thd->protocol;
8870 LEX_CSTRING host= {const_cast<char*>(hostname), strlen(hostname)};
8871
8872 String grant(buff, buffsize, system_charset_info);
8873 for (counter= 0; counter < acl_entry->role_grants.elements; counter++)
8874 {
8875 grant.length(0);
8876 grant.append(STRING_WITH_LEN("GRANT "));
8877 ACL_ROLE *acl_role= *(dynamic_element(&acl_entry->role_grants, counter,
8878 ACL_ROLE**));
8879 append_identifier(thd, &grant, acl_role->user.str, acl_role->user.length);
8880 grant.append(STRING_WITH_LEN(" TO "));
8881 append_identifier(thd, &grant, acl_entry->user.str, acl_entry->user.length);
8882 if (!(acl_entry->flags & IS_ROLE))
8883 {
8884 grant.append('@');
8885 append_identifier(thd, &grant, host.str, host.length);
8886 }
8887
8888 ROLE_GRANT_PAIR *pair=
8889 find_role_grant_pair(&acl_entry->user, &host, &acl_role->user);
8890 DBUG_ASSERT(pair);
8891
8892 if (pair->with_admin)
8893 grant.append(STRING_WITH_LEN(" WITH ADMIN OPTION"));
8894
8895 protocol->prepare_for_resend();
8896 protocol->store(grant.ptr(),grant.length(),grant.charset());
8897 if (protocol->write())
8898 {
8899 return TRUE;
8900 }
8901 }
8902 return FALSE;
8903 }
8904
show_global_privileges(THD * thd,ACL_USER_BASE * acl_entry,bool handle_as_role,char * buff,size_t buffsize)8905 static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
8906 bool handle_as_role,
8907 char *buff, size_t buffsize)
8908 {
8909 uint counter;
8910 ulong want_access;
8911 Protocol *protocol= thd->protocol;
8912
8913 String global(buff, buffsize, system_charset_info);
8914 global.length(0);
8915 global.append(STRING_WITH_LEN("GRANT "));
8916
8917 if (handle_as_role)
8918 want_access= ((ACL_ROLE *)acl_entry)->initial_role_access;
8919 else
8920 want_access= acl_entry->access;
8921 if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
8922 global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
8923 else if (!(want_access & ~GRANT_ACL))
8924 global.append(STRING_WITH_LEN("USAGE"));
8925 else
8926 {
8927 bool found=0;
8928 ulong j,test_access= want_access & ~GRANT_ACL;
8929 for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
8930 {
8931 if (test_access & j)
8932 {
8933 if (found)
8934 global.append(STRING_WITH_LEN(", "));
8935 found=1;
8936 global.append(command_array[counter],command_lengths[counter]);
8937 }
8938 }
8939 }
8940 global.append (STRING_WITH_LEN(" ON *.* TO "));
8941 append_identifier(thd, &global, acl_entry->user.str, acl_entry->user.length);
8942
8943 if (!handle_as_role)
8944 add_user_parameters(thd, &global, (ACL_USER *)acl_entry,
8945 (want_access & GRANT_ACL));
8946
8947 else if (want_access & GRANT_ACL)
8948 global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
8949 protocol->prepare_for_resend();
8950 protocol->store(global.ptr(),global.length(),global.charset());
8951 if (protocol->write())
8952 return TRUE;
8953
8954 return FALSE;
8955
8956 }
8957
8958
add_to_user(THD * thd,String * result,const char * user,bool is_user,const char * host)8959 static void add_to_user(THD *thd, String *result, const char *user,
8960 bool is_user, const char *host)
8961 {
8962 result->append(STRING_WITH_LEN(" TO "));
8963 append_identifier(thd, result, user, strlen(user));
8964 if (is_user)
8965 {
8966 result->append('@');
8967 // host and lex_user->host are equal except for case
8968 append_identifier(thd, result, host, strlen(host));
8969 }
8970 }
8971
8972
show_database_privileges(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)8973 static bool show_database_privileges(THD *thd, const char *username,
8974 const char *hostname,
8975 char *buff, size_t buffsize)
8976 {
8977 ulong want_access;
8978 Protocol *protocol= thd->protocol;
8979
8980 for (uint i=0 ; i < acl_dbs.elements() ; i++)
8981 {
8982 const char *user, *host;
8983
8984 ACL_DB *acl_db= &acl_dbs.at(i);
8985 user= safe_str(acl_db->user);
8986 host=acl_db->host.hostname;
8987
8988 /*
8989 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
8990 but make it case-insensitive because that's the way they are
8991 actually applied, and showing fewer privileges than are applied
8992 would be wrong from a security point of view.
8993 */
8994
8995 if (!strcmp(username, user) &&
8996 !my_strcasecmp(system_charset_info, hostname, host))
8997 {
8998 /*
8999 do not print inherited access bits for roles,
9000 the role bits present in the table are what matters
9001 */
9002 if (*hostname) // User
9003 want_access=acl_db->access;
9004 else // Role
9005 want_access=acl_db->initial_access;
9006 if (want_access)
9007 {
9008 String db(buff, buffsize, system_charset_info);
9009 db.length(0);
9010 db.append(STRING_WITH_LEN("GRANT "));
9011
9012 if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
9013 db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
9014 else if (!(want_access & ~GRANT_ACL))
9015 db.append(STRING_WITH_LEN("USAGE"));
9016 else
9017 {
9018 int found=0, cnt;
9019 ulong j,test_access= want_access & ~GRANT_ACL;
9020 for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
9021 {
9022 if (test_access & j)
9023 {
9024 if (found)
9025 db.append(STRING_WITH_LEN(", "));
9026 found = 1;
9027 db.append(command_array[cnt],command_lengths[cnt]);
9028 }
9029 }
9030 }
9031 db.append (STRING_WITH_LEN(" ON "));
9032 append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
9033 db.append (STRING_WITH_LEN(".*"));
9034 add_to_user(thd, &db, username, (*hostname), host);
9035 if (want_access & GRANT_ACL)
9036 db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9037 protocol->prepare_for_resend();
9038 protocol->store(db.ptr(),db.length(),db.charset());
9039 if (protocol->write())
9040 {
9041 return TRUE;
9042 }
9043 }
9044 }
9045 }
9046 return FALSE;
9047
9048 }
9049
show_table_and_column_privileges(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)9050 static bool show_table_and_column_privileges(THD *thd, const char *username,
9051 const char *hostname,
9052 char *buff, size_t buffsize)
9053 {
9054 uint counter, index;
9055 Protocol *protocol= thd->protocol;
9056
9057 for (index=0 ; index < column_priv_hash.records ; index++)
9058 {
9059 const char *user, *host;
9060 GRANT_TABLE *grant_table= (GRANT_TABLE*)
9061 my_hash_element(&column_priv_hash, index);
9062
9063 user= safe_str(grant_table->user);
9064 host= grant_table->host.hostname;
9065
9066 /*
9067 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
9068 but make it case-insensitive because that's the way they are
9069 actually applied, and showing fewer privileges than are applied
9070 would be wrong from a security point of view.
9071 */
9072
9073 if (!strcmp(username,user) &&
9074 !my_strcasecmp(system_charset_info, hostname, host))
9075 {
9076 ulong table_access;
9077 ulong cols_access;
9078 if (*hostname) // User
9079 {
9080 table_access= grant_table->privs;
9081 cols_access= grant_table->cols;
9082 }
9083 else // Role
9084 {
9085 table_access= grant_table->init_privs;
9086 cols_access= grant_table->init_cols;
9087 }
9088
9089 if ((table_access | cols_access) != 0)
9090 {
9091 String global(buff, sizeof(buff), system_charset_info);
9092 ulong test_access= (table_access | cols_access) & ~GRANT_ACL;
9093
9094 global.length(0);
9095 global.append(STRING_WITH_LEN("GRANT "));
9096
9097 if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
9098 global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
9099 else if (!test_access)
9100 global.append(STRING_WITH_LEN("USAGE"));
9101 else
9102 {
9103 /* Add specific column access */
9104 int found= 0;
9105 ulong j;
9106
9107 for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
9108 {
9109 if (test_access & j)
9110 {
9111 if (found)
9112 global.append(STRING_WITH_LEN(", "));
9113 found= 1;
9114 global.append(command_array[counter],command_lengths[counter]);
9115
9116 if (grant_table->cols)
9117 {
9118 uint found_col= 0;
9119 HASH *hash_columns;
9120 hash_columns= &grant_table->hash_columns;
9121
9122 for (uint col_index=0 ;
9123 col_index < hash_columns->records ;
9124 col_index++)
9125 {
9126 GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
9127 my_hash_element(hash_columns,col_index);
9128 if (j & (*hostname ? grant_column->rights // User
9129 : grant_column->init_rights)) // Role
9130 {
9131 if (!found_col)
9132 {
9133 found_col= 1;
9134 /*
9135 If we have a duplicated table level privilege, we
9136 must write the access privilege name again.
9137 */
9138 if (table_access & j)
9139 {
9140 global.append(STRING_WITH_LEN(", "));
9141 global.append(command_array[counter],
9142 command_lengths[counter]);
9143 }
9144 global.append(STRING_WITH_LEN(" ("));
9145 }
9146 else
9147 global.append(STRING_WITH_LEN(", "));
9148 global.append(grant_column->column,
9149 grant_column->key_length,
9150 system_charset_info);
9151 }
9152 }
9153 if (found_col)
9154 global.append(')');
9155 }
9156 }
9157 }
9158 }
9159 global.append(STRING_WITH_LEN(" ON "));
9160 append_identifier(thd, &global, grant_table->db,
9161 strlen(grant_table->db));
9162 global.append('.');
9163 append_identifier(thd, &global, grant_table->tname,
9164 strlen(grant_table->tname));
9165 add_to_user(thd, &global, username, (*hostname), host);
9166 if (table_access & GRANT_ACL)
9167 global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9168 protocol->prepare_for_resend();
9169 protocol->store(global.ptr(),global.length(),global.charset());
9170 if (protocol->write())
9171 {
9172 return TRUE;
9173 }
9174 }
9175 }
9176 }
9177 return FALSE;
9178
9179 }
9180
show_routine_grants(THD * thd,const char * username,const char * hostname,const Sp_handler * sph,char * buff,int buffsize)9181 static int show_routine_grants(THD* thd,
9182 const char *username, const char *hostname,
9183 const Sp_handler *sph,
9184 char *buff, int buffsize)
9185 {
9186 uint counter, index;
9187 int error= 0;
9188 Protocol *protocol= thd->protocol;
9189 HASH *hash= sph->get_priv_hash();
9190 /* Add routine access */
9191 for (index=0 ; index < hash->records ; index++)
9192 {
9193 const char *user, *host;
9194 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
9195
9196 user= safe_str(grant_proc->user);
9197 host= grant_proc->host.hostname;
9198
9199 /*
9200 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
9201 but make it case-insensitive because that's the way they are
9202 actually applied, and showing fewer privileges than are applied
9203 would be wrong from a security point of view.
9204 */
9205
9206 if (!strcmp(username, user) &&
9207 !my_strcasecmp(system_charset_info, hostname, host))
9208 {
9209 ulong proc_access;
9210 if (*hostname) // User
9211 proc_access= grant_proc->privs;
9212 else // Role
9213 proc_access= grant_proc->init_privs;
9214
9215 if (proc_access != 0)
9216 {
9217 String global(buff, buffsize, system_charset_info);
9218 ulong test_access= proc_access & ~GRANT_ACL;
9219
9220 global.length(0);
9221 global.append(STRING_WITH_LEN("GRANT "));
9222
9223 if (!test_access)
9224 global.append(STRING_WITH_LEN("USAGE"));
9225 else
9226 {
9227 /* Add specific procedure access */
9228 int found= 0;
9229 ulong j;
9230
9231 for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
9232 {
9233 if (test_access & j)
9234 {
9235 if (found)
9236 global.append(STRING_WITH_LEN(", "));
9237 found= 1;
9238 global.append(command_array[counter],command_lengths[counter]);
9239 }
9240 }
9241 }
9242 global.append(STRING_WITH_LEN(" ON "));
9243 LEX_CSTRING tmp= sph->type_lex_cstring();
9244 global.append(&tmp);
9245 global.append(' ');
9246 append_identifier(thd, &global, grant_proc->db,
9247 strlen(grant_proc->db));
9248 global.append('.');
9249 append_identifier(thd, &global, grant_proc->tname,
9250 strlen(grant_proc->tname));
9251 add_to_user(thd, &global, username, (*hostname), host);
9252 if (proc_access & GRANT_ACL)
9253 global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9254 protocol->prepare_for_resend();
9255 protocol->store(global.ptr(),global.length(),global.charset());
9256 if (protocol->write())
9257 {
9258 error= -1;
9259 break;
9260 }
9261 }
9262 }
9263 }
9264 return error;
9265 }
9266
9267
9268 /*
9269 Make a clear-text version of the requested privilege.
9270 */
9271
get_privilege_desc(char * to,uint max_length,ulong access)9272 void get_privilege_desc(char *to, uint max_length, ulong access)
9273 {
9274 uint pos;
9275 char *start=to;
9276 DBUG_ASSERT(max_length >= 30); // For end ', ' removal
9277
9278 if (access)
9279 {
9280 max_length--; // Reserve place for end-zero
9281 for (pos=0 ; access ; pos++, access>>=1)
9282 {
9283 if ((access & 1) &&
9284 command_lengths[pos] + (uint) (to-start) < max_length)
9285 {
9286 to= strmov(to, command_array[pos]);
9287 *to++= ',';
9288 *to++= ' ';
9289 }
9290 }
9291 to--; // Remove end ' '
9292 to--; // Remove end ','
9293 }
9294 *to=0;
9295 }
9296
9297
get_mqh(const char * user,const char * host,USER_CONN * uc)9298 void get_mqh(const char *user, const char *host, USER_CONN *uc)
9299 {
9300 ACL_USER *acl_user;
9301
9302 mysql_mutex_lock(&acl_cache->lock);
9303
9304 if (initialized && (acl_user= find_user_wild(host,user)))
9305 uc->user_resources= acl_user->user_resource;
9306 else
9307 bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
9308
9309 mysql_mutex_unlock(&acl_cache->lock);
9310 }
9311
9312 /*
9313 Modify a privilege table.
9314
9315 SYNOPSIS
9316 modify_grant_table()
9317 table The table to modify.
9318 host_field The host name field.
9319 user_field The user name field.
9320 user_to The new name for the user if to be renamed,
9321 NULL otherwise.
9322
9323 DESCRIPTION
9324 Update user/host in the current record if user_to is not NULL.
9325 Delete the current record if user_to is NULL.
9326
9327 RETURN
9328 0 OK.
9329 != 0 Error.
9330 */
9331
modify_grant_table(TABLE * table,Field * host_field,Field * user_field,LEX_USER * user_to)9332 static int modify_grant_table(TABLE *table, Field *host_field,
9333 Field *user_field, LEX_USER *user_to)
9334 {
9335 int error;
9336 DBUG_ENTER("modify_grant_table");
9337
9338 if (user_to)
9339 {
9340 /* rename */
9341 store_record(table, record[1]);
9342 host_field->store(user_to->host.str, user_to->host.length,
9343 system_charset_info);
9344 user_field->store(user_to->user.str, user_to->user.length,
9345 system_charset_info);
9346 if (unlikely(error= table->file->ha_update_row(table->record[1],
9347 table->record[0])) &&
9348 error != HA_ERR_RECORD_IS_THE_SAME)
9349 table->file->print_error(error, MYF(0));
9350 else
9351 error= 0;
9352 }
9353 else
9354 {
9355 /* delete */
9356 if (unlikely((error=table->file->ha_delete_row(table->record[0]))))
9357 table->file->print_error(error, MYF(0));
9358 }
9359
9360 DBUG_RETURN(error);
9361 }
9362
9363 /*
9364 Handle the roles_mapping privilege table
9365 */
handle_roles_mappings_table(TABLE * table,bool drop,LEX_USER * user_from,LEX_USER * user_to)9366 static int handle_roles_mappings_table(TABLE *table, bool drop,
9367 LEX_USER *user_from, LEX_USER *user_to)
9368 {
9369 /*
9370 All entries (Host, User) that match user_from will be renamed,
9371 as well as all Role entries that match if user_from.host.str == ""
9372
9373 Otherwise, only matching (Host, User) will be renamed.
9374 */
9375 DBUG_ENTER("handle_roles_mappings_table");
9376
9377 int error;
9378 int result= 0;
9379 THD *thd= table->in_use;
9380 const char *host, *user, *role;
9381 Field *host_field= table->field[0];
9382 Field *user_field= table->field[1];
9383 Field *role_field= table->field[2];
9384
9385 DBUG_PRINT("info", ("Rewriting entry in roles_mapping table: %s@%s",
9386 user_from->user.str, user_from->host.str));
9387 table->use_all_columns();
9388
9389 if (unlikely(table->file->ha_rnd_init_with_error(1)))
9390 result= -1;
9391 else
9392 {
9393 while((error= table->file->ha_rnd_next(table->record[0])) !=
9394 HA_ERR_END_OF_FILE)
9395 {
9396 if (error)
9397 {
9398 DBUG_PRINT("info", ("scan error: %d", error));
9399 continue;
9400 }
9401
9402 host= safe_str(get_field(thd->mem_root, host_field));
9403 user= safe_str(get_field(thd->mem_root, user_field));
9404
9405 if (!(strcmp(user_from->user.str, user) ||
9406 my_strcasecmp(system_charset_info, user_from->host.str, host)))
9407 result= ((drop || user_to) &&
9408 modify_grant_table(table, host_field, user_field, user_to)) ?
9409 -1 : result ? result : 1; /* Error or keep result or found. */
9410 else
9411 {
9412 role= safe_str(get_field(thd->mem_root, role_field));
9413
9414 if (!user_from->is_role() || strcmp(user_from->user.str, role))
9415 continue;
9416
9417 error= 0;
9418
9419 if (drop) /* drop if requested */
9420 {
9421 if (unlikely((error= table->file->ha_delete_row(table->record[0]))))
9422 table->file->print_error(error, MYF(0));
9423 }
9424 else if (user_to)
9425 {
9426 store_record(table, record[1]);
9427 role_field->store(user_to->user.str, user_to->user.length,
9428 system_charset_info);
9429 if (unlikely(error= table->file->ha_update_row(table->record[1],
9430 table->record[0])) &&
9431 error != HA_ERR_RECORD_IS_THE_SAME)
9432 table->file->print_error(error, MYF(0));
9433 }
9434
9435 /* Error or keep result or found. */
9436 result= error ? -1 : result ? result : 1;
9437 }
9438 }
9439 table->file->ha_rnd_end();
9440 }
9441 DBUG_RETURN(result);
9442 }
9443
9444 /*
9445 Handle a privilege table.
9446
9447 SYNOPSIS
9448 handle_grant_table()
9449 grant_table An open grant table handle.
9450 which_table Which grant table to handle.
9451 drop If user_from is to be dropped.
9452 user_from The the user to be searched/dropped/renamed.
9453 user_to The new name for the user if to be renamed,
9454 NULL otherwise.
9455
9456 DESCRIPTION
9457 Scan through all records in a grant table and apply the requested
9458 operation. For the "user" table, a single index access is sufficient,
9459 since there is an unique index on (host, user).
9460 Delete from grant table if drop is true.
9461 Update in grant table if drop is false and user_to is not NULL.
9462 Search in grant table if drop is false and user_to is NULL.
9463
9464 RETURN
9465 > 0 At least one record matched.
9466 0 OK, but no record matched.
9467 < 0 Error.
9468
9469 TODO(cvicentiu) refactor handle_grant_table to use
9470 Grant_table_base instead of TABLE directly.
9471 */
9472
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)9473 static int handle_grant_table(THD *thd, const Grant_table_base& grant_table,
9474 enum enum_acl_tables which_table, bool drop,
9475 LEX_USER *user_from, LEX_USER *user_to)
9476 {
9477 int result= 0;
9478 int error;
9479 TABLE *table= grant_table.table();
9480 Field *host_field= table->field[0];
9481 Field *user_field= table->field[which_table == USER_TABLE ||
9482 which_table == PROXIES_PRIV_TABLE ? 1 : 2];
9483 const char *host_str= user_from->host.str;
9484 const char *user_str= user_from->user.str;
9485 const char *host;
9486 const char *user;
9487 uchar user_key[MAX_KEY_LENGTH];
9488 uint key_prefix_length;
9489 DBUG_ENTER("handle_grant_table");
9490
9491 if (which_table == ROLES_MAPPING_TABLE)
9492 {
9493 result= handle_roles_mappings_table(table, drop, user_from, user_to);
9494 DBUG_RETURN(result);
9495 }
9496
9497 table->use_all_columns();
9498 if (which_table == USER_TABLE) // mysql.user table
9499 {
9500 /*
9501 The 'user' table has an unique index on (host, user).
9502 Thus, we can handle everything with a single index access.
9503 The host- and user fields are consecutive in the user table records.
9504 So we set host- and user fields of table->record[0] and use the
9505 pointer to the host field as key.
9506 index_read_idx() will replace table->record[0] (its first argument)
9507 by the searched record, if it exists.
9508 */
9509 DBUG_PRINT("info",("read table: '%s' search: '%s'@'%s'",
9510 table->s->table_name.str, user_str, host_str));
9511 host_field->store(host_str, user_from->host.length, system_charset_info);
9512 user_field->store(user_str, user_from->user.length, system_charset_info);
9513
9514 key_prefix_length= (table->key_info->key_part[0].store_length +
9515 table->key_info->key_part[1].store_length);
9516 key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
9517
9518 error= table->file->ha_index_read_idx_map(table->record[0], 0,
9519 user_key, (key_part_map)3,
9520 HA_READ_KEY_EXACT);
9521 if (!unlikely(error) && !*host_str)
9522 {
9523 // verify that we got a role or a user, as needed
9524 if (static_cast<const User_table&>(grant_table).check_is_role() !=
9525 user_from->is_role())
9526 error= HA_ERR_KEY_NOT_FOUND;
9527 }
9528 if (unlikely(error))
9529 {
9530 if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
9531 {
9532 table->file->print_error(error, MYF(0));
9533 result= -1;
9534 }
9535 }
9536 else
9537 {
9538 /* If requested, delete or update the record. */
9539 result= ((drop || user_to) &&
9540 modify_grant_table(table, host_field, user_field, user_to)) ?
9541 -1 : 1; /* Error or found. */
9542 }
9543 DBUG_PRINT("info",("read result: %d", result));
9544 }
9545 else
9546 {
9547 /*
9548 The non-'user' table do not have indexes on (host, user).
9549 And their host- and user fields are not consecutive.
9550 Thus, we need to do a table scan to find all matching records.
9551 */
9552 if (unlikely(table->file->ha_rnd_init_with_error(1)))
9553 result= -1;
9554 else
9555 {
9556 #ifdef EXTRA_DEBUG
9557 DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'",
9558 table->s->table_name.str, user_str, host_str));
9559 #endif
9560 while ((error= table->file->ha_rnd_next(table->record[0])) !=
9561 HA_ERR_END_OF_FILE)
9562 {
9563 if (error)
9564 {
9565 /* Most probable 'deleted record'. */
9566 DBUG_PRINT("info",("scan error: %d", error));
9567 continue;
9568 }
9569 host= safe_str(get_field(thd->mem_root, host_field));
9570 user= safe_str(get_field(thd->mem_root, user_field));
9571
9572 #ifdef EXTRA_DEBUG
9573 if (which_table != PROXIES_PRIV_TABLE)
9574 {
9575 DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
9576 user, host,
9577 get_field(thd->mem_root, table->field[1]) /*db*/,
9578 get_field(thd->mem_root, table->field[3]) /*table*/,
9579 get_field(thd->mem_root,
9580 table->field[4]) /*column*/));
9581 }
9582 #endif
9583 if (strcmp(user_str, user) ||
9584 my_strcasecmp(system_charset_info, host_str, host))
9585 continue;
9586
9587 /* If requested, delete or update the record. */
9588 result= ((drop || user_to) &&
9589 modify_grant_table(table, host_field, user_field, user_to)) ?
9590 -1 : result ? result : 1; /* Error or keep result or found. */
9591 /* If search is requested, we do not need to search further. */
9592 if (! drop && ! user_to)
9593 break ;
9594 }
9595 (void) table->file->ha_rnd_end();
9596 DBUG_PRINT("info",("scan result: %d", result));
9597 }
9598 }
9599
9600 DBUG_RETURN(result);
9601 }
9602
9603
9604 /**
9605 Handle an in-memory privilege structure.
9606
9607 @param struct_no The number of the structure to handle (0..6).
9608 @param drop If user_from is to be dropped.
9609 @param user_from The the user to be searched/dropped/renamed.
9610 @param user_to The new name for the user if to be renamed, NULL otherwise.
9611
9612 @note
9613 Scan through all elements in an in-memory grant structure and apply
9614 the requested operation.
9615 Delete from grant structure if drop is true.
9616 Update in grant structure if drop is false and user_to is not NULL.
9617 Search in grant structure if drop is false and user_to is NULL.
9618
9619 @retval > 0 At least one element matched.
9620 @retval 0 OK, but no element matched.
9621 */
9622
handle_grant_struct(enum enum_acl_lists struct_no,bool drop,LEX_USER * user_from,LEX_USER * user_to)9623 static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
9624 LEX_USER *user_from, LEX_USER *user_to)
9625 {
9626 int result= 0;
9627 int elements;
9628 bool restart;
9629 const char *UNINIT_VAR(user);
9630 const char *UNINIT_VAR(host);
9631 ACL_USER *acl_user= NULL;
9632 ACL_ROLE *acl_role= NULL;
9633 ACL_DB *acl_db= NULL;
9634 ACL_PROXY_USER *acl_proxy_user= NULL;
9635 GRANT_NAME *grant_name= NULL;
9636 ROLE_GRANT_PAIR *UNINIT_VAR(role_grant_pair);
9637 HASH *grant_name_hash= NULL;
9638 HASH *roles_mappings_hash= NULL;
9639 DBUG_ENTER("handle_grant_struct");
9640 DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
9641 struct_no, user_from->user.str, user_from->host.str));
9642
9643 mysql_mutex_assert_owner(&acl_cache->lock);
9644
9645 /* No point in querying ROLE ACL if user_from is not a role */
9646 if (struct_no == ROLE_ACL && user_from->host.length)
9647 DBUG_RETURN(0);
9648
9649 /* same. no roles in PROXY_USERS_ACL */
9650 if (struct_no == PROXY_USERS_ACL && user_from->is_role())
9651 DBUG_RETURN(0);
9652
9653 if (struct_no == ROLE_ACL) //no need to scan the structures in this case
9654 {
9655 acl_role= find_acl_role(user_from->user.str);
9656 if (!acl_role)
9657 DBUG_RETURN(0);
9658
9659 if (!drop && !user_to) //role was found
9660 DBUG_RETURN(1);
9661
9662 /* this calls for a role update */
9663 const char *old_key= acl_role->user.str;
9664 size_t old_key_length= acl_role->user.length;
9665 if (drop)
9666 {
9667 /* all grants must be revoked from this role by now. propagate this */
9668 propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL);
9669
9670 // delete the role from cross-reference arrays
9671 for (uint i=0; i < acl_role->role_grants.elements; i++)
9672 {
9673 ACL_ROLE *grant= *dynamic_element(&acl_role->role_grants,
9674 i, ACL_ROLE**);
9675 remove_ptr_from_dynarray(&grant->parent_grantee, acl_role);
9676 }
9677
9678 for (uint i=0; i < acl_role->parent_grantee.elements; i++)
9679 {
9680 ACL_USER_BASE *grantee= *dynamic_element(&acl_role->parent_grantee,
9681 i, ACL_USER_BASE**);
9682 remove_ptr_from_dynarray(&grantee->role_grants, acl_role);
9683 }
9684
9685 my_hash_delete(&acl_roles, (uchar*) acl_role);
9686 DBUG_RETURN(1);
9687 }
9688 acl_role->user.str= strdup_root(&acl_memroot, user_to->user.str);
9689 acl_role->user.length= user_to->user.length;
9690
9691 my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key,
9692 old_key_length);
9693 DBUG_RETURN(1);
9694
9695 }
9696
9697 /* Get the number of elements in the in-memory structure. */
9698 switch (struct_no) {
9699 case USER_ACL:
9700 elements= acl_users.elements;
9701 break;
9702 case DB_ACL:
9703 elements= int(acl_dbs.elements());
9704 break;
9705 case COLUMN_PRIVILEGES_HASH:
9706 grant_name_hash= &column_priv_hash;
9707 elements= grant_name_hash->records;
9708 break;
9709 case PROC_PRIVILEGES_HASH:
9710 grant_name_hash= &proc_priv_hash;
9711 elements= grant_name_hash->records;
9712 break;
9713 case FUNC_PRIVILEGES_HASH:
9714 grant_name_hash= &func_priv_hash;
9715 elements= grant_name_hash->records;
9716 break;
9717 case PACKAGE_SPEC_PRIVILEGES_HASH:
9718 grant_name_hash= &package_spec_priv_hash;
9719 elements= grant_name_hash->records;
9720 break;
9721 case PACKAGE_BODY_PRIVILEGES_HASH:
9722 grant_name_hash= &package_body_priv_hash;
9723 elements= grant_name_hash->records;
9724 break;
9725 case PROXY_USERS_ACL:
9726 elements= acl_proxy_users.elements;
9727 break;
9728 case ROLES_MAPPINGS_HASH:
9729 roles_mappings_hash= &acl_roles_mappings;
9730 elements= roles_mappings_hash->records;
9731 break;
9732 default:
9733 DBUG_ASSERT(0);
9734 DBUG_RETURN(-1);
9735 }
9736
9737
9738 #ifdef EXTRA_DEBUG
9739 DBUG_PRINT("loop",("scan struct: %u search user: '%s' host: '%s'",
9740 struct_no, user_from->user.str, user_from->host.str));
9741 #endif
9742 /* Loop over elements backwards as it may reduce the number of mem-moves
9743 for dynamic arrays.
9744
9745 We restart the loop, if we deleted or updated anything in a hash table
9746 because calling my_hash_delete or my_hash_update shuffles elements indices
9747 and we can miss some if we do only one scan.
9748 */
9749 do {
9750 restart= false;
9751 for (int idx= elements - 1; idx >= 0; idx--)
9752 {
9753 /*
9754 Get a pointer to the element.
9755 */
9756 switch (struct_no) {
9757 case USER_ACL:
9758 acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
9759 user= acl_user->user.str;
9760 host= acl_user->host.hostname;
9761 break;
9762
9763 case DB_ACL:
9764 acl_db= &acl_dbs.at(idx);
9765 user= acl_db->user;
9766 host= acl_db->host.hostname;
9767 break;
9768
9769 case COLUMN_PRIVILEGES_HASH:
9770 case PROC_PRIVILEGES_HASH:
9771 case FUNC_PRIVILEGES_HASH:
9772 case PACKAGE_SPEC_PRIVILEGES_HASH:
9773 case PACKAGE_BODY_PRIVILEGES_HASH:
9774 grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
9775 user= grant_name->user;
9776 host= grant_name->host.hostname;
9777 break;
9778
9779 case PROXY_USERS_ACL:
9780 acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
9781 user= acl_proxy_user->get_user();
9782 host= acl_proxy_user->get_host();
9783 break;
9784
9785 case ROLES_MAPPINGS_HASH:
9786 role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx);
9787 user= role_grant_pair->u_uname;
9788 host= role_grant_pair->u_hname;
9789 break;
9790
9791 default:
9792 DBUG_ASSERT(0);
9793 }
9794 if (! user)
9795 user= "";
9796 if (! host)
9797 host= "";
9798
9799 #ifdef EXTRA_DEBUG
9800 DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'",
9801 struct_no, idx, user, host));
9802 #endif
9803
9804 if (struct_no == ROLES_MAPPINGS_HASH)
9805 {
9806 const char* role= role_grant_pair->r_uname? role_grant_pair->r_uname: "";
9807 if (user_from->is_role())
9808 {
9809 /* When searching for roles within the ROLES_MAPPINGS_HASH, we have
9810 to check both the user field as well as the role field for a match.
9811
9812 It is possible to have a role granted to a role. If we are going
9813 to modify the mapping entry, it needs to be done on either on the
9814 "user" end (here represented by a role) or the "role" end. At least
9815 one part must match.
9816
9817 If the "user" end has a not-empty host string, it can never match
9818 as we are searching for a role here. A role always has an empty host
9819 string.
9820 */
9821 if ((*host || strcmp(user_from->user.str, user)) &&
9822 strcmp(user_from->user.str, role))
9823 continue;
9824 }
9825 else
9826 {
9827 if (strcmp(user_from->user.str, user) ||
9828 my_strcasecmp(system_charset_info, user_from->host.str, host))
9829 continue;
9830 }
9831 }
9832 else
9833 {
9834 if (strcmp(user_from->user.str, user) ||
9835 my_strcasecmp(system_charset_info, user_from->host.str, host))
9836 continue;
9837 }
9838
9839 result= 1; /* At least one element found. */
9840 if ( drop )
9841 {
9842 elements--;
9843 switch ( struct_no ) {
9844 case USER_ACL:
9845 free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*));
9846 delete_dynamic_element(&acl_users, idx);
9847 break;
9848
9849 case DB_ACL:
9850 acl_dbs.del(idx);
9851 break;
9852
9853 case COLUMN_PRIVILEGES_HASH:
9854 case PROC_PRIVILEGES_HASH:
9855 case FUNC_PRIVILEGES_HASH:
9856 case PACKAGE_SPEC_PRIVILEGES_HASH:
9857 case PACKAGE_BODY_PRIVILEGES_HASH:
9858 my_hash_delete(grant_name_hash, (uchar*) grant_name);
9859 restart= true;
9860 break;
9861
9862 case PROXY_USERS_ACL:
9863 delete_dynamic_element(&acl_proxy_users, idx);
9864 break;
9865
9866 case ROLES_MAPPINGS_HASH:
9867 my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair);
9868 restart= true;
9869 break;
9870
9871 default:
9872 DBUG_ASSERT(0);
9873 break;
9874 }
9875 }
9876 else if ( user_to )
9877 {
9878 switch ( struct_no ) {
9879 case USER_ACL:
9880 acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str);
9881 acl_user->user.length= user_to->user.length;
9882 update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str));
9883 acl_user->hostname_length= strlen(acl_user->host.hostname);
9884 break;
9885
9886 case DB_ACL:
9887 acl_db->user= strdup_root(&acl_memroot, user_to->user.str);
9888 update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str));
9889 break;
9890
9891 case COLUMN_PRIVILEGES_HASH:
9892 case PROC_PRIVILEGES_HASH:
9893 case FUNC_PRIVILEGES_HASH:
9894 case PACKAGE_SPEC_PRIVILEGES_HASH:
9895 case PACKAGE_BODY_PRIVILEGES_HASH:
9896 {
9897 /*
9898 Save old hash key and its length to be able to properly update
9899 element position in hash.
9900 */
9901 char *old_key= grant_name->hash_key;
9902 size_t old_key_length= grant_name->key_length;
9903
9904 /*
9905 Update the grant structure with the new user name and host name.
9906 */
9907 grant_name->set_user_details(user_to->host.str, grant_name->db,
9908 user_to->user.str, grant_name->tname,
9909 TRUE);
9910
9911 /*
9912 Since username is part of the hash key, when the user name
9913 is renamed, the hash key is changed. Update the hash to
9914 ensure that the position matches the new hash key value
9915 */
9916 my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
9917 old_key_length);
9918 restart= true;
9919 break;
9920 }
9921
9922 case PROXY_USERS_ACL:
9923 acl_proxy_user->set_user (&acl_memroot, user_to->user.str);
9924 acl_proxy_user->set_host (&acl_memroot, user_to->host.str);
9925 break;
9926
9927 case ROLES_MAPPINGS_HASH:
9928 {
9929 /*
9930 Save old hash key and its length to be able to properly update
9931 element position in hash.
9932 */
9933 char *old_key= role_grant_pair->hashkey.str;
9934 size_t old_key_length= role_grant_pair->hashkey.length;
9935 bool oom;
9936
9937 if (user_to->is_role())
9938 oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname,
9939 role_grant_pair->u_hname,
9940 user_to->user.str, false);
9941 else
9942 oom= role_grant_pair->init(&acl_memroot, user_to->user.str,
9943 user_to->host.str,
9944 role_grant_pair->r_uname, false);
9945 if (oom)
9946 DBUG_RETURN(-1);
9947
9948 my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair,
9949 (uchar*) old_key, old_key_length);
9950 restart= true;
9951 break;
9952 }
9953
9954 default:
9955 DBUG_ASSERT(0);
9956 break;
9957 }
9958
9959 }
9960 else
9961 {
9962 /* If search is requested, we do not need to search further. */
9963 break;
9964 }
9965 }
9966 } while (restart);
9967 #ifdef EXTRA_DEBUG
9968 DBUG_PRINT("loop",("scan struct: %u result %d", struct_no, result));
9969 #endif
9970
9971 DBUG_RETURN(result);
9972 }
9973
9974
9975 /*
9976 Handle all privilege tables and in-memory privilege structures.
9977
9978 SYNOPSIS
9979 handle_grant_data()
9980 tables The array with the four open tables.
9981 drop If user_from is to be dropped.
9982 user_from The the user to be searched/dropped/renamed.
9983 user_to The new name for the user if to be renamed,
9984 NULL otherwise.
9985
9986 DESCRIPTION
9987 Go through all grant tables and in-memory grant structures and apply
9988 the requested operation.
9989 Delete from grant data if drop is true.
9990 Update in grant data if drop is false and user_to is not NULL.
9991 Search in grant data if drop is false and user_to is NULL.
9992
9993 RETURN
9994 > 0 At least one element matched.
9995 0 OK, but no element matched.
9996 < 0 Error.
9997 */
9998
handle_grant_data(THD * thd,Grant_tables & tables,bool drop,LEX_USER * user_from,LEX_USER * user_to)9999 static int handle_grant_data(THD *thd, Grant_tables& tables, bool drop,
10000 LEX_USER *user_from, LEX_USER *user_to)
10001 {
10002 int result= 0;
10003 int found;
10004 bool handle_as_role= user_from->is_role();
10005 bool search_only= !drop && !user_to;
10006 DBUG_ENTER("handle_grant_data");
10007
10008 if (user_to)
10009 DBUG_ASSERT(handle_as_role == user_to->is_role());
10010
10011 if (search_only)
10012 {
10013 /* quickly search in-memory structures first */
10014 if (handle_as_role && find_acl_role(user_from->user.str))
10015 DBUG_RETURN(1); // found
10016
10017 if (!handle_as_role && find_user_exact(user_from->host.str, user_from->user.str))
10018 DBUG_RETURN(1); // found
10019 }
10020
10021 /* Handle db table. */
10022 if ((found= handle_grant_table(thd, tables.db_table(),
10023 DB_TABLE, drop, user_from,
10024 user_to)) < 0)
10025 {
10026 /* Handle of table failed, don't touch the in-memory array. */
10027 result= -1;
10028 }
10029 else
10030 {
10031 /* Handle db array. */
10032 if ((handle_grant_struct(DB_ACL, drop, user_from, user_to) || found)
10033 && ! result)
10034 {
10035 result= 1; /* At least one record/element found. */
10036 /* If search is requested, we do not need to search further. */
10037 if (search_only)
10038 goto end;
10039 acl_cache->clear(1);
10040 }
10041 }
10042
10043 /* Handle stored routines table. */
10044 if ((found= handle_grant_table(thd, tables.procs_priv_table(),
10045 PROCS_PRIV_TABLE, drop,
10046 user_from, user_to)) < 0)
10047 {
10048 /* Handle of table failed, don't touch in-memory array. */
10049 result= -1;
10050 }
10051 else
10052 {
10053 /* Handle procs array. */
10054 if ((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10055 && ! result)
10056 {
10057 result= 1; /* At least one record/element found. */
10058 /* If search is requested, we do not need to search further. */
10059 if (search_only)
10060 goto end;
10061 }
10062 /* Handle funcs array. */
10063 if ((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10064 && ! result)
10065 {
10066 result= 1; /* At least one record/element found. */
10067 /* If search is requested, we do not need to search further. */
10068 if (search_only)
10069 goto end;
10070 }
10071 /* Handle package spec array. */
10072 if ((handle_grant_struct(PACKAGE_SPEC_PRIVILEGES_HASH,
10073 drop, user_from, user_to) || found)
10074 && ! result)
10075 {
10076 result= 1; /* At least one record/element found. */
10077 /* If search is requested, we do not need to search further. */
10078 if (search_only)
10079 goto end;
10080 }
10081 /* Handle package body array. */
10082 if ((handle_grant_struct(PACKAGE_BODY_PRIVILEGES_HASH,
10083 drop, user_from, user_to) || found)
10084 && ! result)
10085 {
10086 result= 1; /* At least one record/element found. */
10087 /* If search is requested, we do not need to search further. */
10088 if (search_only)
10089 goto end;
10090 }
10091 }
10092
10093 /* Handle tables table. */
10094 if ((found= handle_grant_table(thd, tables.tables_priv_table(),
10095 TABLES_PRIV_TABLE, drop,
10096 user_from, user_to)) < 0)
10097 {
10098 /* Handle of table failed, don't touch columns and in-memory array. */
10099 result= -1;
10100 }
10101 else
10102 {
10103 if (found && ! result)
10104 {
10105 result= 1; /* At least one record found. */
10106 /* If search is requested, we do not need to search further. */
10107 if (search_only)
10108 goto end;
10109 }
10110
10111 /* Handle columns table. */
10112 if ((found= handle_grant_table(thd, tables.columns_priv_table(),
10113 COLUMNS_PRIV_TABLE, drop,
10114 user_from, user_to)) < 0)
10115 {
10116 /* Handle of table failed, don't touch the in-memory array. */
10117 result= -1;
10118 }
10119 else
10120 {
10121 /* Handle columns hash. */
10122 if ((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10123 && ! result)
10124 result= 1; /* At least one record/element found. */
10125 if (search_only)
10126 goto end;
10127 }
10128 }
10129
10130 /* Handle proxies_priv table. */
10131 if (tables.proxies_priv_table().table_exists())
10132 {
10133 if ((found= handle_grant_table(thd, tables.proxies_priv_table(),
10134 PROXIES_PRIV_TABLE, drop,
10135 user_from, user_to)) < 0)
10136 {
10137 /* Handle of table failed, don't touch the in-memory array. */
10138 result= -1;
10139 }
10140 else
10141 {
10142 /* Handle proxies_priv array. */
10143 if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) || found)
10144 && ! result)
10145 result= 1; /* At least one record/element found. */
10146 if (search_only)
10147 goto end;
10148 }
10149 }
10150
10151 /* Handle roles_mapping table. */
10152 if (tables.roles_mapping_table().table_exists())
10153 {
10154 if ((found= handle_grant_table(thd, tables.roles_mapping_table(),
10155 ROLES_MAPPING_TABLE, drop,
10156 user_from, user_to)) < 0)
10157 {
10158 /* Handle of table failed, don't touch the in-memory array. */
10159 result= -1;
10160 }
10161 else
10162 {
10163 /* Handle acl_roles_mappings array */
10164 if ((handle_grant_struct(ROLES_MAPPINGS_HASH, drop, user_from, user_to) || found)
10165 && ! result)
10166 result= 1; /* At least one record/element found */
10167 if (search_only)
10168 goto end;
10169 }
10170 }
10171
10172 /* Handle user table. */
10173 if ((found= handle_grant_table(thd, tables.user_table(), USER_TABLE,
10174 drop, user_from, user_to)) < 0)
10175 {
10176 /* Handle of table failed, don't touch the in-memory array. */
10177 result= -1;
10178 }
10179 else
10180 {
10181 enum enum_acl_lists what= handle_as_role ? ROLE_ACL : USER_ACL;
10182 if (((handle_grant_struct(what, drop, user_from, user_to)) || found) && !result)
10183 {
10184 result= 1; /* At least one record/element found. */
10185 DBUG_ASSERT(! search_only);
10186 }
10187 }
10188
10189 end:
10190 DBUG_RETURN(result);
10191 }
10192
10193 /*
10194 Create a list of users.
10195
10196 SYNOPSIS
10197 mysql_create_user()
10198 thd The current thread.
10199 list The users to create.
10200 handle_as_role Handle the user list as roles if true
10201
10202 RETURN
10203 FALSE OK.
10204 TRUE Error.
10205 */
10206
mysql_create_user(THD * thd,List<LEX_USER> & list,bool handle_as_role)10207 bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
10208 {
10209 int result;
10210 String wrong_users;
10211 LEX_USER *user_name;
10212 List_iterator <LEX_USER> user_list(list);
10213 bool binlog= false;
10214 bool some_users_dropped= false;
10215 DBUG_ENTER("mysql_create_user");
10216 DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
10217
10218 if (handle_as_role && sp_process_definer(thd))
10219 DBUG_RETURN(TRUE);
10220
10221 /* CREATE USER may be skipped on replication client. */
10222 Grant_tables tables(Table_user | Table_db |
10223 Table_tables_priv | Table_columns_priv |
10224 Table_procs_priv | Table_proxies_priv |
10225 Table_roles_mapping, TL_WRITE);
10226 if ((result= tables.open_and_lock(thd)))
10227 DBUG_RETURN(result != 1);
10228
10229 mysql_rwlock_wrlock(&LOCK_grant);
10230 mysql_mutex_lock(&acl_cache->lock);
10231
10232 while ((user_name= user_list++))
10233 {
10234 if (user_name->user.str == current_user.str)
10235 {
10236 append_str(&wrong_users, STRING_WITH_LEN("CURRENT_USER"));
10237 result= TRUE;
10238 continue;
10239 }
10240
10241 if (user_name->user.str == current_role.str)
10242 {
10243 append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
10244 result= TRUE;
10245 continue;
10246 }
10247
10248 if (handle_as_role && is_invalid_role_name(user_name->user.str))
10249 {
10250 append_user(thd, &wrong_users, user_name);
10251 result= TRUE;
10252 continue;
10253 }
10254
10255 if (!user_name->host.str)
10256 user_name->host= host_not_specified;
10257
10258 if (fix_lex_user(thd, user_name))
10259 {
10260 append_user(thd, &wrong_users, user_name);
10261 result= TRUE;
10262 continue;
10263 }
10264
10265 /*
10266 Search all in-memory structures and grant tables
10267 for a mention of the new user/role name.
10268 */
10269 if (handle_grant_data(thd, tables, 0, user_name, NULL))
10270 {
10271 if (thd->lex->create_info.or_replace())
10272 {
10273 // Drop the existing user
10274 if (handle_grant_data(thd, tables, 1, user_name, NULL) <= 0)
10275 {
10276 // DROP failed
10277 append_user(thd, &wrong_users, user_name);
10278 result= true;
10279 continue;
10280 }
10281 else
10282 some_users_dropped= true;
10283 // Proceed with the creation
10284 }
10285 else if (thd->lex->create_info.if_not_exists())
10286 {
10287 binlog= true;
10288 if (handle_as_role)
10289 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10290 ER_ROLE_CREATE_EXISTS,
10291 ER_THD(thd, ER_ROLE_CREATE_EXISTS),
10292 user_name->user.str);
10293 else
10294 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10295 ER_USER_CREATE_EXISTS,
10296 ER_THD(thd, ER_USER_CREATE_EXISTS),
10297 user_name->user.str, user_name->host.str);
10298 continue;
10299 }
10300 else
10301 {
10302 // "CREATE USER user1" for an existing user
10303 append_user(thd, &wrong_users, user_name);
10304 result= true;
10305 continue;
10306 }
10307 }
10308
10309 if (replace_user_table(thd, tables.user_table(), *user_name, 0, 0, 1, 0))
10310 {
10311 append_user(thd, &wrong_users, user_name);
10312 result= TRUE;
10313 continue;
10314 }
10315 binlog= true;
10316
10317 // every created role is automatically granted to its creator-admin
10318 if (handle_as_role)
10319 {
10320 ACL_USER_BASE *grantee= find_acl_user_base(thd->lex->definer->user.str,
10321 thd->lex->definer->host.str);
10322 ACL_ROLE *role= find_acl_role(user_name->user.str);
10323
10324 /*
10325 just like with routines, views, triggers, and events we allow
10326 non-existant definers here with a warning (see sp_process_definer())
10327 */
10328 if (grantee)
10329 add_role_user_mapping(grantee, role);
10330
10331 /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
10332 Roles_mapping_table instead of TABLE directly. */
10333 if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
10334 &thd->lex->definer->user,
10335 &thd->lex->definer->host,
10336 &user_name->user, true,
10337 NULL, false))
10338 {
10339 append_user(thd, &wrong_users, user_name);
10340 if (grantee)
10341 undo_add_role_user_mapping(grantee, role);
10342 result= TRUE;
10343 }
10344 else if (grantee)
10345 update_role_mapping(&thd->lex->definer->user,
10346 &thd->lex->definer->host,
10347 &user_name->user, true, NULL, false);
10348 }
10349 }
10350
10351 if (result && some_users_dropped && !handle_as_role)
10352 {
10353 /* Rebuild in-memory structs, since 'acl_users' has been modified */
10354 rebuild_check_host();
10355 rebuild_role_grants();
10356 }
10357
10358 mysql_mutex_unlock(&acl_cache->lock);
10359
10360 if (result)
10361 {
10362 my_error(ER_CANNOT_USER, MYF(0),
10363 (handle_as_role) ? "CREATE ROLE" : "CREATE USER",
10364 wrong_users.c_ptr_safe());
10365 }
10366
10367 if (binlog)
10368 result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10369
10370 mysql_rwlock_unlock(&LOCK_grant);
10371 DBUG_RETURN(result);
10372 }
10373
10374 /*
10375 Drop a list of users and all their privileges.
10376
10377 SYNOPSIS
10378 mysql_drop_user()
10379 thd The current thread.
10380 list The users to drop.
10381
10382 RETURN
10383 FALSE OK.
10384 TRUE Error.
10385 */
10386
mysql_drop_user(THD * thd,List<LEX_USER> & list,bool handle_as_role)10387 bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
10388 {
10389 int result;
10390 String wrong_users;
10391 LEX_USER *user_name, *tmp_user_name;
10392 List_iterator <LEX_USER> user_list(list);
10393 bool binlog= false;
10394 sql_mode_t old_sql_mode= thd->variables.sql_mode;
10395 DBUG_ENTER("mysql_drop_user");
10396 DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
10397
10398 /* DROP USER may be skipped on replication client. */
10399 Grant_tables tables(Table_user | Table_db |
10400 Table_tables_priv | Table_columns_priv |
10401 Table_procs_priv | Table_proxies_priv |
10402 Table_roles_mapping, TL_WRITE);
10403 if ((result= tables.open_and_lock(thd)))
10404 DBUG_RETURN(result != 1);
10405
10406 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
10407
10408 mysql_rwlock_wrlock(&LOCK_grant);
10409 mysql_mutex_lock(&acl_cache->lock);
10410
10411 while ((tmp_user_name= user_list++))
10412 {
10413 int rc;
10414 user_name= get_current_user(thd, tmp_user_name, false);
10415 if (!user_name)
10416 {
10417 thd->clear_error();
10418 append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
10419 result= TRUE;
10420 continue;
10421 }
10422
10423 if (handle_as_role != user_name->is_role())
10424 {
10425 append_user(thd, &wrong_users, user_name);
10426 result= TRUE;
10427 continue;
10428 }
10429
10430 if ((rc= handle_grant_data(thd, tables, 1, user_name, NULL)) > 0)
10431 {
10432 // The user or role was successfully deleted
10433 binlog= true;
10434 continue;
10435 }
10436
10437 if (rc == 0 && thd->lex->if_exists())
10438 {
10439 // "DROP USER IF EXISTS user1" for a non-existing user or role
10440 if (handle_as_role)
10441 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10442 ER_ROLE_DROP_EXISTS,
10443 ER_THD(thd, ER_ROLE_DROP_EXISTS),
10444 user_name->user.str);
10445 else
10446 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10447 ER_USER_DROP_EXISTS,
10448 ER_THD(thd, ER_USER_DROP_EXISTS),
10449 user_name->user.str, user_name->host.str);
10450 binlog= true;
10451 continue;
10452 }
10453 // Internal error, or "DROP USER user1" for a non-existing user
10454 append_user(thd, &wrong_users, user_name);
10455 result= TRUE;
10456 }
10457
10458 if (!handle_as_role)
10459 {
10460 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
10461 rebuild_check_host();
10462
10463 /*
10464 Rebuild every user's role_grants since 'acl_users' has been sorted
10465 and old pointers to ACL_USER elements are no longer valid
10466 */
10467 rebuild_role_grants();
10468 }
10469
10470 mysql_mutex_unlock(&acl_cache->lock);
10471
10472 if (result)
10473 my_error(ER_CANNOT_USER, MYF(0),
10474 (handle_as_role) ? "DROP ROLE" : "DROP USER",
10475 wrong_users.c_ptr_safe());
10476
10477 if (binlog)
10478 result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10479
10480 mysql_rwlock_unlock(&LOCK_grant);
10481 thd->variables.sql_mode= old_sql_mode;
10482 DBUG_RETURN(result);
10483 }
10484
10485 /*
10486 Rename a user.
10487
10488 SYNOPSIS
10489 mysql_rename_user()
10490 thd The current thread.
10491 list The user name pairs: (from, to).
10492
10493 RETURN
10494 FALSE OK.
10495 TRUE Error.
10496 */
10497
mysql_rename_user(THD * thd,List<LEX_USER> & list)10498 bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
10499 {
10500 int result;
10501 String wrong_users;
10502 LEX_USER *user_from, *tmp_user_from;
10503 LEX_USER *user_to, *tmp_user_to;
10504 List_iterator <LEX_USER> user_list(list);
10505 bool some_users_renamed= FALSE;
10506 DBUG_ENTER("mysql_rename_user");
10507
10508 /* RENAME USER may be skipped on replication client. */
10509 Grant_tables tables(Table_user | Table_db |
10510 Table_tables_priv | Table_columns_priv |
10511 Table_procs_priv | Table_proxies_priv |
10512 Table_roles_mapping, TL_WRITE);
10513 if ((result= tables.open_and_lock(thd)))
10514 DBUG_RETURN(result != 1);
10515
10516 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
10517
10518 mysql_rwlock_wrlock(&LOCK_grant);
10519 mysql_mutex_lock(&acl_cache->lock);
10520
10521 while ((tmp_user_from= user_list++))
10522 {
10523 tmp_user_to= user_list++;
10524 if (!(user_from= get_current_user(thd, tmp_user_from, false)))
10525 {
10526 append_user(thd, &wrong_users, user_from);
10527 result= TRUE;
10528 continue;
10529 }
10530 if (!(user_to= get_current_user(thd, tmp_user_to, false)))
10531 {
10532 append_user(thd, &wrong_users, user_to);
10533 result= TRUE;
10534 continue;
10535 }
10536 DBUG_ASSERT(!user_from->is_role());
10537 DBUG_ASSERT(!user_to->is_role());
10538
10539 /*
10540 Search all in-memory structures and grant tables
10541 for a mention of the new user name.
10542 */
10543 if (handle_grant_data(thd, tables, 0, user_to, NULL) ||
10544 handle_grant_data(thd, tables, 0, user_from, user_to) <= 0)
10545 {
10546 /* NOTE TODO renaming roles is not yet implemented */
10547 append_user(thd, &wrong_users, user_from);
10548 result= TRUE;
10549 continue;
10550 }
10551 some_users_renamed= TRUE;
10552 }
10553
10554 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
10555 rebuild_check_host();
10556
10557 /*
10558 Rebuild every user's role_grants since 'acl_users' has been sorted
10559 and old pointers to ACL_USER elements are no longer valid
10560 */
10561 rebuild_role_grants();
10562
10563 mysql_mutex_unlock(&acl_cache->lock);
10564
10565 if (result)
10566 my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
10567
10568 if (some_users_renamed && mysql_bin_log.is_open())
10569 result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10570
10571 mysql_rwlock_unlock(&LOCK_grant);
10572 DBUG_RETURN(result);
10573 }
10574
10575 /*
10576 Alter a user's connection and resource settings.
10577
10578 SYNOPSIS
10579 mysql_alter_user()
10580 thd The current thread.
10581 list The users to alter.
10582
10583 RETURN
10584 > 0 Error. Error message already sent.
10585 0 OK.
10586 */
mysql_alter_user(THD * thd,List<LEX_USER> & users_list)10587 int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
10588 {
10589 DBUG_ENTER("mysql_alter_user");
10590 int result= 0;
10591 String wrong_users;
10592 bool some_users_altered= false;
10593
10594 /* The only table we're altering is the user table. */
10595 Grant_tables tables(Table_user, TL_WRITE);
10596 if ((result= tables.open_and_lock(thd)))
10597 DBUG_RETURN(result != 1);
10598
10599 /* Lock ACL data structures until we finish altering all users. */
10600 mysql_rwlock_wrlock(&LOCK_grant);
10601 mysql_mutex_lock(&acl_cache->lock);
10602
10603 LEX_USER *tmp_lex_user;
10604 List_iterator<LEX_USER> users_list_iterator(users_list);
10605 while ((tmp_lex_user= users_list_iterator++))
10606 {
10607 LEX_USER* lex_user= get_current_user(thd, tmp_lex_user, false);
10608 if (!lex_user ||
10609 fix_lex_user(thd, lex_user) ||
10610 replace_user_table(thd, tables.user_table(), *lex_user, 0,
10611 false, false, true))
10612 {
10613 thd->clear_error();
10614 append_user(thd, &wrong_users, tmp_lex_user);
10615 result= TRUE;
10616 continue;
10617 }
10618 some_users_altered= true;
10619 }
10620
10621 /* Unlock ACL data structures. */
10622 mysql_mutex_unlock(&acl_cache->lock);
10623 mysql_rwlock_unlock(&LOCK_grant);
10624
10625 if (result)
10626 {
10627 /* 'if exists' flag leads to warnings instead of errors. */
10628 if (thd->lex->create_info.if_exists())
10629 {
10630 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10631 ER_CANNOT_USER,
10632 ER_THD(thd, ER_CANNOT_USER),
10633 "ALTER USER", wrong_users.c_ptr_safe());
10634 result= FALSE;
10635 }
10636 else
10637 {
10638 my_error(ER_CANNOT_USER, MYF(0),
10639 "ALTER USER",
10640 wrong_users.c_ptr_safe());
10641 }
10642 }
10643
10644 if (some_users_altered)
10645 result|= write_bin_log(thd, FALSE, thd->query(),
10646 thd->query_length());
10647 DBUG_RETURN(result);
10648 }
10649
10650
10651 static bool
mysql_revoke_sp_privs(THD * thd,Grant_tables * tables,const Sp_handler * sph,const LEX_USER * lex_user)10652 mysql_revoke_sp_privs(THD *thd,
10653 Grant_tables *tables,
10654 const Sp_handler *sph,
10655 const LEX_USER *lex_user)
10656 {
10657 bool rc= false;
10658 uint counter, revoked;
10659 do {
10660 HASH *hash= sph->get_priv_hash();
10661 for (counter= 0, revoked= 0 ; counter < hash->records ; )
10662 {
10663 const char *user,*host;
10664 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
10665 user= safe_str(grant_proc->user);
10666 host= safe_str(grant_proc->host.hostname);
10667
10668 if (!strcmp(lex_user->user.str, user) &&
10669 !strcmp(lex_user->host.str, host))
10670 {
10671 if (replace_routine_table(thd, grant_proc,
10672 tables->procs_priv_table().table(),
10673 *lex_user,
10674 grant_proc->db, grant_proc->tname,
10675 sph, ~(ulong)0, 1) == 0)
10676 {
10677 revoked= 1;
10678 continue;
10679 }
10680 rc= true; // Something went wrong
10681 }
10682 counter++;
10683 }
10684 } while (revoked);
10685 return rc;
10686 }
10687
10688
10689 /*
10690 Revoke all privileges from a list of users.
10691
10692 SYNOPSIS
10693 mysql_revoke_all()
10694 thd The current thread.
10695 list The users to revoke all privileges from.
10696
10697 RETURN
10698 > 0 Error. Error message already sent.
10699 0 OK.
10700 < 0 Error. Error message not yet sent.
10701 */
10702
mysql_revoke_all(THD * thd,List<LEX_USER> & list)10703 bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
10704 {
10705 uint counter, revoked;
10706 int result;
10707 ACL_DB *acl_db;
10708 DBUG_ENTER("mysql_revoke_all");
10709
10710 Grant_tables tables(Table_user | Table_db |
10711 Table_tables_priv | Table_columns_priv |
10712 Table_procs_priv | Table_proxies_priv |
10713 Table_roles_mapping, TL_WRITE);
10714 if ((result= tables.open_and_lock(thd)))
10715 DBUG_RETURN(result != 1);
10716
10717 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
10718
10719 mysql_rwlock_wrlock(&LOCK_grant);
10720 mysql_mutex_lock(&acl_cache->lock);
10721
10722 LEX_USER *lex_user, *tmp_lex_user;
10723 List_iterator <LEX_USER> user_list(list);
10724 while ((tmp_lex_user= user_list++))
10725 {
10726 if (!(lex_user= get_current_user(thd, tmp_lex_user, false)))
10727 {
10728 result= -1;
10729 continue;
10730 }
10731
10732 /* This is not a role and the user could not be found */
10733 if (!lex_user->is_role() &&
10734 !find_user_exact(lex_user->host.str, lex_user->user.str))
10735 {
10736 result= -1;
10737 continue;
10738 }
10739
10740 if (replace_user_table(thd, tables.user_table(), *lex_user,
10741 ~(ulong)0, 1, 0, 0))
10742 {
10743 result= -1;
10744 continue;
10745 }
10746
10747 /* Remove db access privileges */
10748 /*
10749 Because acl_dbs and column_priv_hash shrink and may re-order
10750 as privileges are removed, removal occurs in a repeated loop
10751 until no more privileges are revoked.
10752 */
10753 do
10754 {
10755 for (counter= 0, revoked= 0 ; counter < acl_dbs.elements() ; )
10756 {
10757 const char *user,*host;
10758
10759 acl_db=&acl_dbs.at(counter);
10760
10761 user= safe_str(acl_db->user);
10762 host= safe_str(acl_db->host.hostname);
10763
10764 if (!strcmp(lex_user->user.str, user) &&
10765 !strcmp(lex_user->host.str, host))
10766 {
10767 /* TODO(cvicentiu) refactor replace_db_table to use
10768 Db_table instead of TABLE directly. */
10769 if (!replace_db_table(tables.db_table().table(), acl_db->db, *lex_user,
10770 ~(ulong)0, 1))
10771 {
10772 /*
10773 Don't increment counter as replace_db_table deleted the
10774 current element in acl_dbs.
10775 */
10776 revoked= 1;
10777 continue;
10778 }
10779 result= -1; // Something went wrong
10780 }
10781 counter++;
10782 }
10783 } while (revoked);
10784
10785 /* Remove column access */
10786 do
10787 {
10788 for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
10789 {
10790 const char *user,*host;
10791 GRANT_TABLE *grant_table=
10792 (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
10793 user= safe_str(grant_table->user);
10794 host= safe_str(grant_table->host.hostname);
10795
10796 if (!strcmp(lex_user->user.str,user) &&
10797 !strcmp(lex_user->host.str, host))
10798 {
10799 /* TODO(cvicentiu) refactor replace_db_table to use
10800 Db_table instead of TABLE directly. */
10801 if (replace_table_table(thd, grant_table,
10802 tables.tables_priv_table().table(),
10803 *lex_user, grant_table->db,
10804 grant_table->tname, ~(ulong)0, 0, 1))
10805 {
10806 result= -1;
10807 }
10808 else
10809 {
10810 if (!grant_table->cols)
10811 {
10812 revoked= 1;
10813 continue;
10814 }
10815 List<LEX_COLUMN> columns;
10816 /* TODO(cvicentiu) refactor replace_db_table to use
10817 Db_table instead of TABLE directly. */
10818 if (!replace_column_table(grant_table,
10819 tables.columns_priv_table().table(),
10820 *lex_user, columns, grant_table->db,
10821 grant_table->tname, ~(ulong)0, 1))
10822 {
10823 revoked= 1;
10824 continue;
10825 }
10826 result= -1;
10827 }
10828 }
10829 counter++;
10830 }
10831 } while (revoked);
10832
10833 /* Remove procedure access */
10834 if (mysql_revoke_sp_privs(thd, &tables, &sp_handler_function, lex_user) ||
10835 mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user) ||
10836 mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_spec, lex_user) ||
10837 mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_body, lex_user))
10838 result= -1;
10839
10840 ACL_USER_BASE *user_or_role;
10841 /* remove role grants */
10842 if (lex_user->is_role())
10843 {
10844 /* this can not fail due to get_current_user already having searched for it */
10845 user_or_role= find_acl_role(lex_user->user.str);
10846 }
10847 else
10848 {
10849 user_or_role= find_user_exact(lex_user->host.str, lex_user->user.str);
10850 }
10851 /*
10852 Find every role grant pair matching the role_grants array and remove it,
10853 both from the acl_roles_mappings and the roles_mapping table
10854 */
10855 for (counter= 0; counter < user_or_role->role_grants.elements; counter++)
10856 {
10857 ACL_ROLE *role_grant= *dynamic_element(&user_or_role->role_grants,
10858 counter, ACL_ROLE**);
10859 ROLE_GRANT_PAIR *pair = find_role_grant_pair(&lex_user->user,
10860 &lex_user->host,
10861 &role_grant->user);
10862 /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
10863 Roles_mapping_table instead of TABLE directly. */
10864 if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
10865 &lex_user->user, &lex_user->host,
10866 &role_grant->user, false, pair, true))
10867 {
10868 result= -1; //Something went wrong
10869 }
10870 update_role_mapping(&lex_user->user, &lex_user->host,
10871 &role_grant->user, false, pair, true);
10872 /*
10873 Delete from the parent_grantee array of the roles granted,
10874 the entry pointing to this user_or_role
10875 */
10876 remove_ptr_from_dynarray(&role_grant->parent_grantee, user_or_role);
10877 }
10878 /* TODO
10879 How to handle an error in the replace_roles_mapping_table, in
10880 regards to the privileges held in memory
10881 */
10882
10883 /* Finally, clear the role_grants array */
10884 if (counter == user_or_role->role_grants.elements)
10885 {
10886 reset_dynamic(&user_or_role->role_grants);
10887 }
10888 /*
10889 If we are revoking from a role, we need to update all the parent grantees
10890 */
10891 if (lex_user->is_role())
10892 {
10893 propagate_role_grants((ACL_ROLE *)user_or_role, PRIVS_TO_MERGE::ALL);
10894 }
10895 }
10896
10897 mysql_mutex_unlock(&acl_cache->lock);
10898
10899 if (result)
10900 my_message(ER_REVOKE_GRANTS, ER_THD(thd, ER_REVOKE_GRANTS), MYF(0));
10901
10902 result= result |
10903 write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10904
10905 mysql_rwlock_unlock(&LOCK_grant);
10906
10907 DBUG_RETURN(result);
10908 }
10909
10910
10911
10912
10913 /**
10914 If the defining user for a routine does not exist, then the ACL lookup
10915 code should raise two errors which we should intercept. We convert the more
10916 descriptive error into a warning, and consume the other.
10917
10918 If any other errors are raised, then we set a flag that should indicate
10919 that there was some failure we should complain at a higher level.
10920 */
10921 class Silence_routine_definer_errors : public Internal_error_handler
10922 {
10923 public:
Silence_routine_definer_errors()10924 Silence_routine_definer_errors()
10925 : is_grave(FALSE)
10926 {}
10927
~Silence_routine_definer_errors()10928 virtual ~Silence_routine_definer_errors()
10929 {}
10930
10931 virtual bool handle_condition(THD *thd,
10932 uint sql_errno,
10933 const char* sqlstate,
10934 Sql_condition::enum_warning_level *level,
10935 const char* msg,
10936 Sql_condition ** cond_hdl);
10937
has_errors()10938 bool has_errors() { return is_grave; }
10939
10940 private:
10941 bool is_grave;
10942 };
10943
10944 bool
handle_condition(THD * thd,uint sql_errno,const char *,Sql_condition::enum_warning_level * level,const char * msg,Sql_condition ** cond_hdl)10945 Silence_routine_definer_errors::handle_condition(
10946 THD *thd,
10947 uint sql_errno,
10948 const char*,
10949 Sql_condition::enum_warning_level *level,
10950 const char* msg,
10951 Sql_condition ** cond_hdl)
10952 {
10953 *cond_hdl= NULL;
10954 if (*level == Sql_condition::WARN_LEVEL_ERROR)
10955 {
10956 switch (sql_errno)
10957 {
10958 case ER_NONEXISTING_PROC_GRANT:
10959 /* Convert the error into a warning. */
10960 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
10961 sql_errno, msg);
10962 return TRUE;
10963 default:
10964 is_grave= TRUE;
10965 }
10966 }
10967
10968 return FALSE;
10969 }
10970
10971
10972 /**
10973 Revoke privileges for all users on a stored procedure. Use an error handler
10974 that converts errors about missing grants into warnings.
10975
10976 @param
10977 thd The current thread.
10978 @param
10979 db DB of the stored procedure
10980 @param
10981 name Name of the stored procedure
10982
10983 @retval
10984 0 OK.
10985 @retval
10986 < 0 Error. Error message not yet sent.
10987 */
10988
sp_revoke_privileges(THD * thd,const char * sp_db,const char * sp_name,const Sp_handler * sph)10989 bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
10990 const Sp_handler *sph)
10991 {
10992 uint counter, revoked;
10993 int result;
10994 HASH *hash= sph->get_priv_hash();
10995 Silence_routine_definer_errors error_handler;
10996 DBUG_ENTER("sp_revoke_privileges");
10997
10998 Grant_tables tables(Table_user | Table_db |
10999 Table_tables_priv | Table_columns_priv |
11000 Table_procs_priv | Table_proxies_priv |
11001 Table_roles_mapping, TL_WRITE);
11002 if ((result= tables.open_and_lock(thd)))
11003 DBUG_RETURN(result != 1);
11004
11005 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
11006
11007 /* Be sure to pop this before exiting this scope! */
11008 thd->push_internal_handler(&error_handler);
11009
11010 mysql_rwlock_wrlock(&LOCK_grant);
11011 mysql_mutex_lock(&acl_cache->lock);
11012
11013 /* Remove procedure access */
11014 do
11015 {
11016 for (counter= 0, revoked= 0 ; counter < hash->records ; )
11017 {
11018 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
11019 if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
11020 !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
11021 {
11022 LEX_USER lex_user;
11023 lex_user.user.str= grant_proc->user;
11024 lex_user.user.length= strlen(grant_proc->user);
11025 lex_user.host.str= safe_str(grant_proc->host.hostname);
11026 lex_user.host.length= strlen(lex_user.host.str);
11027 if (replace_routine_table(thd, grant_proc,
11028 tables.procs_priv_table().table(), lex_user,
11029 grant_proc->db, grant_proc->tname,
11030 sph, ~(ulong)0, 1) == 0)
11031 {
11032 revoked= 1;
11033 continue;
11034 }
11035 }
11036 counter++;
11037 }
11038 } while (revoked);
11039
11040 mysql_mutex_unlock(&acl_cache->lock);
11041 mysql_rwlock_unlock(&LOCK_grant);
11042
11043 thd->pop_internal_handler();
11044
11045 DBUG_RETURN(error_handler.has_errors());
11046 }
11047
11048
11049 /**
11050 Grant EXECUTE,ALTER privilege for a stored procedure
11051
11052 @param thd The current thread.
11053 @param sp_db
11054 @param sp_name
11055 @param sph
11056
11057 @return
11058 @retval FALSE Success
11059 @retval TRUE An error occurred. Error message not yet sent.
11060 */
11061
sp_grant_privileges(THD * thd,const char * sp_db,const char * sp_name,const Sp_handler * sph)11062 bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
11063 const Sp_handler *sph)
11064 {
11065 Security_context *sctx= thd->security_ctx;
11066 LEX_USER *combo;
11067 TABLE_LIST tables[1];
11068 List<LEX_USER> user_list;
11069 bool result;
11070 ACL_USER *au;
11071 Dummy_error_handler error_handler;
11072 DBUG_ENTER("sp_grant_privileges");
11073
11074 if (!(combo=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
11075 DBUG_RETURN(TRUE);
11076
11077 combo->user.str= (char *) sctx->priv_user;
11078
11079 mysql_mutex_lock(&acl_cache->lock);
11080 if ((au= find_user_exact(combo->host.str= (char *) sctx->priv_host,
11081 combo->user.str)))
11082 goto found_acl;
11083
11084 mysql_mutex_unlock(&acl_cache->lock);
11085 DBUG_RETURN(TRUE);
11086
11087 found_acl:
11088 mysql_mutex_unlock(&acl_cache->lock);
11089
11090 bzero((char*)tables, sizeof(TABLE_LIST));
11091 user_list.empty();
11092
11093 tables->db.str= sp_db;
11094 tables->db.length= sp_db ? strlen(sp_db) : 0;
11095 tables->table_name.str= tables->alias.str= sp_name;
11096 tables->table_name.length= tables->alias.length= sp_name ? strlen(sp_name) : 0;
11097
11098 thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str));
11099 thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str));
11100
11101 combo->reset_auth();
11102
11103 if(au)
11104 {
11105 combo->plugin= au->plugin;
11106 combo->auth= au->auth_string;
11107 }
11108
11109 if (user_list.push_back(combo, thd->mem_root))
11110 DBUG_RETURN(TRUE);
11111
11112 thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
11113 thd->lex->ssl_cipher= thd->lex->x509_subject= thd->lex->x509_issuer= 0;
11114 bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
11115
11116 /*
11117 Only care about whether the operation failed or succeeded
11118 as all errors will be handled later.
11119 */
11120 thd->push_internal_handler(&error_handler);
11121 result= mysql_routine_grant(thd, tables, sph, user_list,
11122 DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
11123 thd->pop_internal_handler();
11124 DBUG_RETURN(result);
11125 }
11126
11127
11128 /**
11129 Validate if a user can proxy as another user
11130
11131 @thd current thread
11132 @param user the logged in user (proxy user)
11133 @param authenticated_as the effective user a plugin is trying to
11134 impersonate as (proxied user)
11135 @return proxy user definition
11136 @retval NULL proxy user definition not found or not applicable
11137 @retval non-null the proxy user data
11138 */
11139
11140 static ACL_PROXY_USER *
acl_find_proxy_user(const char * user,const char * host,const char * ip,const char * authenticated_as,bool * proxy_used)11141 acl_find_proxy_user(const char *user, const char *host, const char *ip,
11142 const char *authenticated_as, bool *proxy_used)
11143 {
11144 uint i;
11145 /* if the proxied and proxy user are the same return OK */
11146 DBUG_ENTER("acl_find_proxy_user");
11147 DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
11148 user, host, ip, authenticated_as));
11149
11150 if (!strcmp(authenticated_as, user))
11151 {
11152 DBUG_PRINT ("info", ("user is the same as authenticated_as"));
11153 DBUG_RETURN (NULL);
11154 }
11155
11156 *proxy_used= TRUE;
11157 for (i=0; i < acl_proxy_users.elements; i++)
11158 {
11159 ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11160 ACL_PROXY_USER *);
11161 if (proxy->matches(host, user, ip, authenticated_as))
11162 DBUG_RETURN(proxy);
11163 }
11164
11165 DBUG_RETURN(NULL);
11166 }
11167
11168
11169 bool
acl_check_proxy_grant_access(THD * thd,const char * host,const char * user,bool with_grant)11170 acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
11171 bool with_grant)
11172 {
11173 DBUG_ENTER("acl_check_proxy_grant_access");
11174 DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
11175 (int) with_grant));
11176 if (!initialized)
11177 {
11178 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
11179 DBUG_RETURN(1);
11180 }
11181
11182 /* replication slave thread can do anything */
11183 if (thd->slave_thread)
11184 {
11185 DBUG_PRINT("info", ("replication slave"));
11186 DBUG_RETURN(FALSE);
11187 }
11188
11189 /*
11190 one can grant proxy for self to others.
11191 Security context in THD contains two pairs of (user,host):
11192 1. (user,host) pair referring to inbound connection.
11193 2. (priv_user,priv_host) pair obtained from mysql.user table after doing
11194 authentication of incoming connection.
11195 Privileges should be checked wrt (priv_user, priv_host) tuple, because
11196 (user,host) pair obtained from inbound connection may have different
11197 values than what is actually stored in mysql.user table and while granting
11198 or revoking proxy privilege, user is expected to provide entries mentioned
11199 in mysql.user table.
11200 */
11201 if (!strcmp(thd->security_ctx->priv_user, user) &&
11202 !my_strcasecmp(system_charset_info, host,
11203 thd->security_ctx->priv_host))
11204 {
11205 DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
11206 thd->security_ctx->priv_user, user,
11207 host, thd->security_ctx->priv_host));
11208 DBUG_RETURN(FALSE);
11209 }
11210
11211 mysql_mutex_lock(&acl_cache->lock);
11212
11213 /* check for matching WITH PROXY rights */
11214 for (uint i=0; i < acl_proxy_users.elements; i++)
11215 {
11216 ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11217 ACL_PROXY_USER *);
11218 if (proxy->matches(thd->security_ctx->host,
11219 thd->security_ctx->user,
11220 thd->security_ctx->ip,
11221 user) &&
11222 proxy->get_with_grant())
11223 {
11224 DBUG_PRINT("info", ("found"));
11225 mysql_mutex_unlock(&acl_cache->lock);
11226 DBUG_RETURN(FALSE);
11227 }
11228 }
11229
11230 mysql_mutex_unlock(&acl_cache->lock);
11231 my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
11232 thd->security_ctx->user,
11233 thd->security_ctx->host_or_ip);
11234 DBUG_RETURN(TRUE);
11235 }
11236
11237
11238 static bool
show_proxy_grants(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)11239 show_proxy_grants(THD *thd, const char *username, const char *hostname,
11240 char *buff, size_t buffsize)
11241 {
11242 Protocol *protocol= thd->protocol;
11243 int error= 0;
11244
11245 for (uint i=0; i < acl_proxy_users.elements; i++)
11246 {
11247 ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11248 ACL_PROXY_USER *);
11249 if (proxy->granted_on(hostname, username))
11250 {
11251 String global(buff, buffsize, system_charset_info);
11252 global.length(0);
11253 proxy->print_grant(&global);
11254 protocol->prepare_for_resend();
11255 protocol->store(global.ptr(), global.length(), global.charset());
11256 if (protocol->write())
11257 {
11258 error= -1;
11259 break;
11260 }
11261 }
11262 }
11263 return error;
11264 }
11265
enabled_roles_insert(ACL_USER_BASE * role,void * context_data)11266 static int enabled_roles_insert(ACL_USER_BASE *role, void *context_data)
11267 {
11268 TABLE *table= (TABLE*) context_data;
11269 DBUG_ASSERT(role->flags & IS_ROLE);
11270
11271 restore_record(table, s->default_values);
11272 table->field[0]->set_notnull();
11273 table->field[0]->store(role->user.str, role->user.length,
11274 system_charset_info);
11275 if (schema_table_store_record(table->in_use, table))
11276 return -1;
11277 return 0;
11278 }
11279
11280 struct APPLICABLE_ROLES_DATA
11281 {
11282 TABLE *table;
11283 const LEX_CSTRING host;
11284 const LEX_CSTRING user_and_host;
11285 ACL_USER *user;
11286 };
11287
11288 static int
applicable_roles_insert(ACL_USER_BASE * grantee,ACL_ROLE * role,void * ptr)11289 applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr)
11290 {
11291 APPLICABLE_ROLES_DATA *data= (APPLICABLE_ROLES_DATA *)ptr;
11292 CHARSET_INFO *cs= system_charset_info;
11293 TABLE *table= data->table;
11294 bool is_role= grantee != data->user;
11295 const LEX_CSTRING *user_and_host= is_role ? &grantee->user
11296 : &data->user_and_host;
11297 const LEX_CSTRING *host= is_role ? &empty_clex_str : &data->host;
11298
11299 restore_record(table, s->default_values);
11300 table->field[0]->store(user_and_host->str, user_and_host->length, cs);
11301 table->field[1]->store(role->user.str, role->user.length, cs);
11302
11303 ROLE_GRANT_PAIR *pair=
11304 find_role_grant_pair(&grantee->user, host, &role->user);
11305 DBUG_ASSERT(pair);
11306
11307 if (pair->with_admin)
11308 table->field[2]->store(STRING_WITH_LEN("YES"), cs);
11309 else
11310 table->field[2]->store(STRING_WITH_LEN("NO"), cs);
11311
11312 /* Default role is only valid when looking at a role granted to a user. */
11313 if (!is_role)
11314 {
11315 if (data->user->default_rolename.length &&
11316 lex_string_eq(&data->user->default_rolename, &role->user))
11317 table->field[3]->store(STRING_WITH_LEN("YES"), cs);
11318 else
11319 table->field[3]->store(STRING_WITH_LEN("NO"), cs);
11320 table->field[3]->set_notnull();
11321 }
11322
11323 if (schema_table_store_record(table->in_use, table))
11324 return -1;
11325 return 0;
11326 }
11327
11328 /**
11329 Hash iterate function to count the number of total column privileges granted.
11330 */
count_column_grants(void * grant_table,void * current_count)11331 static my_bool count_column_grants(void *grant_table,
11332 void *current_count)
11333 {
11334 HASH hash_columns = ((GRANT_TABLE *)grant_table)->hash_columns;
11335 *(ulong *)current_count+= hash_columns.records;
11336 return 0;
11337 }
11338
11339 /**
11340 SHOW function that computes the number of column grants.
11341
11342 This must be performed under the mutex in order to make sure the
11343 iteration does not fail.
11344 */
show_column_grants(THD * thd,SHOW_VAR * var,char * buff,enum enum_var_type scope)11345 static int show_column_grants(THD *thd, SHOW_VAR *var, char *buff,
11346 enum enum_var_type scope)
11347 {
11348 var->type= SHOW_ULONG;
11349 var->value= buff;
11350 *(ulong *)buff= 0;
11351 if (initialized)
11352 {
11353 mysql_rwlock_rdlock(&LOCK_grant);
11354 mysql_mutex_lock(&acl_cache->lock);
11355 my_hash_iterate(&column_priv_hash, count_column_grants, buff);
11356 mysql_mutex_unlock(&acl_cache->lock);
11357 mysql_rwlock_unlock(&LOCK_grant);
11358 }
11359 return 0;
11360 }
11361
show_database_grants(THD * thd,SHOW_VAR * var,char * buff,enum enum_var_type scope)11362 static int show_database_grants(THD *thd, SHOW_VAR *var, char *buff,
11363 enum enum_var_type scope)
11364 {
11365 var->type= SHOW_UINT;
11366 var->value= buff;
11367 *(uint *)buff= uint(acl_dbs.elements());
11368 return 0;
11369 }
11370
11371 #else
check_grant(THD *,ulong,TABLE_LIST *,bool,uint,bool)11372 bool check_grant(THD *, ulong, TABLE_LIST *, bool, uint, bool)
11373 {
11374 return 0;
11375 }
11376 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
11377
11378 SHOW_VAR acl_statistics[] = {
11379 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11380 {"column_grants", (char*)show_column_grants, SHOW_SIMPLE_FUNC},
11381 {"database_grants", (char*)show_database_grants, SHOW_SIMPLE_FUNC},
11382 {"function_grants", (char*)&func_priv_hash.records, SHOW_ULONG},
11383 {"procedure_grants", (char*)&proc_priv_hash.records, SHOW_ULONG},
11384 {"package_spec_grants", (char*)&package_spec_priv_hash.records, SHOW_ULONG},
11385 {"package_body_grants", (char*)&package_body_priv_hash.records, SHOW_ULONG},
11386 {"proxy_users", (char*)&acl_proxy_users.elements, SHOW_UINT},
11387 {"role_grants", (char*)&acl_roles_mappings.records, SHOW_ULONG},
11388 {"roles", (char*)&acl_roles.records, SHOW_ULONG},
11389 {"table_grants", (char*)&column_priv_hash.records, SHOW_ULONG},
11390 {"users", (char*)&acl_users.elements, SHOW_UINT},
11391 #endif
11392 {NullS, NullS, SHOW_LONG},
11393 };
11394
11395 /* Check if a role is granted to a user/role. We traverse the role graph
11396 and return true if we find a match.
11397
11398 hostname == NULL means we are looking for a role as a starting point,
11399 otherwise a user.
11400 */
check_role_is_granted(const char * username,const char * hostname,const char * rolename)11401 bool check_role_is_granted(const char *username,
11402 const char *hostname,
11403 const char *rolename)
11404 {
11405 DBUG_ENTER("check_role_is_granted");
11406 bool result= false;
11407 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11408 ACL_USER_BASE *root;
11409 mysql_mutex_lock(&acl_cache->lock);
11410 if (hostname)
11411 root= find_user_exact(username, hostname);
11412 else
11413 root= find_acl_role(username);
11414
11415 LEX_CSTRING role_lex;
11416 role_lex.str= rolename;
11417 role_lex.length= strlen(rolename);
11418
11419 if (root && /* No grantee, nothing to search. */
11420 traverse_role_graph_down(root, &role_lex, check_role_is_granted_callback,
11421 NULL) == -1)
11422 {
11423 /* We have found the role during our search. */
11424 result= true;
11425 }
11426
11427 /* We haven't found the role or we had no initial grantee to start from. */
11428 mysql_mutex_unlock(&acl_cache->lock);
11429 #endif
11430 DBUG_RETURN(result);
11431 }
11432
fill_schema_enabled_roles(THD * thd,TABLE_LIST * tables,COND * cond)11433 int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond)
11434 {
11435 TABLE *table= tables->table;
11436 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11437 if (thd->security_ctx->priv_role[0])
11438 {
11439 mysql_rwlock_rdlock(&LOCK_grant);
11440 mysql_mutex_lock(&acl_cache->lock);
11441 ACL_ROLE *acl_role= find_acl_role(thd->security_ctx->priv_role);
11442 if (acl_role)
11443 traverse_role_graph_down(acl_role, table, enabled_roles_insert, NULL);
11444 mysql_mutex_unlock(&acl_cache->lock);
11445 mysql_rwlock_unlock(&LOCK_grant);
11446 if (acl_role)
11447 return 0;
11448 }
11449 #endif
11450
11451 restore_record(table, s->default_values);
11452 table->field[0]->set_null();
11453 return schema_table_store_record(table->in_use, table);
11454 }
11455
11456
11457 /*
11458 This shows all roles granted to current user
11459 and recursively all roles granted to those roles
11460 */
fill_schema_applicable_roles(THD * thd,TABLE_LIST * tables,COND * cond)11461 int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond)
11462 {
11463 int res= 0;
11464 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11465 if (initialized)
11466 {
11467 TABLE *table= tables->table;
11468 Security_context *sctx= thd->security_ctx;
11469 mysql_rwlock_rdlock(&LOCK_grant);
11470 mysql_mutex_lock(&acl_cache->lock);
11471 ACL_USER *user= find_user_exact(sctx->priv_host, sctx->priv_user);
11472 if (user)
11473 {
11474 char buff[USER_HOST_BUFF_SIZE+10];
11475 DBUG_ASSERT(user->user.length + user->hostname_length +2 < sizeof(buff));
11476 char *end= strxmov(buff, user->user.str, "@", user->host.hostname, NULL);
11477 APPLICABLE_ROLES_DATA data= { table,
11478 { user->host.hostname, user->hostname_length },
11479 { buff, (size_t)(end - buff) }, user
11480 };
11481
11482 res= traverse_role_graph_down(user, &data, 0, applicable_roles_insert);
11483 }
11484
11485 mysql_mutex_unlock(&acl_cache->lock);
11486 mysql_rwlock_unlock(&LOCK_grant);
11487 }
11488 #endif
11489
11490 return res;
11491 }
11492
11493
wild_case_compare(CHARSET_INFO * cs,const char * str,const char * wildstr)11494 int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
11495 {
11496 int flag;
11497 DBUG_ENTER("wild_case_compare");
11498 DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr));
11499 while (*wildstr)
11500 {
11501 while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
11502 {
11503 if (*wildstr == wild_prefix && wildstr[1])
11504 wildstr++;
11505 if (my_toupper(cs, *wildstr++) !=
11506 my_toupper(cs, *str++)) DBUG_RETURN(1);
11507 }
11508 if (! *wildstr ) DBUG_RETURN (*str != 0);
11509 if (*wildstr++ == wild_one)
11510 {
11511 if (! *str++) DBUG_RETURN (1); /* One char; skip */
11512 }
11513 else
11514 { /* Found '*' */
11515 if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */
11516 flag=(*wildstr != wild_many && *wildstr != wild_one);
11517 do
11518 {
11519 if (flag)
11520 {
11521 char cmp;
11522 if ((cmp= *wildstr) == wild_prefix && wildstr[1])
11523 cmp=wildstr[1];
11524 cmp=my_toupper(cs, cmp);
11525 while (*str && my_toupper(cs, *str) != cmp)
11526 str++;
11527 if (!*str) DBUG_RETURN (1);
11528 }
11529 if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
11530 } while (*str++);
11531 DBUG_RETURN(1);
11532 }
11533 }
11534 DBUG_RETURN (*str != '\0');
11535 }
11536
11537
11538 #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)11539 static bool update_schema_privilege(THD *thd, TABLE *table, const char *buff,
11540 const char* db, const char* t_name,
11541 const char* column, uint col_length,
11542 const char *priv, uint priv_length,
11543 const char* is_grantable)
11544 {
11545 int i= 2;
11546 CHARSET_INFO *cs= system_charset_info;
11547 restore_record(table, s->default_values);
11548 table->field[0]->store(buff, (uint) strlen(buff), cs);
11549 table->field[1]->store(STRING_WITH_LEN("def"), cs);
11550 if (db)
11551 table->field[i++]->store(db, (uint) strlen(db), cs);
11552 if (t_name)
11553 table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
11554 if (column)
11555 table->field[i++]->store(column, col_length, cs);
11556 table->field[i++]->store(priv, priv_length, cs);
11557 table->field[i]->store(is_grantable, strlen(is_grantable), cs);
11558 return schema_table_store_record(thd, table);
11559 }
11560 #endif
11561
11562
11563 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11564 class Grantee_str
11565 {
11566 char m_buff[USER_HOST_BUFF_SIZE + 6 /* 4 quotes, @, '\0' */];
11567 public:
Grantee_str(const char * user,const char * host)11568 Grantee_str(const char *user, const char *host)
11569 {
11570 DBUG_ASSERT(strlen(user) + strlen(host) + 6 < sizeof(m_buff));
11571 strxmov(m_buff, "'", user, "'@'", host, "'", NullS);
11572 }
operator const char*() const11573 operator const char *() const { return m_buff; }
11574 };
11575 #endif
11576
11577
fill_schema_user_privileges(THD * thd,TABLE_LIST * tables,COND * cond)11578 int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11579 {
11580 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11581 int error= 0;
11582 uint counter;
11583 ACL_USER *acl_user;
11584 ulong want_access;
11585 TABLE *table= tables->table;
11586 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11587 NULL, NULL, 1, 1);
11588 char *curr_host= thd->security_ctx->priv_host_name();
11589 DBUG_ENTER("fill_schema_user_privileges");
11590
11591 if (!initialized)
11592 DBUG_RETURN(0);
11593 mysql_mutex_lock(&acl_cache->lock);
11594
11595 for (counter=0 ; counter < acl_users.elements ; counter++)
11596 {
11597 const char *user,*host, *is_grantable="YES";
11598 acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
11599 user= safe_str(acl_user->user.str);
11600 host= safe_str(acl_user->host.hostname);
11601
11602 if (no_global_access &&
11603 (strcmp(thd->security_ctx->priv_user, user) ||
11604 my_strcasecmp(system_charset_info, curr_host, host)))
11605 continue;
11606
11607 want_access= acl_user->access;
11608 if (!(want_access & GRANT_ACL))
11609 is_grantable= "NO";
11610
11611 Grantee_str grantee(user, host);
11612 if (!(want_access & ~GRANT_ACL))
11613 {
11614 if (update_schema_privilege(thd, table, grantee, 0, 0, 0, 0,
11615 STRING_WITH_LEN("USAGE"), is_grantable))
11616 {
11617 error= 1;
11618 goto err;
11619 }
11620 }
11621 else
11622 {
11623 uint priv_id;
11624 ulong j,test_access= want_access & ~GRANT_ACL;
11625 for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
11626 {
11627 if (test_access & j)
11628 {
11629 if (update_schema_privilege(thd, table, grantee, 0, 0, 0, 0,
11630 command_array[priv_id],
11631 command_lengths[priv_id], is_grantable))
11632 {
11633 error= 1;
11634 goto err;
11635 }
11636 }
11637 }
11638 }
11639 }
11640 err:
11641 mysql_mutex_unlock(&acl_cache->lock);
11642
11643 DBUG_RETURN(error);
11644 #else
11645 return(0);
11646 #endif
11647 }
11648
11649
fill_schema_schema_privileges(THD * thd,TABLE_LIST * tables,COND * cond)11650 int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11651 {
11652 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11653 int error= 0;
11654 uint counter;
11655 ACL_DB *acl_db;
11656 ulong want_access;
11657 TABLE *table= tables->table;
11658 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11659 NULL, NULL, 1, 1);
11660 char *curr_host= thd->security_ctx->priv_host_name();
11661 DBUG_ENTER("fill_schema_schema_privileges");
11662
11663 if (!initialized)
11664 DBUG_RETURN(0);
11665 mysql_mutex_lock(&acl_cache->lock);
11666
11667 for (counter=0 ; counter < acl_dbs.elements() ; counter++)
11668 {
11669 const char *user, *host, *is_grantable="YES";
11670
11671 acl_db=&acl_dbs.at(counter);
11672 user= safe_str(acl_db->user);
11673 host= safe_str(acl_db->host.hostname);
11674
11675 if (no_global_access &&
11676 (strcmp(thd->security_ctx->priv_user, user) ||
11677 my_strcasecmp(system_charset_info, curr_host, host)))
11678 continue;
11679
11680 want_access=acl_db->access;
11681 if (want_access)
11682 {
11683 if (!(want_access & GRANT_ACL))
11684 {
11685 is_grantable= "NO";
11686 }
11687 Grantee_str grantee(user, host);
11688 if (!(want_access & ~GRANT_ACL))
11689 {
11690 if (update_schema_privilege(thd, table, grantee, acl_db->db, 0, 0,
11691 0, STRING_WITH_LEN("USAGE"), is_grantable))
11692 {
11693 error= 1;
11694 goto err;
11695 }
11696 }
11697 else
11698 {
11699 int cnt;
11700 ulong j,test_access= want_access & ~GRANT_ACL;
11701 for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
11702 if (test_access & j)
11703 {
11704 if (update_schema_privilege(thd, table,
11705 grantee, acl_db->db, 0, 0, 0,
11706 command_array[cnt], command_lengths[cnt],
11707 is_grantable))
11708 {
11709 error= 1;
11710 goto err;
11711 }
11712 }
11713 }
11714 }
11715 }
11716 err:
11717 mysql_mutex_unlock(&acl_cache->lock);
11718
11719 DBUG_RETURN(error);
11720 #else
11721 return (0);
11722 #endif
11723 }
11724
11725
fill_schema_table_privileges(THD * thd,TABLE_LIST * tables,COND * cond)11726 int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11727 {
11728 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11729 int error= 0;
11730 uint index;
11731 TABLE *table= tables->table;
11732 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11733 NULL, NULL, 1, 1);
11734 char *curr_host= thd->security_ctx->priv_host_name();
11735 DBUG_ENTER("fill_schema_table_privileges");
11736
11737 mysql_rwlock_rdlock(&LOCK_grant);
11738
11739 for (index=0 ; index < column_priv_hash.records ; index++)
11740 {
11741 const char *user, *host, *is_grantable= "YES";
11742 GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
11743 index);
11744 user= safe_str(grant_table->user);
11745 host= safe_str(grant_table->host.hostname);
11746
11747 if (no_global_access &&
11748 (strcmp(thd->security_ctx->priv_user, user) ||
11749 my_strcasecmp(system_charset_info, curr_host, host)))
11750 continue;
11751
11752 ulong table_access= grant_table->privs;
11753 if (table_access)
11754 {
11755 ulong test_access= table_access & ~GRANT_ACL;
11756 /*
11757 We should skip 'usage' privilege on table if
11758 we have any privileges on column(s) of this table
11759 */
11760 if (!test_access && grant_table->cols)
11761 continue;
11762 if (!(table_access & GRANT_ACL))
11763 is_grantable= "NO";
11764
11765 Grantee_str grantee(user, host);
11766 if (!test_access)
11767 {
11768 if (update_schema_privilege(thd, table,
11769 grantee, grant_table->db,
11770 grant_table->tname, 0, 0,
11771 STRING_WITH_LEN("USAGE"), is_grantable))
11772 {
11773 error= 1;
11774 goto err;
11775 }
11776 }
11777 else
11778 {
11779 ulong j;
11780 int cnt;
11781 for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
11782 {
11783 if (test_access & j)
11784 {
11785 if (update_schema_privilege(thd, table,
11786 grantee, grant_table->db,
11787 grant_table->tname, 0, 0,
11788 command_array[cnt],
11789 command_lengths[cnt], is_grantable))
11790 {
11791 error= 1;
11792 goto err;
11793 }
11794 }
11795 }
11796 }
11797 }
11798 }
11799 err:
11800 mysql_rwlock_unlock(&LOCK_grant);
11801
11802 DBUG_RETURN(error);
11803 #else
11804 return (0);
11805 #endif
11806 }
11807
11808
fill_schema_column_privileges(THD * thd,TABLE_LIST * tables,COND * cond)11809 int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11810 {
11811 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11812 int error= 0;
11813 uint index;
11814 TABLE *table= tables->table;
11815 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11816 NULL, NULL, 1, 1);
11817 char *curr_host= thd->security_ctx->priv_host_name();
11818 DBUG_ENTER("fill_schema_table_privileges");
11819
11820 mysql_rwlock_rdlock(&LOCK_grant);
11821
11822 for (index=0 ; index < column_priv_hash.records ; index++)
11823 {
11824 const char *user, *host, *is_grantable= "YES";
11825 GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
11826 index);
11827 user= safe_str(grant_table->user);
11828 host= safe_str(grant_table->host.hostname);
11829
11830 if (no_global_access &&
11831 (strcmp(thd->security_ctx->priv_user, user) ||
11832 my_strcasecmp(system_charset_info, curr_host, host)))
11833 continue;
11834
11835 ulong table_access= grant_table->cols;
11836 if (table_access != 0)
11837 {
11838 if (!(grant_table->privs & GRANT_ACL))
11839 is_grantable= "NO";
11840
11841 ulong test_access= table_access & ~GRANT_ACL;
11842 Grantee_str grantee(user, host);
11843 if (!test_access)
11844 continue;
11845 else
11846 {
11847 ulong j;
11848 int cnt;
11849 for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
11850 {
11851 if (test_access & j)
11852 {
11853 for (uint col_index=0 ;
11854 col_index < grant_table->hash_columns.records ;
11855 col_index++)
11856 {
11857 GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
11858 my_hash_element(&grant_table->hash_columns,col_index);
11859 if ((grant_column->rights & j) && (table_access & j))
11860 {
11861 if (update_schema_privilege(thd, table,
11862 grantee,
11863 grant_table->db,
11864 grant_table->tname,
11865 grant_column->column,
11866 grant_column->key_length,
11867 command_array[cnt],
11868 command_lengths[cnt], is_grantable))
11869 {
11870 error= 1;
11871 goto err;
11872 }
11873 }
11874 }
11875 }
11876 }
11877 }
11878 }
11879 }
11880 err:
11881 mysql_rwlock_unlock(&LOCK_grant);
11882
11883 DBUG_RETURN(error);
11884 #else
11885 return (0);
11886 #endif
11887 }
11888
11889
11890 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11891 /*
11892 fill effective privileges for table
11893
11894 SYNOPSIS
11895 fill_effective_table_privileges()
11896 thd thread handler
11897 grant grants table descriptor
11898 db db name
11899 table table name
11900 */
11901
fill_effective_table_privileges(THD * thd,GRANT_INFO * grant,const char * db,const char * table)11902 void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
11903 const char *db, const char *table)
11904 {
11905 Security_context *sctx= thd->security_ctx;
11906 DBUG_ENTER("fill_effective_table_privileges");
11907 DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
11908 sctx->priv_host, sctx->ip, sctx->priv_user, db, table));
11909 /* --skip-grants */
11910 if (!initialized)
11911 {
11912 DBUG_PRINT("info", ("skip grants"));
11913 grant->privilege= ~NO_ACCESS; // everything is allowed
11914 DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
11915 DBUG_VOID_RETURN;
11916 }
11917
11918 /* global privileges */
11919 grant->privilege= sctx->master_access;
11920
11921 if (!thd->db.str || strcmp(db, thd->db.str))
11922 {
11923 /* db privileges */
11924 grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
11925 /* db privileges for role */
11926 if (sctx->priv_role[0])
11927 grant->privilege|= acl_get("", "", sctx->priv_role, db, 0);
11928 }
11929 else
11930 {
11931 grant->privilege|= sctx->db_access;
11932 }
11933
11934 /* table privileges */
11935 mysql_rwlock_rdlock(&LOCK_grant);
11936 if (grant->version != grant_version)
11937 {
11938 grant->grant_table_user=
11939 table_hash_search(sctx->host, sctx->ip, db,
11940 sctx->priv_user,
11941 table, 0); /* purecov: inspected */
11942 grant->grant_table_role=
11943 sctx->priv_role[0] ? table_hash_search("", "", db,
11944 sctx->priv_role,
11945 table, TRUE) : NULL;
11946 grant->version= grant_version; /* purecov: inspected */
11947 }
11948 if (grant->grant_table_user != 0)
11949 {
11950 grant->privilege|= grant->grant_table_user->privs;
11951 }
11952 if (grant->grant_table_role != 0)
11953 {
11954 grant->privilege|= grant->grant_table_role->privs;
11955 }
11956 mysql_rwlock_unlock(&LOCK_grant);
11957
11958 DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
11959 DBUG_VOID_RETURN;
11960 }
11961
11962 #else /* NO_EMBEDDED_ACCESS_CHECKS */
11963
11964 /****************************************************************************
11965 Dummy wrappers when we don't have any access checks
11966 ****************************************************************************/
11967
check_routine_level_acl(THD * thd,const char * db,const char * name,const Sp_handler * sph)11968 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
11969 const Sp_handler *sph)
11970 {
11971 return FALSE;
11972 }
11973
11974 #endif
11975
11976 /**
11977 Return information about user or current user.
11978
11979 @param[in] thd thread handler
11980 @param[in] user user
11981 @param[in] lock whether &acl_cache->lock mutex needs to be locked
11982
11983 @return
11984 - On success, return a valid pointer to initialized
11985 LEX_USER, which contains user information.
11986 - On error, return 0.
11987 */
11988
get_current_user(THD * thd,LEX_USER * user,bool lock)11989 LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock)
11990 {
11991 if (user->user.str == current_user.str) // current_user
11992 return create_default_definer(thd, false);
11993
11994 if (user->user.str == current_role.str) // current_role
11995 return create_default_definer(thd, true);
11996
11997 if (user->host.str == NULL) // Possibly a role
11998 {
11999 // to be reexecution friendly we have to make a copy
12000 LEX_USER *dup= (LEX_USER*) thd->memdup(user, sizeof(*user));
12001 if (!dup)
12002 return 0;
12003
12004 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12005 if (has_auth(user, thd->lex))
12006 {
12007 dup->host= host_not_specified;
12008 return dup;
12009 }
12010
12011 if (is_invalid_role_name(user->user.str))
12012 return 0;
12013
12014 if (lock)
12015 mysql_mutex_lock(&acl_cache->lock);
12016 if (find_acl_role(dup->user.str))
12017 dup->host= empty_clex_str;
12018 else
12019 dup->host= host_not_specified;
12020 if (lock)
12021 mysql_mutex_unlock(&acl_cache->lock);
12022 #endif
12023
12024 return dup;
12025 }
12026
12027 return user;
12028 }
12029
12030 struct ACL_internal_schema_registry_entry
12031 {
12032 const LEX_CSTRING *m_name;
12033 const ACL_internal_schema_access *m_access;
12034 };
12035
12036 /**
12037 Internal schema registered.
12038 Currently, this is only:
12039 - performance_schema
12040 - information_schema,
12041 This can be reused later for:
12042 - mysql
12043 */
12044 static ACL_internal_schema_registry_entry registry_array[2];
12045 static uint m_registry_array_size= 0;
12046
12047 /**
12048 Add an internal schema to the registry.
12049 @param name the schema name
12050 @param access the schema ACL specific rules
12051 */
register_schema(const LEX_CSTRING * name,const ACL_internal_schema_access * access)12052 void ACL_internal_schema_registry::register_schema
12053 (const LEX_CSTRING *name, const ACL_internal_schema_access *access)
12054 {
12055 DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
12056
12057 /* Not thread safe, and does not need to be. */
12058 registry_array[m_registry_array_size].m_name= name;
12059 registry_array[m_registry_array_size].m_access= access;
12060 m_registry_array_size++;
12061 }
12062
12063 /**
12064 Search per internal schema ACL by name.
12065 @param name a schema name
12066 @return per schema rules, or NULL
12067 */
12068 const ACL_internal_schema_access *
lookup(const char * name)12069 ACL_internal_schema_registry::lookup(const char *name)
12070 {
12071 DBUG_ASSERT(name != NULL);
12072
12073 uint i;
12074
12075 for (i= 0; i<m_registry_array_size; i++)
12076 {
12077 if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
12078 name) == 0)
12079 return registry_array[i].m_access;
12080 }
12081 return NULL;
12082 }
12083
12084 /**
12085 Get a cached internal schema access.
12086 @param grant_internal_info the cache
12087 @param schema_name the name of the internal schema
12088 */
12089 const ACL_internal_schema_access *
get_cached_schema_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name)12090 get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
12091 const char *schema_name)
12092 {
12093 if (grant_internal_info)
12094 {
12095 if (! grant_internal_info->m_schema_lookup_done)
12096 {
12097 grant_internal_info->m_schema_access=
12098 ACL_internal_schema_registry::lookup(schema_name);
12099 grant_internal_info->m_schema_lookup_done= TRUE;
12100 }
12101 return grant_internal_info->m_schema_access;
12102 }
12103 return ACL_internal_schema_registry::lookup(schema_name);
12104 }
12105
12106 /**
12107 Get a cached internal table access.
12108 @param grant_internal_info the cache
12109 @param schema_name the name of the internal schema
12110 @param table_name the name of the internal table
12111 */
12112 const ACL_internal_table_access *
get_cached_table_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name,const char * table_name)12113 get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
12114 const char *schema_name,
12115 const char *table_name)
12116 {
12117 DBUG_ASSERT(grant_internal_info);
12118 if (! grant_internal_info->m_table_lookup_done)
12119 {
12120 const ACL_internal_schema_access *schema_access;
12121 schema_access= get_cached_schema_access(grant_internal_info, schema_name);
12122 if (schema_access)
12123 grant_internal_info->m_table_access= schema_access->lookup(table_name);
12124 grant_internal_info->m_table_lookup_done= TRUE;
12125 }
12126 return grant_internal_info->m_table_access;
12127 }
12128
12129
12130 /****************************************************************************
12131 AUTHENTICATION CODE
12132 including initial connect handshake, invoking appropriate plugins,
12133 client-server plugin negotiation, COM_CHANGE_USER, and native
12134 MySQL authentication plugins.
12135 ****************************************************************************/
12136
12137 /* few defines to have less ifdef's in the code below */
12138 #ifdef EMBEDDED_LIBRARY
12139 #undef HAVE_OPENSSL
12140 #ifdef NO_EMBEDDED_ACCESS_CHECKS
12141 #define initialized 0
12142 #define check_for_max_user_connections(X,Y) 0
12143 #define get_or_create_user_conn(A,B,C,D) 0
12144 #endif
12145 #endif
12146 #ifndef HAVE_OPENSSL
12147 #define ssl_acceptor_fd 0
12148 #define sslaccept(A,B,C,D) 1
12149 #endif
12150
12151 /**
12152 The internal version of what plugins know as MYSQL_PLUGIN_VIO,
12153 basically the context of the authentication session
12154 */
12155 struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
12156 {
12157 MYSQL_SERVER_AUTH_INFO auth_info;
12158 ACL_USER *acl_user; ///< a copy, independent from acl_users array
12159 plugin_ref plugin; ///< what plugin we're under
12160 LEX_CSTRING db; ///< db name from the handshake packet
12161 /** when restarting a plugin this caches the last client reply */
12162 struct {
12163 const char *plugin;
12164 char *pkt; ///< pointer into NET::buff
12165 uint pkt_len;
12166 } cached_client_reply;
12167 /** this caches the first plugin packet for restart request on the client */
12168 struct {
12169 char *pkt;
12170 uint pkt_len;
12171 } cached_server_packet;
12172 int packets_read, packets_written; ///< counters for send/received packets
12173 bool make_it_fail;
12174 /** when plugin returns a failure this tells us what really happened */
12175 enum { SUCCESS, FAILURE, RESTART } status;
12176 };
12177
12178 /**
12179 a helper function to report an access denied error in most proper places
12180 */
login_failed_error(THD * thd)12181 static void login_failed_error(THD *thd)
12182 {
12183 my_error(access_denied_error_code(thd->password), MYF(0),
12184 thd->main_security_ctx.user,
12185 thd->main_security_ctx.host_or_ip,
12186 thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12187 general_log_print(thd, COM_CONNECT,
12188 ER_THD(thd, access_denied_error_code(thd->password)),
12189 thd->main_security_ctx.user,
12190 thd->main_security_ctx.host_or_ip,
12191 thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12192 status_var_increment(thd->status_var.access_denied_errors);
12193 /*
12194 Log access denied messages to the error log when log-warnings = 2
12195 so that the overhead of the general query log is not required to track
12196 failed connections.
12197 */
12198 if (global_system_variables.log_warnings > 1)
12199 {
12200 sql_print_warning(ER_THD(thd, access_denied_error_code(thd->password)),
12201 thd->main_security_ctx.user,
12202 thd->main_security_ctx.host_or_ip,
12203 thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12204 }
12205 }
12206
12207 /**
12208 sends a server handshake initialization packet, the very first packet
12209 after the connection was established
12210
12211 Packet format:
12212
12213 Bytes Content
12214 ----- ----
12215 1 protocol version (always 10)
12216 n server version string, \0-terminated
12217 4 thread id
12218 8 first 8 bytes of the plugin provided data (scramble)
12219 1 \0 byte, terminating the first part of a scramble
12220 2 server capabilities (two lower bytes)
12221 1 server character set
12222 2 server status
12223 2 server capabilities (two upper bytes)
12224 1 length of the scramble
12225 10 reserved, always 0
12226 n rest of the plugin provided data (at least 12 bytes)
12227 1 \0 byte, terminating the second part of a scramble
12228
12229 @retval 0 ok
12230 @retval 1 error
12231 */
send_server_handshake_packet(MPVIO_EXT * mpvio,const char * data,uint data_len)12232 static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
12233 const char *data, uint data_len)
12234 {
12235 DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
12236 DBUG_ASSERT(data_len <= 255);
12237
12238 THD *thd= mpvio->auth_info.thd;
12239 char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + 1 + data_len + 64);
12240 char scramble_buf[SCRAMBLE_LENGTH];
12241 char *end= buff;
12242 DBUG_ENTER("send_server_handshake_packet");
12243
12244 *end++= protocol_version;
12245
12246 thd->client_capabilities= CLIENT_BASIC_FLAGS;
12247
12248 if (opt_using_transactions)
12249 thd->client_capabilities|= CLIENT_TRANSACTIONS;
12250
12251 thd->client_capabilities|= CAN_CLIENT_COMPRESS;
12252
12253 if (ssl_acceptor_fd)
12254 {
12255 thd->client_capabilities |= CLIENT_SSL;
12256 thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
12257 }
12258
12259 if (data_len)
12260 {
12261 mpvio->cached_server_packet.pkt= (char*)thd->memdup(data, data_len);
12262 mpvio->cached_server_packet.pkt_len= data_len;
12263 }
12264
12265 if (data_len < SCRAMBLE_LENGTH)
12266 {
12267 if (data_len)
12268 {
12269 /*
12270 the first packet *must* have at least 20 bytes of a scramble.
12271 if a plugin provided less, we pad it to 20 with zeros
12272 */
12273 memcpy(scramble_buf, data, data_len);
12274 bzero(scramble_buf + data_len, SCRAMBLE_LENGTH - data_len);
12275 data= scramble_buf;
12276 }
12277 else
12278 {
12279 /*
12280 if the default plugin does not provide the data for the scramble at
12281 all, we generate a scramble internally anyway, just in case the
12282 user account (that will be known only later) uses a
12283 native_password_plugin (which needs a scramble). If we don't send a
12284 scramble now - wasting 20 bytes in the packet -
12285 native_password_plugin will have to send it in a separate packet,
12286 adding one more round trip.
12287 */
12288 thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
12289 data= thd->scramble;
12290 }
12291 data_len= SCRAMBLE_LENGTH;
12292 }
12293
12294 /* When server version is specified in config file, don't include
12295 the replication hack prefix. */
12296 if (using_custom_server_version)
12297 end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
12298 else
12299 end= strxnmov(end, SERVER_VERSION_LENGTH, RPL_VERSION_HACK, server_version, NullS) + 1;
12300
12301 int4store((uchar*) end, mpvio->auth_info.thd->thread_id);
12302 end+= 4;
12303
12304 /*
12305 Old clients does not understand long scrambles, but can ignore packet
12306 tail: that's why first part of the scramble is placed here, and second
12307 part at the end of packet.
12308 */
12309 end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
12310 end+= SCRAMBLE_LENGTH_323;
12311 *end++= 0;
12312
12313 int2store(end, thd->client_capabilities);
12314 /* write server characteristics: up to 16 bytes allowed */
12315 end[2]= (char) default_charset_info->number;
12316 int2store(end+3, mpvio->auth_info.thd->server_status);
12317 int2store(end+5, thd->client_capabilities >> 16);
12318 end[7]= data_len;
12319 DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
12320 DBUG_EXECUTE_IF("increase_srv_handshake_scramble_len", end[7]= 50;);
12321 bzero(end + 8, 6);
12322 int4store(end + 14, thd->client_capabilities >> 32);
12323 end+= 18;
12324 /* write scramble tail */
12325 end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
12326 data_len - SCRAMBLE_LENGTH_323);
12327 end+= data_len - SCRAMBLE_LENGTH_323;
12328 end= strmake(end, plugin_name(mpvio->plugin)->str,
12329 plugin_name(mpvio->plugin)->length);
12330
12331 int res= my_net_write(&mpvio->auth_info.thd->net, (uchar*) buff,
12332 (size_t) (end - buff + 1)) ||
12333 net_flush(&mpvio->auth_info.thd->net);
12334 my_afree(buff);
12335 DBUG_RETURN (res);
12336 }
12337
secure_auth(THD * thd)12338 static bool secure_auth(THD *thd)
12339 {
12340 if (!opt_secure_auth)
12341 return 0;
12342
12343 /*
12344 If the server is running in secure auth mode, short scrambles are
12345 forbidden. Extra juggling to report the same error as the old code.
12346 */
12347 if (thd->client_capabilities & CLIENT_PROTOCOL_41)
12348 {
12349 my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
12350 thd->security_ctx->user,
12351 thd->security_ctx->host_or_ip);
12352 general_log_print(thd, COM_CONNECT,
12353 ER_THD(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE),
12354 thd->security_ctx->user,
12355 thd->security_ctx->host_or_ip);
12356 }
12357 else
12358 {
12359 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
12360 general_log_print(thd, COM_CONNECT,
12361 ER_THD(thd, ER_NOT_SUPPORTED_AUTH_MODE));
12362 }
12363 return 1;
12364 }
12365
12366 /**
12367 sends a "change plugin" packet, requesting a client to restart authentication
12368 using a different authentication plugin
12369
12370 Packet format:
12371
12372 Bytes Content
12373 ----- ----
12374 1 byte with the value 254
12375 n client plugin to use, \0-terminated
12376 n plugin provided data
12377
12378 In a special case of switching from native_password_plugin to
12379 old_password_plugin, the packet contains only one - the first - byte,
12380 plugin name is omitted, plugin data aren't needed as the scramble was
12381 already sent. This one-byte packet is identical to the "use the short
12382 scramble" packet in the protocol before plugins were introduced.
12383
12384 @retval 0 ok
12385 @retval 1 error
12386 */
send_plugin_request_packet(MPVIO_EXT * mpvio,const uchar * data,uint data_len)12387 static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
12388 const uchar *data, uint data_len)
12389 {
12390 DBUG_ASSERT(mpvio->packets_written == 1);
12391 DBUG_ASSERT(mpvio->packets_read == 1);
12392 NET *net= &mpvio->auth_info.thd->net;
12393 static uchar switch_plugin_request_buf[]= { 254 };
12394 DBUG_ENTER("send_plugin_request_packet");
12395
12396 mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
12397
12398 const char *client_auth_plugin=
12399 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
12400
12401 DBUG_EXECUTE_IF("auth_disconnect", { DBUG_RETURN(1); });
12402 DBUG_EXECUTE_IF("auth_invalid_plugin", client_auth_plugin="foo/bar"; );
12403 DBUG_ASSERT(client_auth_plugin);
12404
12405 /*
12406 we send an old "short 4.0 scramble request", if we need to request a
12407 client to use 4.0 auth plugin (short scramble) and the scramble was
12408 already sent to the client
12409
12410 below, cached_client_reply.plugin is the plugin name that client has used,
12411 client_auth_plugin is derived from mysql.user table, for the given
12412 user account, it's the plugin that the client need to use to login.
12413 */
12414 bool switch_from_long_to_short_scramble=
12415 native_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
12416 client_auth_plugin == old_password_plugin_name.str;
12417
12418 if (switch_from_long_to_short_scramble)
12419 DBUG_RETURN (secure_auth(mpvio->auth_info.thd) ||
12420 my_net_write(net, switch_plugin_request_buf, 1) ||
12421 net_flush(net));
12422
12423 /*
12424 We never request a client to switch from a short to long scramble.
12425 Plugin-aware clients can do that, but traditionally it meant to
12426 ask an old 4.0 client to use the new 4.1 authentication protocol.
12427 */
12428 bool switch_from_short_to_long_scramble=
12429 old_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
12430 client_auth_plugin == native_password_plugin_name.str;
12431
12432 if (switch_from_short_to_long_scramble)
12433 {
12434 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
12435 general_log_print(mpvio->auth_info.thd, COM_CONNECT,
12436 ER_THD(mpvio->auth_info.thd, ER_NOT_SUPPORTED_AUTH_MODE));
12437 DBUG_RETURN (1);
12438 }
12439
12440 DBUG_PRINT("info", ("requesting client to use the %s plugin",
12441 client_auth_plugin));
12442 DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
12443 (uchar*) client_auth_plugin,
12444 strlen(client_auth_plugin) + 1,
12445 (uchar*) data, data_len));
12446 }
12447
12448 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12449 /**
12450 Finds acl entry in user database for authentication purposes.
12451
12452 Finds a user and copies it into mpvio. Creates a fake user
12453 if no matching user account is found.
12454
12455 @retval 0 found
12456 @retval 1 error
12457 */
find_mpvio_user(MPVIO_EXT * mpvio)12458 static bool find_mpvio_user(MPVIO_EXT *mpvio)
12459 {
12460 Security_context *sctx= mpvio->auth_info.thd->security_ctx;
12461 DBUG_ENTER("find_mpvio_user");
12462 DBUG_ASSERT(mpvio->acl_user == 0);
12463
12464 mysql_mutex_lock(&acl_cache->lock);
12465
12466 ACL_USER *user= find_user_or_anon(sctx->host, sctx->user, sctx->ip);
12467 if (user)
12468 mpvio->acl_user= user->copy(mpvio->auth_info.thd->mem_root);
12469
12470 mysql_mutex_unlock(&acl_cache->lock);
12471
12472 if (!mpvio->acl_user)
12473 {
12474 /*
12475 A matching user was not found. Fake it. Take any user, make the
12476 authentication fail later.
12477 This way we get a realistically looking failure, with occasional
12478 "change auth plugin" requests even for nonexistent users. The ratio
12479 of "change auth plugin" request will be the same for real and
12480 nonexistent users.
12481 Note, that we cannot pick any user at random, it must always be
12482 the same user account for the incoming sctx->user name.
12483 */
12484 ulong nr1=1, nr2=4;
12485 CHARSET_INFO *cs= &my_charset_latin1;
12486 cs->coll->hash_sort(cs, (uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2);
12487
12488 mysql_mutex_lock(&acl_cache->lock);
12489 if (!acl_users.elements)
12490 {
12491 mysql_mutex_unlock(&acl_cache->lock);
12492 login_failed_error(mpvio->auth_info.thd);
12493 DBUG_RETURN(1);
12494 }
12495 uint i= nr1 % acl_users.elements;
12496 ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
12497 mpvio->acl_user= acl_user_tmp->copy(mpvio->auth_info.thd->mem_root);
12498 mysql_mutex_unlock(&acl_cache->lock);
12499
12500 mpvio->make_it_fail= true;
12501 }
12502
12503 /* user account requires non-default plugin and the client is too old */
12504 if (mpvio->acl_user->plugin.str != native_password_plugin_name.str &&
12505 mpvio->acl_user->plugin.str != old_password_plugin_name.str &&
12506 !(mpvio->auth_info.thd->client_capabilities & CLIENT_PLUGIN_AUTH))
12507 {
12508 DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
12509 native_password_plugin_name.str));
12510 DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
12511 old_password_plugin_name.str));
12512 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
12513 general_log_print(mpvio->auth_info.thd, COM_CONNECT,
12514 ER_THD(mpvio->auth_info.thd, ER_NOT_SUPPORTED_AUTH_MODE));
12515 DBUG_RETURN (1);
12516 }
12517
12518 mpvio->auth_info.user_name= sctx->user;
12519 mpvio->auth_info.user_name_length= (uint)strlen(sctx->user);
12520 mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
12521 mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length;
12522 strmake_buf(mpvio->auth_info.authenticated_as, safe_str(mpvio->acl_user->user.str));
12523
12524 DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
12525 "plugin=%s",
12526 mpvio->auth_info.user_name,
12527 mpvio->auth_info.auth_string,
12528 mpvio->auth_info.authenticated_as,
12529 mpvio->acl_user->plugin.str));
12530 DBUG_RETURN(0);
12531 }
12532
12533 static bool
read_client_connect_attrs(char ** ptr,char * end,CHARSET_INFO * from_cs)12534 read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs)
12535 {
12536 ulonglong length;
12537 char *ptr_save= *ptr;
12538
12539 /* not enough bytes to hold the length */
12540 if (ptr_save >= end)
12541 return true;
12542
12543 length= safe_net_field_length_ll((uchar **) ptr, end - ptr_save);
12544
12545 /* cannot even read the length */
12546 if (*ptr == NULL)
12547 return true;
12548
12549 /* length says there're more data than can fit into the packet */
12550 if (*ptr + length > end)
12551 return true;
12552
12553 /* impose an artificial length limit of 64k */
12554 if (length > 65535)
12555 return true;
12556
12557 if (PSI_CALL_set_thread_connect_attrs(*ptr, (uint)length, from_cs) &&
12558 current_thd->variables.log_warnings)
12559 sql_print_warning("Connection attributes of length %llu were truncated",
12560 length);
12561 return false;
12562 }
12563
12564 #endif
12565
12566 /* the packet format is described in send_change_user_packet() */
parse_com_change_user_packet(MPVIO_EXT * mpvio,uint packet_length)12567 static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
12568 {
12569 THD *thd= mpvio->auth_info.thd;
12570 NET *net= &thd->net;
12571 Security_context *sctx= thd->security_ctx;
12572
12573 char *user= (char*) net->read_pos;
12574 char *end= user + packet_length;
12575 /* Safe because there is always a trailing \0 at the end of the packet */
12576 char *passwd= strend(user) + 1;
12577 uint user_len= (uint)(passwd - user - 1);
12578 char *db= passwd;
12579 char db_buff[SAFE_NAME_LEN + 1]; // buffer to store db in utf8
12580 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
12581 uint dummy_errors;
12582 DBUG_ENTER ("parse_com_change_user_packet");
12583
12584 if (passwd >= end)
12585 {
12586 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12587 MYF(0));
12588 DBUG_RETURN (1);
12589 }
12590
12591 /*
12592 Old clients send null-terminated string as password; new clients send
12593 the size (1 byte) + string (not null-terminated). Hence in case of empty
12594 password both send '\0'.
12595
12596 This strlen() can't be easily deleted without changing protocol.
12597
12598 Cast *passwd to an unsigned char, so that it doesn't extend the sign for
12599 *passwd > 127 and become 2**32-127+ after casting to uint.
12600 */
12601 uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
12602 (uchar) (*passwd++) : (uint)strlen(passwd));
12603
12604 db+= passwd_len + 1;
12605 /*
12606 Database name is always NUL-terminated, so in case of empty database
12607 the packet must contain at least the trailing '\0'.
12608 */
12609 if (db >= end)
12610 {
12611 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12612 MYF(0));
12613 DBUG_RETURN (1);
12614 }
12615
12616 size_t db_len= strlen(db);
12617
12618 char *next_field= db + db_len + 1;
12619
12620 if (next_field + 1 < end)
12621 {
12622 if (thd_init_client_charset(thd, uint2korr(next_field)))
12623 DBUG_RETURN(1);
12624 next_field+= 2;
12625 }
12626
12627 /* Convert database and user names to utf8 */
12628 db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
12629 db, db_len, thd->charset(), &dummy_errors);
12630
12631 user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
12632 system_charset_info, user, user_len,
12633 thd->charset(), &dummy_errors);
12634
12635 if (!(sctx->user= my_strndup(user_buff, user_len, MYF(MY_WME))))
12636 DBUG_RETURN(1);
12637
12638 /* Clear variables that are allocated */
12639 thd->user_connect= 0;
12640 strmake_buf(sctx->priv_user, sctx->user);
12641
12642 if (thd->make_lex_string(&mpvio->db, db_buff, db_len) == 0)
12643 DBUG_RETURN(1); /* The error is set by make_lex_string(). */
12644
12645 /*
12646 Clear thd->db as it points to something, that will be freed when
12647 connection is closed. We don't want to accidentally free a wrong
12648 pointer if connect failed.
12649 */
12650 thd->reset_db(&null_clex_str);
12651
12652 if (!initialized)
12653 {
12654 // if mysqld's been started with --skip-grant-tables option
12655 mpvio->status= MPVIO_EXT::SUCCESS;
12656 DBUG_RETURN(0);
12657 }
12658
12659 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12660 thd->password= passwd_len > 0;
12661 if (find_mpvio_user(mpvio))
12662 DBUG_RETURN(1);
12663
12664 const char *client_plugin;
12665 if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
12666 {
12667 if (next_field >= end)
12668 {
12669 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12670 MYF(0));
12671 DBUG_RETURN(1);
12672 }
12673 client_plugin= fix_plugin_ptr(next_field);
12674 next_field+= strlen(next_field) + 1;
12675 }
12676 else
12677 {
12678 if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
12679 client_plugin= native_password_plugin_name.str;
12680 else
12681 {
12682 client_plugin= old_password_plugin_name.str;
12683 /*
12684 For a passwordless accounts we use native_password_plugin.
12685 But when an old 4.0 client connects to it, we change it to
12686 old_password_plugin, otherwise MySQL will think that server
12687 and client plugins don't match.
12688 */
12689 if (mpvio->acl_user->auth_string.length == 0)
12690 mpvio->acl_user->plugin= old_password_plugin_name;
12691 }
12692 }
12693
12694 if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
12695 read_client_connect_attrs(&next_field, end,
12696 thd->charset()))
12697 {
12698 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12699 MYF(0));
12700 DBUG_RETURN(1);
12701 }
12702
12703 DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
12704 /*
12705 Remember the data part of the packet, to present it to plugin in
12706 read_packet()
12707 */
12708 mpvio->cached_client_reply.pkt= passwd;
12709 mpvio->cached_client_reply.pkt_len= passwd_len;
12710 mpvio->cached_client_reply.plugin= client_plugin;
12711 mpvio->status= MPVIO_EXT::RESTART;
12712 #endif
12713
12714 DBUG_RETURN (0);
12715 }
12716
12717
12718 /* the packet format is described in send_client_reply_packet() */
parse_client_handshake_packet(MPVIO_EXT * mpvio,uchar ** buff,ulong pkt_len)12719 static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
12720 uchar **buff, ulong pkt_len)
12721 {
12722 #ifndef EMBEDDED_LIBRARY
12723 THD *thd= mpvio->auth_info.thd;
12724 NET *net= &thd->net;
12725 char *end;
12726 DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
12727
12728 if (pkt_len < MIN_HANDSHAKE_SIZE)
12729 return packet_error;
12730
12731 /*
12732 Protocol buffer is guaranteed to always end with \0. (see my_net_read())
12733 As the code below depends on this, lets check that.
12734 */
12735 DBUG_ASSERT(net->read_pos[pkt_len] == 0);
12736
12737 ulonglong client_capabilities= uint2korr(net->read_pos);
12738 compile_time_assert(sizeof(client_capabilities) >= 8);
12739 if (client_capabilities & CLIENT_PROTOCOL_41)
12740 {
12741 if (pkt_len < 32)
12742 return packet_error;
12743 client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
12744 if (!(client_capabilities & CLIENT_MYSQL))
12745 {
12746 // it is client with mariadb extensions
12747 ulonglong ext_client_capabilities=
12748 (((ulonglong)uint4korr(net->read_pos + 28)) << 32);
12749 client_capabilities|= ext_client_capabilities;
12750 }
12751 }
12752
12753 /* Disable those bits which are not supported by the client. */
12754 compile_time_assert(sizeof(thd->client_capabilities) >= 8);
12755 thd->client_capabilities&= client_capabilities;
12756
12757 DBUG_PRINT("info", ("client capabilities: %llu", thd->client_capabilities));
12758 if (thd->client_capabilities & CLIENT_SSL)
12759 {
12760 unsigned long errptr __attribute__((unused));
12761
12762 /* Do the SSL layering. */
12763 if (!ssl_acceptor_fd)
12764 return packet_error;
12765
12766 DBUG_PRINT("info", ("IO layer change in progress..."));
12767 if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr))
12768 {
12769 DBUG_PRINT("error", ("Failed to accept new SSL connection"));
12770 return packet_error;
12771 }
12772
12773 DBUG_PRINT("info", ("Reading user information over SSL layer"));
12774 pkt_len= my_net_read(net);
12775 if (unlikely(pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE))
12776 {
12777 DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
12778 pkt_len));
12779 return packet_error;
12780 }
12781 }
12782
12783 if (client_capabilities & CLIENT_PROTOCOL_41)
12784 {
12785 thd->max_client_packet_length= uint4korr(net->read_pos+4);
12786 DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
12787 if (thd_init_client_charset(thd, (uint) net->read_pos[8]))
12788 return packet_error;
12789 end= (char*) net->read_pos+32;
12790 }
12791 else
12792 {
12793 if (pkt_len < 5)
12794 return packet_error;
12795 thd->max_client_packet_length= uint3korr(net->read_pos+2);
12796 end= (char*) net->read_pos+5;
12797 }
12798
12799 if (end >= (char*) net->read_pos+ pkt_len +2)
12800 return packet_error;
12801
12802 if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
12803 thd->variables.sql_mode|= MODE_IGNORE_SPACE;
12804 if (thd->client_capabilities & CLIENT_INTERACTIVE)
12805 thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
12806
12807 if (end >= (char*) net->read_pos+ pkt_len +2)
12808 return packet_error;
12809
12810 if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
12811 opt_using_transactions)
12812 net->return_status= &thd->server_status;
12813
12814 char *user= end;
12815 char *passwd= strend(user)+1;
12816 size_t user_len= (size_t)(passwd - user - 1), db_len;
12817 char *db= passwd;
12818 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
12819 uint dummy_errors;
12820
12821 /*
12822 Old clients send null-terminated string as password; new clients send
12823 the size (1 byte) + string (not null-terminated). Hence in case of empty
12824 password both send '\0'.
12825
12826 This strlen() can't be easily deleted without changing protocol.
12827
12828 Cast *passwd to an unsigned char, so that it doesn't extend the sign for
12829 *passwd > 127 and become 2**32-127+ after casting to uint.
12830 */
12831 ulonglong len;
12832 size_t passwd_len;
12833
12834 if (!(thd->client_capabilities & CLIENT_SECURE_CONNECTION))
12835 len= strlen(passwd);
12836 else if (!(thd->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA))
12837 len= (uchar)(*passwd++);
12838 else
12839 {
12840 len= safe_net_field_length_ll((uchar**)&passwd,
12841 net->read_pos + pkt_len - (uchar*)passwd);
12842 if (len > pkt_len)
12843 return packet_error;
12844 }
12845
12846 passwd_len= (size_t)len;
12847 db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
12848 db + passwd_len + 1 : 0;
12849
12850 if (passwd == NULL ||
12851 passwd + passwd_len + MY_TEST(db) > (char*) net->read_pos + pkt_len)
12852 return packet_error;
12853
12854 /* strlen() can't be easily deleted without changing protocol */
12855 db_len= safe_strlen(db);
12856
12857 char *next_field;
12858 const char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0);
12859
12860 /*
12861 Since 4.1 all database names are stored in utf8
12862 The cast is ok as copy_with_error will create a new area for db
12863 */
12864 if (unlikely(thd->copy_with_error(system_charset_info,
12865 (LEX_STRING*) &mpvio->db,
12866 thd->charset(), db, db_len)))
12867 return packet_error;
12868
12869 user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
12870 system_charset_info, user, user_len,
12871 thd->charset(), &dummy_errors);
12872 user= user_buff;
12873
12874 /* If username starts and ends in "'", chop them off */
12875 if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
12876 {
12877 user++;
12878 user_len-= 2;
12879 }
12880
12881 /*
12882 Clip username to allowed length in characters (not bytes). This is
12883 mostly for backward compatibility (to truncate long usernames, as
12884 old 5.1 did)
12885 */
12886 user_len= Well_formed_prefix(system_charset_info, user, user_len,
12887 username_char_length).length();
12888 user[user_len]= '\0';
12889
12890 Security_context *sctx= thd->security_ctx;
12891
12892 my_free((char*) sctx->user);
12893 if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME))))
12894 return packet_error; /* The error is set by my_strdup(). */
12895
12896
12897 /*
12898 Clear thd->db as it points to something, that will be freed when
12899 connection is closed. We don't want to accidentally free a wrong
12900 pointer if connect failed.
12901 */
12902 thd->reset_db(&null_clex_str);
12903
12904 if (!initialized)
12905 {
12906 // if mysqld's been started with --skip-grant-tables option
12907 mpvio->status= MPVIO_EXT::SUCCESS;
12908 return packet_error;
12909 }
12910
12911 thd->password= passwd_len > 0;
12912 if (find_mpvio_user(mpvio))
12913 return packet_error;
12914
12915 if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) &&
12916 (client_plugin < (char *)net->read_pos + pkt_len))
12917 {
12918 client_plugin= fix_plugin_ptr(client_plugin);
12919 next_field+= strlen(next_field) + 1;
12920 }
12921 else
12922 {
12923 /* Some clients lie. Sad, but true */
12924 thd->client_capabilities &= ~CLIENT_PLUGIN_AUTH;
12925
12926 if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
12927 client_plugin= native_password_plugin_name.str;
12928 else
12929 {
12930 client_plugin= old_password_plugin_name.str;
12931 /*
12932 For a passwordless accounts we use native_password_plugin.
12933 But when an old 4.0 client connects to it, we change it to
12934 old_password_plugin, otherwise MySQL will think that server
12935 and client plugins don't match.
12936 */
12937 if (mpvio->acl_user->auth_string.length == 0)
12938 mpvio->acl_user->plugin= old_password_plugin_name;
12939 }
12940 }
12941
12942 if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
12943 read_client_connect_attrs(&next_field, ((char *)net->read_pos) + pkt_len,
12944 mpvio->auth_info.thd->charset()))
12945 return packet_error;
12946
12947 /*
12948 if the acl_user needs a different plugin to authenticate
12949 (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
12950 we need to restart the authentication in the server.
12951 But perhaps the client has already used the correct plugin -
12952 in that case the authentication on the client may not need to be
12953 restarted and a server auth plugin will read the data that the client
12954 has just send. Cache them to return in the next server_mpvio_read_packet().
12955 */
12956 if (!lex_string_eq(&mpvio->acl_user->plugin, plugin_name(mpvio->plugin)))
12957 {
12958 mpvio->cached_client_reply.pkt= passwd;
12959 mpvio->cached_client_reply.pkt_len= (uint)passwd_len;
12960 mpvio->cached_client_reply.plugin= client_plugin;
12961 mpvio->status= MPVIO_EXT::RESTART;
12962 return packet_error;
12963 }
12964
12965 /*
12966 ok, we don't need to restart the authentication on the server.
12967 but if the client used the wrong plugin, we need to restart
12968 the authentication on the client. Do it here, the server plugin
12969 doesn't need to know.
12970 */
12971 const char *client_auth_plugin=
12972 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
12973
12974 if (client_auth_plugin &&
12975 my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
12976 {
12977 mpvio->cached_client_reply.plugin= client_plugin;
12978 if (send_plugin_request_packet(mpvio,
12979 (uchar*) mpvio->cached_server_packet.pkt,
12980 mpvio->cached_server_packet.pkt_len))
12981 return packet_error;
12982
12983 passwd_len= my_net_read(&thd->net);
12984 passwd= (char*)thd->net.read_pos;
12985 }
12986
12987 *buff= (uchar*) passwd;
12988 return (ulong)passwd_len;
12989 #else
12990 return 0;
12991 #endif
12992 }
12993
12994
12995 /**
12996 vio->write_packet() callback method for server authentication plugins
12997
12998 This function is called by a server authentication plugin, when it wants
12999 to send data to the client.
13000
13001 It transparently wraps the data into a handshake packet,
13002 and handles plugin negotiation with the client. If necessary,
13003 it escapes the plugin data, if it starts with a mysql protocol packet byte.
13004 */
server_mpvio_write_packet(MYSQL_PLUGIN_VIO * param,const uchar * packet,int packet_len)13005 static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
13006 const uchar *packet, int packet_len)
13007 {
13008 MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
13009 int res;
13010 DBUG_ENTER("server_mpvio_write_packet");
13011
13012 /* reset cached_client_reply */
13013 mpvio->cached_client_reply.pkt= 0;
13014
13015 /* for the 1st packet we wrap plugin data into the handshake packet */
13016 if (mpvio->packets_written == 0)
13017 res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
13018 else if (mpvio->status == MPVIO_EXT::RESTART)
13019 res= send_plugin_request_packet(mpvio, packet, packet_len);
13020 else if (packet_len > 0 && (*packet == 1 || *packet == 255 || *packet == 254))
13021 {
13022 /*
13023 we cannot allow plugin data packet to start from 255 or 254 -
13024 as the client will treat it as an error or "change plugin" packet.
13025 We'll escape these bytes with \1. Consequently, we
13026 have to escape \1 byte too.
13027 */
13028 res= net_write_command(&mpvio->auth_info.thd->net, 1, (uchar*)"", 0,
13029 packet, packet_len);
13030 }
13031 else
13032 {
13033 res= my_net_write(&mpvio->auth_info.thd->net, packet, packet_len) ||
13034 net_flush(&mpvio->auth_info.thd->net);
13035 }
13036 mpvio->packets_written++;
13037 DBUG_RETURN(res);
13038 }
13039
13040 /**
13041 vio->read_packet() callback method for server authentication plugins
13042
13043 This function is called by a server authentication plugin, when it wants
13044 to read data from the client.
13045
13046 It transparently extracts the client plugin data, if embedded into
13047 a client authentication handshake packet, and handles plugin negotiation
13048 with the client, if necessary.
13049 */
server_mpvio_read_packet(MYSQL_PLUGIN_VIO * param,uchar ** buf)13050 static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
13051 {
13052 MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
13053 ulong pkt_len;
13054 DBUG_ENTER("server_mpvio_read_packet");
13055 if (mpvio->packets_written == 0)
13056 {
13057 /*
13058 plugin wants to read the data without sending anything first.
13059 send an empty packet to force a server handshake packet to be sent
13060 */
13061 if (server_mpvio_write_packet(mpvio, 0, 0))
13062 pkt_len= packet_error;
13063 else
13064 pkt_len= my_net_read(&mpvio->auth_info.thd->net);
13065 }
13066 else if (mpvio->cached_client_reply.pkt)
13067 {
13068 DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
13069 DBUG_ASSERT(mpvio->packets_read > 0);
13070 /*
13071 if the have the data cached from the last server_mpvio_read_packet
13072 (which can be the case if it's a restarted authentication)
13073 and a client has used the correct plugin, then we can return the
13074 cached data straight away and avoid one round trip.
13075 */
13076 const char *client_auth_plugin=
13077 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
13078 if (client_auth_plugin == 0 ||
13079 my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
13080 client_auth_plugin) == 0)
13081 {
13082 mpvio->status= MPVIO_EXT::FAILURE;
13083 *buf= (uchar*) mpvio->cached_client_reply.pkt;
13084 mpvio->cached_client_reply.pkt= 0;
13085 mpvio->packets_read++;
13086
13087 DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
13088 }
13089
13090 /*
13091 But if the client has used the wrong plugin, the cached data are
13092 useless. Furthermore, we have to send a "change plugin" request
13093 to the client.
13094 */
13095 if (server_mpvio_write_packet(mpvio, 0, 0))
13096 pkt_len= packet_error;
13097 else
13098 pkt_len= my_net_read(&mpvio->auth_info.thd->net);
13099 }
13100 else
13101 pkt_len= my_net_read(&mpvio->auth_info.thd->net);
13102
13103 if (unlikely(pkt_len == packet_error))
13104 goto err;
13105
13106 mpvio->packets_read++;
13107
13108 /*
13109 the 1st packet has the plugin data wrapped into the client authentication
13110 handshake packet
13111 */
13112 if (mpvio->packets_read == 1)
13113 {
13114 pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
13115 if (unlikely(pkt_len == packet_error))
13116 goto err;
13117 }
13118 else
13119 *buf= mpvio->auth_info.thd->net.read_pos;
13120
13121 DBUG_RETURN((int)pkt_len);
13122
13123 err:
13124 if (mpvio->status == MPVIO_EXT::FAILURE)
13125 {
13126 if (!mpvio->auth_info.thd->is_error())
13127 my_error(ER_HANDSHAKE_ERROR, MYF(0));
13128 }
13129 DBUG_RETURN(-1);
13130 }
13131
13132 /**
13133 fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
13134 connection
13135 */
server_mpvio_info(MYSQL_PLUGIN_VIO * vio,MYSQL_PLUGIN_VIO_INFO * info)13136 static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
13137 MYSQL_PLUGIN_VIO_INFO *info)
13138 {
13139 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
13140 mpvio_info(mpvio->auth_info.thd->net.vio, info);
13141 }
13142
acl_check_ssl(THD * thd,const ACL_USER * acl_user)13143 static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
13144 {
13145 #ifdef HAVE_OPENSSL
13146 Vio *vio= thd->net.vio;
13147 SSL *ssl= (SSL *) vio->ssl_arg;
13148 X509 *cert;
13149 #endif
13150
13151 /*
13152 At this point we know that user is allowed to connect
13153 from given host by given username/password pair. Now
13154 we check if SSL is required, if user is using SSL and
13155 if X509 certificate attributes are OK
13156 */
13157 switch (acl_user->ssl_type) {
13158 case SSL_TYPE_NOT_SPECIFIED: // Impossible
13159 case SSL_TYPE_NONE: // SSL is not required
13160 return 0;
13161 #ifdef HAVE_OPENSSL
13162 case SSL_TYPE_ANY: // Any kind of SSL is ok
13163 return vio_type(vio) != VIO_TYPE_SSL;
13164 case SSL_TYPE_X509: /* Client should have any valid certificate. */
13165 /*
13166 Connections with non-valid certificates are dropped already
13167 in sslaccept() anyway, so we do not check validity here.
13168
13169 We need to check for absence of SSL because without SSL
13170 we should reject connection.
13171 */
13172 if (vio_type(vio) == VIO_TYPE_SSL &&
13173 SSL_get_verify_result(ssl) == X509_V_OK &&
13174 (cert= SSL_get_peer_certificate(ssl)))
13175 {
13176 X509_free(cert);
13177 return 0;
13178 }
13179 return 1;
13180 case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
13181 /* If a cipher name is specified, we compare it to actual cipher in use. */
13182 if (vio_type(vio) != VIO_TYPE_SSL ||
13183 SSL_get_verify_result(ssl) != X509_V_OK)
13184 return 1;
13185 if (acl_user->ssl_cipher)
13186 {
13187 DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
13188 acl_user->ssl_cipher, SSL_get_cipher(ssl)));
13189 if (strcmp(acl_user->ssl_cipher, SSL_get_cipher(ssl)))
13190 {
13191 if (global_system_variables.log_warnings)
13192 sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
13193 acl_user->ssl_cipher, SSL_get_cipher(ssl));
13194 return 1;
13195 }
13196 }
13197 if (!acl_user->x509_issuer && !acl_user->x509_subject)
13198 return 0; // all done
13199
13200 /* Prepare certificate (if exists) */
13201 if (!(cert= SSL_get_peer_certificate(ssl)))
13202 return 1;
13203 /* If X509 issuer is specified, we check it... */
13204 if (acl_user->x509_issuer)
13205 {
13206 char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
13207 DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
13208 acl_user->x509_issuer, ptr));
13209 if (strcmp(acl_user->x509_issuer, ptr))
13210 {
13211 if (global_system_variables.log_warnings)
13212 sql_print_information("X509 issuer mismatch: should be '%s' "
13213 "but is '%s'", acl_user->x509_issuer, ptr);
13214 free(ptr);
13215 X509_free(cert);
13216 return 1;
13217 }
13218 free(ptr);
13219 }
13220 /* X509 subject is specified, we check it .. */
13221 if (acl_user->x509_subject)
13222 {
13223 char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
13224 DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
13225 acl_user->x509_subject, ptr));
13226 if (strcmp(acl_user->x509_subject, ptr))
13227 {
13228 if (global_system_variables.log_warnings)
13229 sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
13230 acl_user->x509_subject, ptr);
13231 free(ptr);
13232 X509_free(cert);
13233 return 1;
13234 }
13235 free(ptr);
13236 }
13237 X509_free(cert);
13238 return 0;
13239 #else /* HAVE_OPENSSL */
13240 default:
13241 /*
13242 If we don't have SSL but SSL is required for this user the
13243 authentication should fail.
13244 */
13245 return 1;
13246 #endif /* HAVE_OPENSSL */
13247 }
13248 return 1;
13249 }
13250
13251
do_auth_once(THD * thd,const LEX_CSTRING * auth_plugin_name,MPVIO_EXT * mpvio)13252 static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name,
13253 MPVIO_EXT *mpvio)
13254 {
13255 int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
13256 bool unlock_plugin= false;
13257 plugin_ref plugin= NULL;
13258
13259 if (auth_plugin_name->str == native_password_plugin_name.str)
13260 plugin= native_password_plugin;
13261 #ifndef EMBEDDED_LIBRARY
13262 else if (auth_plugin_name->str == old_password_plugin_name.str)
13263 plugin= old_password_plugin;
13264 else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
13265 MYSQL_AUTHENTICATION_PLUGIN)))
13266 unlock_plugin= true;
13267 #endif
13268
13269 mpvio->plugin= plugin;
13270 old_status= mpvio->status;
13271
13272 if (plugin)
13273 {
13274 st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
13275 switch (auth->interface_version >> 8) {
13276 case 0x02:
13277 res= auth->authenticate_user(mpvio, &mpvio->auth_info);
13278 break;
13279 case 0x01:
13280 {
13281 MYSQL_SERVER_AUTH_INFO_0x0100 compat;
13282 compat.downgrade(&mpvio->auth_info);
13283 res= auth->authenticate_user(mpvio, (MYSQL_SERVER_AUTH_INFO *)&compat);
13284 compat.upgrade(&mpvio->auth_info);
13285 }
13286 break;
13287 default: DBUG_ASSERT(0);
13288 }
13289
13290 if (unlock_plugin)
13291 plugin_unlock(thd, plugin);
13292 }
13293 else
13294 {
13295 /* Server cannot load the required plugin. */
13296 Host_errors errors;
13297 errors.m_no_auth_plugin= 1;
13298 inc_host_errors(mpvio->auth_info.thd->security_ctx->ip, &errors);
13299 my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
13300 res= CR_ERROR;
13301 }
13302
13303 /*
13304 If the status was MPVIO_EXT::RESTART before the authenticate_user() call
13305 it can never be MPVIO_EXT::RESTART after the call, because any call
13306 to write_packet() or read_packet() will reset the status.
13307
13308 But (!) if a plugin never called a read_packet() or write_packet(), the
13309 status will stay unchanged. We'll fix it, by resetting the status here.
13310 */
13311 if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART)
13312 mpvio->status= MPVIO_EXT::FAILURE; // reset to the default
13313
13314 return res;
13315 }
13316
13317
13318 /**
13319 Perform the handshake, authorize the client and update thd sctx variables.
13320
13321 @param thd thread handle
13322 @param com_change_user_pkt_len size of the COM_CHANGE_USER packet
13323 (without the first, command, byte) or 0
13324 if it's not a COM_CHANGE_USER (that is, if
13325 it's a new connection)
13326
13327 @retval 0 success, thd is updated.
13328 @retval 1 error
13329 */
acl_authenticate(THD * thd,uint com_change_user_pkt_len)13330 bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
13331 {
13332 int res= CR_OK;
13333 MPVIO_EXT mpvio;
13334 const LEX_CSTRING *auth_plugin_name= default_auth_plugin_name;
13335 enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
13336 : COM_CONNECT;
13337 DBUG_ENTER("acl_authenticate");
13338
13339 bzero(&mpvio, sizeof(mpvio));
13340 mpvio.read_packet= server_mpvio_read_packet;
13341 mpvio.write_packet= server_mpvio_write_packet;
13342 mpvio.info= server_mpvio_info;
13343 mpvio.status= MPVIO_EXT::FAILURE;
13344 mpvio.make_it_fail= false;
13345 mpvio.auth_info.thd= thd;
13346 mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
13347 mpvio.auth_info.host_or_ip_length=
13348 (unsigned int) strlen(thd->security_ctx->host_or_ip);
13349
13350 DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
13351
13352 if (command == COM_CHANGE_USER)
13353 {
13354 mpvio.packets_written++; // pretend that a server handshake packet was sent
13355 mpvio.packets_read++; // take COM_CHANGE_USER packet into account
13356
13357 if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
13358 DBUG_RETURN(1);
13359
13360 DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
13361 mpvio.status == MPVIO_EXT::SUCCESS);
13362 }
13363 else
13364 {
13365 /* mark the thd as having no scramble yet */
13366 thd->scramble[SCRAMBLE_LENGTH]= 1;
13367
13368 /*
13369 perform the first authentication attempt, with the default plugin.
13370 This sends the server handshake packet, reads the client reply
13371 with a user name, and performs the authentication if everyone has used
13372 the correct plugin.
13373 */
13374
13375 res= do_auth_once(thd, auth_plugin_name, &mpvio);
13376 }
13377
13378 /*
13379 retry the authentication, if - after receiving the user name -
13380 we found that we need to switch to a non-default plugin
13381 */
13382 if (mpvio.status == MPVIO_EXT::RESTART)
13383 {
13384 DBUG_ASSERT(mpvio.acl_user);
13385 DBUG_ASSERT(command == COM_CHANGE_USER ||
13386 !lex_string_eq(auth_plugin_name, &mpvio.acl_user->plugin));
13387 auth_plugin_name= &mpvio.acl_user->plugin;
13388 res= do_auth_once(thd, auth_plugin_name, &mpvio);
13389 }
13390 if (mpvio.make_it_fail && res == CR_OK)
13391 {
13392 mpvio.status= MPVIO_EXT::FAILURE;
13393 res= CR_ERROR;
13394 }
13395
13396 Security_context *sctx= thd->security_ctx;
13397 const ACL_USER *acl_user= mpvio.acl_user;
13398
13399 thd->password= mpvio.auth_info.password_used; // remember for error messages
13400
13401 /*
13402 Log the command here so that the user can check the log
13403 for the tried logins and also to detect break-in attempts.
13404
13405 if sctx->user is unset it's protocol failure, bad packet.
13406 */
13407 if (sctx->user)
13408 {
13409 if (strcmp(sctx->priv_user, sctx->user))
13410 {
13411 general_log_print(thd, command, "%s@%s as %s on %s",
13412 sctx->user, sctx->host_or_ip,
13413 sctx->priv_user[0] ? sctx->priv_user : "anonymous",
13414 safe_str(mpvio.db.str));
13415 }
13416 else
13417 general_log_print(thd, command, (char*) "%s@%s on %s",
13418 sctx->user, sctx->host_or_ip,
13419 safe_str(mpvio.db.str));
13420 }
13421
13422 if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
13423 {
13424 Host_errors errors;
13425 DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
13426 switch (res)
13427 {
13428 case CR_AUTH_PLUGIN_ERROR:
13429 errors.m_auth_plugin= 1;
13430 break;
13431 case CR_AUTH_HANDSHAKE:
13432 errors.m_handshake= 1;
13433 break;
13434 case CR_AUTH_USER_CREDENTIALS:
13435 errors.m_authentication= 1;
13436 break;
13437 case CR_ERROR:
13438 default:
13439 /* Unknown of unspecified auth plugin error. */
13440 errors.m_auth_plugin= 1;
13441 break;
13442 }
13443 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13444 if (!thd->is_error())
13445 login_failed_error(thd);
13446 DBUG_RETURN(1);
13447 }
13448
13449 sctx->proxy_user[0]= 0;
13450
13451 if (initialized) // if not --skip-grant-tables
13452 {
13453 #ifndef NO_EMBEDDED_ACCESS_CHECKS
13454 bool is_proxy_user= FALSE;
13455 const char *auth_user = safe_str(acl_user->user.str);
13456 ACL_PROXY_USER *proxy_user;
13457 /* check if the user is allowed to proxy as another user */
13458 proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
13459 mpvio.auth_info.authenticated_as,
13460 &is_proxy_user);
13461 if (is_proxy_user)
13462 {
13463 ACL_USER *acl_proxy_user;
13464
13465 /* we need to find the proxy user, but there was none */
13466 if (!proxy_user)
13467 {
13468 Host_errors errors;
13469 errors.m_proxy_user= 1;
13470 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13471 if (!thd->is_error())
13472 login_failed_error(thd);
13473 DBUG_RETURN(1);
13474 }
13475
13476 my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
13477 "'%s'@'%s'", auth_user,
13478 safe_str(acl_user->host.hostname));
13479
13480 /* we're proxying : find the proxy user definition */
13481 mysql_mutex_lock(&acl_cache->lock);
13482 acl_proxy_user= find_user_exact(safe_str(proxy_user->get_proxied_host()),
13483 mpvio.auth_info.authenticated_as);
13484 if (!acl_proxy_user)
13485 {
13486 mysql_mutex_unlock(&acl_cache->lock);
13487
13488 Host_errors errors;
13489 errors.m_proxy_user_acl= 1;
13490 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13491 if (!thd->is_error())
13492 login_failed_error(thd);
13493 DBUG_RETURN(1);
13494 }
13495 acl_user= acl_proxy_user->copy(thd->mem_root);
13496 mysql_mutex_unlock(&acl_cache->lock);
13497 }
13498 #endif
13499
13500 sctx->master_access= acl_user->access;
13501 if (acl_user->user.str)
13502 strmake_buf(sctx->priv_user, acl_user->user.str);
13503 else
13504 *sctx->priv_user= 0;
13505
13506 if (acl_user->host.hostname)
13507 strmake_buf(sctx->priv_host, acl_user->host.hostname);
13508 else
13509 *sctx->priv_host= 0;
13510
13511 /*
13512 OK. Let's check the SSL. Historically it was checked after the password,
13513 as an additional layer, not instead of the password
13514 (in which case it would've been a plugin too).
13515 */
13516 if (acl_check_ssl(thd, acl_user))
13517 {
13518 Host_errors errors;
13519 errors.m_ssl= 1;
13520 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13521 login_failed_error(thd);
13522 DBUG_RETURN(1);
13523 }
13524
13525 /*
13526 Don't allow the user to connect if he has done too many queries.
13527 As we are testing max_user_connections == 0 here, it means that we
13528 can't let the user change max_user_connections from 0 in the server
13529 without a restart as it would lead to wrong connect counting.
13530 */
13531 if ((acl_user->user_resource.questions ||
13532 acl_user->user_resource.updates ||
13533 acl_user->user_resource.conn_per_hour ||
13534 acl_user->user_resource.user_conn ||
13535 acl_user->user_resource.max_statement_time != 0.0 ||
13536 max_user_connections_checking) &&
13537 get_or_create_user_conn(thd,
13538 (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
13539 (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
13540 &acl_user->user_resource))
13541 DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
13542
13543 if (acl_user->user_resource.max_statement_time != 0.0)
13544 {
13545 thd->variables.max_statement_time_double=
13546 acl_user->user_resource.max_statement_time;
13547 thd->variables.max_statement_time=
13548 (ulonglong) (thd->variables.max_statement_time_double * 1e6 + 0.1);
13549 }
13550 }
13551 else
13552 sctx->skip_grants();
13553
13554 if (thd->user_connect &&
13555 (thd->user_connect->user_resources.conn_per_hour ||
13556 thd->user_connect->user_resources.user_conn ||
13557 max_user_connections_checking) &&
13558 check_for_max_user_connections(thd, thd->user_connect))
13559 {
13560 /* Ensure we don't decrement thd->user_connections->connections twice */
13561 thd->user_connect= 0;
13562 status_var_increment(denied_connections);
13563 DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
13564 }
13565
13566 DBUG_PRINT("info",
13567 ("Capabilities: %llu packet_length: %ld Host: '%s' "
13568 "Login user: '%s' Priv_user: '%s' Using password: %s "
13569 "Access: %lu db: '%s'",
13570 thd->client_capabilities, thd->max_client_packet_length,
13571 sctx->host_or_ip, sctx->user, sctx->priv_user,
13572 thd->password ? "yes": "no",
13573 sctx->master_access, mpvio.db.str));
13574
13575 if (command == COM_CONNECT &&
13576 !(thd->main_security_ctx.master_access & SUPER_ACL))
13577 {
13578 mysql_mutex_lock(&LOCK_connection_count);
13579 bool count_ok= (*thd->scheduler->connection_count <=
13580 *thd->scheduler->max_connections);
13581 mysql_mutex_unlock(&LOCK_connection_count);
13582 if (!count_ok)
13583 { // too many connections
13584 my_error(ER_CON_COUNT_ERROR, MYF(0));
13585 DBUG_RETURN(1);
13586 }
13587 }
13588
13589 /*
13590 This is the default access rights for the current database. It's
13591 set to 0 here because we don't have an active database yet (and we
13592 may not have an active database to set.
13593 */
13594 sctx->db_access=0;
13595
13596 #ifndef NO_EMBEDDED_ACCESS_CHECKS
13597 /*
13598 In case the user has a default role set, attempt to set that role
13599 */
13600 if (initialized && acl_user->default_rolename.length) {
13601 ulonglong access= 0;
13602 int result;
13603 result= acl_check_setrole(thd, acl_user->default_rolename.str, &access);
13604 if (!result)
13605 result= acl_setrole(thd, acl_user->default_rolename.str, access);
13606 if (result)
13607 thd->clear_error(); // even if the default role was not granted, do not
13608 // close the connection
13609 }
13610 #endif
13611
13612 /* Change a database if necessary */
13613 if (mpvio.db.length)
13614 {
13615 uint err = mysql_change_db(thd, &mpvio.db, FALSE);
13616 if(err)
13617 {
13618 if (err == ER_DBACCESS_DENIED_ERROR)
13619 {
13620 /*
13621 Got an "access denied" error, which must be handled
13622 other access denied errors (see login_failed_error()).
13623 mysql_change_db() already sent error to client, and
13624 wrote to general log, we only need to increment the counter
13625 and maybe write a warning to error log.
13626 */
13627 status_var_increment(thd->status_var.access_denied_errors);
13628 if (global_system_variables.log_warnings > 1)
13629 {
13630 Security_context* sctx = thd->security_ctx;
13631 sql_print_warning(ER_THD(thd, err),
13632 sctx->priv_user, sctx->priv_host, mpvio.db.str);
13633 }
13634 }
13635 DBUG_RETURN(1);
13636 }
13637 }
13638
13639 thd->net.net_skip_rest_factor= 2; // skip at most 2*max_packet_size
13640
13641 if (mpvio.auth_info.external_user[0])
13642 sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));
13643
13644 if (res == CR_OK_HANDSHAKE_COMPLETE)
13645 thd->get_stmt_da()->disable_status();
13646 else
13647 my_ok(thd);
13648
13649 PSI_CALL_set_thread_user_host
13650 (thd->main_security_ctx.user, (uint)strlen(thd->main_security_ctx.user),
13651 thd->main_security_ctx.host_or_ip, (uint)strlen(thd->main_security_ctx.host_or_ip));
13652
13653 /* Ready to handle queries */
13654 DBUG_RETURN(0);
13655 }
13656
13657 /**
13658 MySQL Server Password Authentication Plugin
13659
13660 In the MySQL authentication protocol:
13661 1. the server sends the random scramble to the client
13662 2. client sends the encrypted password back to the server
13663 3. the server checks the password.
13664 */
native_password_authenticate(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)13665 static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
13666 MYSQL_SERVER_AUTH_INFO *info)
13667 {
13668 uchar *pkt;
13669 int pkt_len;
13670 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
13671 THD *thd=info->thd;
13672 DBUG_ENTER("native_password_authenticate");
13673
13674 /* generate the scramble, or reuse the old one */
13675 if (thd->scramble[SCRAMBLE_LENGTH])
13676 {
13677 thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
13678 /* and send it to the client */
13679 if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
13680 DBUG_RETURN(CR_AUTH_HANDSHAKE);
13681 }
13682
13683 /* reply and authenticate */
13684
13685 /*
13686 <digression>
13687 This is more complex than it looks.
13688
13689 The plugin (we) may be called right after the client was connected -
13690 and will need to send a scramble, read reply, authenticate.
13691
13692 Or the plugin may be called after another plugin has sent a scramble,
13693 and read the reply. If the client has used the correct client-plugin,
13694 we won't need to read anything here from the client, the client
13695 has already sent a reply with everything we need for authentication.
13696
13697 Or the plugin may be called after another plugin has sent a scramble,
13698 and read the reply, but the client has used the wrong client-plugin.
13699 We'll need to sent a "switch to another plugin" packet to the
13700 client and read the reply. "Use the short scramble" packet is a special
13701 case of "switch to another plugin" packet.
13702
13703 Or, perhaps, the plugin may be called after another plugin has
13704 done the handshake but did not send a useful scramble. We'll need
13705 to send a scramble (and perhaps a "switch to another plugin" packet)
13706 and read the reply.
13707
13708 Besides, a client may be an old one, that doesn't understand plugins.
13709 Or doesn't even understand 4.0 scramble.
13710
13711 And we want to keep the same protocol on the wire unless non-native
13712 plugins are involved.
13713
13714 Anyway, it still looks simple from a plugin point of view:
13715 "send the scramble, read the reply and authenticate".
13716 All the magic is transparently handled by the server.
13717 </digression>
13718 */
13719
13720 /* read the reply with the encrypted password */
13721 if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
13722 DBUG_RETURN(CR_AUTH_HANDSHAKE);
13723 DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
13724
13725 #ifdef NO_EMBEDDED_ACCESS_CHECKS
13726 DBUG_RETURN(CR_OK);
13727 #endif
13728
13729 DBUG_EXECUTE_IF("native_password_bad_reply", { pkt_len= 12; });
13730
13731 if (pkt_len == 0) /* no password */
13732 DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK);
13733
13734 info->password_used= PASSWORD_USED_YES;
13735 if (pkt_len == SCRAMBLE_LENGTH)
13736 {
13737 if (!mpvio->acl_user->salt_len)
13738 DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
13739
13740 if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt))
13741 DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
13742 else
13743 DBUG_RETURN(CR_OK);
13744 }
13745
13746 my_error(ER_HANDSHAKE_ERROR, MYF(0));
13747 DBUG_RETURN(CR_AUTH_HANDSHAKE);
13748 }
13749
old_password_authenticate(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)13750 static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
13751 MYSQL_SERVER_AUTH_INFO *info)
13752 {
13753 uchar *pkt;
13754 int pkt_len;
13755 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
13756 THD *thd=info->thd;
13757
13758 /* generate the scramble, or reuse the old one */
13759 if (thd->scramble[SCRAMBLE_LENGTH])
13760 {
13761 thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
13762 /* and send it to the client */
13763 if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
13764 return CR_AUTH_HANDSHAKE;
13765 }
13766
13767 /* read the reply and authenticate */
13768 if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
13769 return CR_AUTH_HANDSHAKE;
13770
13771 #ifdef NO_EMBEDDED_ACCESS_CHECKS
13772 return CR_OK;
13773 #endif
13774
13775 /*
13776 legacy: if switch_from_long_to_short_scramble,
13777 the password is sent \0-terminated, the pkt_len is always 9 bytes.
13778 We need to figure out the correct scramble length here.
13779 */
13780 if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
13781 pkt_len= (int)strnlen((char*)pkt, pkt_len);
13782
13783 if (pkt_len == 0) /* no password */
13784 return info->auth_string[0] ? CR_AUTH_USER_CREDENTIALS : CR_OK;
13785
13786 if (secure_auth(thd))
13787 return CR_AUTH_HANDSHAKE;
13788
13789 info->password_used= PASSWORD_USED_YES;
13790
13791 if (pkt_len == SCRAMBLE_LENGTH_323)
13792 {
13793 if (!mpvio->acl_user->salt_len)
13794 return CR_AUTH_USER_CREDENTIALS;
13795
13796 return check_scramble_323(pkt, thd->scramble,
13797 (ulong *) mpvio->acl_user->salt) ?
13798 CR_AUTH_USER_CREDENTIALS : CR_OK;
13799 }
13800
13801 my_error(ER_HANDSHAKE_ERROR, MYF(0));
13802 return CR_AUTH_HANDSHAKE;
13803 }
13804
13805 static struct st_mysql_auth native_password_handler=
13806 {
13807 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
13808 native_password_plugin_name.str,
13809 native_password_authenticate
13810 };
13811
13812 static struct st_mysql_auth old_password_handler=
13813 {
13814 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
13815 old_password_plugin_name.str,
13816 old_password_authenticate
13817 };
13818
maria_declare_plugin(mysql_password)13819 maria_declare_plugin(mysql_password)
13820 {
13821 MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
13822 &native_password_handler, /* type descriptor */
13823 native_password_plugin_name.str, /* Name */
13824 "R.J.Silk, Sergei Golubchik", /* Author */
13825 "Native MySQL authentication", /* Description */
13826 PLUGIN_LICENSE_GPL, /* License */
13827 NULL, /* Init function */
13828 NULL, /* Deinit function */
13829 0x0100, /* Version (1.0) */
13830 NULL, /* status variables */
13831 NULL, /* system variables */
13832 "1.0", /* String version */
13833 MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */
13834 },
13835 {
13836 MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
13837 &old_password_handler, /* type descriptor */
13838 old_password_plugin_name.str, /* Name */
13839 "R.J.Silk, Sergei Golubchik", /* Author */
13840 "Old MySQL-4.0 authentication", /* Description */
13841 PLUGIN_LICENSE_GPL, /* License */
13842 NULL, /* Init function */
13843 NULL, /* Deinit function */
13844 0x0100, /* Version (1.0) */
13845 NULL, /* status variables */
13846 NULL, /* system variables */
13847 "1.0", /* String version */
13848 MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */
13849 }
13850 maria_declare_plugin_end;
13851