1/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
2 Copyright (c) 2009, 2018, 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-1301, 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
60bool mysql_user_table_is_in_short_password_format= false;
61
62static LEX_CSTRING native_password_plugin_name= {
63 STRING_WITH_LEN("mysql_native_password")
64};
65
66static LEX_CSTRING old_password_plugin_name= {
67 STRING_WITH_LEN("mysql_old_password")
68};
69
70/// @todo make it configurable
71LEX_CSTRING *default_auth_plugin_name= &native_password_plugin_name;
72
73/*
74 Wildcard host, matches any hostname
75*/
76LEX_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*/
83LEX_CSTRING current_user= { STRING_WITH_LEN("*current_user") };
84LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role") };
85LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role") };
86
87
88#ifndef NO_EMBEDDED_ACCESS_CHECKS
89static plugin_ref old_password_plugin;
90#endif
91static plugin_ref native_password_plugin;
92
93/* Classes */
94
95struct acl_host_and_ip
96{
97 char *hostname;
98 long ip, ip_mask; // Used with masked ip:s
99};
100
101#ifndef NO_EMBEDDED_ACCESS_CHECKS
102static bool compare_hostname(const acl_host_and_ip *, const char *, const char *);
103#else
104#define compare_hostname(X,Y,Z) 0
105#endif
106
107class ACL_ACCESS {
108public:
109 ulong sort;
110 ulong access;
111};
112
113/* ACL_HOST is used if no host is specified */
114
115class ACL_HOST :public ACL_ACCESS
116{
117public:
118 acl_host_and_ip host;
119 char *db;
120};
121
122class ACL_USER_BASE :public ACL_ACCESS
123{
124
125public:
126 static void *operator new(size_t size, MEM_ROOT *mem_root)
127 { return (void*) alloc_root(mem_root, size); }
128 static void operator delete(void *, MEM_ROOT *){}
129 uchar flags; // field used to store various state information
130 LEX_CSTRING user;
131 /* list to hold references to granted roles (ACL_ROLE instances) */
132 DYNAMIC_ARRAY role_grants;
133};
134
135class ACL_USER :public ACL_USER_BASE
136{
137public:
138 acl_host_and_ip host;
139 size_t hostname_length;
140 USER_RESOURCES user_resource;
141 uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form
142 uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
143 enum SSL_type ssl_type;
144 const char *ssl_cipher, *x509_issuer, *x509_subject;
145 LEX_CSTRING plugin;
146 LEX_CSTRING auth_string;
147 LEX_CSTRING default_rolename;
148
149 ACL_USER *copy(MEM_ROOT *root)
150 {
151 ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
152 if (!dst)
153 return 0;
154 *dst= *this;
155 dst->user.str= safe_strdup_root(root, user.str);
156 dst->user.length= user.length;
157 dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
158 dst->x509_issuer= safe_strdup_root(root, x509_issuer);
159 dst->x509_subject= safe_strdup_root(root, x509_subject);
160 if (plugin.str == native_password_plugin_name.str ||
161 plugin.str == old_password_plugin_name.str)
162 dst->plugin= plugin;
163 else
164 dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
165 dst->auth_string.str= safe_strdup_root(root, auth_string.str);
166 dst->host.hostname= safe_strdup_root(root, host.hostname);
167 dst->default_rolename.str= safe_strdup_root(root, default_rolename.str);
168 dst->default_rolename.length= default_rolename.length;
169 bzero(&dst->role_grants, sizeof(role_grants));
170 return dst;
171 }
172
173 int cmp(const char *user2, const char *host2)
174 {
175 CHARSET_INFO *cs= system_charset_info;
176 int res;
177 res= strcmp(safe_str(user.str), safe_str(user2));
178 if (!res)
179 res= my_strcasecmp(cs, host.hostname, host2);
180 return res;
181 }
182
183 bool eq(const char *user2, const char *host2) { return !cmp(user2, host2); }
184
185 bool wild_eq(const char *user2, const char *host2, const char *ip2)
186 {
187 if (strcmp(safe_str(user.str), safe_str(user2)))
188 return false;
189
190 return compare_hostname(&host, host2, ip2 ? ip2 : host2);
191 }
192};
193
194class ACL_ROLE :public ACL_USER_BASE
195{
196public:
197 /*
198 In case of granting a role to a role, the access bits are merged together
199 via a bit OR operation and placed in the ACL_USER::access field.
200
201 When rebuilding role_grants via the rebuild_role_grant function,
202 the ACL_USER::access field needs to be reset first. The field
203 initial_role_access holds initial grants, as granted directly to the role
204 */
205 ulong initial_role_access;
206 /*
207 In subgraph traversal, when we need to traverse only a part of the graph
208 (e.g. all direct and indirect grantees of a role X), the counter holds the
209 number of affected neighbour nodes.
210 See also propagate_role_grants()
211 */
212 uint counter;
213 DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted
214
215 ACL_ROLE(ACL_USER * user, MEM_ROOT *mem);
216 ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *mem);
217
218};
219
220class ACL_DB :public ACL_ACCESS
221{
222public:
223 acl_host_and_ip host;
224 const char *user,*db;
225 ulong initial_access; /* access bits present in the table */
226};
227
228#ifndef DBUG_OFF
229/* status variables, only visible in SHOW STATUS after -#d,role_merge_stats */
230ulong role_global_merges= 0, role_db_merges= 0, role_table_merges= 0,
231 role_column_merges= 0, role_routine_merges= 0;
232#endif
233
234#ifndef NO_EMBEDDED_ACCESS_CHECKS
235static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd);
236static void update_hostname(acl_host_and_ip *host, const char *hostname);
237static ulong get_sort(uint count,...);
238static bool show_proxy_grants (THD *, const char *, const char *,
239 char *, size_t);
240static bool show_role_grants(THD *, const char *, const char *,
241 ACL_USER_BASE *, char *, size_t);
242static bool show_global_privileges(THD *, ACL_USER_BASE *,
243 bool, char *, size_t);
244static bool show_database_privileges(THD *, const char *, const char *,
245 char *, size_t);
246static bool show_table_and_column_privileges(THD *, const char *, const char *,
247 char *, size_t);
248static int show_routine_grants(THD *, const char *, const char *,
249 const Sp_handler *sph, char *, int);
250
251class Grant_tables;
252class User_table;
253class Proxies_priv_table;
254
255class 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;
271public:
272 ACL_PROXY_USER () {};
273
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
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
304 bool get_with_grant() { return with_grant; }
305 const char *get_user() { return user; }
306 const char *get_host() { return host.hostname; }
307 const char *get_proxied_user() { return proxied_user; }
308 const char *get_proxied_host() { return proxied_host.hostname; }
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 }
313 void set_host(MEM_ROOT *mem, const char *host_arg)
314 {
315 update_hostname(&host, safe_strdup_root(mem, host_arg));
316 }
317
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
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
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
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
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
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
410 void set_data(ACL_PROXY_USER *grant)
411 {
412 with_grant= grant->with_grant;
413 }
414
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
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
471class acl_entry :public hash_filo_element
472{
473public:
474 ulong access;
475 uint16 length;
476 char key[1]; // Key will be stored here
477};
478
479
480static 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
487static 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
494struct 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
506static 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
513bool 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
597static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users;
598static HASH acl_roles;
599/*
600 An hash containing mappings user <--> role
601
602 A hash is used so as to make updates quickly
603 The hashkey used represents all the entries combined
604*/
605static HASH acl_roles_mappings;
606static MEM_ROOT acl_memroot, grant_memroot;
607static bool initialized=0;
608static bool allow_all_hosts=1;
609static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
610static HASH package_spec_priv_hash, package_body_priv_hash;
611static DYNAMIC_ARRAY acl_wild_hosts;
612static Hash_filo<acl_entry> *acl_cache;
613static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
614static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
615static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
616static ulong get_sort(uint count,...);
617static void init_check_host(void);
618static void rebuild_check_host(void);
619static void rebuild_role_grants(void);
620static ACL_USER *find_user_exact(const char *host, const char *user);
621static ACL_USER *find_user_wild(const char *host, const char *user, const char *ip= 0);
622static ACL_ROLE *find_acl_role(const char *user);
623static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, const LEX_CSTRING *h, const LEX_CSTRING *r);
624static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host);
625static bool update_user_table(THD *, const User_table &, const char *, const char *, const
626 char *, size_t new_password_len);
627static bool acl_load(THD *thd, const Grant_tables& grant_tables);
628static inline void get_grantor(THD *thd, char* grantor);
629static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname);
630static bool get_YN_as_bool(Field *field);
631
632#define ROLE_CYCLE_FOUND 2
633static int traverse_role_graph_up(ACL_ROLE *, void *,
634 int (*) (ACL_ROLE *, void *),
635 int (*) (ACL_ROLE *, ACL_ROLE *, void *));
636
637static int traverse_role_graph_down(ACL_USER_BASE *, void *,
638 int (*) (ACL_USER_BASE *, void *),
639 int (*) (ACL_USER_BASE *, ACL_ROLE *, void *));
640
641
642HASH *Sp_handler_procedure::get_priv_hash() const
643{
644 return &proc_priv_hash;
645}
646
647
648HASH *Sp_handler_function::get_priv_hash() const
649{
650 return &func_priv_hash;
651}
652
653
654HASH *Sp_handler_package_spec::get_priv_hash() const
655{
656 return &package_spec_priv_hash;
657}
658
659
660HASH *Sp_handler_package_body::get_priv_hash() const
661{
662 return &package_body_priv_hash;
663}
664
665
666/*
667 Enumeration of ACL/GRANT tables in the mysql database
668*/
669enum enum_acl_tables
670{
671 USER_TABLE,
672 DB_TABLE,
673 TABLES_PRIV_TABLE,
674 COLUMNS_PRIV_TABLE,
675#define FIRST_OPTIONAL_TABLE HOST_TABLE
676 HOST_TABLE,
677 PROCS_PRIV_TABLE,
678 PROXIES_PRIV_TABLE,
679 ROLES_MAPPING_TABLE,
680 TABLES_MAX // <== always the last
681};
682// bits for open_grant_tables
683static const int Table_user= 1 << USER_TABLE;
684static const int Table_db= 1 << DB_TABLE;
685static const int Table_tables_priv= 1 << TABLES_PRIV_TABLE;
686static const int Table_columns_priv= 1 << COLUMNS_PRIV_TABLE;
687static const int Table_host= 1 << HOST_TABLE;
688static const int Table_procs_priv= 1 << PROCS_PRIV_TABLE;
689static const int Table_proxies_priv= 1 << PROXIES_PRIV_TABLE;
690static const int Table_roles_mapping= 1 << ROLES_MAPPING_TABLE;
691
692/**
693 Base class representing a generic grant table from the mysql database.
694
695 The potential tables that this class can represent are:
696 user, db, columns_priv, tables_priv, host, procs_priv, proxies_priv,
697 roles_mapping
698
699 Objects belonging to this parent class can only be constructed by the
700 Grants_table class. This ensures the correct initialization of the objects.
701*/
702class Grant_table_base
703{
704 public:
705 /* Number of fields for this Grant Table. */
706 uint num_fields() const { return tl.table->s->fields; }
707 /* Check if the table exists after an attempt to open it was made.
708 Some tables, such as the host table in MySQL 5.6.7+ are missing. */
709 bool table_exists() const { return tl.table; };
710 /* Initializes the READ_RECORD structure provided as a parameter
711 to read through the whole table, with all columns available. Cleaning up
712 is the caller's job. */
713 bool init_read_record(READ_RECORD* info, THD* thd) const
714 {
715 DBUG_ASSERT(tl.table);
716 bool result= ::init_read_record(info, thd, tl.table, NULL, NULL, 1,
717 true, false);
718 if (!result)
719 tl.table->use_all_columns();
720 return result;
721 }
722
723 /* Return the number of privilege columns for this table. */
724 uint num_privileges() const { return num_privilege_cols; }
725 /* Return a privilege column by index. */
726 Field* priv_field(uint privilege_idx) const
727 {
728 DBUG_ASSERT(privilege_idx < num_privileges());
729 return tl.table->field[start_privilege_column + privilege_idx];
730 }
731
732 /* Fetch the privileges from the table as a set of bits. The first column
733 is represented by the first bit in the result, the second column by the
734 second bit, etc. */
735 ulong get_access() const
736 {
737 return get_access(start_privilege_column,
738 start_privilege_column + num_privileges() - 1);
739 }
740
741 /* Return the underlying TABLE handle. */
742 TABLE* table() const
743 {
744 return tl.table;
745 }
746
747 /** Check if the table was opened, issue an error otherwise. */
748 int no_such_table() const
749 {
750 if (table_exists())
751 return 0;
752
753 my_error(ER_NO_SUCH_TABLE, MYF(0), tl.db.str, tl.alias.str);
754 return 1;
755 }
756
757
758 protected:
759 friend class Grant_tables;
760
761 Grant_table_base() : start_privilege_column(0), num_privilege_cols(0)
762 {
763 bzero(&tl, sizeof(tl));
764 };
765
766 /* Initialization sequence common for all grant tables. This should be called
767 after all table-specific initialization is performed. */
768 void init(enum thr_lock_type lock_type, bool is_optional)
769 {
770 tl.open_type= OT_BASE_ONLY;
771 if (lock_type >= TL_WRITE_ALLOW_WRITE)
772 tl.updating= 1;
773 if (is_optional)
774 tl.open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
775 }
776
777 /*
778 Get all access bits from table between start_field and end_field indices.
779
780 IMPLEMENTATION
781 The record should be already read in table->record[0]. All privileges
782 are specified as an ENUM(Y,N).
783
784 SYNOPSIS
785 get_access()
786 start_field_idx The field index at which the first privilege
787 specification begins.
788 end_field_idx The field index at which the last privilege
789 specification is located.
790
791 RETURN VALUE
792 privilege mask
793 */
794 ulong get_access(uint start_field_idx, uint end_field_idx) const
795 {
796 ulong access_bits= 0, bit= 1;
797 for (uint i = start_field_idx; i <= end_field_idx; i++, bit<<=1)
798 {
799 if (get_YN_as_bool(tl.table->field[i]))
800 access_bits|= bit;
801 }
802 return access_bits;
803 }
804
805 /* Compute how many privilege columns this table has. This method
806 can only be called after the table has been opened.
807
808 IMPLEMENTATION
809 A privilege column is of type enum('Y', 'N'). Privilege columns are
810 expected to be one after another.
811 */
812 void compute_num_privilege_cols()
813 {
814 if (!table_exists()) // Table does not exist or not opened.
815 return;
816
817 num_privilege_cols= 0;
818 for (uint i= 0; i < num_fields(); i++)
819 {
820 Field *field= tl.table->field[i];
821 if (num_privilege_cols > 0 && field->real_type() != MYSQL_TYPE_ENUM)
822 return;
823 if (field->real_type() == MYSQL_TYPE_ENUM &&
824 static_cast<Field_enum*>(field)->typelib->count == 2)
825 {
826 num_privilege_cols++;
827 if (num_privilege_cols == 1)
828 start_privilege_column= i;
829 }
830 }
831 }
832
833 /* The index at which privilege columns start. */
834 uint start_privilege_column;
835 /* The number of privilege columns in the table. */
836 uint num_privilege_cols;
837
838 TABLE_LIST tl;
839};
840
841class User_table: public Grant_table_base
842{
843 public:
844 /* Field getters return NULL if the column is not present in the table.
845 This is consistent only if the table is in a supported version. We do
846 not guard against corrupt tables. (yet) */
847 Field* host() const
848 { return get_field(0); }
849 Field* user() const
850 { return get_field(1); }
851 Field* password() const
852 { return have_password() ? NULL : tl.table->field[2]; }
853 /* Columns after privilege columns. */
854 Field* ssl_type() const
855 { return get_field(start_privilege_column + num_privileges()); }
856 Field* ssl_cipher() const
857 { return get_field(start_privilege_column + num_privileges() + 1); }
858 Field* x509_issuer() const
859 { return get_field(start_privilege_column + num_privileges() + 2); }
860 Field* x509_subject() const
861 { return get_field(start_privilege_column + num_privileges() + 3); }
862 Field* max_questions() const
863 { return get_field(start_privilege_column + num_privileges() + 4); }
864 Field* max_updates() const
865 { return get_field(start_privilege_column + num_privileges() + 5); }
866 Field* max_connections() const
867 { return get_field(start_privilege_column + num_privileges() + 6); }
868 Field* max_user_connections() const
869 { return get_field(start_privilege_column + num_privileges() + 7); }
870 Field* plugin() const
871 { return get_field(start_privilege_column + num_privileges() + 8); }
872 Field* authentication_string() const
873 { return get_field(start_privilege_column + num_privileges() + 9); }
874 Field* password_expired() const
875 { return get_field(start_privilege_column + num_privileges() + 10); }
876 Field* is_role() const
877 { return get_field(start_privilege_column + num_privileges() + 11); }
878 Field* default_role() const
879 { return get_field(start_privilege_column + num_privileges() + 12); }
880 Field* max_statement_time() const
881 { return get_field(start_privilege_column + num_privileges() + 13); }
882
883 /*
884 Check if a user entry in the user table is marked as being a role entry
885
886 IMPLEMENTATION
887 Access the coresponding column and check the coresponding ENUM of the form
888 ENUM('N', 'Y')
889
890 SYNOPSIS
891 check_is_role()
892 form an open table to read the entry from.
893 The record should be already read in table->record[0]
894
895 RETURN VALUE
896 TRUE if the user is marked as a role
897 FALSE otherwise
898 */
899 bool check_is_role() const
900 {
901 /* Table version does not support roles */
902 if (!is_role())
903 return false;
904
905 return get_YN_as_bool(is_role());
906 }
907
908
909 private:
910 friend class Grant_tables;
911
912 /* Only Grant_tables can instantiate this class. */
913 User_table() {};
914
915 void init(enum thr_lock_type lock_type)
916 {
917 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
918 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, lock_type);
919 Grant_table_base::init(lock_type, false);
920 }
921
922 /* The user table is a bit different compared to the other Grant tables.
923 Usually, we only add columns to the grant tables when adding functionality.
924 This makes it easy to test which version of the table we are using, by
925 just looking at the number of fields present in the table.
926
927 In MySQL 5.7.6 the Password column was removed. We need to guard for that.
928 The field-fetching methods for the User table return NULL if the field
929 doesn't exist. This simplifies checking of table "version", as we don't
930 have to make use of num_fields() any more.
931 */
932 inline Field* get_field(uint field_num) const
933 {
934 if (field_num >= num_fields())
935 return NULL;
936
937 return tl.table->field[field_num];
938 }
939
940 /* Normally password column is the third column in the table. If privileges
941 start on the third column instead, we are missing the password column.
942 This means we are using a MySQL 5.7.6+ data directory. */
943 bool have_password() const { return start_privilege_column == 2; }
944
945};
946
947class Db_table: public Grant_table_base
948{
949 public:
950 Field* host() const { return tl.table->field[0]; }
951 Field* db() const { return tl.table->field[1]; }
952 Field* user() const { return tl.table->field[2]; }
953
954 private:
955 friend class Grant_tables;
956
957 Db_table() {};
958
959 void init(enum thr_lock_type lock_type)
960 {
961 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
962 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_DB_NAME, NULL, lock_type);
963 Grant_table_base::init(lock_type, false);
964 }
965};
966
967class Tables_priv_table: public Grant_table_base
968{
969 public:
970 Field* host() const { return tl.table->field[0]; }
971 Field* db() const { return tl.table->field[1]; }
972 Field* user() const { return tl.table->field[2]; }
973 Field* table_name() const { return tl.table->field[3]; }
974 Field* grantor() const { return tl.table->field[4]; }
975 Field* timestamp() const { return tl.table->field[5]; }
976 Field* table_priv() const { return tl.table->field[6]; }
977 Field* column_priv() const { return tl.table->field[7]; }
978
979 private:
980 friend class Grant_tables;
981
982 Tables_priv_table() {};
983
984 void init(enum thr_lock_type lock_type, Grant_table_base *next_table= NULL)
985 {
986 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
987 LEX_CSTRING MYSQL_TABLES_PRIV_NAME={STRING_WITH_LEN("tables_priv") };
988 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLES_PRIV_NAME, NULL, lock_type);
989 Grant_table_base::init(lock_type, false);
990 }
991};
992
993class Columns_priv_table: public Grant_table_base
994{
995 public:
996 Field* host() const { return tl.table->field[0]; }
997 Field* db() const { return tl.table->field[1]; }
998 Field* user() const { return tl.table->field[2]; }
999 Field* table_name() const { return tl.table->field[3]; }
1000 Field* column_name() const { return tl.table->field[4]; }
1001 Field* timestamp() const { return tl.table->field[5]; }
1002 Field* column_priv() const { return tl.table->field[6]; }
1003
1004 private:
1005 friend class Grant_tables;
1006
1007 Columns_priv_table() {};
1008
1009 void init(enum thr_lock_type lock_type)
1010 {
1011 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1012 LEX_CSTRING MYSQL_COLUMNS_PRIV_NAME={ STRING_WITH_LEN("columns_priv") };
1013 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_COLUMNS_PRIV_NAME, NULL, lock_type);
1014 Grant_table_base::init(lock_type, false);
1015 }
1016};
1017
1018class Host_table: public Grant_table_base
1019{
1020 public:
1021 Field* host() const { return tl.table->field[0]; }
1022 Field* db() const { return tl.table->field[1]; }
1023
1024 private:
1025 friend class Grant_tables;
1026
1027 Host_table() {}
1028
1029 void init(enum thr_lock_type lock_type)
1030 {
1031 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1032 LEX_CSTRING MYSQL_HOST_NAME={STRING_WITH_LEN("host") };
1033 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HOST_NAME, NULL, lock_type);
1034 Grant_table_base::init(lock_type, true);
1035 }
1036};
1037
1038class Procs_priv_table: public Grant_table_base
1039{
1040 public:
1041 Field* host() const { return tl.table->field[0]; }
1042 Field* db() const { return tl.table->field[1]; }
1043 Field* user() const { return tl.table->field[2]; }
1044 Field* routine_name() const { return tl.table->field[3]; }
1045 Field* routine_type() const { return tl.table->field[4]; }
1046 Field* grantor() const { return tl.table->field[5]; }
1047 Field* proc_priv() const { return tl.table->field[6]; }
1048 Field* timestamp() const { return tl.table->field[7]; }
1049
1050 private:
1051 friend class Grant_tables;
1052
1053 Procs_priv_table() {}
1054
1055 void init(enum thr_lock_type lock_type)
1056 {
1057 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1058 LEX_CSTRING MYSQL_PROCS_PRIV_NAME={STRING_WITH_LEN("procs_priv") };
1059 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROCS_PRIV_NAME, NULL, lock_type);
1060 Grant_table_base::init(lock_type, true);
1061 }
1062};
1063
1064class Proxies_priv_table: public Grant_table_base
1065{
1066 public:
1067 Field* host() const { return tl.table->field[0]; }
1068 Field* user() const { return tl.table->field[1]; }
1069 Field* proxied_host() const { return tl.table->field[2]; }
1070 Field* proxied_user() const { return tl.table->field[3]; }
1071 Field* with_grant() const { return tl.table->field[4]; }
1072 Field* grantor() const { return tl.table->field[5]; }
1073 Field* timestamp() const { return tl.table->field[6]; }
1074
1075 private:
1076 friend class Grant_tables;
1077
1078 Proxies_priv_table() {}
1079
1080 void init(enum thr_lock_type lock_type)
1081 {
1082 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1083 LEX_CSTRING MYSQL_PROXIES_PRIV_NAME={STRING_WITH_LEN("proxies_priv") };
1084 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROXIES_PRIV_NAME, NULL, lock_type);
1085 Grant_table_base::init(lock_type, true);
1086 }
1087};
1088
1089class Roles_mapping_table: public Grant_table_base
1090{
1091 public:
1092 Field* host() const { return tl.table->field[0]; }
1093 Field* user() const { return tl.table->field[1]; }
1094 Field* role() const { return tl.table->field[2]; }
1095 Field* admin_option() const { return tl.table->field[3]; }
1096
1097 private:
1098 friend class Grant_tables;
1099
1100 Roles_mapping_table() {}
1101
1102 void init(enum thr_lock_type lock_type)
1103 {
1104 /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1105 LEX_CSTRING MYSQL_ROLES_MAPPING_NAME={STRING_WITH_LEN("roles_mapping") };
1106 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_ROLES_MAPPING_NAME, NULL, lock_type);
1107 Grant_table_base::init(lock_type, true);
1108 }
1109};
1110
1111/**
1112 Class that represents a collection of grant tables.
1113*/
1114class Grant_tables
1115{
1116 public:
1117 /* When constructing the Grant_tables object, we initialize only
1118 the tables which are going to be opened.
1119 @param which_tables Bitmap of which tables to open.
1120 @param lock_type Lock type to use when opening tables.
1121 */
1122 Grant_tables(int which_tables, enum thr_lock_type lock_type)
1123 {
1124 DBUG_ENTER("Grant_tables::Grant_tables");
1125 DBUG_PRINT("info", ("which_tables: %x, lock_type: %u",
1126 which_tables, lock_type));
1127 DBUG_ASSERT(which_tables); /* At least one table must be opened. */
1128 Grant_table_base* prev= NULL;
1129 /* We start from the last table, Table_roles_mapping, such that
1130 the first one in the linked list is Table_user. */
1131 if (which_tables & Table_roles_mapping)
1132 {
1133 m_roles_mapping_table.init(lock_type);
1134 prev= &m_roles_mapping_table;
1135 }
1136 if (which_tables & Table_proxies_priv)
1137 {
1138 m_proxies_priv_table.init(lock_type);
1139 link_tables(&m_proxies_priv_table, prev);
1140 prev= &m_proxies_priv_table;
1141 }
1142 if (which_tables & Table_procs_priv)
1143 {
1144 m_procs_priv_table.init(lock_type);
1145 link_tables(&m_procs_priv_table, prev);
1146 prev= &m_procs_priv_table;
1147 }
1148 if (which_tables & Table_host)
1149 {
1150 m_host_table.init(lock_type);
1151 link_tables(&m_host_table, prev);
1152 prev= &m_host_table;
1153 }
1154 if (which_tables & Table_columns_priv)
1155 {
1156 m_columns_priv_table.init(lock_type);
1157 link_tables(&m_columns_priv_table, prev);
1158 prev= &m_columns_priv_table;
1159 }
1160 if (which_tables & Table_tables_priv)
1161 {
1162 m_tables_priv_table.init(lock_type);
1163 link_tables(&m_tables_priv_table, prev);
1164 prev= &m_tables_priv_table;
1165 }
1166 if (which_tables & Table_db)
1167 {
1168 m_db_table.init(lock_type);
1169 link_tables(&m_db_table, prev);
1170 prev= &m_db_table;
1171 }
1172 if (which_tables & Table_user)
1173 {
1174 m_user_table.init(lock_type);
1175 link_tables(&m_user_table, prev);
1176 prev= &m_user_table;
1177 }
1178
1179 first_table_in_list= prev;
1180 DBUG_VOID_RETURN;
1181 }
1182
1183 /* Before any operation is possible on grant tables, they must be opened.
1184 This opens the tables according to the lock type specified during
1185 construction.
1186
1187 @retval 1 replication filters matched. Abort the operation,
1188 but return OK (!)
1189 @retval 0 tables were opened successfully
1190 @retval -1 error, tables could not be opened
1191 */
1192 int open_and_lock(THD *thd)
1193 {
1194 DBUG_ENTER("Grant_tables::open_and_lock");
1195 DBUG_ASSERT(first_table_in_list);
1196#ifdef HAVE_REPLICATION
1197 if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE &&
1198 thd->slave_thread && !thd->spcont)
1199 {
1200 /*
1201 GRANT and REVOKE are applied the slave in/exclusion rules as they are
1202 some kind of updates to the mysql.% tables.
1203 */
1204 Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
1205 if (rpl_filter->is_on() &&
1206 !rpl_filter->tables_ok(0, &first_table_in_list->tl))
1207 DBUG_RETURN(1);
1208 }
1209#endif
1210 if (open_and_lock_tables(thd, &first_table_in_list->tl, FALSE,
1211 MYSQL_LOCK_IGNORE_TIMEOUT))
1212 DBUG_RETURN(-1);
1213
1214 /*
1215 We can read privilege tables even when !initialized.
1216 This can be acl_load() - server startup or FLUSH PRIVILEGES
1217 */
1218 if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE &&
1219 !initialized)
1220 {
1221 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1222 DBUG_RETURN(-1);
1223 }
1224
1225 /* The privilge columns vary based on MariaDB version. Figure out
1226 how many we have after we've opened the table. */
1227 m_user_table.compute_num_privilege_cols();
1228 m_db_table.compute_num_privilege_cols();
1229 m_tables_priv_table.compute_num_privilege_cols();
1230 m_columns_priv_table.compute_num_privilege_cols();
1231 m_host_table.compute_num_privilege_cols();
1232 m_procs_priv_table.compute_num_privilege_cols();
1233 m_proxies_priv_table.compute_num_privilege_cols();
1234 m_roles_mapping_table.compute_num_privilege_cols();
1235 DBUG_RETURN(0);
1236 }
1237
1238 inline const User_table& user_table() const
1239 {
1240 return m_user_table;
1241 }
1242
1243 inline const Db_table& db_table() const
1244 {
1245 return m_db_table;
1246 }
1247
1248
1249 inline const Tables_priv_table& tables_priv_table() const
1250 {
1251 return m_tables_priv_table;
1252 }
1253
1254 inline const Columns_priv_table& columns_priv_table() const
1255 {
1256 return m_columns_priv_table;
1257 }
1258
1259 inline const Host_table& host_table() const
1260 {
1261 return m_host_table;
1262 }
1263
1264 inline const Procs_priv_table& procs_priv_table() const
1265 {
1266 return m_procs_priv_table;
1267 }
1268
1269 inline const Proxies_priv_table& proxies_priv_table() const
1270 {
1271 return m_proxies_priv_table;
1272 }
1273
1274 inline const Roles_mapping_table& roles_mapping_table() const
1275 {
1276 return m_roles_mapping_table;
1277 }
1278
1279 private:
1280 User_table m_user_table;
1281 Db_table m_db_table;
1282 Tables_priv_table m_tables_priv_table;
1283 Columns_priv_table m_columns_priv_table;
1284 Host_table m_host_table;
1285 Procs_priv_table m_procs_priv_table;
1286 Proxies_priv_table m_proxies_priv_table;
1287 Roles_mapping_table m_roles_mapping_table;
1288
1289 /* The grant tables are set-up in a linked list. We keep the head of it. */
1290 Grant_table_base *first_table_in_list;
1291 /**
1292 Chain two grant tables' TABLE_LIST members.
1293 */
1294 static void link_tables(Grant_table_base *from, Grant_table_base *to)
1295 {
1296 DBUG_ASSERT(from);
1297 if (to)
1298 from->tl.next_local= from->tl.next_global= &to->tl;
1299 else
1300 from->tl.next_local= from->tl.next_global= NULL;
1301 }
1302};
1303
1304
1305void ACL_PROXY_USER::init(const Proxies_priv_table& proxies_priv_table,
1306 MEM_ROOT *mem)
1307{
1308 init(get_field(mem, proxies_priv_table.host()),
1309 get_field(mem, proxies_priv_table.user()),
1310 get_field(mem, proxies_priv_table.proxied_host()),
1311 get_field(mem, proxies_priv_table.proxied_user()),
1312 proxies_priv_table.with_grant()->val_int() != 0);
1313}
1314
1315
1316
1317/*
1318 Enumeration of various ACL's and Hashes used in handle_grant_struct()
1319*/
1320enum enum_acl_lists
1321{
1322 USER_ACL= 0,
1323 ROLE_ACL,
1324 DB_ACL,
1325 COLUMN_PRIVILEGES_HASH,
1326 PROC_PRIVILEGES_HASH,
1327 FUNC_PRIVILEGES_HASH,
1328 PACKAGE_SPEC_PRIVILEGES_HASH,
1329 PACKAGE_BODY_PRIVILEGES_HASH,
1330 PROXY_USERS_ACL,
1331 ROLES_MAPPINGS_HASH
1332};
1333
1334ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) : counter(0)
1335{
1336
1337 access= user->access;
1338 /* set initial role access the same as the table row privileges */
1339 initial_role_access= user->access;
1340 this->user.str= safe_strdup_root(root, user->user.str);
1341 this->user.length= user->user.length;
1342 bzero(&role_grants, sizeof(role_grants));
1343 bzero(&parent_grantee, sizeof(parent_grantee));
1344 flags= IS_ROLE;
1345}
1346
1347ACL_ROLE::ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *root) :
1348 initial_role_access(privileges), counter(0)
1349{
1350 this->access= initial_role_access;
1351 this->user.str= safe_strdup_root(root, rolename);
1352 this->user.length= strlen(rolename);
1353 bzero(&role_grants, sizeof(role_grants));
1354 bzero(&parent_grantee, sizeof(parent_grantee));
1355 flags= IS_ROLE;
1356}
1357
1358
1359static bool is_invalid_role_name(const char *str)
1360{
1361 if (*str && strcasecmp(str, "PUBLIC") && strcasecmp(str, "NONE"))
1362 return false;
1363
1364 my_error(ER_INVALID_ROLE, MYF(0), str);
1365 return true;
1366}
1367
1368
1369static void free_acl_user(ACL_USER *user)
1370{
1371 delete_dynamic(&(user->role_grants));
1372}
1373
1374static void free_acl_role(ACL_ROLE *role)
1375{
1376 delete_dynamic(&(role->role_grants));
1377 delete_dynamic(&(role->parent_grantee));
1378}
1379
1380static my_bool check_if_exists(THD *, plugin_ref, void *)
1381{
1382 return TRUE;
1383}
1384
1385static bool has_validation_plugins()
1386{
1387 return plugin_foreach(NULL, check_if_exists,
1388 MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL);
1389}
1390
1391struct validation_data { LEX_CSTRING *user, *password; };
1392
1393static my_bool do_validate(THD *, plugin_ref plugin, void *arg)
1394{
1395 struct validation_data *data= (struct validation_data *)arg;
1396 struct st_mariadb_password_validation *handler=
1397 (st_mariadb_password_validation *)plugin_decl(plugin)->info;
1398 return handler->validate_password(data->user, data->password);
1399}
1400
1401
1402static bool validate_password(LEX_USER *user, THD *thd)
1403{
1404 if (user->pwtext.length || !user->pwhash.length)
1405 {
1406 struct validation_data data= { &user->user,
1407 user->pwtext.str ? &user->pwtext :
1408 const_cast<LEX_CSTRING *>(&empty_clex_str) };
1409 if (plugin_foreach(NULL, do_validate,
1410 MariaDB_PASSWORD_VALIDATION_PLUGIN, &data))
1411 {
1412 my_error(ER_NOT_VALID_PASSWORD, MYF(0));
1413 return true;
1414 }
1415 }
1416 else
1417 {
1418 if (!thd->slave_thread &&
1419 strict_password_validation && has_validation_plugins())
1420 {
1421 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--strict-password-validation");
1422 return true;
1423 }
1424 }
1425 return false;
1426}
1427
1428/**
1429 Convert scrambled password to binary form, according to scramble type,
1430 Binary form is stored in user.salt.
1431
1432 @param acl_user The object where to store the salt
1433 @param password The password hash containing the salt
1434 @param password_len The length of the password hash
1435
1436 Despite the name of the function it is used when loading ACLs from disk
1437 to store the password hash in the ACL_USER object.
1438*/
1439
1440static void
1441set_user_salt(ACL_USER *acl_user, const char *password, size_t password_len)
1442{
1443 if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
1444 {
1445 get_salt_from_password(acl_user->salt, password);
1446 acl_user->salt_len= SCRAMBLE_LENGTH;
1447 }
1448 else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1449 {
1450 get_salt_from_password_323((ulong *) acl_user->salt, password);
1451 acl_user->salt_len= SCRAMBLE_LENGTH_323;
1452 }
1453 else
1454 acl_user->salt_len= 0;
1455}
1456
1457static const char *fix_plugin_ptr(const char *name)
1458{
1459 if (my_strcasecmp(system_charset_info, name,
1460 native_password_plugin_name.str) == 0)
1461 return native_password_plugin_name.str;
1462 else
1463 if (my_strcasecmp(system_charset_info, name,
1464 old_password_plugin_name.str) == 0)
1465 return old_password_plugin_name.str;
1466 else
1467 return name;
1468}
1469
1470/**
1471 Fix ACL::plugin pointer to point to a hard-coded string, if appropriate
1472
1473 Make sure that if ACL_USER's plugin is a built-in, then it points
1474 to a hard coded string, not to an allocated copy. Run-time, for
1475 authentication, we want to be able to detect built-ins by comparing
1476 pointers, not strings.
1477
1478 Additionally - update the salt if the plugin is built-in.
1479
1480 @retval 0 the pointers were fixed
1481 @retval 1 this ACL_USER uses a not built-in plugin
1482*/
1483static bool fix_user_plugin_ptr(ACL_USER *user)
1484{
1485 if (lex_string_eq(&user->plugin, &native_password_plugin_name))
1486 user->plugin= native_password_plugin_name;
1487 else
1488 if (lex_string_eq(&user->plugin, &old_password_plugin_name))
1489 user->plugin= old_password_plugin_name;
1490 else
1491 return true;
1492
1493 if (user->auth_string.length)
1494 set_user_salt(user, user->auth_string.str, user->auth_string.length);
1495 return false;
1496}
1497
1498
1499/*
1500 Validates the password, calculates password hash, transforms
1501 equivalent LEX_USER representations.
1502
1503 Upon entering this function:
1504
1505 - if user->plugin is specified, user->auth is the plugin auth data.
1506 - if user->plugin is mysql_native_password or mysql_old_password,
1507 user->auth is the password hash, and LEX_USER is transformed
1508 to match the next case (that is, user->plugin is cleared).
1509 - if user->plugin is NOT specified, built-in auth is assumed, that is
1510 mysql_native_password or mysql_old_password. In that case,
1511 user->pwhash is the password hash. And user->pwtext is the original
1512 plain-text password. Either one can be set or both.
1513
1514 Upon exiting this function:
1515
1516 - user->pwtext is left untouched
1517 - user->pwhash is the password hash, as the mysql.user.password column
1518 - user->plugin is the plugin name, as the mysql.user.plugin column
1519 - user->auth is the plugin auth data, as the mysql.user.authentication_string column
1520*/
1521static bool fix_lex_user(THD *thd, LEX_USER *user)
1522{
1523 size_t check_length;
1524
1525 DBUG_ASSERT(user->plugin.length || !user->auth.length);
1526 DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length)));
1527
1528 if (lex_string_eq(&user->plugin, &native_password_plugin_name))
1529 check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
1530 else
1531 if (lex_string_eq(&user->plugin, &old_password_plugin_name))
1532 check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
1533 else
1534 if (user->plugin.length)
1535 return false; // nothing else to do
1536 else if (thd->variables.old_passwords == 1 ||
1537 user->pwhash.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1538 check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
1539 else
1540 check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
1541
1542 if (user->plugin.length)
1543 {
1544 user->pwhash= user->auth;
1545 user->plugin= empty_clex_str;
1546 user->auth= empty_clex_str;
1547 }
1548
1549 if (user->pwhash.length && user->pwhash.length != check_length)
1550 {
1551 my_error(ER_PASSWD_LENGTH, MYF(0), (int) check_length);
1552 return true;
1553 }
1554
1555 if (user->pwtext.length && !user->pwhash.length)
1556 {
1557 size_t scramble_length;
1558 void (*make_scramble)(char *, const char *, size_t);
1559
1560 if (thd->variables.old_passwords == 1)
1561 {
1562 scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
1563 make_scramble= my_make_scrambled_password_323;
1564 }
1565 else
1566 {
1567 scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
1568 make_scramble= my_make_scrambled_password;
1569 }
1570
1571 Query_arena *arena, backup;
1572 arena= thd->activate_stmt_arena_if_needed(&backup);
1573 char *buff= (char *) thd->alloc(scramble_length + 1);
1574 if (arena)
1575 thd->restore_active_arena(arena, &backup);
1576
1577 if (buff == NULL)
1578 return true;
1579 make_scramble(buff, user->pwtext.str, user->pwtext.length);
1580 user->pwhash.str= buff;
1581 user->pwhash.length= scramble_length;
1582 }
1583
1584 return false;
1585}
1586
1587
1588static bool get_YN_as_bool(Field *field)
1589{
1590 char buff[2];
1591 String res(buff,sizeof(buff),&my_charset_latin1);
1592 field->val_str(&res);
1593 return res[0] == 'Y' || res[0] == 'y';
1594}
1595
1596
1597/*
1598 Initialize structures responsible for user/db-level privilege checking and
1599 load privilege information for them from tables in the 'mysql' database.
1600
1601 SYNOPSIS
1602 acl_init()
1603 dont_read_acl_tables TRUE if we want to skip loading data from
1604 privilege tables and disable privilege checking.
1605
1606 NOTES
1607 This function is mostly responsible for preparatory steps, main work
1608 on initialization and grants loading is done in acl_reload().
1609
1610 RETURN VALUES
1611 0 ok
1612 1 Could not initialize grant's
1613*/
1614
1615bool acl_init(bool dont_read_acl_tables)
1616{
1617 THD *thd;
1618 bool return_val;
1619 DBUG_ENTER("acl_init");
1620
1621 acl_cache= new Hash_filo<acl_entry>(ACL_CACHE_SIZE, 0, 0,
1622 (my_hash_get_key) acl_entry_get_key,
1623 (my_hash_free_key) free,
1624 &my_charset_utf8_bin);
1625
1626 /*
1627 cache built-in native authentication plugins,
1628 to avoid hash searches and a global mutex lock on every connect
1629 */
1630 native_password_plugin= my_plugin_lock_by_name(0,
1631 &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
1632 old_password_plugin= my_plugin_lock_by_name(0,
1633 &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
1634
1635 if (!native_password_plugin || !old_password_plugin)
1636 DBUG_RETURN(1);
1637
1638 if (dont_read_acl_tables)
1639 {
1640 DBUG_RETURN(0); /* purecov: tested */
1641 }
1642
1643 /*
1644 To be able to run this from boot, we allocate a temporary THD
1645 */
1646 if (!(thd=new THD(0)))
1647 DBUG_RETURN(1); /* purecov: inspected */
1648 thd->thread_stack= (char*) &thd;
1649 thd->store_globals();
1650 /*
1651 It is safe to call acl_reload() since acl_* arrays and hashes which
1652 will be freed there are global static objects and thus are initialized
1653 by zeros at startup.
1654 */
1655 return_val= acl_reload(thd);
1656 delete thd;
1657 DBUG_RETURN(return_val);
1658}
1659
1660/**
1661 Choose from either native or old password plugins when assigning a password
1662*/
1663
1664static bool set_user_plugin (ACL_USER *user, size_t password_len)
1665{
1666 switch (password_len)
1667 {
1668 case 0: /* no password */
1669 case SCRAMBLED_PASSWORD_CHAR_LENGTH:
1670 user->plugin= native_password_plugin_name;
1671 return FALSE;
1672 case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
1673 user->plugin= old_password_plugin_name;
1674 return FALSE;
1675 default:
1676 sql_print_warning("Found invalid password for user: '%s@%s'; "
1677 "Ignoring user", safe_str(user->user.str),
1678 safe_str(user->host.hostname));
1679 return TRUE;
1680 }
1681}
1682
1683
1684/*
1685 Initialize structures responsible for user/db-level privilege checking
1686 and load information about grants from open privilege tables.
1687
1688 SYNOPSIS
1689 acl_load()
1690 thd Current thread
1691 tables List containing open "mysql.host", "mysql.user",
1692 "mysql.db", "mysql.proxies_priv" and "mysql.roles_mapping"
1693 tables.
1694
1695 RETURN VALUES
1696 FALSE Success
1697 TRUE Error
1698*/
1699
1700static bool acl_load(THD *thd, const Grant_tables& tables)
1701{
1702 READ_RECORD read_record_info;
1703 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
1704 char tmp_name[SAFE_NAME_LEN+1];
1705 int password_length;
1706 Sql_mode_save old_mode_save(thd);
1707 DBUG_ENTER("acl_load");
1708
1709 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
1710
1711 grant_version++; /* Privileges updated */
1712
1713 const Host_table& host_table= tables.host_table();
1714 init_sql_alloc(&acl_memroot, "ACL", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
1715 if (host_table.table_exists()) // "host" table may not exist (e.g. in MySQL 5.6.7+)
1716 {
1717 if (host_table.init_read_record(&read_record_info, thd))
1718 DBUG_RETURN(true);
1719 while (!(read_record_info.read_record()))
1720 {
1721 ACL_HOST host;
1722 update_hostname(&host.host, get_field(&acl_memroot, host_table.host()));
1723 host.db= get_field(&acl_memroot, host_table.db());
1724 if (lower_case_table_names && host.db)
1725 {
1726 /*
1727 convert db to lower case and give a warning if the db wasn't
1728 already in lower case
1729 */
1730 char *end = strnmov(tmp_name, host.db, sizeof(tmp_name));
1731 if (end >= tmp_name + sizeof(tmp_name))
1732 {
1733 sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), host.db);
1734 continue;
1735 }
1736 my_casedn_str(files_charset_info, host.db);
1737 if (strcmp(host.db, tmp_name) != 0)
1738 sql_print_warning("'host' entry '%s|%s' had database in mixed "
1739 "case that has been forced to lowercase because "
1740 "lower_case_table_names is set. It will not be "
1741 "possible to remove this privilege using REVOKE.",
1742 host.host.hostname, host.db);
1743 }
1744 host.access= host_table.get_access();
1745 host.access= fix_rights_for_db(host.access);
1746 host.sort= get_sort(2, host.host.hostname, host.db);
1747 if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
1748 {
1749 sql_print_warning("'host' entry '%s|%s' "
1750 "ignored in --skip-name-resolve mode.",
1751 safe_str(host.host.hostname),
1752 safe_str(host.db));
1753 continue;
1754 }
1755#ifndef TO_BE_REMOVED
1756 if (host_table.num_fields() == 8)
1757 { // Without grant
1758 if (host.access & CREATE_ACL)
1759 host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
1760 }
1761#endif
1762 (void) push_dynamic(&acl_hosts,(uchar*) &host);
1763 }
1764 my_qsort((uchar*) dynamic_element(&acl_hosts, 0, ACL_HOST*),
1765 acl_hosts.elements, sizeof(ACL_HOST),(qsort_cmp) acl_compare);
1766 end_read_record(&read_record_info);
1767 }
1768 freeze_size(&acl_hosts);
1769
1770 const User_table& user_table= tables.user_table();
1771 if (user_table.init_read_record(&read_record_info, thd))
1772 DBUG_RETURN(true);
1773
1774 username_char_length= MY_MIN(user_table.user()->char_length(),
1775 USERNAME_CHAR_LENGTH);
1776 if (user_table.password()) // Password column might be missing. (MySQL 5.7.6+)
1777 {
1778 password_length= user_table.password()->field_length /
1779 user_table.password()->charset()->mbmaxlen;
1780 if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1781 {
1782 sql_print_error("Fatal error: mysql.user table is damaged or in "
1783 "unsupported 3.20 format.");
1784 DBUG_RETURN(TRUE);
1785 }
1786
1787 DBUG_PRINT("info",("user table fields: %d, password length: %d",
1788 user_table.num_fields(), password_length));
1789
1790 mysql_mutex_lock(&LOCK_global_system_variables);
1791 if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
1792 {
1793 if (opt_secure_auth)
1794 {
1795 mysql_mutex_unlock(&LOCK_global_system_variables);
1796 sql_print_error("Fatal error: mysql.user table is in old format, "
1797 "but server started with --secure-auth option.");
1798 DBUG_RETURN(TRUE);
1799 }
1800 mysql_user_table_is_in_short_password_format= true;
1801 if (global_system_variables.old_passwords)
1802 mysql_mutex_unlock(&LOCK_global_system_variables);
1803 else
1804 {
1805 extern sys_var *Sys_old_passwords_ptr;
1806 Sys_old_passwords_ptr->value_origin= sys_var::AUTO;
1807 global_system_variables.old_passwords= 1;
1808 mysql_mutex_unlock(&LOCK_global_system_variables);
1809 sql_print_warning("mysql.user table is not updated to new password format; "
1810 "Disabling new password usage until "
1811 "mysql_fix_privilege_tables is run");
1812 }
1813 thd->variables.old_passwords= 1;
1814 }
1815 else
1816 {
1817 mysql_user_table_is_in_short_password_format= false;
1818 mysql_mutex_unlock(&LOCK_global_system_variables);
1819 }
1820 }
1821
1822 allow_all_hosts=0;
1823 while (!(read_record_info.read_record()))
1824 {
1825 ACL_USER user;
1826 bool is_role= FALSE;
1827 bzero(&user, sizeof(user));
1828 update_hostname(&user.host, get_field(&acl_memroot, user_table.host()));
1829 char *username= get_field(&acl_memroot, user_table.user());
1830 user.user.str= username;
1831 user.user.length= safe_strlen(username);
1832
1833 /*
1834 If the user entry is a role, skip password and hostname checks
1835 A user can not log in with a role so some checks are not necessary
1836 */
1837 is_role= user_table.check_is_role();
1838
1839 if (is_role && is_invalid_role_name(username))
1840 {
1841 thd->clear_error(); // the warning is still issued
1842 continue;
1843 }
1844
1845 if (!is_role && check_no_resolve &&
1846 hostname_requires_resolving(user.host.hostname))
1847 {
1848 sql_print_warning("'user' entry '%s@%s' "
1849 "ignored in --skip-name-resolve mode.",
1850 safe_str(user.user.str),
1851 safe_str(user.host.hostname));
1852 continue;
1853 }
1854
1855 char *password= const_cast<char*>("");
1856 if (user_table.password())
1857 password= get_field(&acl_memroot, user_table.password());
1858 size_t password_len= safe_strlen(password);
1859 user.auth_string.str= safe_str(password);
1860 user.auth_string.length= password_len;
1861 set_user_salt(&user, password, password_len);
1862
1863 if (!is_role && set_user_plugin(&user, password_len))
1864 continue;
1865
1866 {
1867 user.access= user_table.get_access() & GLOBAL_ACLS;
1868 /*
1869 if it is pre 5.0.1 privilege table then map CREATE privilege on
1870 CREATE VIEW & SHOW VIEW privileges
1871 */
1872 if (user_table.num_fields() <= 31 && (user.access & CREATE_ACL))
1873 user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
1874
1875 /*
1876 if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
1877 CREATE PROCEDURE & ALTER PROCEDURE privileges
1878 */
1879 if (user_table.num_fields() <= 33 && (user.access & CREATE_ACL))
1880 user.access|= CREATE_PROC_ACL;
1881 if (user_table.num_fields() <= 33 && (user.access & ALTER_ACL))
1882 user.access|= ALTER_PROC_ACL;
1883
1884 /*
1885 pre 5.0.3 did not have CREATE_USER_ACL
1886 */
1887 if (user_table.num_fields() <= 36 && (user.access & GRANT_ACL))
1888 user.access|= CREATE_USER_ACL;
1889
1890
1891 /*
1892 if it is pre 5.1.6 privilege table then map CREATE privilege on
1893 CREATE|ALTER|DROP|EXECUTE EVENT
1894 */
1895 if (user_table.num_fields() <= 37 && (user.access & SUPER_ACL))
1896 user.access|= EVENT_ACL;
1897
1898 /*
1899 if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
1900 */
1901 if (user_table.num_fields() <= 38 && (user.access & SUPER_ACL))
1902 user.access|= TRIGGER_ACL;
1903
1904 if (user_table.num_fields() <= 46 && (user.access & DELETE_ACL))
1905 user.access|= DELETE_HISTORY_ACL;
1906
1907 user.sort= get_sort(2, user.host.hostname, user.user.str);
1908 user.hostname_length= safe_strlen(user.host.hostname);
1909 user.user_resource.user_conn= 0;
1910 user.user_resource.max_statement_time= 0.0;
1911
1912 /* Starting from 4.0.2 we have more fields */
1913 if (user_table.ssl_type())
1914 {
1915 char *ssl_type=get_field(thd->mem_root, user_table.ssl_type());
1916 if (!ssl_type)
1917 user.ssl_type=SSL_TYPE_NONE;
1918 else if (!strcmp(ssl_type, "ANY"))
1919 user.ssl_type=SSL_TYPE_ANY;
1920 else if (!strcmp(ssl_type, "X509"))
1921 user.ssl_type=SSL_TYPE_X509;
1922 else /* !strcmp(ssl_type, "SPECIFIED") */
1923 user.ssl_type=SSL_TYPE_SPECIFIED;
1924
1925 user.ssl_cipher= get_field(&acl_memroot, user_table.ssl_cipher());
1926 user.x509_issuer= get_field(&acl_memroot, user_table.x509_issuer());
1927 user.x509_subject= get_field(&acl_memroot, user_table.x509_subject());
1928
1929 char *ptr = get_field(thd->mem_root, user_table.max_questions());
1930 user.user_resource.questions=ptr ? atoi(ptr) : 0;
1931 ptr = get_field(thd->mem_root, user_table.max_updates());
1932 user.user_resource.updates=ptr ? atoi(ptr) : 0;
1933 ptr = get_field(thd->mem_root, user_table.max_connections());
1934 user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
1935 if (user.user_resource.questions || user.user_resource.updates ||
1936 user.user_resource.conn_per_hour)
1937 mqh_used=1;
1938
1939 if (user_table.max_user_connections())
1940 {
1941 /* Starting from 5.0.3 we have max_user_connections field */
1942 ptr= get_field(thd->mem_root, user_table.max_user_connections());
1943 user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
1944 }
1945
1946 if (!is_role && user_table.plugin())
1947 {
1948 /* We may have plugin & auth_String fields */
1949 char *tmpstr= get_field(&acl_memroot, user_table.plugin());
1950 if (tmpstr)
1951 {
1952 user.plugin.str= tmpstr;
1953 user.plugin.length= strlen(user.plugin.str);
1954 user.auth_string.str=
1955 safe_str(get_field(&acl_memroot,
1956 user_table.authentication_string()));
1957 user.auth_string.length= strlen(user.auth_string.str);
1958
1959 if (user.auth_string.length && password_len)
1960 {
1961 sql_print_warning("'user' entry '%s@%s' has both a password "
1962 "and an authentication plugin specified. The "
1963 "password will be ignored.",
1964 safe_str(user.user.str),
1965 safe_str(user.host.hostname));
1966 }
1967
1968 fix_user_plugin_ptr(&user);
1969 }
1970 }
1971
1972 if (user_table.max_statement_time())
1973 {
1974 /* Starting from 10.1.1 we can have max_statement_time */
1975 ptr= get_field(thd->mem_root,
1976 user_table.max_statement_time());
1977 user.user_resource.max_statement_time= ptr ? atof(ptr) : 0.0;
1978 }
1979 }
1980 else
1981 {
1982 user.ssl_type=SSL_TYPE_NONE;
1983#ifndef TO_BE_REMOVED
1984 if (user_table.num_fields() <= 13)
1985 { // Without grant
1986 if (user.access & CREATE_ACL)
1987 user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
1988 }
1989 /* Convert old privileges */
1990 user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
1991 if (user.access & FILE_ACL)
1992 user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
1993 if (user.access & PROCESS_ACL)
1994 user.access|= SUPER_ACL | EXECUTE_ACL;
1995#endif
1996 }
1997
1998 (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *),
1999 8, 8, MYF(0));
2000
2001 /* check default role, if any */
2002 if (!is_role && user_table.default_role())
2003 {
2004 user.default_rolename.str=
2005 get_field(&acl_memroot, user_table.default_role());
2006 user.default_rolename.length= safe_strlen(user.default_rolename.str);
2007 }
2008
2009 if (is_role)
2010 {
2011 DBUG_PRINT("info", ("Found role %s", user.user.str));
2012 ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot);
2013 entry->role_grants = user.role_grants;
2014 (void) my_init_dynamic_array(&entry->parent_grantee,
2015 sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
2016 my_hash_insert(&acl_roles, (uchar *)entry);
2017
2018 continue;
2019 }
2020 else
2021 {
2022 DBUG_PRINT("info", ("Found user %s", user.user.str));
2023 (void) push_dynamic(&acl_users,(uchar*) &user);
2024 }
2025 if (!user.host.hostname ||
2026 (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
2027 allow_all_hosts=1; // Anyone can connect
2028 }
2029 }
2030 my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
2031 sizeof(ACL_USER),(qsort_cmp) acl_compare);
2032 end_read_record(&read_record_info);
2033 freeze_size(&acl_users);
2034
2035 const Db_table& db_table= tables.db_table();
2036 if (db_table.init_read_record(&read_record_info, thd))
2037 DBUG_RETURN(TRUE);
2038 while (!(read_record_info.read_record()))
2039 {
2040 ACL_DB db;
2041 char *db_name;
2042 db.user=get_field(&acl_memroot, db_table.user());
2043 const char *hostname= get_field(&acl_memroot, db_table.host());
2044 if (!hostname && find_acl_role(db.user))
2045 hostname= "";
2046 update_hostname(&db.host, hostname);
2047 db.db= db_name= get_field(&acl_memroot, db_table.db());
2048 if (!db.db)
2049 {
2050 sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
2051 continue;
2052 }
2053 if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
2054 {
2055 sql_print_warning("'db' entry '%s %s@%s' "
2056 "ignored in --skip-name-resolve mode.",
2057 db.db, safe_str(db.user), safe_str(db.host.hostname));
2058 continue;
2059 }
2060 db.access= db_table.get_access();
2061 db.access=fix_rights_for_db(db.access);
2062 db.initial_access= db.access;
2063 if (lower_case_table_names)
2064 {
2065 /*
2066 convert db to lower case and give a warning if the db wasn't
2067 already in lower case
2068 */
2069 char *end = strnmov(tmp_name, db.db, sizeof(tmp_name));
2070 if (end >= tmp_name + sizeof(tmp_name))
2071 {
2072 sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), db.db);
2073 continue;
2074 }
2075 my_casedn_str(files_charset_info, db_name);
2076 if (strcmp(db_name, tmp_name) != 0)
2077 {
2078 sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
2079 "case that has been forced to lowercase because "
2080 "lower_case_table_names is set. It will not be "
2081 "possible to remove this privilege using REVOKE.",
2082 db.db, safe_str(db.user), safe_str(db.host.hostname));
2083 }
2084 }
2085 db.sort=get_sort(3,db.host.hostname,db.db,db.user);
2086#ifndef TO_BE_REMOVED
2087 if (db_table.num_fields() <= 9)
2088 { // Without grant
2089 if (db.access & CREATE_ACL)
2090 db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
2091 }
2092#endif
2093 (void) push_dynamic(&acl_dbs,(uchar*) &db);
2094 }
2095 my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
2096 sizeof(ACL_DB),(qsort_cmp) acl_compare);
2097 end_read_record(&read_record_info);
2098 freeze_size(&acl_dbs);
2099
2100 const Proxies_priv_table& proxies_priv_table= tables.proxies_priv_table();
2101 if (proxies_priv_table.table_exists())
2102 {
2103 if (proxies_priv_table.init_read_record(&read_record_info, thd))
2104 DBUG_RETURN(TRUE);
2105 while (!(read_record_info.read_record()))
2106 {
2107 ACL_PROXY_USER proxy;
2108 proxy.init(proxies_priv_table, &acl_memroot);
2109 if (proxy.check_validity(check_no_resolve))
2110 continue;
2111 if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
2112 DBUG_RETURN(TRUE);
2113 }
2114 my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
2115 acl_proxy_users.elements,
2116 sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
2117 end_read_record(&read_record_info);
2118 }
2119 else
2120 {
2121 sql_print_error("Missing system table mysql.proxies_priv; "
2122 "please run mysql_upgrade to create it");
2123 }
2124 freeze_size(&acl_proxy_users);
2125
2126 const Roles_mapping_table& roles_mapping_table= tables.roles_mapping_table();
2127 if (roles_mapping_table.table_exists())
2128 {
2129 if (roles_mapping_table.init_read_record(&read_record_info, thd))
2130 DBUG_RETURN(TRUE);
2131
2132 MEM_ROOT temp_root;
2133 init_alloc_root(&temp_root, "ACL_tmp", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
2134 while (!(read_record_info.read_record()))
2135 {
2136 char *hostname= safe_str(get_field(&temp_root, roles_mapping_table.host()));
2137 char *username= safe_str(get_field(&temp_root, roles_mapping_table.user()));
2138 char *rolename= safe_str(get_field(&temp_root, roles_mapping_table.role()));
2139 bool with_grant_option= get_YN_as_bool(roles_mapping_table.admin_option());
2140
2141 if (add_role_user_mapping(username, hostname, rolename)) {
2142 sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'",
2143 username, hostname, rolename);
2144 continue;
2145 }
2146
2147 ROLE_GRANT_PAIR *mapping= new (&acl_memroot) ROLE_GRANT_PAIR;
2148
2149 if (mapping->init(&acl_memroot, username, hostname, rolename, with_grant_option))
2150 continue;
2151
2152 my_hash_insert(&acl_roles_mappings, (uchar*) mapping);
2153 }
2154
2155 free_root(&temp_root, MYF(0));
2156 end_read_record(&read_record_info);
2157 }
2158 else
2159 {
2160 sql_print_error("Missing system table mysql.roles_mapping; "
2161 "please run mysql_upgrade to create it");
2162 }
2163
2164 init_check_host();
2165
2166 initialized=1;
2167 DBUG_RETURN(FALSE);
2168}
2169
2170
2171void acl_free(bool end)
2172{
2173 my_hash_free(&acl_roles);
2174 free_root(&acl_memroot,MYF(0));
2175 delete_dynamic(&acl_hosts);
2176 delete_dynamic_with_callback(&acl_users, (FREE_FUNC) free_acl_user);
2177 delete_dynamic(&acl_dbs);
2178 delete_dynamic(&acl_wild_hosts);
2179 delete_dynamic(&acl_proxy_users);
2180 my_hash_free(&acl_check_hosts);
2181 my_hash_free(&acl_roles_mappings);
2182 if (!end)
2183 acl_cache->clear(1); /* purecov: inspected */
2184 else
2185 {
2186 plugin_unlock(0, native_password_plugin);
2187 plugin_unlock(0, old_password_plugin);
2188 delete acl_cache;
2189 acl_cache=0;
2190 }
2191}
2192
2193
2194/*
2195 Forget current user/db-level privileges and read new privileges
2196 from the privilege tables.
2197
2198 SYNOPSIS
2199 acl_reload()
2200 thd Current thread
2201
2202 NOTE
2203 All tables of calling thread which were open and locked by LOCK TABLES
2204 statement will be unlocked and closed.
2205 This function is also used for initialization of structures responsible
2206 for user/db-level privilege checking.
2207
2208 RETURN VALUE
2209 FALSE Success
2210 TRUE Failure
2211*/
2212
2213bool acl_reload(THD *thd)
2214{
2215 DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users;
2216 HASH old_acl_roles, old_acl_roles_mappings;
2217 MEM_ROOT old_mem;
2218 int result;
2219 DBUG_ENTER("acl_reload");
2220
2221 Grant_tables tables(Table_host | Table_user | Table_db | Table_proxies_priv |
2222 Table_roles_mapping, TL_READ);
2223 /*
2224 To avoid deadlocks we should obtain table locks before
2225 obtaining acl_cache->lock mutex.
2226 */
2227 if (unlikely((result= tables.open_and_lock(thd))))
2228 {
2229 DBUG_ASSERT(result <= 0);
2230 /*
2231 Execution might have been interrupted; only print the error message
2232 if an error condition has been raised.
2233 */
2234 if (thd->get_stmt_da()->is_error())
2235 sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
2236 thd->get_stmt_da()->message());
2237 goto end;
2238 }
2239
2240 acl_cache->clear(0);
2241 mysql_mutex_lock(&acl_cache->lock);
2242
2243 old_acl_hosts= acl_hosts;
2244 old_acl_users= acl_users;
2245 old_acl_roles= acl_roles;
2246 old_acl_roles_mappings= acl_roles_mappings;
2247 old_acl_proxy_users= acl_proxy_users;
2248 old_acl_dbs= acl_dbs;
2249 my_init_dynamic_array(&acl_hosts, sizeof(ACL_HOST), 20, 50, MYF(0));
2250 my_init_dynamic_array(&acl_users, sizeof(ACL_USER), 50, 100, MYF(0));
2251 my_init_dynamic_array(&acl_dbs, sizeof(ACL_DB), 50, 100, MYF(0));
2252 my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), 50, 100, MYF(0));
2253 my_hash_init2(&acl_roles,50, &my_charset_utf8_bin,
2254 0, 0, 0, (my_hash_get_key) acl_role_get_key, 0,
2255 (void (*)(void *))free_acl_role, 0);
2256 my_hash_init2(&acl_roles_mappings, 50, &my_charset_utf8_bin, 0, 0, 0,
2257 (my_hash_get_key) acl_role_map_get_key, 0, 0, 0);
2258 old_mem= acl_memroot;
2259 delete_dynamic(&acl_wild_hosts);
2260 my_hash_free(&acl_check_hosts);
2261
2262 if ((result= acl_load(thd, tables)))
2263 { // Error. Revert to old list
2264 DBUG_PRINT("error",("Reverting to old privileges"));
2265 acl_free(); /* purecov: inspected */
2266 acl_hosts= old_acl_hosts;
2267 acl_users= old_acl_users;
2268 acl_roles= old_acl_roles;
2269 acl_roles_mappings= old_acl_roles_mappings;
2270 acl_proxy_users= old_acl_proxy_users;
2271 acl_dbs= old_acl_dbs;
2272 acl_memroot= old_mem;
2273 init_check_host();
2274 }
2275 else
2276 {
2277 my_hash_free(&old_acl_roles);
2278 free_root(&old_mem,MYF(0));
2279 delete_dynamic(&old_acl_hosts);
2280 delete_dynamic_with_callback(&old_acl_users, (FREE_FUNC) free_acl_user);
2281 delete_dynamic(&old_acl_proxy_users);
2282 delete_dynamic(&old_acl_dbs);
2283 my_hash_free(&old_acl_roles_mappings);
2284 }
2285 mysql_mutex_unlock(&acl_cache->lock);
2286end:
2287 close_mysql_tables(thd);
2288 DBUG_RETURN(result);
2289}
2290
2291/*
2292 Get all access bits from table after fieldnr
2293
2294 IMPLEMENTATION
2295 We know that the access privileges ends when there is no more fields
2296 or the field is not an enum with two elements.
2297
2298 SYNOPSIS
2299 get_access()
2300 form an open table to read privileges from.
2301 The record should be already read in table->record[0]
2302 fieldnr number of the first privilege (that is ENUM('N','Y') field
2303 next_field on return - number of the field next to the last ENUM
2304 (unless next_field == 0)
2305
2306 RETURN VALUE
2307 privilege mask
2308*/
2309
2310static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
2311{
2312 ulong access_bits=0,bit;
2313 char buff[2];
2314 String res(buff,sizeof(buff),&my_charset_latin1);
2315 Field **pos;
2316
2317 for (pos=form->field+fieldnr, bit=1;
2318 *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
2319 ((Field_enum*) (*pos))->typelib->count == 2 ;
2320 pos++, fieldnr++, bit<<=1)
2321 {
2322 if (get_YN_as_bool(*pos))
2323 access_bits|= bit;
2324 }
2325 if (next_field)
2326 *next_field=fieldnr;
2327 return access_bits;
2328}
2329
2330
2331/*
2332 Return a number which, if sorted 'desc', puts strings in this order:
2333 no wildcards
2334 wildcards
2335 empty string
2336*/
2337
2338static ulong get_sort(uint count,...)
2339{
2340 va_list args;
2341 va_start(args,count);
2342 ulong sort=0;
2343
2344 /* Should not use this function with more than 4 arguments for compare. */
2345 DBUG_ASSERT(count <= 4);
2346
2347 while (count--)
2348 {
2349 char *start, *str= va_arg(args,char*);
2350 uint chars= 0;
2351 uint wild_pos= 0; /* first wildcard position */
2352
2353 if ((start= str))
2354 {
2355 for (; *str ; str++)
2356 {
2357 if (*str == wild_prefix && str[1])
2358 str++;
2359 else if (*str == wild_many || *str == wild_one)
2360 {
2361 wild_pos= (uint) (str - start) + 1;
2362 break;
2363 }
2364 chars= 128; // Marker that chars existed
2365 }
2366 }
2367 sort= (sort << 8) + (wild_pos ? MY_MIN(wild_pos, 127U) : chars);
2368 }
2369 va_end(args);
2370 return sort;
2371}
2372
2373
2374static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
2375{
2376 if (a->sort > b->sort)
2377 return -1;
2378 if (a->sort < b->sort)
2379 return 1;
2380 return 0;
2381}
2382
2383
2384/*
2385 Gets user credentials without authentication and resource limit checks.
2386
2387 SYNOPSIS
2388 acl_getroot()
2389 sctx Context which should be initialized
2390 user user name
2391 host host name
2392 ip IP
2393 db current data base name
2394
2395 RETURN
2396 FALSE OK
2397 TRUE Error
2398*/
2399
2400bool acl_getroot(Security_context *sctx, const char *user, const char *host,
2401 const char *ip, const char *db)
2402{
2403 int res= 1;
2404 uint i;
2405 ACL_USER *acl_user= 0;
2406 DBUG_ENTER("acl_getroot");
2407
2408 DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
2409 host, ip, user, db));
2410 sctx->user= user;
2411 sctx->host= host;
2412 sctx->ip= ip;
2413 sctx->host_or_ip= host ? host : (safe_str(ip));
2414
2415 if (!initialized)
2416 {
2417 /*
2418 here if mysqld's been started with --skip-grant-tables option.
2419 */
2420 sctx->skip_grants();
2421 DBUG_RETURN(FALSE);
2422 }
2423
2424 mysql_mutex_lock(&acl_cache->lock);
2425
2426 sctx->master_access= 0;
2427 sctx->db_access= 0;
2428 *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0;
2429
2430 if (host[0]) // User, not Role
2431 {
2432 acl_user= find_user_wild(host, user, ip);
2433
2434 if (acl_user)
2435 {
2436 res= 0;
2437 for (i=0 ; i < acl_dbs.elements ; i++)
2438 {
2439 ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
2440 if (!acl_db->user ||
2441 (user && user[0] && !strcmp(user, acl_db->user)))
2442 {
2443 if (compare_hostname(&acl_db->host, host, ip))
2444 {
2445 if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
2446 {
2447 sctx->db_access= acl_db->access;
2448 break;
2449 }
2450 }
2451 }
2452 }
2453 sctx->master_access= acl_user->access;
2454
2455 if (acl_user->user.str)
2456 strmake_buf(sctx->priv_user, user);
2457
2458 if (acl_user->host.hostname)
2459 strmake_buf(sctx->priv_host, acl_user->host.hostname);
2460 }
2461 }
2462 else // Role, not User
2463 {
2464 ACL_ROLE *acl_role= find_acl_role(user);
2465 if (acl_role)
2466 {
2467 res= 0;
2468 for (i=0 ; i < acl_dbs.elements ; i++)
2469 {
2470 ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
2471 if (!acl_db->user ||
2472 (user && user[0] && !strcmp(user, acl_db->user)))
2473 {
2474 if (compare_hostname(&acl_db->host, "", ""))
2475 {
2476 if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
2477 {
2478 sctx->db_access= acl_db->access;
2479 break;
2480 }
2481 }
2482 }
2483 }
2484 sctx->master_access= acl_role->access;
2485
2486 if (acl_role->user.str)
2487 strmake_buf(sctx->priv_role, user);
2488 }
2489 }
2490
2491 mysql_mutex_unlock(&acl_cache->lock);
2492 DBUG_RETURN(res);
2493}
2494
2495static int check_user_can_set_role(const char *user, const char *host,
2496 const char *ip, const char *rolename, ulonglong *access)
2497{
2498 ACL_ROLE *role;
2499 ACL_USER_BASE *acl_user_base;
2500 ACL_USER *UNINIT_VAR(acl_user);
2501 bool is_granted= FALSE;
2502 int result= 0;
2503
2504 /* clear role privileges */
2505 mysql_mutex_lock(&acl_cache->lock);
2506
2507 if (!strcasecmp(rolename, "NONE"))
2508 {
2509 /* have to clear the privileges */
2510 /* get the current user */
2511 acl_user= find_user_wild(host, user, ip);
2512 if (acl_user == NULL)
2513 {
2514 my_error(ER_INVALID_CURRENT_USER, MYF(0));
2515 result= -1;
2516 }
2517 else if (access)
2518 *access= acl_user->access;
2519
2520 goto end;
2521 }
2522
2523 role= find_acl_role(rolename);
2524
2525 /* According to SQL standard, the same error message must be presented */
2526 if (role == NULL) {
2527 my_error(ER_INVALID_ROLE, MYF(0), rolename);
2528 result= -1;
2529 goto end;
2530 }
2531
2532 for (uint i=0 ; i < role->parent_grantee.elements ; i++)
2533 {
2534 acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**));
2535 if (acl_user_base->flags & IS_ROLE)
2536 continue;
2537
2538 acl_user= (ACL_USER *)acl_user_base;
2539 if (acl_user->wild_eq(user, host, ip))
2540 {
2541 is_granted= TRUE;
2542 break;
2543 }
2544 }
2545
2546 /* According to SQL standard, the same error message must be presented */
2547 if (!is_granted)
2548 {
2549 my_error(ER_INVALID_ROLE, MYF(0), rolename);
2550 result= 1;
2551 goto end;
2552 }
2553
2554 if (access)
2555 {
2556 *access = acl_user->access | role->access;
2557 }
2558end:
2559 mysql_mutex_unlock(&acl_cache->lock);
2560 return result;
2561
2562}
2563
2564int acl_check_setrole(THD *thd, const char *rolename, ulonglong *access)
2565{
2566 /* Yes! priv_user@host. Don't ask why - that's what check_access() does. */
2567 return check_user_can_set_role(thd->security_ctx->priv_user,
2568 thd->security_ctx->host, thd->security_ctx->ip, rolename, access);
2569}
2570
2571
2572int acl_setrole(THD *thd, const char *rolename, ulonglong access)
2573{
2574 /* merge the privileges */
2575 Security_context *sctx= thd->security_ctx;
2576 sctx->master_access= static_cast<ulong>(access);
2577 if (thd->db.str)
2578 sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db.str, FALSE);
2579
2580 if (!strcasecmp(rolename, "NONE"))
2581 {
2582 thd->security_ctx->priv_role[0]= 0;
2583 }
2584 else
2585 {
2586 if (thd->db.str)
2587 sctx->db_access|= acl_get("", "", rolename, thd->db.str, FALSE);
2588 /* mark the current role */
2589 strmake_buf(thd->security_ctx->priv_role, rolename);
2590 }
2591 return 0;
2592}
2593
2594static uchar* check_get_key(ACL_USER *buff, size_t *length,
2595 my_bool not_used __attribute__((unused)))
2596{
2597 *length=buff->hostname_length;
2598 return (uchar*) buff->host.hostname;
2599}
2600
2601
2602static void acl_update_role(const char *rolename, ulong privileges)
2603{
2604 ACL_ROLE *role= find_acl_role(rolename);
2605 if (role)
2606 role->initial_role_access= role->access= privileges;
2607}
2608
2609
2610static void acl_update_user(const char *user, const char *host,
2611 const char *password, size_t password_len,
2612 enum SSL_type ssl_type,
2613 const char *ssl_cipher,
2614 const char *x509_issuer,
2615 const char *x509_subject,
2616 USER_RESOURCES *mqh,
2617 ulong privileges,
2618 const LEX_CSTRING *plugin,
2619 const LEX_CSTRING *auth)
2620{
2621 mysql_mutex_assert_owner(&acl_cache->lock);
2622
2623 for (uint i=0 ; i < acl_users.elements ; i++)
2624 {
2625 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
2626 if (acl_user->eq(user, host))
2627 {
2628 if (plugin->str[0])
2629 {
2630 acl_user->plugin= *plugin;
2631 acl_user->auth_string.str= auth->str ?
2632 strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
2633 acl_user->auth_string.length= auth->length;
2634 if (fix_user_plugin_ptr(acl_user))
2635 acl_user->plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
2636 }
2637 else
2638 if (password[0])
2639 {
2640 acl_user->auth_string.str= strmake_root(&acl_memroot, password, password_len);
2641 acl_user->auth_string.length= password_len;
2642 set_user_salt(acl_user, password, password_len);
2643 set_user_plugin(acl_user, password_len);
2644 }
2645 acl_user->access=privileges;
2646 if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
2647 acl_user->user_resource.questions=mqh->questions;
2648 if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
2649 acl_user->user_resource.updates=mqh->updates;
2650 if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
2651 acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
2652 if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
2653 acl_user->user_resource.user_conn= mqh->user_conn;
2654 if (mqh->specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
2655 acl_user->user_resource.max_statement_time= mqh->max_statement_time;
2656 if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
2657 {
2658 acl_user->ssl_type= ssl_type;
2659 acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) :
2660 0);
2661 acl_user->x509_issuer= (x509_issuer ? strdup_root(&acl_memroot,x509_issuer) :
2662 0);
2663 acl_user->x509_subject= (x509_subject ?
2664 strdup_root(&acl_memroot,x509_subject) : 0);
2665 }
2666 /* search complete: */
2667 break;
2668 }
2669 }
2670}
2671
2672
2673static void acl_insert_role(const char *rolename, ulong privileges)
2674{
2675 ACL_ROLE *entry;
2676
2677 mysql_mutex_assert_owner(&acl_cache->lock);
2678 entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot);
2679 (void) my_init_dynamic_array(&entry->parent_grantee,
2680 sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
2681 (void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *),
2682 8, 8, MYF(0));
2683
2684 my_hash_insert(&acl_roles, (uchar *)entry);
2685}
2686
2687
2688static void acl_insert_user(const char *user, const char *host,
2689 const char *password, size_t password_len,
2690 enum SSL_type ssl_type,
2691 const char *ssl_cipher,
2692 const char *x509_issuer,
2693 const char *x509_subject,
2694 USER_RESOURCES *mqh,
2695 ulong privileges,
2696 const LEX_CSTRING *plugin,
2697 const LEX_CSTRING *auth)
2698{
2699 ACL_USER acl_user;
2700
2701 mysql_mutex_assert_owner(&acl_cache->lock);
2702
2703 bzero(&acl_user, sizeof(acl_user));
2704 acl_user.user.str=*user ? strdup_root(&acl_memroot,user) : 0;
2705 acl_user.user.length= strlen(user);
2706 update_hostname(&acl_user.host, safe_strdup_root(&acl_memroot, host));
2707 if (plugin->str[0])
2708 {
2709 acl_user.plugin= *plugin;
2710 acl_user.auth_string.str= auth->str ?
2711 strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
2712 acl_user.auth_string.length= auth->length;
2713 if (fix_user_plugin_ptr(&acl_user))
2714 acl_user.plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
2715 }
2716 else
2717 {
2718 acl_user.auth_string.str= strmake_root(&acl_memroot, password, password_len);
2719 acl_user.auth_string.length= password_len;
2720 set_user_salt(&acl_user, password, password_len);
2721 set_user_plugin(&acl_user, password_len);
2722 }
2723
2724 acl_user.flags= 0;
2725 acl_user.access=privileges;
2726 acl_user.user_resource = *mqh;
2727 acl_user.sort=get_sort(2, acl_user.host.hostname, acl_user.user.str);
2728 acl_user.hostname_length=(uint) strlen(host);
2729 acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ?
2730 ssl_type : SSL_TYPE_NONE);
2731 acl_user.ssl_cipher= ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) : 0;
2732 acl_user.x509_issuer= x509_issuer ? strdup_root(&acl_memroot,x509_issuer) : 0;
2733 acl_user.x509_subject=x509_subject ? strdup_root(&acl_memroot,x509_subject) : 0;
2734 (void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *),
2735 8, 8, MYF(0));
2736
2737 (void) push_dynamic(&acl_users,(uchar*) &acl_user);
2738 if (!acl_user.host.hostname ||
2739 (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
2740 allow_all_hosts=1; // Anyone can connect /* purecov: tested */
2741 my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
2742 sizeof(ACL_USER),(qsort_cmp) acl_compare);
2743
2744 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
2745 rebuild_check_host();
2746
2747 /*
2748 Rebuild every user's role_grants since 'acl_users' has been sorted
2749 and old pointers to ACL_USER elements are no longer valid
2750 */
2751 rebuild_role_grants();
2752}
2753
2754
2755static bool acl_update_db(const char *user, const char *host, const char *db,
2756 ulong privileges)
2757{
2758 mysql_mutex_assert_owner(&acl_cache->lock);
2759
2760 bool updated= false;
2761
2762 for (uint i=0 ; i < acl_dbs.elements ; i++)
2763 {
2764 ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
2765 if ((!acl_db->user && !user[0]) ||
2766 (acl_db->user &&
2767 !strcmp(user,acl_db->user)))
2768 {
2769 if ((!acl_db->host.hostname && !host[0]) ||
2770 (acl_db->host.hostname &&
2771 !strcmp(host, acl_db->host.hostname)))
2772 {
2773 if ((!acl_db->db && !db[0]) ||
2774 (acl_db->db && !strcmp(db,acl_db->db)))
2775
2776 {
2777 if (privileges)
2778 {
2779 acl_db->access= privileges;
2780 acl_db->initial_access= acl_db->access;
2781 }
2782 else
2783 delete_dynamic_element(&acl_dbs,i);
2784 updated= true;
2785 }
2786 }
2787 }
2788 }
2789
2790 return updated;
2791}
2792
2793
2794/*
2795 Insert a user/db/host combination into the global acl_cache
2796
2797 SYNOPSIS
2798 acl_insert_db()
2799 user User name
2800 host Host name
2801 db Database name
2802 privileges Bitmap of privileges
2803
2804 NOTES
2805 acl_cache->lock must be locked when calling this
2806*/
2807
2808static void acl_insert_db(const char *user, const char *host, const char *db,
2809 ulong privileges)
2810{
2811 ACL_DB acl_db;
2812 mysql_mutex_assert_owner(&acl_cache->lock);
2813 acl_db.user=strdup_root(&acl_memroot,user);
2814 update_hostname(&acl_db.host, safe_strdup_root(&acl_memroot, host));
2815 acl_db.db=strdup_root(&acl_memroot,db);
2816 acl_db.initial_access= acl_db.access= privileges;
2817 acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
2818 (void) push_dynamic(&acl_dbs,(uchar*) &acl_db);
2819 my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
2820 sizeof(ACL_DB),(qsort_cmp) acl_compare);
2821}
2822
2823
2824/*
2825 Get privilege for a host, user and db combination
2826
2827 as db_is_pattern changes the semantics of comparison,
2828 acl_cache is not used if db_is_pattern is set.
2829*/
2830
2831ulong acl_get(const char *host, const char *ip,
2832 const char *user, const char *db, my_bool db_is_pattern)
2833{
2834 ulong host_access= ~(ulong)0, db_access= 0;
2835 uint i;
2836 size_t key_length;
2837 char key[ACL_KEY_LENGTH],*tmp_db,*end;
2838 acl_entry *entry;
2839 DBUG_ENTER("acl_get");
2840
2841 tmp_db= strmov(strmov(key, safe_str(ip)) + 1, user) + 1;
2842 end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db);
2843
2844 if (end >= key + sizeof(key)) // db name was truncated
2845 DBUG_RETURN(0); // no privileges for an invalid db name
2846
2847 if (lower_case_table_names)
2848 {
2849 my_casedn_str(files_charset_info, tmp_db);
2850 db=tmp_db;
2851 }
2852 key_length= (size_t) (end-key);
2853
2854 mysql_mutex_lock(&acl_cache->lock);
2855 if (!db_is_pattern && (entry=acl_cache->search((uchar*) key, key_length)))
2856 {
2857 db_access=entry->access;
2858 mysql_mutex_unlock(&acl_cache->lock);
2859 DBUG_PRINT("exit", ("access: 0x%lx", db_access));
2860 DBUG_RETURN(db_access);
2861 }
2862
2863 /*
2864 Check if there are some access rights for database and user
2865 */
2866 for (i=0 ; i < acl_dbs.elements ; i++)
2867 {
2868 ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
2869 if (!acl_db->user || !strcmp(user,acl_db->user))
2870 {
2871 if (compare_hostname(&acl_db->host,host,ip))
2872 {
2873 if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
2874 {
2875 db_access=acl_db->access;
2876 if (acl_db->host.hostname)
2877 goto exit; // Fully specified. Take it
2878 /* the host table is not used for roles */
2879 if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user))
2880 goto exit;
2881 break; /* purecov: tested */
2882 }
2883 }
2884 }
2885 }
2886 if (!db_access)
2887 goto exit; // Can't be better
2888
2889 /*
2890 No host specified for user. Get hostdata from host table
2891 */
2892 host_access=0; // Host must be found
2893 for (i=0 ; i < acl_hosts.elements ; i++)
2894 {
2895 ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
2896 if (compare_hostname(&acl_host->host,host,ip))
2897 {
2898 if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
2899 {
2900 host_access=acl_host->access; // Fully specified. Take it
2901 break;
2902 }
2903 }
2904 }
2905exit:
2906 /* Save entry in cache for quick retrieval */
2907 if (!db_is_pattern &&
2908 (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
2909 {
2910 entry->access=(db_access & host_access);
2911 DBUG_ASSERT(key_length < 0xffff);
2912 entry->length=(uint16)key_length;
2913 memcpy((uchar*) entry->key,key,key_length);
2914 acl_cache->add(entry);
2915 }
2916 mysql_mutex_unlock(&acl_cache->lock);
2917 DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
2918 DBUG_RETURN(db_access & host_access);
2919}
2920
2921/*
2922 Check if there are any possible matching entries for this host
2923
2924 NOTES
2925 All host names without wild cards are stored in a hash table,
2926 entries with wildcards are stored in a dynamic array
2927*/
2928
2929static void init_check_host(void)
2930{
2931 DBUG_ENTER("init_check_host");
2932 (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
2933 acl_users.elements, 1, MYF(0));
2934 (void) my_hash_init(&acl_check_hosts,system_charset_info,
2935 acl_users.elements, 0, 0,
2936 (my_hash_get_key) check_get_key, 0, 0);
2937 if (!allow_all_hosts)
2938 {
2939 for (uint i=0 ; i < acl_users.elements ; i++)
2940 {
2941 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
2942 if (strchr(acl_user->host.hostname,wild_many) ||
2943 strchr(acl_user->host.hostname,wild_one) ||
2944 acl_user->host.ip_mask)
2945 { // Has wildcard
2946 uint j;
2947 for (j=0 ; j < acl_wild_hosts.elements ; j++)
2948 { // Check if host already exists
2949 acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j,
2950 acl_host_and_ip *);
2951 if (!my_strcasecmp(system_charset_info,
2952 acl_user->host.hostname, acl->hostname))
2953 break; // already stored
2954 }
2955 if (j == acl_wild_hosts.elements) // If new
2956 (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
2957 }
2958 else if (!my_hash_search(&acl_check_hosts,(uchar*)
2959 acl_user->host.hostname,
2960 strlen(acl_user->host.hostname)))
2961 {
2962 if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
2963 { // End of memory
2964 allow_all_hosts=1; // Should never happen
2965 DBUG_VOID_RETURN;
2966 }
2967 }
2968 }
2969 }
2970 freeze_size(&acl_wild_hosts);
2971 freeze_size(&acl_check_hosts.array);
2972 DBUG_VOID_RETURN;
2973}
2974
2975
2976/*
2977 Rebuild lists used for checking of allowed hosts
2978
2979 We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
2980 dropping or renaming user, since they contain pointers to elements of
2981 'acl_user' array, which are invalidated by drop operation, and use
2982 ACL_USER::host::hostname as a key, which is changed by rename.
2983*/
2984static void rebuild_check_host(void)
2985{
2986 delete_dynamic(&acl_wild_hosts);
2987 my_hash_free(&acl_check_hosts);
2988 init_check_host();
2989}
2990
2991/*
2992 Reset a role role_grants dynamic array.
2993 Also, the role's access bits are reset to the ones present in the table.
2994*/
2995static my_bool acl_role_reset_role_arrays(void *ptr,
2996 void * not_used __attribute__((unused)))
2997{
2998 ACL_ROLE *role= (ACL_ROLE *)ptr;
2999 reset_dynamic(&role->role_grants);
3000 reset_dynamic(&role->parent_grantee);
3001 role->counter= 0;
3002 return 0;
3003}
3004
3005/*
3006 Add a the coresponding pointers present in the mapping to the entries in
3007 acl_users and acl_roles
3008*/
3009static bool add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
3010{
3011 return push_dynamic(&grantee->role_grants, (uchar*) &role)
3012 || push_dynamic(&role->parent_grantee, (uchar*) &grantee);
3013
3014}
3015
3016/*
3017 Revert the last add_role_user_mapping() action
3018*/
3019static void undo_add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
3020{
3021 void *pop __attribute__((unused));
3022
3023 pop= pop_dynamic(&grantee->role_grants);
3024 DBUG_ASSERT(role == *(ACL_ROLE**)pop);
3025
3026 pop= pop_dynamic(&role->parent_grantee);
3027 DBUG_ASSERT(grantee == *(ACL_USER_BASE**)pop);
3028}
3029
3030/*
3031 this helper is used when building role_grants and parent_grantee arrays
3032 from scratch.
3033
3034 this happens either on initial loading of data from tables, in acl_load().
3035 or in rebuild_role_grants after acl_role_reset_role_arrays().
3036*/
3037static bool add_role_user_mapping(const char *uname, const char *hname,
3038 const char *rname)
3039{
3040 ACL_USER_BASE *grantee= find_acl_user_base(uname, hname);
3041 ACL_ROLE *role= find_acl_role(rname);
3042
3043 if (grantee == NULL || role == NULL)
3044 return 1;
3045
3046 /*
3047 because all arrays are rebuilt completely, and counters were also reset,
3048 we can increment them here, and after the rebuild all counters will
3049 have correct values (equal to the number of roles granted).
3050 */
3051 if (grantee->flags & IS_ROLE)
3052 ((ACL_ROLE*)grantee)->counter++;
3053 return add_role_user_mapping(grantee, role);
3054}
3055
3056/*
3057 This helper function is used to removes roles and grantees
3058 from the corresponding cross-reference arrays. see remove_role_user_mapping().
3059 as such, it asserts that an element to delete is present in the array,
3060 and is present only once.
3061*/
3062static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr)
3063{
3064 bool found __attribute__((unused))= false;
3065 for (uint i= 0; i < array->elements; i++)
3066 {
3067 if (ptr == *dynamic_element(array, i, void**))
3068 {
3069 DBUG_ASSERT(!found);
3070 delete_dynamic_element(array, i);
3071 IF_DBUG_ASSERT(found= true, break);
3072 }
3073 }
3074 DBUG_ASSERT(found);
3075}
3076
3077static void remove_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role,
3078 int grantee_idx=-1, int role_idx=-1)
3079{
3080 remove_ptr_from_dynarray(&grantee->role_grants, role);
3081 remove_ptr_from_dynarray(&role->parent_grantee, grantee);
3082}
3083
3084
3085static my_bool add_role_user_mapping_action(void *ptr, void *unused __attribute__((unused)))
3086{
3087 ROLE_GRANT_PAIR *pair= (ROLE_GRANT_PAIR*)ptr;
3088 bool status __attribute__((unused));
3089 status= add_role_user_mapping(pair->u_uname, pair->u_hname, pair->r_uname);
3090 /*
3091 The invariant chosen is that acl_roles_mappings should _always_
3092 only contain valid entries, referencing correct user and role grants.
3093 If add_role_user_mapping detects an invalid entry, it will not add
3094 the mapping into the ACL_USER::role_grants array.
3095 */
3096 DBUG_ASSERT(status == 0);
3097 return 0;
3098}
3099
3100
3101/*
3102 Rebuild the role grants every time the acl_users is modified
3103
3104 The role grants in the ACL_USER class need to be rebuilt, as they contain
3105 pointers to elements of the acl_users array.
3106*/
3107
3108static void rebuild_role_grants(void)
3109{
3110 DBUG_ENTER("rebuild_role_grants");
3111 /*
3112 Reset every user's and role's role_grants array
3113 */
3114 for (uint i=0; i < acl_users.elements; i++) {
3115 ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *);
3116 reset_dynamic(&user->role_grants);
3117 }
3118 my_hash_iterate(&acl_roles, acl_role_reset_role_arrays, NULL);
3119
3120 /* Rebuild the direct links between users and roles in ACL_USER::role_grants */
3121 my_hash_iterate(&acl_roles_mappings, add_role_user_mapping_action, NULL);
3122
3123 DBUG_VOID_RETURN;
3124}
3125
3126
3127/* Return true if there is no users that can match the given host */
3128bool acl_check_host(const char *host, const char *ip)
3129{
3130 if (allow_all_hosts)
3131 return 0;
3132 mysql_mutex_lock(&acl_cache->lock);
3133
3134 if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
3135 (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
3136 {
3137 mysql_mutex_unlock(&acl_cache->lock);
3138 return 0; // Found host
3139 }
3140 for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
3141 {
3142 acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
3143 if (compare_hostname(acl, host, ip))
3144 {
3145 mysql_mutex_unlock(&acl_cache->lock);
3146 return 0; // Host ok
3147 }
3148 }
3149 mysql_mutex_unlock(&acl_cache->lock);
3150 if (ip != NULL)
3151 {
3152 /* Increment HOST_CACHE.COUNT_HOST_ACL_ERRORS. */
3153 Host_errors errors;
3154 errors.m_host_acl= 1;
3155 inc_host_errors(ip, &errors);
3156 }
3157 return 1; // Host is not allowed
3158}
3159
3160/**
3161 Check if the user is allowed to alter the mysql.user table
3162
3163 @param thd THD
3164 @param host Hostname for the user
3165 @param user User name
3166
3167 @return Error status
3168 @retval 0 OK
3169 @retval 1 Error
3170*/
3171
3172static int check_alter_user(THD *thd, const char *host, const char *user)
3173{
3174 int error = 1;
3175 if (!initialized)
3176 {
3177 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3178 goto end;
3179 }
3180
3181 if (IF_WSREP((!WSREP(thd) || !thd->wsrep_applier), 1) &&
3182 !thd->slave_thread && !thd->security_ctx->priv_user[0] &&
3183 !in_bootstrap)
3184 {
3185 my_message(ER_PASSWORD_ANONYMOUS_USER,
3186 ER_THD(thd, ER_PASSWORD_ANONYMOUS_USER),
3187 MYF(0));
3188 goto end;
3189 }
3190 if (!host) // Role
3191 {
3192 my_error(ER_PASSWORD_NO_MATCH, MYF(0));
3193 goto end;
3194 }
3195
3196 if (!thd->slave_thread &&
3197 IF_WSREP((!WSREP(thd) || !thd->wsrep_applier),1) &&
3198 (strcmp(thd->security_ctx->priv_user, user) ||
3199 my_strcasecmp(system_charset_info, host,
3200 thd->security_ctx->priv_host)))
3201 {
3202 if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
3203 goto end;
3204 }
3205
3206 error = 0;
3207
3208end:
3209 return error;
3210}
3211/**
3212 Check if the user is allowed to change password
3213
3214 @param thd THD
3215 @param user User, hostname, new password or password hash
3216
3217 @return Error status
3218 @retval 0 OK
3219 @retval 1 ERROR; In this case the error is sent to the client.
3220*/
3221
3222bool check_change_password(THD *thd, LEX_USER *user)
3223{
3224 LEX_USER *real_user= get_current_user(thd, user);
3225
3226 if (fix_and_copy_user(real_user, user, thd) ||
3227 validate_password(real_user, thd))
3228 return true;
3229
3230 *user= *real_user;
3231
3232 return check_alter_user(thd, user->host.str, user->user.str);
3233}
3234
3235
3236/**
3237 Change a password for a user.
3238
3239 @param thd THD
3240 @param user User, hostname, new password hash
3241
3242 @return Error code
3243 @retval 0 ok
3244 @retval 1 ERROR; In this case the error is sent to the client.
3245*/
3246bool change_password(THD *thd, LEX_USER *user)
3247{
3248 Grant_tables tables(Table_user, TL_WRITE);
3249 /* Buffer should be extended when password length is extended. */
3250 char buff[512];
3251 ulong query_length= 0;
3252 enum_binlog_format save_binlog_format;
3253 int result=0;
3254 const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
3255 DBUG_ENTER("change_password");
3256 DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
3257 user->host.str, user->user.str, user->pwhash.str));
3258 DBUG_ASSERT(user->host.str != 0); // Ensured by parent
3259
3260 /*
3261 This statement will be replicated as a statement, even when using
3262 row-based replication. The flag will be reset at the end of the
3263 statement.
3264 This has to be handled here as it's called by set_var.cc, which is
3265 not automaticly handled by sql_parse.cc
3266 */
3267 save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
3268
3269 if (mysql_bin_log.is_open() ||
3270 (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)))
3271 {
3272 query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
3273 safe_str(user->user.str), safe_str(user->host.str),
3274 safe_str(user->pwhash.str));
3275 }
3276
3277 if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
3278 {
3279 thd->set_query(buff, query_length, system_charset_info);
3280 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL);
3281 }
3282
3283 if ((result= tables.open_and_lock(thd)))
3284 DBUG_RETURN(result != 1);
3285
3286 result= 1;
3287
3288 mysql_mutex_lock(&acl_cache->lock);
3289 ACL_USER *acl_user;
3290 if (!(acl_user= find_user_exact(user->host.str, user->user.str)))
3291 {
3292 mysql_mutex_unlock(&acl_cache->lock);
3293 my_message(ER_PASSWORD_NO_MATCH,
3294 ER_THD(thd, ER_PASSWORD_NO_MATCH), MYF(0));
3295 goto end;
3296 }
3297
3298 /* update loaded acl entry: */
3299 if (acl_user->plugin.str == native_password_plugin_name.str ||
3300 acl_user->plugin.str == old_password_plugin_name.str)
3301 {
3302 acl_user->auth_string.str= strmake_root(&acl_memroot, user->pwhash.str, user->pwhash.length);
3303 acl_user->auth_string.length= user->pwhash.length;
3304 set_user_salt(acl_user, user->pwhash.str, user->pwhash.length);
3305
3306 set_user_plugin(acl_user, user->pwhash.length);
3307 }
3308 else
3309 push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
3310 ER_SET_PASSWORD_AUTH_PLUGIN,
3311 ER_THD(thd, ER_SET_PASSWORD_AUTH_PLUGIN));
3312
3313 if (update_user_table(thd, tables.user_table(),
3314 safe_str(acl_user->host.hostname),
3315 safe_str(acl_user->user.str),
3316 user->pwhash.str, user->pwhash.length))
3317 {
3318 mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
3319 goto end;
3320 }
3321
3322 acl_cache->clear(1); // Clear locked hostname cache
3323 mysql_mutex_unlock(&acl_cache->lock);
3324 result= 0;
3325 if (mysql_bin_log.is_open())
3326 {
3327 DBUG_ASSERT(query_length);
3328 thd->clear_error();
3329 result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
3330 FALSE, FALSE, FALSE, 0);
3331 }
3332end:
3333 close_mysql_tables(thd);
3334
3335#ifdef WITH_WSREP
3336error: // this label is used in WSREP_TO_ISOLATION_BEGIN
3337 if (WSREP(thd) && !thd->wsrep_applier)
3338 {
3339 WSREP_TO_ISOLATION_END;
3340
3341 thd->set_query(query_save);
3342 thd->wsrep_exec_mode = LOCAL_STATE;
3343 }
3344#endif /* WITH_WSREP */
3345 thd->restore_stmt_binlog_format(save_binlog_format);
3346
3347 DBUG_RETURN(result);
3348}
3349
3350int acl_check_set_default_role(THD *thd, const char *host, const char *user)
3351{
3352 return check_alter_user(thd, host, user);
3353}
3354
3355int acl_set_default_role(THD *thd, const char *host, const char *user,
3356 const char *rolename)
3357{
3358 Grant_tables tables(Table_user, TL_WRITE);
3359 char user_key[MAX_KEY_LENGTH];
3360 int result= 1;
3361 int error;
3362 ulong query_length= 0;
3363 bool clear_role= FALSE;
3364 char buff[512];
3365 enum_binlog_format save_binlog_format;
3366 const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
3367
3368 DBUG_ENTER("acl_set_default_role");
3369 DBUG_PRINT("enter",("host: '%s' user: '%s' rolename: '%s'",
3370 safe_str(user), safe_str(host), safe_str(rolename)));
3371
3372 if (rolename == current_role.str) {
3373 if (!thd->security_ctx->priv_role[0])
3374 rolename= "NONE";
3375 else
3376 rolename= thd->security_ctx->priv_role;
3377 }
3378
3379 if (check_user_can_set_role(user, host, host, rolename, NULL))
3380 DBUG_RETURN(result);
3381
3382 if (!strcasecmp(rolename, "NONE"))
3383 clear_role= TRUE;
3384
3385 if (mysql_bin_log.is_open() ||
3386 (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)))
3387 {
3388 query_length=
3389 sprintf(buff,"SET DEFAULT ROLE '%-.120s' FOR '%-.120s'@'%-.120s'",
3390 safe_str(rolename), safe_str(user), safe_str(host));
3391 }
3392
3393 /*
3394 This statement will be replicated as a statement, even when using
3395 row-based replication. The flag will be reset at the end of the
3396 statement.
3397 This has to be handled here as it's called by set_var.cc, which is
3398 not automaticly handled by sql_parse.cc
3399 */
3400 save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
3401
3402 if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
3403 {
3404 thd->set_query(buff, query_length, system_charset_info);
3405 WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL);
3406 }
3407
3408 /*
3409 Extra block due to WSREP_TO_ISOLATION_BEGIN using goto.
3410 TODO(cvicentiu) Should move this block out in a new function.
3411 */
3412 {
3413 if ((result= tables.open_and_lock(thd)))
3414 DBUG_RETURN(result != 1);
3415
3416 const User_table& user_table= tables.user_table();
3417 TABLE *table= user_table.table();
3418
3419 result= 1;
3420
3421 mysql_mutex_lock(&acl_cache->lock);
3422 ACL_USER *acl_user;
3423 if (!(acl_user= find_user_exact(host, user)))
3424 {
3425 mysql_mutex_unlock(&acl_cache->lock);
3426 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
3427 MYF(0));
3428 goto end;
3429 }
3430
3431 if (!clear_role)
3432 {
3433 /* set new default_rolename */
3434 acl_user->default_rolename.str= safe_strdup_root(&acl_memroot, rolename);
3435 acl_user->default_rolename.length= strlen(rolename);
3436 }
3437 else
3438 {
3439 /* clear the default_rolename */
3440 acl_user->default_rolename.str = NULL;
3441 acl_user->default_rolename.length = 0;
3442 }
3443
3444 /* update the mysql.user table with the new default role */
3445 tables.user_table().table()->use_all_columns();
3446 if (!tables.user_table().default_role())
3447 {
3448 my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
3449 table->alias.c_ptr(), DEFAULT_ROLE_COLUMN_IDX + 1,
3450 tables.user_table().num_fields(),
3451 static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
3452 mysql_mutex_unlock(&acl_cache->lock);
3453 goto end;
3454 }
3455 user_table.host()->store(host,(uint) strlen(host), system_charset_info);
3456 user_table.user()->store(user,(uint) strlen(user), system_charset_info);
3457 key_copy((uchar *) user_key, table->record[0], table->key_info,
3458 table->key_info->key_length);
3459
3460 if (table->file->ha_index_read_idx_map(table->record[0], 0,
3461 (uchar *) user_key, HA_WHOLE_KEY,
3462 HA_READ_KEY_EXACT))
3463 {
3464 mysql_mutex_unlock(&acl_cache->lock);
3465 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
3466 MYF(0));
3467 goto end;
3468 }
3469 store_record(table, record[1]);
3470 user_table.default_role()->store(acl_user->default_rolename.str,
3471 acl_user->default_rolename.length,
3472 system_charset_info);
3473 if (unlikely(error= table->file->ha_update_row(table->record[1],
3474 table->record[0])) &&
3475 error != HA_ERR_RECORD_IS_THE_SAME)
3476 {
3477 mysql_mutex_unlock(&acl_cache->lock);
3478 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
3479 goto end;
3480 }
3481
3482 acl_cache->clear(1);
3483 mysql_mutex_unlock(&acl_cache->lock);
3484 result= 0;
3485 if (mysql_bin_log.is_open())
3486 {
3487 DBUG_ASSERT(query_length);
3488 thd->clear_error();
3489 result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
3490 FALSE, FALSE, FALSE, 0);
3491 }
3492 end:
3493 close_mysql_tables(thd);
3494 }
3495
3496#ifdef WITH_WSREP
3497error: // this label is used in WSREP_TO_ISOLATION_END
3498 if (WSREP(thd) && !thd->wsrep_applier)
3499 {
3500 WSREP_TO_ISOLATION_END;
3501
3502 thd->set_query(query_save);
3503 thd->wsrep_exec_mode = LOCAL_STATE;
3504 }
3505#endif /* WITH_WSREP */
3506
3507 thd->restore_stmt_binlog_format(save_binlog_format);
3508
3509 DBUG_RETURN(result);
3510}
3511
3512
3513/*
3514 Find user in ACL
3515
3516 SYNOPSIS
3517 is_acl_user()
3518 host host name
3519 user user name
3520
3521 RETURN
3522 FALSE user not fond
3523 TRUE there is such user
3524*/
3525
3526bool is_acl_user(const char *host, const char *user)
3527{
3528 bool res;
3529
3530 /* --skip-grants */
3531 if (!initialized)
3532 return TRUE;
3533
3534 mysql_mutex_lock(&acl_cache->lock);
3535
3536 if (*host) // User
3537 res= find_user_exact(host, user) != NULL;
3538 else // Role
3539 res= find_acl_role(user) != NULL;
3540
3541 mysql_mutex_unlock(&acl_cache->lock);
3542 return res;
3543}
3544
3545
3546/*
3547 unlike find_user_exact and find_user_wild,
3548 this function finds anonymous users too, it's when a
3549 user is not empty, but priv_user (acl_user->user) is empty.
3550*/
3551static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip)
3552{
3553 ACL_USER *result= NULL;
3554 mysql_mutex_assert_owner(&acl_cache->lock);
3555 for (uint i=0; i < acl_users.elements; i++)
3556 {
3557 ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
3558 if ((!acl_user_tmp->user.str ||
3559 !strcmp(user, acl_user_tmp->user.str)) &&
3560 compare_hostname(&acl_user_tmp->host, host, ip))
3561 {
3562 result= acl_user_tmp;
3563 break;
3564 }
3565 }
3566 return result;
3567}
3568
3569
3570/*
3571 Find first entry that matches the specified user@host pair
3572*/
3573static ACL_USER * find_user_exact(const char *host, const char *user)
3574{
3575 mysql_mutex_assert_owner(&acl_cache->lock);
3576
3577 for (uint i=0 ; i < acl_users.elements ; i++)
3578 {
3579 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
3580 if (acl_user->eq(user, host))
3581 return acl_user;
3582 }
3583 return 0;
3584}
3585
3586/*
3587 Find first entry that matches the specified user@host pair
3588*/
3589static ACL_USER * find_user_wild(const char *host, const char *user, const char *ip)
3590{
3591 mysql_mutex_assert_owner(&acl_cache->lock);
3592
3593 for (uint i=0 ; i < acl_users.elements ; i++)
3594 {
3595 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
3596 if (acl_user->wild_eq(user, host, ip))
3597 return acl_user;
3598 }
3599 return 0;
3600}
3601
3602/*
3603 Find a role with the specified name
3604*/
3605static ACL_ROLE *find_acl_role(const char *role)
3606{
3607 DBUG_ENTER("find_acl_role");
3608 DBUG_PRINT("enter",("role: '%s'", role));
3609 DBUG_PRINT("info", ("Hash elements: %ld", acl_roles.records));
3610
3611 mysql_mutex_assert_owner(&acl_cache->lock);
3612
3613 ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role,
3614 safe_strlen(role));
3615 DBUG_RETURN(r);
3616}
3617
3618
3619static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host)
3620{
3621 if (*host)
3622 return find_user_exact(host, user);
3623
3624 return find_acl_role(user);
3625}
3626
3627
3628/*
3629 Comparing of hostnames
3630
3631 NOTES
3632 A hostname may be of type:
3633 hostname (May include wildcards); monty.pp.sci.fi
3634 ip (May include wildcards); 192.168.0.0
3635 ip/netmask 192.168.0.0/255.255.255.0
3636
3637 A net mask of 0.0.0.0 is not allowed.
3638*/
3639
3640static const char *calc_ip(const char *ip, long *val, char end)
3641{
3642 long ip_val,tmp;
3643 if (!(ip=str2int(ip,10,0,255,&ip_val)) || *ip != '.')
3644 return 0;
3645 ip_val<<=24;
3646 if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
3647 return 0;
3648 ip_val+=tmp<<16;
3649 if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
3650 return 0;
3651 ip_val+=tmp<<8;
3652 if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != end)
3653 return 0;
3654 *val=ip_val+tmp;
3655 return ip;
3656}
3657
3658
3659static void update_hostname(acl_host_and_ip *host, const char *hostname)
3660{
3661 // fix historical undocumented convention that empty host is the same as '%'
3662 hostname=const_cast<char*>(hostname ? hostname : host_not_specified.str);
3663 host->hostname=(char*) hostname; // This will not be modified!
3664 if (!(hostname= calc_ip(hostname,&host->ip,'/')) ||
3665 !(hostname= calc_ip(hostname+1,&host->ip_mask,'\0')))
3666 {
3667 host->ip= host->ip_mask=0; // Not a masked ip
3668 }
3669}
3670
3671
3672static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
3673 const char *ip)
3674{
3675 long tmp;
3676 if (host->ip_mask && ip && calc_ip(ip,&tmp,'\0'))
3677 {
3678 return (tmp & host->ip_mask) == host->ip;
3679 }
3680 return (!host->hostname ||
3681 (hostname && !wild_case_compare(system_charset_info,
3682 hostname, host->hostname)) ||
3683 (ip && !wild_compare(ip, host->hostname, 0)));
3684}
3685
3686/**
3687 Check if the given host name needs to be resolved or not.
3688 Host name has to be resolved if it actually contains *name*.
3689
3690 For example:
3691 192.168.1.1 --> FALSE
3692 192.168.1.0/255.255.255.0 --> FALSE
3693 % --> FALSE
3694 192.168.1.% --> FALSE
3695 AB% --> FALSE
3696
3697 AAAAFFFF --> TRUE (Hostname)
3698 AAAA:FFFF:1234:5678 --> FALSE
3699 ::1 --> FALSE
3700
3701 This function does not check if the given string is a valid host name or
3702 not. It assumes that the argument is a valid host name.
3703
3704 @param hostname the string to check.
3705
3706 @return a flag telling if the argument needs to be resolved or not.
3707 @retval TRUE the argument is a host name and needs to be resolved.
3708 @retval FALSE the argument is either an IP address, or a patter and
3709 should not be resolved.
3710*/
3711
3712bool hostname_requires_resolving(const char *hostname)
3713{
3714 if (!hostname)
3715 return FALSE;
3716
3717 /* Check if hostname is the localhost. */
3718
3719 size_t hostname_len= strlen(hostname);
3720 size_t localhost_len= strlen(my_localhost);
3721
3722 if (hostname == my_localhost ||
3723 (hostname_len == localhost_len &&
3724 !my_strnncoll(system_charset_info,
3725 (const uchar *) hostname, hostname_len,
3726 (const uchar *) my_localhost, strlen(my_localhost))))
3727 {
3728 return FALSE;
3729 }
3730
3731 /*
3732 If the string contains any of {':', '%', '_', '/'}, it is definitely
3733 not a host name:
3734 - ':' means that the string is an IPv6 address;
3735 - '%' or '_' means that the string is a pattern;
3736 - '/' means that the string is an IPv4 network address;
3737 */
3738
3739 for (const char *p= hostname; *p; ++p)
3740 {
3741 switch (*p) {
3742 case ':':
3743 case '%':
3744 case '_':
3745 case '/':
3746 return FALSE;
3747 }
3748 }
3749
3750 /*
3751 Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
3752 (12.34.56.78). The assumption is that if the string contains only
3753 digits and dots, it is an IPv4 address. Otherwise -- a host name.
3754 */
3755
3756 for (const char *p= hostname; *p; ++p)
3757 {
3758 if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
3759 return TRUE; /* a "letter" has been found. */
3760 }
3761
3762 return FALSE; /* all characters are either dots or digits. */
3763}
3764
3765
3766void set_authentication_plugin_from_password(const User_table& user_table,
3767 const char* password, size_t password_length)
3768{
3769 if (password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH)
3770 {
3771 user_table.plugin()->store(native_password_plugin_name.str,
3772 native_password_plugin_name.length,
3773 system_charset_info);
3774 }
3775 else
3776 {
3777 DBUG_ASSERT(password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
3778 user_table.plugin()->store(old_password_plugin_name.str,
3779 old_password_plugin_name.length,
3780 system_charset_info);
3781 }
3782 user_table.authentication_string()->store(password,
3783 password_length,
3784 system_charset_info);
3785}
3786/**
3787 Update record for user in mysql.user privilege table with new password.
3788
3789 @param thd THD
3790 @param table Pointer to TABLE object for open mysql.user table
3791 @param host Hostname
3792 @param user Username
3793 @param new_password New password hash
3794 @param new_password_len Length of new password hash
3795
3796 @see change_password
3797*/
3798
3799static bool update_user_table(THD *thd, const User_table& user_table,
3800 const char *host, const char *user,
3801 const char *new_password, size_t new_password_len)
3802{
3803 char user_key[MAX_KEY_LENGTH];
3804 int error;
3805 DBUG_ENTER("update_user_table");
3806 DBUG_PRINT("enter",("user: %s host: %s",user,host));
3807
3808 TABLE *table= user_table.table();
3809 table->use_all_columns();
3810 user_table.host()->store(host,(uint) strlen(host), system_charset_info);
3811 user_table.user()->store(user,(uint) strlen(user), system_charset_info);
3812 key_copy((uchar *) user_key, table->record[0], table->key_info,
3813 table->key_info->key_length);
3814
3815 if (table->file->ha_index_read_idx_map(table->record[0], 0,
3816 (uchar *) user_key, HA_WHOLE_KEY,
3817 HA_READ_KEY_EXACT))
3818 {
3819 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
3820 MYF(0)); /* purecov: deadcode */
3821 DBUG_RETURN(1); /* purecov: deadcode */
3822 }
3823 store_record(table,record[1]);
3824 /* If the password column is missing, we use the
3825 authentication_string column. */
3826 if (user_table.password())
3827 user_table.password()->store(new_password, new_password_len, system_charset_info);
3828 else
3829 set_authentication_plugin_from_password(user_table, new_password,
3830 new_password_len);
3831
3832
3833 if (unlikely(error= table->file->ha_update_row(table->record[1],
3834 table->record[0])) &&
3835 error != HA_ERR_RECORD_IS_THE_SAME)
3836 {
3837 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
3838 DBUG_RETURN(1);
3839 }
3840 DBUG_RETURN(0);
3841}
3842
3843
3844/*
3845 Return 1 if we are allowed to create new users
3846 the logic here is: INSERT_ACL is sufficient.
3847 It's also a requirement in opt_safe_user_create,
3848 otherwise CREATE_USER_ACL is enough.
3849*/
3850
3851static bool test_if_create_new_users(THD *thd)
3852{
3853 Security_context *sctx= thd->security_ctx;
3854 bool create_new_users= MY_TEST(sctx->master_access & INSERT_ACL) ||
3855 (!opt_safe_user_create &&
3856 MY_TEST(sctx->master_access & CREATE_USER_ACL));
3857 if (!create_new_users)
3858 {
3859 TABLE_LIST tl;
3860 ulong db_access;
3861 tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, TL_WRITE);
3862 create_new_users= 1;
3863
3864 db_access=acl_get(sctx->host, sctx->ip,
3865 sctx->priv_user, tl.db.str, 0);
3866 if (sctx->priv_role[0])
3867 db_access|= acl_get("", "", sctx->priv_role, tl.db.str, 0);
3868 if (!(db_access & INSERT_ACL))
3869 {
3870 if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
3871 create_new_users=0;
3872 }
3873 }
3874 return create_new_users;
3875}
3876
3877
3878/****************************************************************************
3879 Handle GRANT commands
3880****************************************************************************/
3881
3882static int replace_user_table(THD *thd, const User_table &user_table,
3883 LEX_USER &combo,
3884 ulong rights, bool revoke_grant,
3885 bool can_create_user, bool no_auto_create)
3886{
3887 int error = -1;
3888 bool old_row_exists=0;
3889 char what= (revoke_grant) ? 'N' : 'Y';
3890 uchar user_key[MAX_KEY_LENGTH];
3891 bool handle_as_role= combo.is_role();
3892 LEX *lex= thd->lex;
3893 TABLE *table= user_table.table();
3894 DBUG_ENTER("replace_user_table");
3895
3896 mysql_mutex_assert_owner(&acl_cache->lock);
3897
3898 if (combo.pwhash.str && combo.pwhash.str[0])
3899 {
3900 if (combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
3901 combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
3902 {
3903 DBUG_ASSERT(0);
3904 my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
3905 DBUG_RETURN(-1);
3906 }
3907 }
3908 else
3909 combo.pwhash= empty_clex_str;
3910
3911 /* if the user table is not up to date, we can't handle role updates */
3912 if (!user_table.is_role() && handle_as_role)
3913 {
3914 my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
3915 "user", ROLE_ASSIGN_COLUMN_IDX + 1, user_table.num_fields(),
3916 static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
3917 DBUG_RETURN(-1);
3918 }
3919
3920 table->use_all_columns();
3921 user_table.host()->store(combo.host.str,combo.host.length,
3922 system_charset_info);
3923 user_table.user()->store(combo.user.str,combo.user.length,
3924 system_charset_info);
3925 key_copy(user_key, table->record[0], table->key_info,
3926 table->key_info->key_length);
3927
3928 if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
3929 HA_WHOLE_KEY,
3930 HA_READ_KEY_EXACT))
3931 {
3932 /* what == 'N' means revoke */
3933 if (what == 'N')
3934 {
3935 my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
3936 goto end;
3937 }
3938 /*
3939 There are four options which affect the process of creation of
3940 a new user (mysqld option --safe-create-user, 'insert' privilege
3941 on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
3942 SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
3943 how it should work.
3944 if (safe-user-create && ! INSERT_priv) => reject
3945 else if (identified_by) => create
3946 else if (no_auto_create_user) => reject
3947 else create
3948
3949 see also test_if_create_new_users()
3950 */
3951 else if (!combo.pwhash.length && !combo.plugin.length && no_auto_create)
3952 {
3953 my_error(ER_PASSWORD_NO_MATCH, MYF(0));
3954 goto end;
3955 }
3956 else if (!can_create_user)
3957 {
3958 my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
3959 goto end;
3960 }
3961 else if (combo.plugin.str[0])
3962 {
3963 if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN))
3964 {
3965 my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str);
3966 goto end;
3967 }
3968 }
3969
3970 old_row_exists = 0;
3971 restore_record(table,s->default_values);
3972 user_table.host()->store(combo.host.str,combo.host.length,
3973 system_charset_info);
3974 user_table.user()->store(combo.user.str,combo.user.length,
3975 system_charset_info);
3976 }
3977 else
3978 {
3979 old_row_exists = 1;
3980 store_record(table,record[1]); // Save copy for update
3981 }
3982
3983 if (!old_row_exists || combo.pwtext.length || combo.pwhash.length)
3984 if (!handle_as_role && validate_password(&combo, thd))
3985 goto end;
3986
3987 /* Update table columns with new privileges */
3988
3989 ulong priv;
3990 priv = SELECT_ACL;
3991 for (uint i= 0; i < user_table.num_privileges(); i++, priv <<= 1)
3992 {
3993 if (priv & rights)
3994 user_table.priv_field(i)->store(&what, 1, &my_charset_latin1);
3995 }
3996
3997 rights= user_table.get_access();
3998
3999 DBUG_PRINT("info",("table fields: %d", user_table.num_fields()));
4000 /* If we don't have a password column, we'll use the authentication_string
4001 column later. */
4002 if (combo.pwhash.str[0] && user_table.password())
4003 user_table.password()->store(combo.pwhash.str, combo.pwhash.length,
4004 system_charset_info);
4005 /* We either have the password column, the plugin column, or both. Otherwise
4006 we have a corrupt user table. */
4007 DBUG_ASSERT(user_table.password() || user_table.plugin());
4008 if (user_table.ssl_type()) /* From 4.0.0 we have more fields */
4009 {
4010 /* We write down SSL related ACL stuff */
4011 switch (lex->ssl_type) {
4012 case SSL_TYPE_ANY:
4013 user_table.ssl_type()->store(STRING_WITH_LEN("ANY"),
4014 &my_charset_latin1);
4015 user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4016 user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4017 user_table.x509_subject()->store("", 0, &my_charset_latin1);
4018 break;
4019 case SSL_TYPE_X509:
4020 user_table.ssl_type()->store(STRING_WITH_LEN("X509"),
4021 &my_charset_latin1);
4022 user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4023 user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4024 user_table.x509_subject()->store("", 0, &my_charset_latin1);
4025 break;
4026 case SSL_TYPE_SPECIFIED:
4027 user_table.ssl_type()->store(STRING_WITH_LEN("SPECIFIED"),
4028 &my_charset_latin1);
4029 user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4030 user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4031 user_table.x509_subject()->store("", 0, &my_charset_latin1);
4032 if (lex->ssl_cipher)
4033 user_table.ssl_cipher()->store(lex->ssl_cipher,
4034 strlen(lex->ssl_cipher),
4035 system_charset_info);
4036 if (lex->x509_issuer)
4037 user_table.x509_issuer()->store(lex->x509_issuer,
4038 strlen(lex->x509_issuer),
4039 system_charset_info);
4040 if (lex->x509_subject)
4041 user_table.x509_subject()->store(lex->x509_subject,
4042 strlen(lex->x509_subject),
4043 system_charset_info);
4044 break;
4045 case SSL_TYPE_NOT_SPECIFIED:
4046 break;
4047 case SSL_TYPE_NONE:
4048 user_table.ssl_type()->store("", 0, &my_charset_latin1);
4049 user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4050 user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4051 user_table.x509_subject()->store("", 0, &my_charset_latin1);
4052 break;
4053 }
4054
4055 USER_RESOURCES mqh= lex->mqh;
4056 if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
4057 user_table.max_questions()->store((longlong) mqh.questions, TRUE);
4058 if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
4059 user_table.max_updates()->store((longlong) mqh.updates, TRUE);
4060 if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
4061 user_table.max_connections()->store((longlong) mqh.conn_per_hour, TRUE);
4062 if (user_table.max_user_connections() &&
4063 (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
4064 user_table.max_user_connections()->store((longlong) mqh.user_conn, FALSE);
4065 if (user_table.plugin())
4066 {
4067 user_table.plugin()->set_notnull();
4068 user_table.authentication_string()->set_notnull();
4069 if (combo.plugin.str[0])
4070 {
4071 DBUG_ASSERT(combo.pwhash.str[0] == 0);
4072 if (user_table.password())
4073 user_table.password()->reset();
4074 user_table.plugin()->store(combo.plugin.str, combo.plugin.length,
4075 system_charset_info);
4076 user_table.authentication_string()->store(combo.auth.str, combo.auth.length,
4077 system_charset_info);
4078 }
4079 if (combo.pwhash.str[0])
4080 {
4081 DBUG_ASSERT(combo.plugin.str[0] == 0);
4082 /* We have Password column. */
4083 if (user_table.password())
4084 {
4085 user_table.plugin()->reset();
4086 user_table.authentication_string()->reset();
4087 }
4088 else
4089 {
4090 /* We do not have Password column. Use PLUGIN && Authentication_string
4091 columns instead. */
4092 set_authentication_plugin_from_password(user_table,
4093 combo.pwhash.str,
4094 combo.pwhash.length);
4095 }
4096 }
4097
4098 if (user_table.max_statement_time())
4099 {
4100 if (mqh.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
4101 user_table.max_statement_time()->store(mqh.max_statement_time);
4102 }
4103 }
4104 mqh_used= (mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour ||
4105 mqh.user_conn || mqh.max_statement_time != 0.0);
4106
4107 /* table format checked earlier */
4108 if (handle_as_role)
4109 {
4110 if (old_row_exists && !user_table.check_is_role())
4111 {
4112 goto end;
4113 }
4114 user_table.is_role()->store("Y", 1, system_charset_info);
4115 }
4116 }
4117
4118 if (old_row_exists)
4119 {
4120 /*
4121 We should NEVER delete from the user table, as a uses can still
4122 use mysqld even if he doesn't have any privileges in the user table!
4123 */
4124 if (cmp_record(table, record[1]))
4125 {
4126 if (unlikely(error= table->file->ha_update_row(table->record[1],
4127 table->record[0])) &&
4128 error != HA_ERR_RECORD_IS_THE_SAME)
4129 { // This should never happen
4130 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4131 error= -1; /* purecov: deadcode */
4132 goto end; /* purecov: deadcode */
4133 }
4134 else
4135 error= 0;
4136 }
4137 }
4138 else if (unlikely(error=table->file->ha_write_row(table->record[0])))
4139 {
4140 // This should never happen
4141 if (table->file->is_fatal_error(error, HA_CHECK_DUP))
4142 {
4143 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4144 error= -1; /* purecov: deadcode */
4145 goto end; /* purecov: deadcode */
4146 }
4147 }
4148 error=0; // Privileges granted / revoked
4149
4150end:
4151 if (likely(!error))
4152 {
4153 acl_cache->clear(1); // Clear privilege cache
4154 if (old_row_exists)
4155 {
4156 if (handle_as_role)
4157 acl_update_role(combo.user.str, rights);
4158 else
4159 acl_update_user(combo.user.str, combo.host.str,
4160 combo.pwhash.str, combo.pwhash.length,
4161 lex->ssl_type,
4162 lex->ssl_cipher,
4163 lex->x509_issuer,
4164 lex->x509_subject,
4165 &lex->mqh,
4166 rights,
4167 &combo.plugin,
4168 &combo.auth);
4169 }
4170 else
4171 {
4172 if (handle_as_role)
4173 acl_insert_role(combo.user.str, rights);
4174 else
4175 acl_insert_user(combo.user.str, combo.host.str,
4176 combo.pwhash.str, combo.pwhash.length,
4177 lex->ssl_type,
4178 lex->ssl_cipher,
4179 lex->x509_issuer,
4180 lex->x509_subject,
4181 &lex->mqh,
4182 rights,
4183 &combo.plugin,
4184 &combo.auth);
4185 }
4186 }
4187 DBUG_RETURN(error);
4188}
4189
4190
4191/*
4192 change grants in the mysql.db table
4193*/
4194
4195static int replace_db_table(TABLE *table, const char *db,
4196 const LEX_USER &combo,
4197 ulong rights, bool revoke_grant)
4198{
4199 uint i;
4200 ulong priv,store_rights;
4201 bool old_row_exists=0;
4202 int error;
4203 char what= (revoke_grant) ? 'N' : 'Y';
4204 uchar user_key[MAX_KEY_LENGTH];
4205 DBUG_ENTER("replace_db_table");
4206
4207 /* Check if there is such a user in user table in memory? */
4208 if (!find_user_wild(combo.host.str,combo.user.str))
4209 {
4210 /* The user could be a role, check if the user is registered as a role */
4211 if (!combo.host.length && !find_acl_role(combo.user.str))
4212 {
4213 my_message(ER_PASSWORD_NO_MATCH, ER_THD(table->in_use,
4214 ER_PASSWORD_NO_MATCH), MYF(0));
4215 DBUG_RETURN(-1);
4216 }
4217 }
4218
4219 table->use_all_columns();
4220 table->field[0]->store(combo.host.str,combo.host.length,
4221 system_charset_info);
4222 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4223 table->field[2]->store(combo.user.str,combo.user.length,
4224 system_charset_info);
4225 key_copy(user_key, table->record[0], table->key_info,
4226 table->key_info->key_length);
4227
4228 if (table->file->ha_index_read_idx_map(table->record[0],0, user_key,
4229 HA_WHOLE_KEY,
4230 HA_READ_KEY_EXACT))
4231 {
4232 if (what == 'N')
4233 { // no row, no revoke
4234 my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
4235 goto abort;
4236 }
4237 old_row_exists = 0;
4238 restore_record(table, s->default_values);
4239 table->field[0]->store(combo.host.str,combo.host.length,
4240 system_charset_info);
4241 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4242 table->field[2]->store(combo.user.str,combo.user.length,
4243 system_charset_info);
4244 }
4245 else
4246 {
4247 old_row_exists = 1;
4248 store_record(table,record[1]);
4249 }
4250
4251 store_rights=get_rights_for_db(rights);
4252 for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
4253 {
4254 if (priv & store_rights) // do it if priv is chosen
4255 table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
4256 }
4257 rights=get_access(table,3);
4258 rights=fix_rights_for_db(rights);
4259
4260 if (old_row_exists)
4261 {
4262 /* update old existing row */
4263 if (rights)
4264 {
4265 if (unlikely((error= table->file->ha_update_row(table->record[1],
4266 table->record[0]))) &&
4267 error != HA_ERR_RECORD_IS_THE_SAME)
4268 goto table_error; /* purecov: deadcode */
4269 }
4270 else /* must have been a revoke of all privileges */
4271 {
4272 if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4273 goto table_error; /* purecov: deadcode */
4274 }
4275 }
4276 else if (rights &&
4277 (unlikely(error= table->file->ha_write_row(table->record[0]))))
4278 {
4279 if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4280 goto table_error; /* purecov: deadcode */
4281 }
4282
4283 acl_cache->clear(1); // Clear privilege cache
4284 if (old_row_exists)
4285 acl_update_db(combo.user.str,combo.host.str,db,rights);
4286 else if (rights)
4287 {
4288 /*
4289 If we did not have an already existing row, for users, we must always
4290 insert an ACL_DB entry. For roles however, it is possible that one was
4291 already created when DB privileges were propagated from other granted
4292 roles onto the current role. For this case, first try to update the
4293 existing entry, otherwise insert a new one.
4294 */
4295 if (!combo.is_role() ||
4296 !acl_update_db(combo.user.str, combo.host.str, db, rights))
4297 {
4298 acl_insert_db(combo.user.str,combo.host.str,db,rights);
4299 }
4300 }
4301 DBUG_RETURN(0);
4302
4303 /* This could only happen if the grant tables got corrupted */
4304table_error:
4305 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4306
4307abort:
4308 DBUG_RETURN(-1);
4309}
4310
4311/**
4312 Updates the mysql.roles_mapping table
4313
4314 @param table TABLE to update
4315 @param user user name of the grantee
4316 @param host host name of the grantee
4317 @param role role name to grant
4318 @param with_admin WITH ADMIN OPTION flag
4319 @param existing the entry in the acl_roles_mappings hash or NULL.
4320 it is never NULL if revoke_grant is true.
4321 it is NULL when a new pair is added, it's not NULL
4322 when an existing pair is updated.
4323 @param revoke_grant true for REVOKE, false for GRANT
4324*/
4325static int
4326replace_roles_mapping_table(TABLE *table, LEX_CSTRING *user, LEX_CSTRING *host,
4327 LEX_CSTRING *role, bool with_admin,
4328 ROLE_GRANT_PAIR *existing, bool revoke_grant)
4329{
4330 DBUG_ENTER("replace_roles_mapping_table");
4331
4332 uchar row_key[MAX_KEY_LENGTH];
4333 int error;
4334 table->use_all_columns();
4335 restore_record(table, s->default_values);
4336 table->field[0]->store(host->str, host->length, system_charset_info);
4337 table->field[1]->store(user->str, user->length, system_charset_info);
4338 table->field[2]->store(role->str, role->length, system_charset_info);
4339
4340 DBUG_ASSERT(!revoke_grant || existing);
4341
4342 if (existing) // delete or update
4343 {
4344 key_copy(row_key, table->record[0], table->key_info,
4345 table->key_info->key_length);
4346 if (table->file->ha_index_read_idx_map(table->record[1], 0, row_key,
4347 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
4348 {
4349 /* No match */
4350 DBUG_RETURN(1);
4351 }
4352 if (revoke_grant && !with_admin)
4353 {
4354 if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4355 {
4356 DBUG_PRINT("info", ("error deleting row '%s' '%s' '%s'",
4357 host->str, user->str, role->str));
4358 goto table_error;
4359 }
4360 }
4361 else if (with_admin)
4362 {
4363 table->field[3]->store(!revoke_grant + 1);
4364
4365 if (unlikely((error= table->file->ha_update_row(table->record[1],
4366 table->record[0]))))
4367 {
4368 DBUG_PRINT("info", ("error updating row '%s' '%s' '%s'",
4369 host->str, user->str, role->str));
4370 goto table_error;
4371 }
4372 }
4373 DBUG_RETURN(0);
4374 }
4375
4376 table->field[3]->store(with_admin + 1);
4377
4378 if (unlikely((error= table->file->ha_write_row(table->record[0]))))
4379 {
4380 DBUG_PRINT("info", ("error inserting row '%s' '%s' '%s'",
4381 host->str, user->str, role->str));
4382 goto table_error;
4383 }
4384
4385 /* all ok */
4386 DBUG_RETURN(0);
4387
4388table_error:
4389 DBUG_PRINT("info", ("table error"));
4390 table->file->print_error(error, MYF(0));
4391 DBUG_RETURN(1);
4392}
4393
4394
4395/**
4396 Updates the acl_roles_mappings hash
4397
4398 @param user user name of the grantee
4399 @param host host name of the grantee
4400 @param role role name to grant
4401 @param with_admin WITH ADMIN OPTION flag
4402 @param existing the entry in the acl_roles_mappings hash or NULL.
4403 it is never NULL if revoke_grant is true.
4404 it is NULL when a new pair is added, it's not NULL
4405 when an existing pair is updated.
4406 @param revoke_grant true for REVOKE, false for GRANT
4407*/
4408static int
4409update_role_mapping(LEX_CSTRING *user, LEX_CSTRING *host, LEX_CSTRING *role,
4410 bool with_admin, ROLE_GRANT_PAIR *existing, bool revoke_grant)
4411{
4412 if (revoke_grant)
4413 {
4414 if (with_admin)
4415 {
4416 existing->with_admin= false;
4417 return 0;
4418 }
4419 return my_hash_delete(&acl_roles_mappings, (uchar*)existing);
4420 }
4421
4422 if (existing)
4423 {
4424 existing->with_admin|= with_admin;
4425 return 0;
4426 }
4427
4428 /* allocate a new entry that will go in the hash */
4429 ROLE_GRANT_PAIR *hash_entry= new (&acl_memroot) ROLE_GRANT_PAIR;
4430 if (hash_entry->init(&acl_memroot, user->str, host->str,
4431 role->str, with_admin))
4432 return 1;
4433 return my_hash_insert(&acl_roles_mappings, (uchar*) hash_entry);
4434}
4435
4436static void
4437acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
4438{
4439 mysql_mutex_assert_owner(&acl_cache->lock);
4440
4441 DBUG_ENTER("acl_update_proxy_user");
4442 for (uint i= 0; i < acl_proxy_users.elements; i++)
4443 {
4444 ACL_PROXY_USER *acl_user=
4445 dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
4446
4447 if (acl_user->pk_equals(new_value))
4448 {
4449 if (is_revoke)
4450 {
4451 DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
4452 delete_dynamic_element(&acl_proxy_users, i);
4453 }
4454 else
4455 {
4456 DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
4457 acl_user->set_data(new_value);
4458 }
4459 break;
4460 }
4461 }
4462 DBUG_VOID_RETURN;
4463}
4464
4465
4466static void
4467acl_insert_proxy_user(ACL_PROXY_USER *new_value)
4468{
4469 DBUG_ENTER("acl_insert_proxy_user");
4470 mysql_mutex_assert_owner(&acl_cache->lock);
4471 (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
4472 my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
4473 acl_proxy_users.elements,
4474 sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
4475 DBUG_VOID_RETURN;
4476}
4477
4478
4479static int
4480replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
4481 const LEX_USER *proxied_user, bool with_grant_arg,
4482 bool revoke_grant)
4483{
4484 bool old_row_exists= 0;
4485 int error;
4486 uchar user_key[MAX_KEY_LENGTH];
4487 ACL_PROXY_USER new_grant;
4488 char grantor[USER_HOST_BUFF_SIZE];
4489
4490 DBUG_ENTER("replace_proxies_priv_table");
4491
4492 /* Check if there is such a user in user table in memory? */
4493 if (!find_user_wild(user->host.str,user->user.str))
4494 {
4495 my_message(ER_PASSWORD_NO_MATCH,
4496 ER_THD(thd, ER_PASSWORD_NO_MATCH), MYF(0));
4497 DBUG_RETURN(-1);
4498 }
4499
4500 table->use_all_columns();
4501 ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
4502 &proxied_user->host, &proxied_user->user);
4503
4504 key_copy(user_key, table->record[0], table->key_info,
4505 table->key_info->key_length);
4506
4507 get_grantor(thd, grantor);
4508
4509 if (unlikely((error= table->file->ha_index_init(0, 1))))
4510 {
4511 table->file->print_error(error, MYF(0));
4512 DBUG_PRINT("info", ("ha_index_init error"));
4513 DBUG_RETURN(-1);
4514 }
4515
4516 if (table->file->ha_index_read_map(table->record[0], user_key,
4517 HA_WHOLE_KEY,
4518 HA_READ_KEY_EXACT))
4519 {
4520 DBUG_PRINT ("info", ("Row not found"));
4521 if (revoke_grant)
4522 { // no row, no revoke
4523 my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
4524 goto abort;
4525 }
4526 old_row_exists= 0;
4527 restore_record(table, s->default_values);
4528 ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
4529 &proxied_user->host,
4530 &proxied_user->user,
4531 with_grant_arg,
4532 grantor);
4533 }
4534 else
4535 {
4536 DBUG_PRINT("info", ("Row found"));
4537 old_row_exists= 1;
4538 store_record(table, record[1]);
4539 }
4540
4541 if (old_row_exists)
4542 {
4543 /* update old existing row */
4544 if (!revoke_grant)
4545 {
4546 if (unlikely(error= table->file->ha_update_row(table->record[1],
4547 table->record[0])) &&
4548 error != HA_ERR_RECORD_IS_THE_SAME)
4549 goto table_error; /* purecov: inspected */
4550 }
4551 else
4552 {
4553 if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4554 goto table_error; /* purecov: inspected */
4555 }
4556 }
4557 else if (unlikely((error= table->file->ha_write_row(table->record[0]))))
4558 {
4559 DBUG_PRINT("info", ("error inserting the row"));
4560 if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4561 goto table_error; /* purecov: inspected */
4562 }
4563
4564 acl_cache->clear(1); // Clear privilege cache
4565 if (old_row_exists)
4566 {
4567 new_grant.init(user->host.str, user->user.str,
4568 proxied_user->host.str, proxied_user->user.str,
4569 with_grant_arg);
4570 acl_update_proxy_user(&new_grant, revoke_grant);
4571 }
4572 else
4573 {
4574 new_grant.init(&acl_memroot, user->host.str, user->user.str,
4575 proxied_user->host.str, proxied_user->user.str,
4576 with_grant_arg);
4577 acl_insert_proxy_user(&new_grant);
4578 }
4579
4580 table->file->ha_index_end();
4581 DBUG_RETURN(0);
4582
4583 /* This could only happen if the grant tables got corrupted */
4584table_error:
4585 DBUG_PRINT("info", ("table error"));
4586 table->file->print_error(error, MYF(0)); /* purecov: inspected */
4587
4588abort:
4589 DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
4590 table->file->ha_index_end();
4591 DBUG_RETURN(-1);
4592}
4593
4594
4595class GRANT_COLUMN :public Sql_alloc
4596{
4597public:
4598 char *column;
4599 ulong rights;
4600 ulong init_rights;
4601 uint key_length;
4602 GRANT_COLUMN(String &c, ulong y) :rights (y), init_rights(y)
4603 {
4604 column= (char*) memdup_root(&grant_memroot,c.ptr(), key_length=c.length());
4605 }
4606
4607 /* this constructor assumes thas source->column is allocated in grant_memroot */
4608 GRANT_COLUMN(GRANT_COLUMN *source) : column(source->column),
4609 rights (source->rights), init_rights(0), key_length(source->key_length) { }
4610};
4611
4612
4613static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
4614 my_bool not_used __attribute__((unused)))
4615{
4616 *length=buff->key_length;
4617 return (uchar*) buff->column;
4618}
4619
4620class GRANT_NAME :public Sql_alloc
4621{
4622public:
4623 acl_host_and_ip host;
4624 char *db, *user, *tname, *hash_key;
4625 ulong privs;
4626 ulong init_privs; /* privileges found in physical table */
4627 ulong sort;
4628 size_t key_length;
4629 GRANT_NAME(const char *h, const char *d,const char *u,
4630 const char *t, ulong p, bool is_routine);
4631 GRANT_NAME (TABLE *form, bool is_routine);
4632 virtual ~GRANT_NAME() {};
4633 virtual bool ok() { return privs != 0; }
4634 void set_user_details(const char *h, const char *d,
4635 const char *u, const char *t,
4636 bool is_routine);
4637};
4638
4639
4640class GRANT_TABLE :public GRANT_NAME
4641{
4642public:
4643 ulong cols;
4644 ulong init_cols; /* privileges found in physical table */
4645 HASH hash_columns;
4646
4647 GRANT_TABLE(const char *h, const char *d,const char *u,
4648 const char *t, ulong p, ulong c);
4649 GRANT_TABLE (TABLE *form, TABLE *col_privs);
4650 ~GRANT_TABLE();
4651 bool ok() { return privs != 0 || cols != 0; }
4652 void init_hash()
4653 {
4654 my_hash_init2(&hash_columns, 4, system_charset_info, 0, 0, 0,
4655 (my_hash_get_key) get_key_column, 0, 0, 0);
4656 }
4657};
4658
4659
4660void GRANT_NAME::set_user_details(const char *h, const char *d,
4661 const char *u, const char *t,
4662 bool is_routine)
4663{
4664 /* Host given by user */
4665 update_hostname(&host, strdup_root(&grant_memroot, h));
4666 if (db != d)
4667 {
4668 db= strdup_root(&grant_memroot, d);
4669 if (lower_case_table_names)
4670 my_casedn_str(files_charset_info, db);
4671 }
4672 user = strdup_root(&grant_memroot,u);
4673 sort= get_sort(3,host.hostname,db,user);
4674 if (tname != t)
4675 {
4676 tname= strdup_root(&grant_memroot, t);
4677 if (lower_case_table_names || is_routine)
4678 my_casedn_str(files_charset_info, tname);
4679 }
4680 key_length= strlen(d) + strlen(u)+ strlen(t)+3;
4681 hash_key= (char*) alloc_root(&grant_memroot,key_length);
4682 strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
4683}
4684
4685GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
4686 const char *t, ulong p, bool is_routine)
4687 :db(0), tname(0), privs(p), init_privs(p)
4688{
4689 set_user_details(h, d, u, t, is_routine);
4690}
4691
4692GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
4693 const char *t, ulong p, ulong c)
4694 :GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
4695{
4696 init_hash();
4697}
4698
4699/*
4700 create a new GRANT_TABLE entry for role inheritance. init_* fields are set
4701 to 0
4702*/
4703GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
4704{
4705 user= safe_str(get_field(&grant_memroot,form->field[2]));
4706
4707 const char *hostname= get_field(&grant_memroot, form->field[0]);
4708 mysql_mutex_lock(&acl_cache->lock);
4709 if (!hostname && find_acl_role(user))
4710 hostname= "";
4711 mysql_mutex_unlock(&acl_cache->lock);
4712 update_hostname(&host, hostname);
4713
4714 db= get_field(&grant_memroot,form->field[1]);
4715 sort= get_sort(3, host.hostname, db, user);
4716 tname= get_field(&grant_memroot,form->field[3]);
4717 if (!db || !tname)
4718 {
4719 /* Wrong table row; Ignore it */
4720 privs= 0;
4721 return; /* purecov: inspected */
4722 }
4723 if (lower_case_table_names)
4724 {
4725 my_casedn_str(files_charset_info, db);
4726 }
4727 if (lower_case_table_names || is_routine)
4728 {
4729 my_casedn_str(files_charset_info, tname);
4730 }
4731 key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
4732 hash_key= (char*) alloc_root(&grant_memroot, key_length);
4733 strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
4734 privs = (ulong) form->field[6]->val_int();
4735 privs = fix_rights_for_table(privs);
4736 init_privs= privs;
4737}
4738
4739
4740GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
4741 :GRANT_NAME(form, FALSE)
4742{
4743 uchar key[MAX_KEY_LENGTH];
4744
4745 if (!db || !tname)
4746 {
4747 /* Wrong table row; Ignore it */
4748 my_hash_clear(&hash_columns); /* allow for destruction */
4749 cols= 0;
4750 return;
4751 }
4752 cols= (ulong) form->field[7]->val_int();
4753 cols= fix_rights_for_column(cols);
4754 /*
4755 Initial columns privileges are the same as column privileges on creation.
4756 In case of roles, the cols privilege bits can get inherited and thus
4757 cause the cols field to change. The init_cols field is always the same
4758 as the physical table entry
4759 */
4760 init_cols= cols;
4761
4762 init_hash();
4763
4764 if (cols)
4765 {
4766 uint key_prefix_len;
4767 KEY_PART_INFO *key_part= col_privs->key_info->key_part;
4768 col_privs->field[0]->store(host.hostname,
4769 (uint) safe_strlen(host.hostname),
4770 system_charset_info);
4771 col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
4772 col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
4773 col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
4774
4775 key_prefix_len= (key_part[0].store_length +
4776 key_part[1].store_length +
4777 key_part[2].store_length +
4778 key_part[3].store_length);
4779 key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
4780 col_privs->field[4]->store("",0, &my_charset_latin1);
4781
4782 if (col_privs->file->ha_index_init(0, 1))
4783 {
4784 cols= 0;
4785 init_cols= 0;
4786 return;
4787 }
4788
4789 if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
4790 (key_part_map)15,
4791 HA_READ_KEY_EXACT))
4792 {
4793 cols= 0; /* purecov: deadcode */
4794 init_cols= 0;
4795 col_privs->file->ha_index_end();
4796 return;
4797 }
4798 do
4799 {
4800 String *res,column_name;
4801 GRANT_COLUMN *mem_check;
4802 /* As column name is a string, we don't have to supply a buffer */
4803 res=col_privs->field[4]->val_str(&column_name);
4804 ulong priv= (ulong) col_privs->field[6]->val_int();
4805 if (!(mem_check = new GRANT_COLUMN(*res,
4806 fix_rights_for_column(priv))))
4807 {
4808 /* Don't use this entry */
4809 privs= cols= init_privs= init_cols=0; /* purecov: deadcode */
4810 return; /* purecov: deadcode */
4811 }
4812 if (my_hash_insert(&hash_columns, (uchar *) mem_check))
4813 {
4814 /* Invalidate this entry */
4815 privs= cols= init_privs= init_cols=0;
4816 return;
4817 }
4818 } while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
4819 !key_cmp_if_same(col_privs,key,0,key_prefix_len));
4820 col_privs->file->ha_index_end();
4821 }
4822}
4823
4824
4825GRANT_TABLE::~GRANT_TABLE()
4826{
4827 my_hash_free(&hash_columns);
4828}
4829
4830
4831static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
4832 my_bool not_used __attribute__((unused)))
4833{
4834 *length=buff->key_length;
4835 return (uchar*) buff->hash_key;
4836}
4837
4838
4839static void free_grant_table(GRANT_TABLE *grant_table)
4840{
4841 grant_table->~GRANT_TABLE();
4842}
4843
4844
4845/* Search after a matching grant. Prefer exact grants before not exact ones */
4846
4847static GRANT_NAME *name_hash_search(HASH *name_hash,
4848 const char *host,const char* ip,
4849 const char *db,
4850 const char *user, const char *tname,
4851 bool exact, bool name_tolower)
4852{
4853 char helping[SAFE_NAME_LEN*2+USERNAME_LENGTH+3];
4854 char *hend = helping + sizeof(helping);
4855 uint len;
4856 GRANT_NAME *grant_name,*found=0;
4857 HASH_SEARCH_STATE state;
4858
4859 char *db_ptr= strmov(helping, user) + 1;
4860 char *tname_ptr= strnmov(db_ptr, db, hend - db_ptr) + 1;
4861 if (tname_ptr > hend)
4862 return 0; // invalid name = not found
4863 char *end= strnmov(tname_ptr, tname, hend - tname_ptr) + 1;
4864 if (end > hend)
4865 return 0; // invalid name = not found
4866
4867 len = (uint) (end - helping);
4868 if (name_tolower)
4869 my_casedn_str(files_charset_info, tname_ptr);
4870 for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
4871 len, &state);
4872 grant_name ;
4873 grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
4874 len, &state))
4875 {
4876 if (exact)
4877 {
4878 if (!grant_name->host.hostname ||
4879 (host &&
4880 !my_strcasecmp(system_charset_info, host,
4881 grant_name->host.hostname)) ||
4882 (ip && !strcmp(ip, grant_name->host.hostname)))
4883 return grant_name;
4884 }
4885 else
4886 {
4887 if (compare_hostname(&grant_name->host, host, ip) &&
4888 (!found || found->sort < grant_name->sort))
4889 found=grant_name; // Host ok
4890 }
4891 }
4892 return found;
4893}
4894
4895
4896static GRANT_NAME *
4897routine_hash_search(const char *host, const char *ip, const char *db,
4898 const char *user, const char *tname, const Sp_handler *sph,
4899 bool exact)
4900{
4901 return (GRANT_TABLE*)
4902 name_hash_search(sph->get_priv_hash(),
4903 host, ip, db, user, tname, exact, TRUE);
4904}
4905
4906
4907static GRANT_TABLE *
4908table_hash_search(const char *host, const char *ip, const char *db,
4909 const char *user, const char *tname, bool exact)
4910{
4911 return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
4912 user, tname, exact, FALSE);
4913}
4914
4915
4916static GRANT_COLUMN *
4917column_hash_search(GRANT_TABLE *t, const char *cname, size_t length)
4918{
4919 if (!my_hash_inited(&t->hash_columns))
4920 return (GRANT_COLUMN*) 0;
4921 return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
4922 (uchar*) cname, length);
4923}
4924
4925
4926static int replace_column_table(GRANT_TABLE *g_t,
4927 TABLE *table, const LEX_USER &combo,
4928 List <LEX_COLUMN> &columns,
4929 const char *db, const char *table_name,
4930 ulong rights, bool revoke_grant)
4931{
4932 int result=0;
4933 uchar key[MAX_KEY_LENGTH];
4934 uint key_prefix_length;
4935 KEY_PART_INFO *key_part= table->key_info->key_part;
4936 DBUG_ENTER("replace_column_table");
4937
4938 table->use_all_columns();
4939 table->field[0]->store(combo.host.str,combo.host.length,
4940 system_charset_info);
4941 table->field[1]->store(db,(uint) strlen(db),
4942 system_charset_info);
4943 table->field[2]->store(combo.user.str,combo.user.length,
4944 system_charset_info);
4945 table->field[3]->store(table_name,(uint) strlen(table_name),
4946 system_charset_info);
4947
4948 /* Get length of 4 first key parts */
4949 key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
4950 key_part[2].store_length + key_part[3].store_length);
4951 key_copy(key, table->record[0], table->key_info, key_prefix_length);
4952
4953 rights&= COL_ACLS; // Only ACL for columns
4954
4955 /* first fix privileges for all columns in column list */
4956
4957 List_iterator <LEX_COLUMN> iter(columns);
4958 class LEX_COLUMN *column;
4959 int error= table->file->ha_index_init(0, 1);
4960 if (unlikely(error))
4961 {
4962 table->file->print_error(error, MYF(0));
4963 DBUG_RETURN(-1);
4964 }
4965
4966 while ((column= iter++))
4967 {
4968 ulong privileges= column->rights;
4969 bool old_row_exists=0;
4970 uchar user_key[MAX_KEY_LENGTH];
4971
4972 key_restore(table->record[0],key,table->key_info,
4973 key_prefix_length);
4974 table->field[4]->store(column->column.ptr(), column->column.length(),
4975 system_charset_info);
4976 /* Get key for the first 4 columns */
4977 key_copy(user_key, table->record[0], table->key_info,
4978 table->key_info->key_length);
4979
4980 if (table->file->ha_index_read_map(table->record[0], user_key,
4981 HA_WHOLE_KEY, HA_READ_KEY_EXACT))
4982 {
4983 if (revoke_grant)
4984 {
4985 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
4986 combo.user.str, combo.host.str,
4987 table_name); /* purecov: inspected */
4988 result= -1; /* purecov: inspected */
4989 continue; /* purecov: inspected */
4990 }
4991 old_row_exists = 0;
4992 restore_record(table, s->default_values); // Get empty record
4993 key_restore(table->record[0],key,table->key_info,
4994 key_prefix_length);
4995 table->field[4]->store(column->column.ptr(),column->column.length(),
4996 system_charset_info);
4997 }
4998 else
4999 {
5000 ulong tmp= (ulong) table->field[6]->val_int();
5001 tmp=fix_rights_for_column(tmp);
5002
5003 if (revoke_grant)
5004 privileges = tmp & ~(privileges | rights);
5005 else
5006 privileges |= tmp;
5007 old_row_exists = 1;
5008 store_record(table,record[1]); // copy original row
5009 }
5010
5011 table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
5012
5013 if (old_row_exists)
5014 {
5015 GRANT_COLUMN *grant_column;
5016 if (privileges)
5017 error=table->file->ha_update_row(table->record[1],table->record[0]);
5018 else
5019 error=table->file->ha_delete_row(table->record[1]);
5020 if (unlikely(error) && error != HA_ERR_RECORD_IS_THE_SAME)
5021 {
5022 table->file->print_error(error,MYF(0)); /* purecov: inspected */
5023 result= -1; /* purecov: inspected */
5024 goto end; /* purecov: inspected */
5025 }
5026 else
5027 error= 0;
5028 grant_column= column_hash_search(g_t, column->column.ptr(),
5029 column->column.length());
5030 if (grant_column) // Should always be true
5031 grant_column->rights= privileges; // Update hash
5032 }
5033 else // new grant
5034 {
5035 GRANT_COLUMN *grant_column;
5036 if (unlikely((error=table->file->ha_write_row(table->record[0]))))
5037 {
5038 table->file->print_error(error,MYF(0)); /* purecov: inspected */
5039 result= -1; /* purecov: inspected */
5040 goto end; /* purecov: inspected */
5041 }
5042 grant_column= new GRANT_COLUMN(column->column,privileges);
5043 if (my_hash_insert(&g_t->hash_columns,(uchar*) grant_column))
5044 {
5045 result= -1;
5046 goto end;
5047 }
5048 }
5049 }
5050
5051 /*
5052 If revoke of privileges on the table level, remove all such privileges
5053 for all columns
5054 */
5055
5056 if (revoke_grant)
5057 {
5058 uchar user_key[MAX_KEY_LENGTH];
5059 key_copy(user_key, table->record[0], table->key_info,
5060 key_prefix_length);
5061
5062 if (table->file->ha_index_read_map(table->record[0], user_key,
5063 (key_part_map)15,
5064 HA_READ_KEY_EXACT))
5065 goto end;
5066
5067 /* Scan through all rows with the same host,db,user and table */
5068 do
5069 {
5070 ulong privileges = (ulong) table->field[6]->val_int();
5071 privileges=fix_rights_for_column(privileges);
5072 store_record(table,record[1]);
5073
5074 if (privileges & rights) // is in this record the priv to be revoked ??
5075 {
5076 GRANT_COLUMN *grant_column = NULL;
5077 char colum_name_buf[HOSTNAME_LENGTH+1];
5078 String column_name(colum_name_buf,sizeof(colum_name_buf),
5079 system_charset_info);
5080
5081 privileges&= ~rights;
5082 table->field[6]->store((longlong)
5083 get_rights_for_column(privileges), TRUE);
5084 table->field[4]->val_str(&column_name);
5085 grant_column = column_hash_search(g_t,
5086 column_name.ptr(),
5087 column_name.length());
5088 if (privileges)
5089 {
5090 int tmp_error;
5091 if (unlikely(tmp_error=
5092 table->file->ha_update_row(table->record[1],
5093 table->record[0])) &&
5094 tmp_error != HA_ERR_RECORD_IS_THE_SAME)
5095 { /* purecov: deadcode */
5096 table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
5097 result= -1; /* purecov: deadcode */
5098 goto end; /* purecov: deadcode */
5099 }
5100 if (grant_column)
5101 {
5102 grant_column->rights = privileges; // Update hash
5103 grant_column->init_rights = privileges;
5104 }
5105 }
5106 else
5107 {
5108 int tmp_error;
5109 if (unlikely((tmp_error=
5110 table->file->ha_delete_row(table->record[1]))))
5111 { /* purecov: deadcode */
5112 table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
5113 result= -1; /* purecov: deadcode */
5114 goto end; /* purecov: deadcode */
5115 }
5116 if (grant_column)
5117 my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
5118 }
5119 }
5120 } while (!table->file->ha_index_next(table->record[0]) &&
5121 !key_cmp_if_same(table, key, 0, key_prefix_length));
5122 }
5123
5124end:
5125 table->file->ha_index_end();
5126 DBUG_RETURN(result);
5127}
5128
5129static inline void get_grantor(THD *thd, char *grantor)
5130{
5131 const char *user= thd->security_ctx->user;
5132 const char *host= thd->security_ctx->host_or_ip;
5133
5134#if defined(HAVE_REPLICATION)
5135 if (thd->slave_thread && thd->has_invoker())
5136 {
5137 user= thd->get_invoker_user().str;
5138 host= thd->get_invoker_host().str;
5139 }
5140#endif
5141 strxmov(grantor, user, "@", host, NullS);
5142}
5143
5144static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
5145 TABLE *table, const LEX_USER &combo,
5146 const char *db, const char *table_name,
5147 ulong rights, ulong col_rights,
5148 bool revoke_grant)
5149{
5150 char grantor[USER_HOST_BUFF_SIZE];
5151 int old_row_exists = 1;
5152 int error=0;
5153 ulong store_table_rights, store_col_rights;
5154 uchar user_key[MAX_KEY_LENGTH];
5155 DBUG_ENTER("replace_table_table");
5156
5157 get_grantor(thd, grantor);
5158 /*
5159 The following should always succeed as new users are created before
5160 this function is called!
5161 */
5162 if (!find_user_wild(combo.host.str,combo.user.str))
5163 {
5164 if (!combo.host.length && !find_acl_role(combo.user.str))
5165 {
5166 my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
5167 MYF(0)); /* purecov: deadcode */
5168 DBUG_RETURN(-1); /* purecov: deadcode */
5169 }
5170 }
5171
5172 table->use_all_columns();
5173 restore_record(table, s->default_values); // Get empty record
5174 table->field[0]->store(combo.host.str,combo.host.length,
5175 system_charset_info);
5176 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
5177 table->field[2]->store(combo.user.str,combo.user.length,
5178 system_charset_info);
5179 table->field[3]->store(table_name,(uint) strlen(table_name),
5180 system_charset_info);
5181 store_record(table,record[1]); // store at pos 1
5182 key_copy(user_key, table->record[0], table->key_info,
5183 table->key_info->key_length);
5184
5185 if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
5186 HA_WHOLE_KEY,
5187 HA_READ_KEY_EXACT))
5188 {
5189 /*
5190 The following should never happen as we first check the in memory
5191 grant tables for the user. There is however always a small change that
5192 the user has modified the grant tables directly.
5193 */
5194 if (revoke_grant)
5195 { // no row, no revoke
5196 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
5197 combo.user.str, combo.host.str,
5198 table_name); /* purecov: deadcode */
5199 DBUG_RETURN(-1); /* purecov: deadcode */
5200 }
5201 old_row_exists = 0;
5202 restore_record(table,record[1]); // Get saved record
5203 }
5204
5205 store_table_rights= get_rights_for_table(rights);
5206 store_col_rights= get_rights_for_column(col_rights);
5207 if (old_row_exists)
5208 {
5209 ulong j,k;
5210 store_record(table,record[1]);
5211 j = (ulong) table->field[6]->val_int();
5212 k = (ulong) table->field[7]->val_int();
5213
5214 if (revoke_grant)
5215 {
5216 /* column rights are already fixed in mysql_table_grant */
5217 store_table_rights=j & ~store_table_rights;
5218 }
5219 else
5220 {
5221 store_table_rights|= j;
5222 store_col_rights|= k;
5223 }
5224 }
5225
5226 table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
5227 table->field[6]->store((longlong) store_table_rights, TRUE);
5228 table->field[7]->store((longlong) store_col_rights, TRUE);
5229 rights=fix_rights_for_table(store_table_rights);
5230 col_rights=fix_rights_for_column(store_col_rights);
5231
5232 if (old_row_exists)
5233 {
5234 if (store_table_rights || store_col_rights)
5235 {
5236 if (unlikely(error=table->file->ha_update_row(table->record[1],
5237 table->record[0])) &&
5238 error != HA_ERR_RECORD_IS_THE_SAME)
5239 goto table_error; /* purecov: deadcode */
5240 }
5241 else if (unlikely((error = table->file->ha_delete_row(table->record[1]))))
5242 goto table_error; /* purecov: deadcode */
5243 }
5244 else
5245 {
5246 error=table->file->ha_write_row(table->record[0]);
5247 if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
5248 goto table_error; /* purecov: deadcode */
5249 }
5250
5251 if (rights | col_rights)
5252 {
5253 grant_table->init_privs= rights;
5254 grant_table->init_cols= col_rights;
5255
5256 grant_table->privs= rights;
5257 grant_table->cols= col_rights;
5258 }
5259 else
5260 {
5261 my_hash_delete(&column_priv_hash,(uchar*) grant_table);
5262 }
5263 DBUG_RETURN(0);
5264
5265 /* This should never happen */
5266table_error:
5267 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
5268 DBUG_RETURN(-1); /* purecov: deadcode */
5269}
5270
5271
5272/**
5273 @retval 0 success
5274 @retval -1 error
5275*/
5276static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
5277 TABLE *table, const LEX_USER &combo,
5278 const char *db, const char *routine_name,
5279 const Sp_handler *sph,
5280 ulong rights, bool revoke_grant)
5281{
5282 char grantor[USER_HOST_BUFF_SIZE];
5283 int old_row_exists= 1;
5284 int error=0;
5285 ulong store_proc_rights;
5286 HASH *hash= sph->get_priv_hash();
5287 DBUG_ENTER("replace_routine_table");
5288
5289 if (revoke_grant && !grant_name->init_privs) // only inherited role privs
5290 {
5291 my_hash_delete(hash, (uchar*) grant_name);
5292 DBUG_RETURN(0);
5293 }
5294
5295 get_grantor(thd, grantor);
5296 /*
5297 New users are created before this function is called.
5298
5299 There may be some cases where a routine's definer is removed but the
5300 routine remains.
5301 */
5302
5303 table->use_all_columns();
5304 restore_record(table, s->default_values); // Get empty record
5305 table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
5306 table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
5307 table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
5308 table->field[3]->store(routine_name,(uint) strlen(routine_name),
5309 &my_charset_latin1);
5310 table->field[4]->store((longlong) sph->type(), true);
5311 store_record(table,record[1]); // store at pos 1
5312
5313 if (table->file->ha_index_read_idx_map(table->record[0], 0,
5314 (uchar*) table->field[0]->ptr,
5315 HA_WHOLE_KEY,
5316 HA_READ_KEY_EXACT))
5317 {
5318 /*
5319 The following should never happen as we first check the in memory
5320 grant tables for the user. There is however always a small change that
5321 the user has modified the grant tables directly.
5322
5323 Also, there is also a second posibility that this routine entry
5324 is created for a role by being inherited from a granted role.
5325 */
5326 if (revoke_grant)
5327 { // no row, no revoke
5328 my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
5329 combo.user.str, combo.host.str, routine_name);
5330 DBUG_RETURN(-1);
5331 }
5332 old_row_exists= 0;
5333 restore_record(table,record[1]); // Get saved record
5334 }
5335
5336 store_proc_rights= get_rights_for_procedure(rights);
5337 if (old_row_exists)
5338 {
5339 ulong j;
5340 store_record(table,record[1]);
5341 j= (ulong) table->field[6]->val_int();
5342
5343 if (revoke_grant)
5344 {
5345 /* column rights are already fixed in mysql_table_grant */
5346 store_proc_rights=j & ~store_proc_rights;
5347 }
5348 else
5349 {
5350 store_proc_rights|= j;
5351 }
5352 }
5353
5354 table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
5355 table->field[6]->store((longlong) store_proc_rights, TRUE);
5356 rights=fix_rights_for_procedure(store_proc_rights);
5357
5358 if (old_row_exists)
5359 {
5360 if (store_proc_rights)
5361 {
5362 if (unlikely(error=table->file->ha_update_row(table->record[1],
5363 table->record[0])) &&
5364 error != HA_ERR_RECORD_IS_THE_SAME)
5365 goto table_error;
5366 }
5367 else if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
5368 goto table_error;
5369 }
5370 else
5371 {
5372 error=table->file->ha_write_row(table->record[0]);
5373 if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
5374 goto table_error;
5375 }
5376
5377 if (rights)
5378 {
5379 grant_name->init_privs= rights;
5380 grant_name->privs= rights;
5381 }
5382 else
5383 {
5384 my_hash_delete(hash, (uchar*) grant_name);
5385 }
5386 DBUG_RETURN(0);
5387
5388 /* This should never happen */
5389table_error:
5390 table->file->print_error(error,MYF(0));
5391 DBUG_RETURN(-1);
5392}
5393
5394
5395/*****************************************************************
5396 Role privilege propagation and graph traversal functionality
5397
5398 According to the SQL standard, a role can be granted to a role,
5399 thus role grants can create an arbitrarily complex directed acyclic
5400 graph (a standard explicitly specifies that cycles are not allowed).
5401
5402 When a privilege is granted to a role, it becomes available to all grantees.
5403 The code below recursively traverses a DAG of role grants, propagating
5404 privilege changes.
5405
5406 The traversal function can work both ways, from roles to grantees or
5407 from grantees to roles. The first is used for privilege propagation,
5408 the second - for SHOW GRANTS and I_S.APPLICABLE_ROLES
5409
5410 The role propagation code is smart enough to propagate only privilege
5411 changes to one specific database, table, or routine, if only they
5412 were changed (like in GRANT ... ON ... TO ...) or it can propagate
5413 everything (on startup or after FLUSH PRIVILEGES).
5414
5415 It traverses only a subgraph that's accessible from the modified role,
5416 only visiting roles that can be possibly affected by the GRANT statement.
5417
5418 Additionally, it stops traversal early, if this particular GRANT statement
5419 didn't result in any changes of privileges (e.g. both role1 and role2
5420 are granted to the role3, both role1 and role2 have SELECT privilege.
5421 if SELECT is revoked from role1 it won't change role3 privileges,
5422 so we won't traverse from role3 to its grantees).
5423******************************************************************/
5424struct PRIVS_TO_MERGE
5425{
5426 enum what
5427 {
5428 ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC, PACKAGE_SPEC, PACKAGE_BODY
5429 } what;
5430 const char *db, *name;
5431};
5432
5433
5434static enum PRIVS_TO_MERGE::what sp_privs_to_merge(stored_procedure_type type)
5435{
5436 switch (type) {
5437 case TYPE_ENUM_FUNCTION:
5438 return PRIVS_TO_MERGE::FUNC;
5439 case TYPE_ENUM_PROCEDURE:
5440 return PRIVS_TO_MERGE::PROC;
5441 case TYPE_ENUM_PACKAGE:
5442 return PRIVS_TO_MERGE::PACKAGE_SPEC;
5443 case TYPE_ENUM_PACKAGE_BODY:
5444 return PRIVS_TO_MERGE::PACKAGE_BODY;
5445 case TYPE_ENUM_TRIGGER:
5446 case TYPE_ENUM_PROXY:
5447 break;
5448 }
5449 DBUG_ASSERT(0);
5450 return PRIVS_TO_MERGE::PROC;
5451}
5452
5453
5454static int init_role_for_merging(ACL_ROLE *role, void *context)
5455{
5456 role->counter= 0;
5457 return 0;
5458}
5459
5460static int count_subgraph_nodes(ACL_ROLE *role, ACL_ROLE *grantee, void *context)
5461{
5462 grantee->counter++;
5463 return 0;
5464}
5465
5466static int merge_role_privileges(ACL_ROLE *, ACL_ROLE *, void *);
5467
5468/**
5469 rebuild privileges of all affected roles
5470
5471 entry point into role privilege propagation. after privileges of the
5472 'role' were changed, this function rebuilds privileges of all affected roles
5473 as necessary.
5474*/
5475static void propagate_role_grants(ACL_ROLE *role,
5476 enum PRIVS_TO_MERGE::what what,
5477 const char *db= 0, const char *name= 0)
5478{
5479
5480 mysql_mutex_assert_owner(&acl_cache->lock);
5481 PRIVS_TO_MERGE data= { what, db, name };
5482
5483 /*
5484 Changing privileges of a role causes all other roles that had
5485 this role granted to them to have their rights invalidated.
5486
5487 We need to rebuild all roles' related access bits.
5488
5489 This cannot be a simple depth-first search, instead we have to merge
5490 privieges for all roles granted to a specific grantee, *before*
5491 merging privileges for this grantee. In other words, we must visit all
5492 parent nodes of a specific node, before descencing into this node.
5493
5494 For example, if role1 is granted to role2 and role3, and role3 is
5495 granted to role2, after "GRANT ... role1", we cannot merge privileges
5496 for role2, until role3 is merged. The counter will be 0 for role1, 2
5497 for role2, 1 for role3. Traversal will start from role1, go to role2,
5498 decrement the counter, backtrack, go to role3, merge it, go to role2
5499 again, merge it.
5500
5501 And the counter is not just "all parent nodes", but only parent nodes
5502 that are part of the subgraph we're interested in. For example, if
5503 both roleA and roleB are granted to roleC, then roleC has two parent
5504 nodes. But when granting a privilege to roleA, we're only looking at a
5505 subgraph that includes roleA and roleC (roleB cannot be possibly
5506 affected by that grant statement). In this subgraph roleC has only one
5507 parent.
5508
5509 (on the other hand, in acl_load we want to update all roles, and
5510 the counter is exactly equal to the number of all parent nodes)
5511
5512 Thus, we do two graph traversals here. First we only count parents
5513 that are part of the subgraph. On the second traversal we decrement
5514 the counter and actually merge privileges for a node when a counter
5515 drops to zero.
5516 */
5517 traverse_role_graph_up(role, &data, init_role_for_merging, count_subgraph_nodes);
5518 traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
5519}
5520
5521
5522// State of a node during a Depth First Search exploration
5523struct NODE_STATE
5524{
5525 ACL_USER_BASE *node_data; /* pointer to the node data */
5526 uint neigh_idx; /* the neighbour that needs to be evaluated next */
5527};
5528
5529/**
5530 Traverse the role grant graph and invoke callbacks at the specified points.
5531
5532 @param user user or role to start traversal from
5533 @param context opaque parameter to pass to callbacks
5534 @param offset offset to ACL_ROLE::parent_grantee or to
5535 ACL_USER_BASE::role_grants. Depending on this value,
5536 traversal will go from roles to grantees or from
5537 grantees to roles.
5538 @param on_node called when a node is visited for the first time.
5539 Returning a value <0 will abort the traversal.
5540 @param on_edge called for every edge in the graph, when traversal
5541 goes from a node to a neighbour node.
5542 Returning <0 will abort the traversal. Returning >0
5543 will make the traversal not to follow this edge.
5544
5545 @note
5546 The traverse method is a DEPTH FIRST SEARCH, but callbacks can influence
5547 that (on_edge returning >0 value).
5548
5549 @note
5550 This function should not be called directly, use
5551 traverse_role_graph_up() and traverse_role_graph_down() instead.
5552
5553 @retval 0 traversal finished successfully
5554 @retval ROLE_CYCLE_FOUND traversal aborted, cycle detected
5555 @retval <0 traversal was aborted, because a callback returned
5556 this error code
5557*/
5558static int traverse_role_graph_impl(ACL_USER_BASE *user, void *context,
5559 off_t offset,
5560 int (*on_node) (ACL_USER_BASE *role, void *context),
5561 int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
5562{
5563 DBUG_ENTER("traverse_role_graph_impl");
5564 DBUG_ASSERT(user);
5565 DBUG_PRINT("enter",("role: '%s'", user->user.str));
5566 /*
5567 The search operation should always leave the ROLE_ON_STACK and
5568 ROLE_EXPLORED flags clean for all nodes involved in the search
5569 */
5570 DBUG_ASSERT(!(user->flags & ROLE_ON_STACK));
5571 DBUG_ASSERT(!(user->flags & ROLE_EXPLORED));
5572 mysql_mutex_assert_owner(&acl_cache->lock);
5573
5574 /*
5575 Stack used to simulate the recursive calls of DFS.
5576 It uses a Dynamic_array to reduce the number of
5577 malloc calls to a minimum
5578 */
5579 Dynamic_array<NODE_STATE> stack(20,50);
5580 Dynamic_array<ACL_USER_BASE *> to_clear(20,50);
5581 NODE_STATE state; /* variable used to insert elements in the stack */
5582 int result= 0;
5583
5584 state.neigh_idx= 0;
5585 state.node_data= user;
5586 user->flags|= ROLE_ON_STACK;
5587
5588 stack.push(state);
5589 to_clear.push(user);
5590
5591 user->flags|= ROLE_OPENED;
5592 if (on_node && ((result= on_node(user, context)) < 0))
5593 goto end;
5594
5595 while (stack.elements())
5596 {
5597 NODE_STATE *curr_state= stack.back();
5598
5599 DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
5600
5601 ACL_USER_BASE *current= curr_state->node_data;
5602 ACL_USER_BASE *neighbour= NULL;
5603 DBUG_PRINT("info", ("Examining role %s", current->user.str));
5604 /*
5605 Iterate through the neighbours until a first valid jump-to
5606 neighbour is found
5607 */
5608 bool found= FALSE;
5609 uint i;
5610 DYNAMIC_ARRAY *array= (DYNAMIC_ARRAY *)(((char*)current) + offset);
5611
5612 DBUG_ASSERT(array == &current->role_grants || current->flags & IS_ROLE);
5613 for (i= curr_state->neigh_idx; i < array->elements; i++)
5614 {
5615 neighbour= *(dynamic_element(array, i, ACL_ROLE**));
5616 if (!(neighbour->flags & IS_ROLE))
5617 continue;
5618
5619 DBUG_PRINT("info", ("Examining neighbour role %s", neighbour->user.str));
5620
5621 /* check if it forms a cycle */
5622 if (neighbour->flags & ROLE_ON_STACK)
5623 {
5624 DBUG_PRINT("info", ("Found cycle"));
5625 result= ROLE_CYCLE_FOUND;
5626 goto end;
5627 }
5628
5629 if (!(neighbour->flags & ROLE_OPENED))
5630 {
5631 neighbour->flags|= ROLE_OPENED;
5632 to_clear.push(neighbour);
5633 if (on_node && ((result= on_node(neighbour, context)) < 0))
5634 goto end;
5635 }
5636
5637 if (on_edge)
5638 {
5639 result= on_edge(current, (ACL_ROLE*)neighbour, context);
5640 if (result < 0)
5641 goto end;
5642 if (result > 0)
5643 continue;
5644 }
5645
5646 /* Check if it was already explored, in that case, move on */
5647 if (neighbour->flags & ROLE_EXPLORED)
5648 continue;
5649
5650 found= TRUE;
5651 break;
5652 }
5653
5654 /* found states that we have found a node to jump next into */
5655 if (found)
5656 {
5657 curr_state->neigh_idx= i + 1;
5658
5659 /* some sanity checks */
5660 DBUG_ASSERT(!(neighbour->flags & ROLE_ON_STACK));
5661
5662 /* add the neighbour on the stack */
5663 neighbour->flags|= ROLE_ON_STACK;
5664 state.neigh_idx= 0;
5665 state.node_data= neighbour;
5666 stack.push(state);
5667 }
5668 else
5669 {
5670 /* Make sure we got a correct node */
5671 DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
5672 /* Finished with exploring the current node, pop it off the stack */
5673 curr_state= &stack.pop();
5674 curr_state->node_data->flags&= ~ROLE_ON_STACK; /* clear the on-stack bit */
5675 curr_state->node_data->flags|= ROLE_EXPLORED;
5676 }
5677 }
5678
5679end:
5680 /* Cleanup */
5681 for (uint i= 0; i < to_clear.elements(); i++)
5682 {
5683 ACL_USER_BASE *current= to_clear.at(i);
5684 DBUG_ASSERT(current->flags & (ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED));
5685 current->flags&= ~(ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED);
5686 }
5687 DBUG_RETURN(result);
5688}
5689
5690/**
5691 Traverse the role grant graph, going from a role to its grantees.
5692
5693 This is used to propagate changes in privileges, for example,
5694 when GRANT or REVOKE is issued for a role.
5695*/
5696
5697static int traverse_role_graph_up(ACL_ROLE *role, void *context,
5698 int (*on_node) (ACL_ROLE *role, void *context),
5699 int (*on_edge) (ACL_ROLE *current, ACL_ROLE *neighbour, void *context))
5700{
5701 return traverse_role_graph_impl(role, context,
5702 my_offsetof(ACL_ROLE, parent_grantee),
5703 (int (*)(ACL_USER_BASE *, void *))on_node,
5704 (int (*)(ACL_USER_BASE *, ACL_ROLE *, void *))on_edge);
5705}
5706
5707/**
5708 Traverse the role grant graph, going from a user or a role to granted roles.
5709
5710 This is used, for example, to print all grants available to a user or a role
5711 (as in SHOW GRANTS).
5712*/
5713
5714static int traverse_role_graph_down(ACL_USER_BASE *user, void *context,
5715 int (*on_node) (ACL_USER_BASE *role, void *context),
5716 int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
5717{
5718 return traverse_role_graph_impl(user, context,
5719 my_offsetof(ACL_USER_BASE, role_grants),
5720 on_node, on_edge);
5721}
5722
5723/*
5724 To find all db/table/routine privilege for a specific role
5725 we need to scan the array of privileges. It can be big.
5726 But the set of privileges granted to a role in question (or
5727 to roles directly granted to the role in question) is supposedly
5728 much smaller.
5729
5730 We put a role and all roles directly granted to it in a hash, and iterate
5731 the (suposedly long) array of privileges, filtering out "interesting"
5732 entries using the role hash. We put all these "interesting"
5733 entries in a (suposedly small) dynamic array and them use it for merging.
5734*/
5735static uchar* role_key(const ACL_ROLE *role, size_t *klen, my_bool)
5736{
5737 *klen= role->user.length;
5738 return (uchar*) role->user.str;
5739}
5740typedef Hash_set<ACL_ROLE> role_hash_t;
5741
5742static bool merge_role_global_privileges(ACL_ROLE *grantee)
5743{
5744 ulong old= grantee->access;
5745 grantee->access= grantee->initial_role_access;
5746
5747 DBUG_EXECUTE_IF("role_merge_stats", role_global_merges++;);
5748
5749 for (uint i= 0; i < grantee->role_grants.elements; i++)
5750 {
5751 ACL_ROLE *r= *dynamic_element(&grantee->role_grants, i, ACL_ROLE**);
5752 grantee->access|= r->access;
5753 }
5754 return old != grantee->access;
5755}
5756
5757static int db_name_sort(ACL_DB * const *db1, ACL_DB * const *db2)
5758{
5759 return strcmp((*db1)->db, (*db2)->db);
5760}
5761
5762/**
5763 update ACL_DB for given database and a given role with merged privileges
5764
5765 @param merged ACL_DB of the role in question (or NULL if it wasn't found)
5766 @param first first ACL_DB in an array for the database in question
5767 @param access new privileges for the given role on the gived database
5768 @param role the name of the given role
5769
5770 @return a bitmap of
5771 1 - privileges were changed
5772 2 - ACL_DB was added
5773 4 - ACL_DB was deleted
5774*/
5775static int update_role_db(ACL_DB *merged, ACL_DB **first, ulong access,
5776 const char *role)
5777{
5778 if (!first)
5779 return 0;
5780
5781 DBUG_EXECUTE_IF("role_merge_stats", role_db_merges++;);
5782
5783 if (merged == NULL)
5784 {
5785 /*
5786 there's no ACL_DB for this role (all db grants come from granted roles)
5787 we need to create it
5788
5789 Note that we cannot use acl_insert_db() now:
5790 1. it'll sort elements in the acl_dbs, so the pointers will become invalid
5791 2. we may need many of them, no need to sort every time
5792 */
5793 DBUG_ASSERT(access);
5794 ACL_DB acl_db;
5795 acl_db.user= role;
5796 acl_db.host.hostname= const_cast<char*>("");
5797 acl_db.host.ip= acl_db.host.ip_mask= 0;
5798 acl_db.db= first[0]->db;
5799 acl_db.access= access;
5800 acl_db.initial_access= 0;
5801 acl_db.sort=get_sort(3, "", acl_db.db, role);
5802 push_dynamic(&acl_dbs,(uchar*) &acl_db);
5803 return 2;
5804 }
5805 else if (access == 0)
5806 {
5807 /*
5808 there is ACL_DB but the role has no db privileges granted
5809 (all privileges were coming from granted roles, and now those roles
5810 were dropped or had their privileges revoked).
5811 we need to remove this ACL_DB entry
5812
5813 Note, that we cannot delete now:
5814 1. it'll shift elements in the acl_dbs, so the pointers will become invalid
5815 2. it's O(N) operation, and we may need many of them
5816 so we only mark elements deleted and will delete later.
5817 */
5818 merged->sort= 0; // lower than any valid ACL_DB sort value, will be sorted last
5819 return 4;
5820 }
5821 else if (merged->access != access)
5822 {
5823 /* this is easy */
5824 merged->access= access;
5825 return 1;
5826 }
5827 return 0;
5828}
5829
5830/**
5831 merges db privileges from roles granted to the role 'grantee'.
5832
5833 @return true if database privileges of the 'grantee' were changed
5834
5835*/
5836static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname,
5837 role_hash_t *rhash)
5838{
5839 Dynamic_array<ACL_DB *> dbs;
5840
5841 /*
5842 Supposedly acl_dbs can be huge, but only a handful of db grants
5843 apply to grantee or roles directly granted to grantee.
5844
5845 Collect these applicable db grants.
5846 */
5847 for (uint i=0 ; i < acl_dbs.elements ; i++)
5848 {
5849 ACL_DB *db= dynamic_element(&acl_dbs,i,ACL_DB*);
5850 if (db->host.hostname[0])
5851 continue;
5852 if (dbname && strcmp(db->db, dbname))
5853 continue;
5854 ACL_ROLE *r= rhash->find(db->user, strlen(db->user));
5855 if (!r)
5856 continue;
5857 dbs.append(db);
5858 }
5859 dbs.sort(db_name_sort);
5860
5861 /*
5862 Because dbs array is sorted by the db name, all grants for the same db
5863 (that should be merged) are sorted together. The grantee's ACL_DB element
5864 is not necessarily the first and may be not present at all.
5865 */
5866 ACL_DB **first= NULL, *merged= NULL;
5867 ulong access= 0, update_flags= 0;
5868 for (ACL_DB **cur= dbs.front(); cur <= dbs.back(); cur++)
5869 {
5870 if (!first || (!dbname && strcmp(cur[0]->db, cur[-1]->db)))
5871 { // new db name series
5872 update_flags|= update_role_db(merged, first, access, grantee->user.str);
5873 merged= NULL;
5874 access= 0;
5875 first= cur;
5876 }
5877 if (strcmp(cur[0]->user, grantee->user.str) == 0)
5878 access|= (merged= cur[0])->initial_access;
5879 else
5880 access|= cur[0]->access;
5881 }
5882 update_flags|= update_role_db(merged, first, access, grantee->user.str);
5883
5884 /*
5885 to make this code a bit simpler, we sort on deletes, to move
5886 deleted elements to the end of the array. strictly speaking it's
5887 unnecessary, it'd be faster to remove them in one O(N) array scan.
5888
5889 on the other hand, qsort on almost sorted array is pretty fast anyway...
5890 */
5891 if (update_flags & (2|4))
5892 { // inserted or deleted, need to sort
5893 my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
5894 sizeof(ACL_DB),(qsort_cmp) acl_compare);
5895 }
5896 if (update_flags & 4)
5897 { // deleted, trim the end
5898 while (acl_dbs.elements &&
5899 dynamic_element(&acl_dbs, acl_dbs.elements-1, ACL_DB*)->sort == 0)
5900 acl_dbs.elements--;
5901 }
5902 return update_flags;
5903}
5904
5905static int table_name_sort(GRANT_TABLE * const *tbl1, GRANT_TABLE * const *tbl2)
5906{
5907 int res = strcmp((*tbl1)->db, (*tbl2)->db);
5908 if (res) return res;
5909 return strcmp((*tbl1)->tname, (*tbl2)->tname);
5910}
5911
5912/**
5913 merges column privileges for the entry 'merged'
5914
5915 @param merged GRANT_TABLE to merge the privileges into
5916 @param cur first entry in the array of GRANT_TABLE's for a given table
5917 @param last last entry in the array of GRANT_TABLE's for a given table,
5918 all entries between cur and last correspond to the *same* table
5919
5920 @return 1 if the _set of columns_ in 'merged' was changed
5921 (not if the _set of privileges_ was changed).
5922*/
5923static int update_role_columns(GRANT_TABLE *merged,
5924 GRANT_TABLE **cur, GRANT_TABLE **last)
5925
5926{
5927 ulong rights __attribute__((unused))= 0;
5928 int changed= 0;
5929 if (!merged->cols)
5930 {
5931 changed= merged->hash_columns.records > 0;
5932 my_hash_reset(&merged->hash_columns);
5933 return changed;
5934 }
5935
5936 DBUG_EXECUTE_IF("role_merge_stats", role_column_merges++;);
5937
5938 HASH *mh= &merged->hash_columns;
5939 for (uint i=0 ; i < mh->records ; i++)
5940 {
5941 GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
5942 col->rights= col->init_rights;
5943 }
5944
5945 for (; cur < last; cur++)
5946 {
5947 if (*cur == merged)
5948 continue;
5949 HASH *ch= &cur[0]->hash_columns;
5950 for (uint i=0 ; i < ch->records ; i++)
5951 {
5952 GRANT_COLUMN *ccol = (GRANT_COLUMN *)my_hash_element(ch, i);
5953 GRANT_COLUMN *mcol = (GRANT_COLUMN *)my_hash_search(mh,
5954 (uchar *)ccol->column, ccol->key_length);
5955 if (mcol)
5956 mcol->rights|= ccol->rights;
5957 else
5958 {
5959 changed= 1;
5960 my_hash_insert(mh, (uchar*)new (&grant_memroot) GRANT_COLUMN(ccol));
5961 }
5962 }
5963 }
5964
5965 for (uint i=0 ; i < mh->records ; i++)
5966 {
5967 GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
5968 rights|= col->rights;
5969 if (!col->rights)
5970 {
5971 changed= 1;
5972 my_hash_delete(mh, (uchar*)col);
5973 }
5974 }
5975 DBUG_ASSERT(rights == merged->cols);
5976 return changed;
5977}
5978
5979/**
5980 update GRANT_TABLE for a given table and a given role with merged privileges
5981
5982 @param merged GRANT_TABLE of the role in question (or NULL if it wasn't found)
5983 @param first first GRANT_TABLE in an array for the table in question
5984 @param last last entry in the array of GRANT_TABLE's for a given table,
5985 all entries between first and last correspond to the *same* table
5986 @param privs new table-level privileges for 'merged'
5987 @param cols new OR-ed column-level privileges for 'merged'
5988 @param role the name of the given role
5989
5990 @return a bitmap of
5991 1 - privileges were changed
5992 2 - GRANT_TABLE was added
5993 4 - GRANT_TABLE was deleted
5994*/
5995static int update_role_table_columns(GRANT_TABLE *merged,
5996 GRANT_TABLE **first, GRANT_TABLE **last,
5997 ulong privs, ulong cols, const char *role)
5998{
5999 if (!first)
6000 return 0;
6001
6002 DBUG_EXECUTE_IF("role_merge_stats", role_table_merges++;);
6003
6004 if (merged == NULL)
6005 {
6006 /*
6007 there's no GRANT_TABLE for this role (all table grants come from granted
6008 roles) we need to create it
6009 */
6010 DBUG_ASSERT(privs | cols);
6011 merged= new (&grant_memroot) GRANT_TABLE("", first[0]->db, role, first[0]->tname,
6012 privs, cols);
6013 merged->init_privs= merged->init_cols= 0;
6014 update_role_columns(merged, first, last);
6015 my_hash_insert(&column_priv_hash,(uchar*) merged);
6016 return 2;
6017 }
6018 else if ((privs | cols) == 0)
6019 {
6020 /*
6021 there is GRANT_TABLE object but the role has no table or column
6022 privileges granted (all privileges were coming from granted roles, and
6023 now those roles were dropped or had their privileges revoked).
6024 we need to remove this GRANT_TABLE
6025 */
6026 DBUG_EXECUTE_IF("role_merge_stats",
6027 role_column_merges+= MY_TEST(merged->cols););
6028 my_hash_delete(&column_priv_hash,(uchar*) merged);
6029 return 4;
6030 }
6031 else
6032 {
6033 bool changed= merged->cols != cols || merged->privs != privs;
6034 merged->cols= cols;
6035 merged->privs= privs;
6036 if (update_role_columns(merged, first, last))
6037 changed= true;
6038 return changed;
6039 }
6040}
6041
6042/**
6043 merges table privileges from roles granted to the role 'grantee'.
6044
6045 @return true if table privileges of the 'grantee' were changed
6046
6047*/
6048static bool merge_role_table_and_column_privileges(ACL_ROLE *grantee,
6049 const char *db, const char *tname, role_hash_t *rhash)
6050{
6051 Dynamic_array<GRANT_TABLE *> grants;
6052 DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
6053
6054 /*
6055 first, collect table/column privileges granted to
6056 roles in question.
6057 */
6058 for (uint i=0 ; i < column_priv_hash.records ; i++)
6059 {
6060 GRANT_TABLE *grant= (GRANT_TABLE *) my_hash_element(&column_priv_hash, i);
6061 if (grant->host.hostname[0])
6062 continue;
6063 if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
6064 continue;
6065 ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
6066 if (!r)
6067 continue;
6068 grants.append(grant);
6069 }
6070 grants.sort(table_name_sort);
6071
6072 GRANT_TABLE **first= NULL, *merged= NULL, **cur;
6073 ulong privs= 0, cols= 0, update_flags= 0;
6074 for (cur= grants.front(); cur <= grants.back(); cur++)
6075 {
6076 if (!first ||
6077 (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
6078 strcmp(cur[0]->tname, cur[-1]->tname))))
6079 { // new db.tname series
6080 update_flags|= update_role_table_columns(merged, first, cur,
6081 privs, cols, grantee->user.str);
6082 merged= NULL;
6083 privs= cols= 0;
6084 first= cur;
6085 }
6086 if (strcmp(cur[0]->user, grantee->user.str) == 0)
6087 {
6088 merged= cur[0];
6089 cols|= cur[0]->init_cols;
6090 privs|= cur[0]->init_privs;
6091 }
6092 else
6093 {
6094 cols|= cur[0]->cols;
6095 privs|= cur[0]->privs;
6096 }
6097 }
6098 update_flags|= update_role_table_columns(merged, first, cur,
6099 privs, cols, grantee->user.str);
6100
6101 return update_flags;
6102}
6103
6104static int routine_name_sort(GRANT_NAME * const *r1, GRANT_NAME * const *r2)
6105{
6106 int res= strcmp((*r1)->db, (*r2)->db);
6107 if (res) return res;
6108 return strcmp((*r1)->tname, (*r2)->tname);
6109}
6110
6111/**
6112 update GRANT_NAME for a given routine and a given role with merged privileges
6113
6114 @param merged GRANT_NAME of the role in question (or NULL if it wasn't found)
6115 @param first first GRANT_NAME in an array for the routine in question
6116 @param privs new routine-level privileges for 'merged'
6117 @param role the name of the given role
6118 @param hash proc_priv_hash or func_priv_hash
6119
6120 @return a bitmap of
6121 1 - privileges were changed
6122 2 - GRANT_NAME was added
6123 4 - GRANT_NAME was deleted
6124*/
6125static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first,
6126 ulong privs, const char *role, HASH *hash)
6127{
6128 if (!first)
6129 return 0;
6130
6131 DBUG_EXECUTE_IF("role_merge_stats", role_routine_merges++;);
6132
6133 if (merged == NULL)
6134 {
6135 /*
6136 there's no GRANT_NAME for this role (all routine grants come from granted
6137 roles) we need to create it
6138 */
6139 DBUG_ASSERT(privs);
6140 merged= new (&grant_memroot) GRANT_NAME("", first[0]->db, role, first[0]->tname,
6141 privs, true);
6142 merged->init_privs= 0; // all privs are inherited
6143 my_hash_insert(hash, (uchar *)merged);
6144 return 2;
6145 }
6146 else if (privs == 0)
6147 {
6148 /*
6149 there is GRANT_NAME but the role has no privileges granted
6150 (all privileges were coming from granted roles, and now those roles
6151 were dropped or had their privileges revoked).
6152 we need to remove this entry
6153 */
6154 my_hash_delete(hash, (uchar*)merged);
6155 return 4;
6156 }
6157 else if (merged->privs != privs)
6158 {
6159 /* this is easy */
6160 merged->privs= privs;
6161 return 1;
6162 }
6163 return 0;
6164}
6165
6166/**
6167 merges routine privileges from roles granted to the role 'grantee'.
6168
6169 @return true if routine privileges of the 'grantee' were changed
6170
6171*/
6172static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee,
6173 const char *db, const char *tname, role_hash_t *rhash, HASH *hash)
6174{
6175 ulong update_flags= 0;
6176
6177 DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
6178
6179 Dynamic_array<GRANT_NAME *> grants;
6180
6181 /* first, collect routine privileges granted to roles in question */
6182 for (uint i=0 ; i < hash->records ; i++)
6183 {
6184 GRANT_NAME *grant= (GRANT_NAME *) my_hash_element(hash, i);
6185 if (grant->host.hostname[0])
6186 continue;
6187 if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
6188 continue;
6189 ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
6190 if (!r)
6191 continue;
6192 grants.append(grant);
6193 }
6194 grants.sort(routine_name_sort);
6195
6196 GRANT_NAME **first= NULL, *merged= NULL;
6197 ulong privs= 0 ;
6198 for (GRANT_NAME **cur= grants.front(); cur <= grants.back(); cur++)
6199 {
6200 if (!first ||
6201 (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
6202 strcmp(cur[0]->tname, cur[-1]->tname))))
6203 { // new db.tname series
6204 update_flags|= update_role_routines(merged, first, privs,
6205 grantee->user.str, hash);
6206 merged= NULL;
6207 privs= 0;
6208 first= cur;
6209 }
6210 if (strcmp(cur[0]->user, grantee->user.str) == 0)
6211 {
6212 merged= cur[0];
6213 privs|= cur[0]->init_privs;
6214 }
6215 else
6216 {
6217 privs|= cur[0]->privs;
6218 }
6219 }
6220 update_flags|= update_role_routines(merged, first, privs,
6221 grantee->user.str, hash);
6222 return update_flags;
6223}
6224
6225/**
6226 update privileges of the 'grantee' from all roles, granted to it
6227*/
6228static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
6229 ACL_ROLE *grantee, void *context)
6230{
6231 PRIVS_TO_MERGE *data= (PRIVS_TO_MERGE *)context;
6232
6233 DBUG_ASSERT(grantee->counter > 0);
6234 if (--grantee->counter)
6235 return 1; // don't recurse into grantee just yet
6236
6237 grantee->counter= 1; // Mark the grantee as merged.
6238
6239 /* if we'll do db/table/routine privileges, create a hash of role names */
6240 role_hash_t role_hash(role_key);
6241 if (data->what != PRIVS_TO_MERGE::GLOBAL)
6242 {
6243 role_hash.insert(grantee);
6244 for (uint i= 0; i < grantee->role_grants.elements; i++)
6245 role_hash.insert(*dynamic_element(&grantee->role_grants, i, ACL_ROLE**));
6246 }
6247
6248 bool all= data->what == PRIVS_TO_MERGE::ALL;
6249 bool changed= false;
6250 if (all || data->what == PRIVS_TO_MERGE::GLOBAL)
6251 changed|= merge_role_global_privileges(grantee);
6252 if (all || data->what == PRIVS_TO_MERGE::DB)
6253 changed|= merge_role_db_privileges(grantee, data->db, &role_hash);
6254 if (all || data->what == PRIVS_TO_MERGE::TABLE_COLUMN)
6255 changed|= merge_role_table_and_column_privileges(grantee,
6256 data->db, data->name, &role_hash);
6257 if (all || data->what == PRIVS_TO_MERGE::PROC)
6258 changed|= merge_role_routine_grant_privileges(grantee,
6259 data->db, data->name, &role_hash, &proc_priv_hash);
6260 if (all || data->what == PRIVS_TO_MERGE::FUNC)
6261 changed|= merge_role_routine_grant_privileges(grantee,
6262 data->db, data->name, &role_hash, &func_priv_hash);
6263 if (all || data->what == PRIVS_TO_MERGE::PACKAGE_SPEC)
6264 changed|= merge_role_routine_grant_privileges(grantee,
6265 data->db, data->name, &role_hash,
6266 &package_spec_priv_hash);
6267 if (all || data->what == PRIVS_TO_MERGE::PACKAGE_BODY)
6268 changed|= merge_role_routine_grant_privileges(grantee,
6269 data->db, data->name, &role_hash,
6270 &package_body_priv_hash);
6271 return !changed; // don't recurse into the subgraph if privs didn't change
6272}
6273
6274static bool merge_one_role_privileges(ACL_ROLE *grantee)
6275{
6276 PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
6277 grantee->counter= 1;
6278 return merge_role_privileges(0, grantee, &data);
6279}
6280
6281/*****************************************************************
6282 End of the role privilege propagation and graph traversal code
6283******************************************************************/
6284
6285static bool has_auth(LEX_USER *user, LEX *lex)
6286{
6287 return user->pwtext.length || user->pwhash.length || user->plugin.length || user->auth.length ||
6288 lex->ssl_type != SSL_TYPE_NOT_SPECIFIED || lex->ssl_cipher ||
6289 lex->x509_issuer || lex->x509_subject ||
6290 lex->mqh.specified_limits;
6291}
6292
6293static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd)
6294{
6295 if (to != from)
6296 {
6297 /* preserve authentication information, if LEX_USER was reallocated */
6298 to->pwtext= from->pwtext;
6299 to->pwhash= from->pwhash;
6300 to->plugin= from->plugin;
6301 to->auth= from->auth;
6302 }
6303
6304 if (fix_lex_user(thd, to))
6305 return true;
6306
6307 return false;
6308}
6309
6310static bool copy_and_check_auth(LEX_USER *to, LEX_USER *from, THD *thd)
6311{
6312 if (fix_and_copy_user(to, from, thd))
6313 return true;
6314
6315 // if changing auth for an existing user
6316 if (has_auth(to, thd->lex) && find_user_exact(to->host.str, to->user.str))
6317 {
6318 mysql_mutex_unlock(&acl_cache->lock);
6319 bool res= check_alter_user(thd, to->host.str, to->user.str);
6320 mysql_mutex_lock(&acl_cache->lock);
6321 return res;
6322 }
6323
6324 return false;
6325}
6326
6327
6328/*
6329 Store table level and column level grants in the privilege tables
6330
6331 SYNOPSIS
6332 mysql_table_grant()
6333 thd Thread handle
6334 table_list List of tables to give grant
6335 user_list List of users to give grant
6336 columns List of columns to give grant
6337 rights Table level grant
6338 revoke_grant Set to 1 if this is a REVOKE command
6339
6340 RETURN
6341 FALSE ok
6342 TRUE error
6343*/
6344
6345int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
6346 List <LEX_USER> &user_list,
6347 List <LEX_COLUMN> &columns, ulong rights,
6348 bool revoke_grant)
6349{
6350 ulong column_priv= 0;
6351 int result;
6352 List_iterator <LEX_USER> str_list (user_list);
6353 LEX_USER *Str, *tmp_Str;
6354 bool create_new_users=0;
6355 const char *db_name, *table_name;
6356 DBUG_ENTER("mysql_table_grant");
6357
6358 if (rights & ~TABLE_ACLS)
6359 {
6360 my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
6361 ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
6362 MYF(0));
6363 DBUG_RETURN(TRUE);
6364 }
6365
6366 if (!revoke_grant)
6367 {
6368 if (columns.elements)
6369 {
6370 class LEX_COLUMN *column;
6371 List_iterator <LEX_COLUMN> column_iter(columns);
6372
6373 if (open_normal_and_derived_tables(thd, table_list, 0, DT_PREPARE))
6374 DBUG_RETURN(TRUE);
6375
6376 while ((column = column_iter++))
6377 {
6378 uint unused_field_idx= NO_CACHED_FIELD_INDEX;
6379 TABLE_LIST *dummy;
6380 Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
6381 column->column.length(),
6382 column->column.ptr(), NULL, NULL,
6383 NULL, TRUE, FALSE,
6384 &unused_field_idx, FALSE, &dummy);
6385 if (unlikely(f == (Field*)0))
6386 {
6387 my_error(ER_BAD_FIELD_ERROR, MYF(0),
6388 column->column.c_ptr(), table_list->alias.str);
6389 DBUG_RETURN(TRUE);
6390 }
6391 if (unlikely(f == (Field *)-1))
6392 DBUG_RETURN(TRUE);
6393 column_priv|= column->rights;
6394 }
6395 close_mysql_tables(thd);
6396 }
6397 else
6398 {
6399 if (!(rights & CREATE_ACL))
6400 {
6401 if (!ha_table_exists(thd, &table_list->db, &table_list->table_name))
6402 {
6403 my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db.str,
6404 table_list->alias.str);
6405 DBUG_RETURN(TRUE);
6406 }
6407 }
6408 if (table_list->grant.want_privilege)
6409 {
6410 char command[128];
6411 get_privilege_desc(command, sizeof(command),
6412 table_list->grant.want_privilege);
6413 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
6414 command, thd->security_ctx->priv_user,
6415 thd->security_ctx->host_or_ip, table_list->alias.str);
6416 DBUG_RETURN(-1);
6417 }
6418 }
6419 }
6420
6421 /*
6422 Open the mysql.user and mysql.tables_priv tables.
6423 Don't open column table if we don't need it !
6424 */
6425 int maybe_columns_priv= 0;
6426 if (column_priv ||
6427 (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
6428 maybe_columns_priv= Table_columns_priv;
6429
6430 /*
6431 The lock api is depending on the thd->lex variable which needs to be
6432 re-initialized.
6433 */
6434 Query_tables_list backup;
6435 thd->lex->reset_n_backup_query_tables_list(&backup);
6436 /*
6437 Restore Query_tables_list::sql_command value, which was reset
6438 above, as the code writing query to the binary log assumes that
6439 this value corresponds to the statement being executed.
6440 */
6441 thd->lex->sql_command= backup.sql_command;
6442
6443 Grant_tables tables(Table_user | Table_tables_priv | maybe_columns_priv,
6444 TL_WRITE);
6445 if ((result= tables.open_and_lock(thd)))
6446 {
6447 thd->lex->restore_backup_query_tables_list(&backup);
6448 DBUG_RETURN(result != 1);
6449 }
6450
6451 if (!revoke_grant)
6452 create_new_users= test_if_create_new_users(thd);
6453 mysql_rwlock_wrlock(&LOCK_grant);
6454 mysql_mutex_lock(&acl_cache->lock);
6455 MEM_ROOT *old_root= thd->mem_root;
6456 thd->mem_root= &grant_memroot;
6457 grant_version++;
6458
6459 while ((tmp_Str = str_list++))
6460 {
6461 int error;
6462 GRANT_TABLE *grant_table;
6463 if (!(Str= get_current_user(thd, tmp_Str, false)))
6464 {
6465 result= TRUE;
6466 continue;
6467 }
6468 /* Create user if needed */
6469 error= copy_and_check_auth(Str, tmp_Str, thd) ||
6470 replace_user_table(thd, tables.user_table(), *Str,
6471 0, revoke_grant, create_new_users,
6472 MY_TEST(thd->variables.sql_mode &
6473 MODE_NO_AUTO_CREATE_USER));
6474 if (unlikely(error))
6475 {
6476 result= TRUE; // Remember error
6477 continue; // Add next user
6478 }
6479
6480 db_name= table_list->get_db_name();
6481 table_name= table_list->get_table_name();
6482
6483 /* Find/create cached table grant */
6484 grant_table= table_hash_search(Str->host.str, NullS, db_name,
6485 Str->user.str, table_name, 1);
6486 if (!grant_table)
6487 {
6488 if (revoke_grant)
6489 {
6490 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
6491 Str->user.str, Str->host.str, table_list->table_name.str);
6492 result= TRUE;
6493 continue;
6494 }
6495 grant_table = new GRANT_TABLE (Str->host.str, db_name,
6496 Str->user.str, table_name,
6497 rights,
6498 column_priv);
6499 if (!grant_table ||
6500 my_hash_insert(&column_priv_hash,(uchar*) grant_table))
6501 {
6502 result= TRUE; /* purecov: deadcode */
6503 continue; /* purecov: deadcode */
6504 }
6505 }
6506
6507 /* If revoke_grant, calculate the new column privilege for tables_priv */
6508 if (revoke_grant)
6509 {
6510 class LEX_COLUMN *column;
6511 List_iterator <LEX_COLUMN> column_iter(columns);
6512 GRANT_COLUMN *grant_column;
6513
6514 /* Fix old grants */
6515 while ((column = column_iter++))
6516 {
6517 grant_column = column_hash_search(grant_table,
6518 column->column.ptr(),
6519 column->column.length());
6520 if (grant_column)
6521 grant_column->rights&= ~(column->rights | rights);
6522 }
6523 /* scan trough all columns to get new column grant */
6524 column_priv= 0;
6525 for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
6526 {
6527 grant_column= (GRANT_COLUMN*)
6528 my_hash_element(&grant_table->hash_columns, idx);
6529 grant_column->rights&= ~rights; // Fix other columns
6530 column_priv|= grant_column->rights;
6531 }
6532 }
6533 else
6534 {
6535 column_priv|= grant_table->cols;
6536 }
6537
6538
6539 /* update table and columns */
6540
6541 /* TODO(cvicentiu) refactor replace_table_table to use Tables_priv_table
6542 instead of TABLE directly. */
6543 if (replace_table_table(thd, grant_table, tables.tables_priv_table().table(),
6544 *Str, db_name, table_name,
6545 rights, column_priv, revoke_grant))
6546 {
6547 /* Should only happen if table is crashed */
6548 result= TRUE; /* purecov: deadcode */
6549 }
6550 else if (tables.columns_priv_table().table_exists())
6551 {
6552 /* TODO(cvicentiu) refactor replace_column_table to use Columns_priv_table
6553 instead of TABLE directly. */
6554 if (replace_column_table(grant_table, tables.columns_priv_table().table(),
6555 *Str, columns, db_name, table_name, rights,
6556 revoke_grant))
6557 {
6558 result= TRUE;
6559 }
6560 }
6561 if (Str->is_role())
6562 propagate_role_grants(find_acl_role(Str->user.str),
6563 PRIVS_TO_MERGE::TABLE_COLUMN, db_name, table_name);
6564 }
6565
6566 thd->mem_root= old_root;
6567 mysql_mutex_unlock(&acl_cache->lock);
6568
6569 if (!result) /* success */
6570 {
6571 result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
6572 }
6573
6574 mysql_rwlock_unlock(&LOCK_grant);
6575
6576 if (!result) /* success */
6577 my_ok(thd);
6578
6579 thd->lex->restore_backup_query_tables_list(&backup);
6580 DBUG_RETURN(result);
6581}
6582
6583
6584/**
6585 Store routine level grants in the privilege tables
6586
6587 @param thd Thread handle
6588 @param table_list List of routines to give grant
6589 @param sph SP handler
6590 @param user_list List of users to give grant
6591 @param rights Table level grant
6592 @param revoke_grant Is this is a REVOKE command?
6593
6594 @return
6595 @retval FALSE Success.
6596 @retval TRUE An error occurred.
6597*/
6598
6599bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list,
6600 const Sp_handler *sph,
6601 List <LEX_USER> &user_list, ulong rights,
6602 bool revoke_grant, bool write_to_binlog)
6603{
6604 List_iterator <LEX_USER> str_list (user_list);
6605 LEX_USER *Str, *tmp_Str;
6606 bool create_new_users= 0, result;
6607 const char *db_name, *table_name;
6608 DBUG_ENTER("mysql_routine_grant");
6609
6610 if (rights & ~PROC_ACLS)
6611 {
6612 my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
6613 ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
6614 MYF(0));
6615 DBUG_RETURN(TRUE);
6616 }
6617
6618 if (!revoke_grant)
6619 {
6620 if (sph->sp_exist_routines(thd, table_list))
6621 DBUG_RETURN(TRUE);
6622 }
6623
6624 Grant_tables tables(Table_user | Table_procs_priv, TL_WRITE);
6625 if ((result= tables.open_and_lock(thd)))
6626 DBUG_RETURN(result != 1);
6627
6628 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6629
6630 if (!revoke_grant)
6631 create_new_users= test_if_create_new_users(thd);
6632 mysql_rwlock_wrlock(&LOCK_grant);
6633 mysql_mutex_lock(&acl_cache->lock);
6634 MEM_ROOT *old_root= thd->mem_root;
6635 thd->mem_root= &grant_memroot;
6636
6637 DBUG_PRINT("info",("now time to iterate and add users"));
6638
6639 while ((tmp_Str= str_list++))
6640 {
6641 GRANT_NAME *grant_name;
6642 if (!(Str= get_current_user(thd, tmp_Str, false)))
6643 {
6644 result= TRUE;
6645 continue;
6646 }
6647 /* Create user if needed */
6648 if (copy_and_check_auth(Str, tmp_Str, thd) ||
6649 replace_user_table(thd, tables.user_table(), *Str,
6650 0, revoke_grant, create_new_users,
6651 MY_TEST(thd->variables.sql_mode &
6652 MODE_NO_AUTO_CREATE_USER)))
6653 {
6654 result= TRUE;
6655 continue;
6656 }
6657
6658 db_name= table_list->db.str;
6659 table_name= table_list->table_name.str;
6660 grant_name= routine_hash_search(Str->host.str, NullS, db_name,
6661 Str->user.str, table_name, sph, 1);
6662 if (!grant_name || !grant_name->init_privs)
6663 {
6664 if (revoke_grant)
6665 {
6666 my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
6667 Str->user.str, Str->host.str, table_name);
6668 result= TRUE;
6669 continue;
6670 }
6671 grant_name= new GRANT_NAME(Str->host.str, db_name,
6672 Str->user.str, table_name,
6673 rights, TRUE);
6674 if (!grant_name ||
6675 my_hash_insert(sph->get_priv_hash(), (uchar*) grant_name))
6676 {
6677 result= TRUE;
6678 continue;
6679 }
6680 }
6681
6682 /* TODO(cvicentiu) refactor replace_routine_table to use Tables_procs_priv
6683 instead of TABLE directly. */
6684 if (tables.procs_priv_table().no_such_table() ||
6685 replace_routine_table(thd, grant_name, tables.procs_priv_table().table(),
6686 *Str, db_name, table_name, sph, rights,
6687 revoke_grant) != 0)
6688 {
6689 result= TRUE;
6690 continue;
6691 }
6692 if (Str->is_role())
6693 propagate_role_grants(find_acl_role(Str->user.str),
6694 sp_privs_to_merge(sph->type()),
6695 db_name, table_name);
6696 }
6697 thd->mem_root= old_root;
6698 mysql_mutex_unlock(&acl_cache->lock);
6699
6700 if (write_to_binlog)
6701 {
6702 if (write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
6703 result= TRUE;
6704 }
6705
6706 mysql_rwlock_unlock(&LOCK_grant);
6707
6708 /* Tables are automatically closed */
6709 DBUG_RETURN(result);
6710}
6711
6712/**
6713 append a user or role name to a buffer that will be later used as an error message
6714*/
6715static void append_user(THD *thd, String *str,
6716 const LEX_CSTRING *u, const LEX_CSTRING *h)
6717{
6718 if (str->length())
6719 str->append(',');
6720 append_query_string(system_charset_info, str, u->str, u->length,
6721 thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
6722 /* hostname part is not relevant for roles, it is always empty */
6723 if (u->length == 0 || h->length != 0)
6724 {
6725 str->append('@');
6726 append_query_string(system_charset_info, str, h->str, h->length,
6727 thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
6728 }
6729}
6730
6731static void append_user(THD *thd, String *str, LEX_USER *user)
6732{
6733 append_user(thd, str, & user->user, & user->host);
6734}
6735
6736/**
6737 append a string to a buffer that will be later used as an error message
6738
6739 @note
6740 a string can be either CURRENT_USER or CURRENT_ROLE or NONE, it should be
6741 neither quoted nor escaped.
6742*/
6743static void append_str(String *str, const char *s, size_t l)
6744{
6745 if (str->length())
6746 str->append(',');
6747 str->append(s, l);
6748}
6749
6750static int can_grant_role_callback(ACL_USER_BASE *grantee,
6751 ACL_ROLE *role, void *data)
6752{
6753 ROLE_GRANT_PAIR *pair;
6754
6755 if (role != (ACL_ROLE*)data)
6756 return 0; // keep searching
6757
6758 if (grantee->flags & IS_ROLE)
6759 pair= find_role_grant_pair(&grantee->user, &empty_clex_str, &role->user);
6760 else
6761 {
6762 ACL_USER *user= (ACL_USER *)grantee;
6763 LEX_CSTRING host= { user->host.hostname, user->hostname_length };
6764 pair= find_role_grant_pair(&user->user, &host, &role->user);
6765 }
6766 if (!pair->with_admin)
6767 return 0; // keep searching
6768
6769 return -1; // abort the traversal
6770}
6771
6772
6773/*
6774 One can only grant a role if SELECT * FROM I_S.APPLICABLE_ROLES shows this
6775 role as grantable.
6776
6777 What this really means - we need to traverse role graph for the current user
6778 looking for our role being granted with the admin option.
6779*/
6780static bool can_grant_role(THD *thd, ACL_ROLE *role)
6781{
6782 Security_context *sctx= thd->security_ctx;
6783
6784 if (!sctx->user) // replication
6785 return true;
6786
6787 ACL_USER *grantee= find_user_exact(sctx->priv_host, sctx->priv_user);
6788 if (!grantee)
6789 return false;
6790
6791 return traverse_role_graph_down(grantee, role, NULL,
6792 can_grant_role_callback) == -1;
6793}
6794
6795
6796bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
6797{
6798 DBUG_ENTER("mysql_grant_role");
6799 /*
6800 The first entry in the list is the granted role. Need at least two
6801 entries for the command to be valid
6802 */
6803 DBUG_ASSERT(list.elements >= 2);
6804 int result;
6805 bool create_new_user, no_auto_create_user;
6806 String wrong_users;
6807 LEX_USER *user, *granted_role;
6808 LEX_CSTRING rolename;
6809 LEX_CSTRING username;
6810 LEX_CSTRING hostname;
6811 ACL_ROLE *role, *role_as_user;
6812
6813 List_iterator <LEX_USER> user_list(list);
6814 granted_role= user_list++;
6815 if (!(granted_role= get_current_user(thd, granted_role)))
6816 DBUG_RETURN(TRUE);
6817
6818 DBUG_ASSERT(granted_role->is_role());
6819 rolename= granted_role->user;
6820
6821 create_new_user= test_if_create_new_users(thd);
6822 no_auto_create_user= MY_TEST(thd->variables.sql_mode &
6823 MODE_NO_AUTO_CREATE_USER);
6824
6825 Grant_tables tables(Table_user | Table_roles_mapping, TL_WRITE);
6826 if ((result= tables.open_and_lock(thd)))
6827 DBUG_RETURN(result != 1);
6828
6829 mysql_rwlock_wrlock(&LOCK_grant);
6830 mysql_mutex_lock(&acl_cache->lock);
6831 if (!(role= find_acl_role(rolename.str)))
6832 {
6833 mysql_mutex_unlock(&acl_cache->lock);
6834 mysql_rwlock_unlock(&LOCK_grant);
6835 my_error(ER_INVALID_ROLE, MYF(0), rolename.str);
6836 DBUG_RETURN(TRUE);
6837 }
6838
6839 if (!can_grant_role(thd, role))
6840 {
6841 mysql_mutex_unlock(&acl_cache->lock);
6842 mysql_rwlock_unlock(&LOCK_grant);
6843 my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
6844 thd->security_ctx->priv_user, thd->security_ctx->priv_host);
6845 DBUG_RETURN(TRUE);
6846 }
6847
6848 while ((user= user_list++))
6849 {
6850 role_as_user= NULL;
6851 /* current_role is treated slightly different */
6852 if (user->user.str == current_role.str)
6853 {
6854 /* current_role is NONE */
6855 if (!thd->security_ctx->priv_role[0])
6856 {
6857 my_error(ER_INVALID_ROLE, MYF(0), "NONE");
6858 append_str(&wrong_users, STRING_WITH_LEN("NONE"));
6859 result= 1;
6860 continue;
6861 }
6862 if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role)))
6863 {
6864 LEX_CSTRING ls= { thd->security_ctx->priv_role,
6865 strlen(thd->security_ctx->priv_role) };
6866 append_user(thd, &wrong_users, &ls, &empty_clex_str);
6867 result= 1;
6868 continue;
6869 }
6870
6871 /* can not grant current_role to current_role */
6872 if (granted_role->user.str == current_role.str)
6873 {
6874 append_user(thd, &wrong_users, &role_as_user->user, &empty_clex_str);
6875 result= 1;
6876 continue;
6877 }
6878 username.str= thd->security_ctx->priv_role;
6879 username.length= strlen(username.str);
6880 hostname= empty_clex_str;
6881 }
6882 else if (user->user.str == current_user.str)
6883 {
6884 username.str= thd->security_ctx->priv_user;
6885 username.length= strlen(username.str);
6886 hostname.str= thd->security_ctx->priv_host;
6887 hostname.length= strlen(hostname.str);
6888 }
6889 else
6890 {
6891 username= user->user;
6892 if (user->host.str)
6893 hostname= user->host;
6894 else
6895 if ((role_as_user= find_acl_role(user->user.str)))
6896 hostname= empty_clex_str;
6897 else
6898 {
6899 if (is_invalid_role_name(username.str))
6900 {
6901 append_user(thd, &wrong_users, &username, &empty_clex_str);
6902 result= 1;
6903 continue;
6904 }
6905 hostname= host_not_specified;
6906 }
6907 }
6908
6909 ROLE_GRANT_PAIR *hash_entry= find_role_grant_pair(&username, &hostname,
6910 &rolename);
6911 ACL_USER_BASE *grantee= role_as_user;
6912
6913 if (has_auth(user, thd->lex))
6914 DBUG_ASSERT(!grantee);
6915 else if (!grantee)
6916 grantee= find_user_exact(hostname.str, username.str);
6917
6918 if (!grantee && !revoke)
6919 {
6920 LEX_USER user_combo = *user;
6921 user_combo.host = hostname;
6922 user_combo.user = username;
6923
6924 if (copy_and_check_auth(&user_combo, &user_combo, thd) ||
6925 replace_user_table(thd, tables.user_table(), user_combo, 0,
6926 false, create_new_user,
6927 no_auto_create_user))
6928 {
6929 append_user(thd, &wrong_users, &username, &hostname);
6930 result= 1;
6931 continue;
6932 }
6933 grantee= find_user_exact(hostname.str, username.str);
6934
6935 /* either replace_user_table failed, or we've added the user */
6936 DBUG_ASSERT(grantee);
6937 }
6938
6939 if (!grantee)
6940 {
6941 append_user(thd, &wrong_users, &username, &hostname);
6942 result= 1;
6943 continue;
6944 }
6945
6946 if (!revoke)
6947 {
6948 if (hash_entry)
6949 {
6950 // perhaps, updating an existing grant, adding WITH ADMIN OPTION
6951 }
6952 else
6953 {
6954 add_role_user_mapping(grantee, role);
6955
6956 /*
6957 Check if this grant would cause a cycle. It only needs to be run
6958 if we're granting a role to a role
6959 */
6960 if (role_as_user &&
6961 traverse_role_graph_down(role, 0, 0, 0) == ROLE_CYCLE_FOUND)
6962 {
6963 append_user(thd, &wrong_users, &username, &empty_clex_str);
6964 result= 1;
6965 undo_add_role_user_mapping(grantee, role);
6966 continue;
6967 }
6968 }
6969 }
6970 else
6971 {
6972 /* grant was already removed or never existed */
6973 if (!hash_entry)
6974 {
6975 append_user(thd, &wrong_users, &username, &hostname);
6976 result= 1;
6977 continue;
6978 }
6979 if (thd->lex->with_admin_option)
6980 {
6981 // only revoking an admin option, not the complete grant
6982 }
6983 else
6984 {
6985 /* revoke a role grant */
6986 remove_role_user_mapping(grantee, role);
6987 }
6988 }
6989
6990 /* write into the roles_mapping table */
6991 /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
6992 Roles_mapping_table instead of TABLE directly. */
6993 if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
6994 &username, &hostname, &rolename,
6995 thd->lex->with_admin_option,
6996 hash_entry, revoke))
6997 {
6998 append_user(thd, &wrong_users, &username, &empty_clex_str);
6999 result= 1;
7000 if (!revoke)
7001 {
7002 /* need to remove the mapping added previously */
7003 undo_add_role_user_mapping(grantee, role);
7004 }
7005 else
7006 {
7007 /* need to restore the mapping deleted previously */
7008 add_role_user_mapping(grantee, role);
7009 }
7010 continue;
7011 }
7012 update_role_mapping(&username, &hostname, &rolename,
7013 thd->lex->with_admin_option, hash_entry, revoke);
7014
7015 /*
7016 Only need to propagate grants when granting/revoking a role to/from
7017 a role
7018 */
7019 if (role_as_user && merge_one_role_privileges(role_as_user) == 0)
7020 propagate_role_grants(role_as_user, PRIVS_TO_MERGE::ALL);
7021 }
7022
7023 mysql_mutex_unlock(&acl_cache->lock);
7024
7025 if (result)
7026 my_error(revoke ? ER_CANNOT_REVOKE_ROLE : ER_CANNOT_GRANT_ROLE, MYF(0),
7027 rolename.str, wrong_users.c_ptr_safe());
7028 else
7029 result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7030
7031 mysql_rwlock_unlock(&LOCK_grant);
7032
7033 DBUG_RETURN(result);
7034}
7035
7036
7037bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
7038 ulong rights, bool revoke_grant, bool is_proxy)
7039{
7040 List_iterator <LEX_USER> str_list (list);
7041 LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
7042 char tmp_db[SAFE_NAME_LEN+1];
7043 bool create_new_users=0, result;
7044 DBUG_ENTER("mysql_grant");
7045
7046 if (lower_case_table_names && db)
7047 {
7048 char *end= strnmov(tmp_db,db, sizeof(tmp_db));
7049 if (end >= tmp_db + sizeof(tmp_db))
7050 {
7051 my_error(ER_WRONG_DB_NAME ,MYF(0), db);
7052 DBUG_RETURN(TRUE);
7053 }
7054 my_casedn_str(files_charset_info, tmp_db);
7055 db=tmp_db;
7056 }
7057
7058 if (is_proxy)
7059 {
7060 DBUG_ASSERT(!db);
7061 proxied_user= str_list++;
7062 }
7063
7064 Grant_tables tables(Table_user | (is_proxy ? Table_proxies_priv : Table_db),
7065 TL_WRITE);
7066 if ((result= tables.open_and_lock(thd)))
7067 DBUG_RETURN(result != 1);
7068
7069 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7070
7071 if (!revoke_grant)
7072 create_new_users= test_if_create_new_users(thd);
7073
7074 /* go through users in user_list */
7075 mysql_rwlock_wrlock(&LOCK_grant);
7076 mysql_mutex_lock(&acl_cache->lock);
7077 grant_version++;
7078
7079 if (proxied_user)
7080 {
7081 if (!(proxied_user= get_current_user(thd, proxied_user, false)))
7082 DBUG_RETURN(TRUE);
7083 DBUG_ASSERT(proxied_user->host.length); // not a Role
7084 }
7085
7086 while ((tmp_Str = str_list++))
7087 {
7088 if (!(Str= get_current_user(thd, tmp_Str, false)))
7089 {
7090 result= true;
7091 continue;
7092 }
7093
7094 if (copy_and_check_auth(Str, tmp_Str, thd) ||
7095 replace_user_table(thd, tables.user_table(), *Str,
7096 (!db ? rights : 0), revoke_grant, create_new_users,
7097 MY_TEST(thd->variables.sql_mode &
7098 MODE_NO_AUTO_CREATE_USER)))
7099 result= true;
7100 else if (db)
7101 {
7102 ulong db_rights= rights & DB_ACLS;
7103 if (db_rights == rights)
7104 {
7105 if (replace_db_table(tables.db_table().table(), db, *Str, db_rights,
7106 revoke_grant))
7107 result= true;
7108 }
7109 else
7110 {
7111 my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
7112 result= true;
7113 }
7114 }
7115 else if (is_proxy)
7116 {
7117 /* TODO(cvicentiu) refactor replace_proxies_priv_table to use
7118 Proxies_priv_table instead of TABLE directly. */
7119 if (tables.proxies_priv_table().no_such_table() ||
7120 replace_proxies_priv_table (thd, tables.proxies_priv_table().table(),
7121 Str, proxied_user,
7122 rights & GRANT_ACL ? TRUE : FALSE,
7123 revoke_grant))
7124 result= true;
7125 }
7126 if (Str->is_role())
7127 propagate_role_grants(find_acl_role(Str->user.str),
7128 db ? PRIVS_TO_MERGE::DB : PRIVS_TO_MERGE::GLOBAL,
7129 db);
7130 }
7131 mysql_mutex_unlock(&acl_cache->lock);
7132
7133 if (!result)
7134 {
7135 result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7136 }
7137
7138 mysql_rwlock_unlock(&LOCK_grant);
7139
7140 if (!result)
7141 my_ok(thd);
7142
7143 DBUG_RETURN(result);
7144}
7145
7146
7147/* Free grant array if possible */
7148
7149void grant_free(void)
7150{
7151 DBUG_ENTER("grant_free");
7152 my_hash_free(&column_priv_hash);
7153 my_hash_free(&proc_priv_hash);
7154 my_hash_free(&func_priv_hash);
7155 my_hash_free(&package_spec_priv_hash);
7156 my_hash_free(&package_body_priv_hash);
7157 free_root(&grant_memroot,MYF(0));
7158 DBUG_VOID_RETURN;
7159}
7160
7161
7162/**
7163 @brief Initialize structures responsible for table/column-level privilege
7164 checking and load information for them from tables in the 'mysql' database.
7165
7166 @return Error status
7167 @retval 0 OK
7168 @retval 1 Could not initialize grant subsystem.
7169*/
7170
7171bool grant_init()
7172{
7173 THD *thd;
7174 bool return_val;
7175 DBUG_ENTER("grant_init");
7176
7177 if (!(thd= new THD(0)))
7178 DBUG_RETURN(1); /* purecov: deadcode */
7179 thd->thread_stack= (char*) &thd;
7180 thd->store_globals();
7181 return_val= grant_reload(thd);
7182 delete thd;
7183 DBUG_RETURN(return_val);
7184}
7185
7186
7187/**
7188 @brief Initialize structures responsible for table/column-level privilege
7189 checking and load information about grants from open privilege tables.
7190
7191 @param thd Current thread
7192 @param tables List containing open "mysql.tables_priv" and
7193 "mysql.columns_priv" tables.
7194
7195 @see grant_reload
7196
7197 @return Error state
7198 @retval FALSE Success
7199 @retval TRUE Error
7200*/
7201
7202static bool grant_load(THD *thd,
7203 const Tables_priv_table& tables_priv,
7204 const Columns_priv_table& columns_priv,
7205 const Procs_priv_table& procs_priv)
7206{
7207 bool return_val= 1;
7208 TABLE *t_table, *c_table, *p_table;
7209 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
7210 MEM_ROOT *save_mem_root= thd->mem_root;
7211 sql_mode_t old_sql_mode= thd->variables.sql_mode;
7212 DBUG_ENTER("grant_load");
7213
7214 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
7215
7216 (void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
7217 0,0,0, (my_hash_get_key) get_grant_table,
7218 (my_hash_free_key) free_grant_table,0);
7219 (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
7220 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7221 (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
7222 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7223 (void) my_hash_init(&package_spec_priv_hash, &my_charset_utf8_bin,
7224 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7225 (void) my_hash_init(&package_body_priv_hash, &my_charset_utf8_bin,
7226 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7227 init_sql_alloc(&grant_memroot, "GRANT", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
7228
7229 t_table= tables_priv.table();
7230 c_table= columns_priv.table();
7231 p_table= procs_priv.table(); // this can be NULL
7232
7233 if (t_table->file->ha_index_init(0, 1))
7234 goto end_index_init;
7235
7236 t_table->use_all_columns();
7237 c_table->use_all_columns();
7238
7239 thd->mem_root= &grant_memroot;
7240
7241 if (!t_table->file->ha_index_first(t_table->record[0]))
7242 {
7243 do
7244 {
7245 GRANT_TABLE *mem_check;
7246 /* TODO(cvicentiu) convert this to use tables_priv and columns_priv. */
7247 if (!(mem_check= new (&grant_memroot) GRANT_TABLE(t_table, c_table)))
7248 {
7249 /* This could only happen if we are out memory */
7250 goto end_unlock;
7251 }
7252
7253 if (check_no_resolve)
7254 {
7255 if (hostname_requires_resolving(mem_check->host.hostname))
7256 {
7257 sql_print_warning("'tables_priv' entry '%s %s@%s' "
7258 "ignored in --skip-name-resolve mode.",
7259 mem_check->tname,
7260 safe_str(mem_check->user),
7261 safe_str(mem_check->host.hostname));
7262 continue;
7263 }
7264 }
7265
7266 if (! mem_check->ok())
7267 delete mem_check;
7268 else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
7269 {
7270 delete mem_check;
7271 goto end_unlock;
7272 }
7273 }
7274 while (!t_table->file->ha_index_next(t_table->record[0]));
7275 }
7276
7277 return_val= 0;
7278
7279 if (p_table)
7280 {
7281 if (p_table->file->ha_index_init(0, 1))
7282 goto end_unlock;
7283
7284 p_table->use_all_columns();
7285
7286 if (!p_table->file->ha_index_first(p_table->record[0]))
7287 {
7288 do
7289 {
7290 GRANT_NAME *mem_check;
7291 HASH *hash;
7292 if (!(mem_check= new (&grant_memroot) GRANT_NAME(p_table, TRUE)))
7293 {
7294 /* This could only happen if we are out memory */
7295 goto end_unlock_p;
7296 }
7297
7298 if (check_no_resolve)
7299 {
7300 if (hostname_requires_resolving(mem_check->host.hostname))
7301 {
7302 sql_print_warning("'procs_priv' entry '%s %s@%s' "
7303 "ignored in --skip-name-resolve mode.",
7304 mem_check->tname, mem_check->user,
7305 safe_str(mem_check->host.hostname));
7306 continue;
7307 }
7308 }
7309 stored_procedure_type type= (stored_procedure_type)procs_priv.routine_type()->val_int();
7310 const Sp_handler *sph= Sp_handler::handler(type);
7311 if (!sph || !(hash= sph->get_priv_hash()))
7312 {
7313 sql_print_warning("'procs_priv' entry '%s' "
7314 "ignored, bad routine type",
7315 mem_check->tname);
7316 continue;
7317 }
7318
7319 mem_check->privs= fix_rights_for_procedure(mem_check->privs);
7320 mem_check->init_privs= mem_check->privs;
7321 if (! mem_check->ok())
7322 delete mem_check;
7323 else if (my_hash_insert(hash, (uchar*) mem_check))
7324 {
7325 delete mem_check;
7326 goto end_unlock_p;
7327 }
7328 }
7329 while (!p_table->file->ha_index_next(p_table->record[0]));
7330 }
7331 }
7332
7333end_unlock_p:
7334 if (p_table)
7335 p_table->file->ha_index_end();
7336end_unlock:
7337 t_table->file->ha_index_end();
7338 thd->mem_root= save_mem_root;
7339end_index_init:
7340 thd->variables.sql_mode= old_sql_mode;
7341 DBUG_RETURN(return_val);
7342}
7343
7344static my_bool propagate_role_grants_action(void *role_ptr,
7345 void *ptr __attribute__((unused)))
7346{
7347 ACL_ROLE *role= static_cast<ACL_ROLE *>(role_ptr);
7348 if (role->counter)
7349 return 0;
7350
7351 mysql_mutex_assert_owner(&acl_cache->lock);
7352 PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
7353 traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
7354 return 0;
7355}
7356
7357
7358/**
7359 @brief Reload information about table and column level privileges if possible
7360
7361 @param thd Current thread
7362
7363 Locked tables are checked by acl_reload() and doesn't have to be checked
7364 in this call.
7365 This function is also used for initialization of structures responsible
7366 for table/column-level privilege checking.
7367
7368 @return Error state
7369 @retval FALSE Success
7370 @retval TRUE Error
7371*/
7372
7373bool grant_reload(THD *thd)
7374{
7375 HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
7376 HASH old_package_spec_priv_hash, old_package_body_priv_hash;
7377 MEM_ROOT old_mem;
7378 int result;
7379 DBUG_ENTER("grant_reload");
7380
7381 /*
7382 To avoid deadlocks we should obtain table locks before
7383 obtaining LOCK_grant rwlock.
7384 */
7385
7386 Grant_tables tables(Table_tables_priv | Table_columns_priv| Table_procs_priv,
7387 TL_READ);
7388 if ((result= tables.open_and_lock(thd)))
7389 DBUG_RETURN(result != 1);
7390
7391 mysql_rwlock_wrlock(&LOCK_grant);
7392 grant_version++;
7393 old_column_priv_hash= column_priv_hash;
7394 old_proc_priv_hash= proc_priv_hash;
7395 old_func_priv_hash= func_priv_hash;
7396 old_package_spec_priv_hash= package_spec_priv_hash;
7397 old_package_body_priv_hash= package_body_priv_hash;
7398
7399 /*
7400 Create a new memory pool but save the current memory pool to make an undo
7401 opertion possible in case of failure.
7402 */
7403 old_mem= grant_memroot;
7404
7405 if ((result= grant_load(thd,
7406 tables.tables_priv_table(),
7407 tables.columns_priv_table(),
7408 tables.procs_priv_table())))
7409 { // Error. Revert to old hash
7410 DBUG_PRINT("error",("Reverting to old privileges"));
7411 grant_free(); /* purecov: deadcode */
7412 column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
7413 proc_priv_hash= old_proc_priv_hash;
7414 func_priv_hash= old_func_priv_hash;
7415 package_spec_priv_hash= old_package_spec_priv_hash;
7416 package_body_priv_hash= old_package_body_priv_hash;
7417 grant_memroot= old_mem; /* purecov: deadcode */
7418 }
7419 else
7420 {
7421 my_hash_free(&old_column_priv_hash);
7422 my_hash_free(&old_proc_priv_hash);
7423 my_hash_free(&old_func_priv_hash);
7424 my_hash_free(&old_package_spec_priv_hash);
7425 my_hash_free(&old_package_body_priv_hash);
7426 free_root(&old_mem,MYF(0));
7427 }
7428
7429 mysql_mutex_lock(&acl_cache->lock);
7430 my_hash_iterate(&acl_roles, propagate_role_grants_action, NULL);
7431 mysql_mutex_unlock(&acl_cache->lock);
7432
7433 mysql_rwlock_unlock(&LOCK_grant);
7434
7435 close_mysql_tables(thd);
7436
7437 DBUG_RETURN(result);
7438}
7439
7440
7441/**
7442 @brief Check table level grants
7443
7444 @param thd Thread handler
7445 @param want_access Bits of privileges user needs to have.
7446 @param tables List of tables to check. The user should have
7447 'want_access' to all tables in list.
7448 @param any_combination_will_do TRUE if it's enough to have any privilege for
7449 any combination of the table columns.
7450 @param number Check at most this number of tables.
7451 @param no_errors TRUE if no error should be sent directly to the client.
7452
7453 If table->grant.want_privilege != 0 then the requested privileges where
7454 in the set of COL_ACLS but access was not granted on the table level. As
7455 a consequence an extra check of column privileges is required.
7456
7457 Specifically if this function returns FALSE the user has some kind of
7458 privilege on a combination of columns in each table.
7459
7460 This function is usually preceeded by check_access which establish the
7461 User-, Db- and Host access rights.
7462
7463 @see check_access
7464 @see check_table_access
7465
7466 @note
7467 This functions assumes that either number of tables to be inspected
7468 by it is limited explicitly (i.e. is is not UINT_MAX) or table list
7469 used and thd->lex->query_tables_own_last value correspond to each
7470 other (the latter should be either 0 or point to next_global member
7471 of one of elements of this table list).
7472
7473 We delay locking of LOCK_grant until we really need it as we assume that
7474 most privileges be resolved with user or db level accesses.
7475
7476 @return Access status
7477 @retval FALSE Access granted; But column privileges might need to be
7478 checked.
7479 @retval TRUE The user did not have the requested privileges on any of the
7480 tables.
7481
7482*/
7483
7484bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
7485 bool any_combination_will_do, uint number, bool no_errors)
7486{
7487 TABLE_LIST *tl;
7488 TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
7489 Security_context *sctx= thd->security_ctx;
7490 uint i;
7491 ulong original_want_access= want_access;
7492 bool locked= 0;
7493 GRANT_TABLE *grant_table;
7494 GRANT_TABLE *grant_table_role= NULL;
7495 DBUG_ENTER("check_grant");
7496 DBUG_ASSERT(number > 0);
7497
7498 /*
7499 Walk through the list of tables that belong to the query and save the
7500 requested access (orig_want_privilege) to be able to use it when
7501 checking access rights to the underlying tables of a view. Our grant
7502 system gradually eliminates checked bits from want_privilege and thus
7503 after all checks are done we can no longer use it.
7504 The check that first_not_own_table is not reached is for the case when
7505 the given table list refers to the list for prelocking (contains tables
7506 of other queries). For simple queries first_not_own_table is 0.
7507 */
7508 for (i= 0, tl= tables;
7509 i < number && tl != first_not_own_table;
7510 tl= tl->next_global, i++)
7511 {
7512 /*
7513 Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
7514 It will be checked during making view.
7515 */
7516 tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
7517 }
7518 number= i;
7519
7520 for (tl= tables; number-- ; tl= tl->next_global)
7521 {
7522 TABLE_LIST *const t_ref=
7523 tl->correspondent_table ? tl->correspondent_table : tl;
7524 sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx;
7525 ulong orig_want_access= original_want_access;
7526
7527 /*
7528 If sequence is used as part of NEXT VALUE, PREVIOUS VALUE or SELECT,
7529 we need to modify the requested access rights depending on how the
7530 sequence is used.
7531 */
7532 if (t_ref->sequence &&
7533 !(want_access & ~(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL)))
7534 {
7535 /*
7536 We want to have either SELECT or INSERT rights to sequences depending
7537 on how they are accessed
7538 */
7539 orig_want_access= ((t_ref->lock_type == TL_WRITE_ALLOW_WRITE) ?
7540 INSERT_ACL : SELECT_ACL);
7541 }
7542
7543 if (tl->with ||
7544 (tl->select_lex &&
7545 (tl->with= tl->select_lex->find_table_def_in_with_clauses(tl))))
7546 continue;
7547
7548 const ACL_internal_table_access *access=
7549 get_cached_table_access(&t_ref->grant.m_internal,
7550 t_ref->get_db_name(),
7551 t_ref->get_table_name());
7552
7553 if (access)
7554 {
7555 switch(access->check(orig_want_access, &t_ref->grant.privilege))
7556 {
7557 case ACL_INTERNAL_ACCESS_GRANTED:
7558 /*
7559 Currently,
7560 - the information_schema does not subclass ACL_internal_table_access,
7561 there are no per table privilege checks for I_S,
7562 - the performance schema does use per tables checks, but at most
7563 returns 'CHECK_GRANT', and never 'ACCESS_GRANTED'.
7564 so this branch is not used.
7565 */
7566 DBUG_ASSERT(0);
7567 case ACL_INTERNAL_ACCESS_DENIED:
7568 goto err;
7569 case ACL_INTERNAL_ACCESS_CHECK_GRANT:
7570 break;
7571 }
7572 }
7573
7574 want_access= orig_want_access;
7575 want_access&= ~sctx->master_access;
7576 if (!want_access)
7577 continue; // ok
7578
7579 if (!(~t_ref->grant.privilege & want_access) ||
7580 t_ref->is_anonymous_derived_table() || t_ref->schema_table)
7581 {
7582 /*
7583 It is subquery in the FROM clause. VIEW set t_ref->derived after
7584 table opening, but this function always called before table opening.
7585
7586 NOTE: is_derived() can't be used here because subquery in this case
7587 the FROM clase (derived tables) can be not be marked yet.
7588 */
7589 if (t_ref->is_anonymous_derived_table() || t_ref->schema_table)
7590 {
7591 /*
7592 If it's a temporary table created for a subquery in the FROM
7593 clause, or an INFORMATION_SCHEMA table, drop the request for
7594 a privilege.
7595 */
7596 t_ref->grant.want_privilege= 0;
7597 }
7598 continue;
7599 }
7600
7601 if (is_temporary_table(t_ref))
7602 {
7603 /*
7604 If this table list element corresponds to a pre-opened temporary
7605 table skip checking of all relevant table-level privileges for it.
7606 Note that during creation of temporary table we still need to check
7607 if user has CREATE_TMP_ACL.
7608 */
7609 t_ref->grant.privilege|= TMP_TABLE_ACLS;
7610 t_ref->grant.want_privilege= 0;
7611 continue;
7612 }
7613
7614 if (!locked)
7615 {
7616 locked= 1;
7617 mysql_rwlock_rdlock(&LOCK_grant);
7618 }
7619
7620 grant_table= table_hash_search(sctx->host, sctx->ip,
7621 t_ref->get_db_name(),
7622 sctx->priv_user,
7623 t_ref->get_table_name(),
7624 FALSE);
7625 if (sctx->priv_role[0])
7626 grant_table_role= table_hash_search("", NULL, t_ref->get_db_name(),
7627 sctx->priv_role,
7628 t_ref->get_table_name(),
7629 TRUE);
7630
7631 if (!grant_table && !grant_table_role)
7632 {
7633 want_access&= ~t_ref->grant.privilege;
7634 goto err; // No grants
7635 }
7636
7637 /*
7638 For SHOW COLUMNS, SHOW INDEX it is enough to have some
7639 privileges on any column combination on the table.
7640 */
7641 if (any_combination_will_do)
7642 continue;
7643
7644 t_ref->grant.grant_table_user= grant_table; // Remember for column test
7645 t_ref->grant.grant_table_role= grant_table_role;
7646 t_ref->grant.version= grant_version;
7647 t_ref->grant.privilege|= grant_table ? grant_table->privs : 0;
7648 t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : 0;
7649 t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege);
7650
7651 if (!(~t_ref->grant.privilege & want_access))
7652 continue;
7653
7654 if ((want_access&= ~((grant_table ? grant_table->cols : 0) |
7655 (grant_table_role ? grant_table_role->cols : 0) |
7656 t_ref->grant.privilege)))
7657 {
7658 goto err; // impossible
7659 }
7660 }
7661 if (locked)
7662 mysql_rwlock_unlock(&LOCK_grant);
7663 DBUG_RETURN(FALSE);
7664
7665err:
7666 if (locked)
7667 mysql_rwlock_unlock(&LOCK_grant);
7668 if (!no_errors) // Not a silent skip of table
7669 {
7670 char command[128];
7671 get_privilege_desc(command, sizeof(command), want_access);
7672 status_var_increment(thd->status_var.access_denied_errors);
7673
7674 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
7675 command,
7676 sctx->priv_user,
7677 sctx->host_or_ip,
7678 tl ? tl->get_table_name() : "unknown");
7679 }
7680 DBUG_RETURN(TRUE);
7681}
7682
7683
7684/*
7685 Check column rights in given security context
7686
7687 SYNOPSIS
7688 check_grant_column()
7689 thd thread handler
7690 grant grant information structure
7691 db_name db name
7692 table_name table name
7693 name column name
7694 length column name length
7695 sctx security context
7696
7697 RETURN
7698 FALSE OK
7699 TRUE access denied
7700*/
7701
7702bool check_grant_column(THD *thd, GRANT_INFO *grant,
7703 const char *db_name, const char *table_name,
7704 const char *name, size_t length, Security_context *sctx)
7705{
7706 GRANT_TABLE *grant_table;
7707 GRANT_TABLE *grant_table_role;
7708 GRANT_COLUMN *grant_column;
7709 ulong want_access= grant->want_privilege & ~grant->privilege;
7710 DBUG_ENTER("check_grant_column");
7711 DBUG_PRINT("enter", ("table: %s want_access: %lu", table_name, want_access));
7712
7713 if (!want_access)
7714 DBUG_RETURN(0); // Already checked
7715
7716 mysql_rwlock_rdlock(&LOCK_grant);
7717
7718 /* reload table if someone has modified any grants */
7719
7720 if (grant->version != grant_version)
7721 {
7722 grant->grant_table_user=
7723 table_hash_search(sctx->host, sctx->ip, db_name,
7724 sctx->priv_user,
7725 table_name, 0); /* purecov: inspected */
7726 grant->grant_table_role=
7727 sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
7728 sctx->priv_role,
7729 table_name, TRUE) : NULL;
7730 grant->version= grant_version; /* purecov: inspected */
7731 }
7732
7733 grant_table= grant->grant_table_user;
7734 grant_table_role= grant->grant_table_role;
7735
7736 if (!grant_table && !grant_table_role)
7737 goto err;
7738
7739 if (grant_table)
7740 {
7741 grant_column= column_hash_search(grant_table, name, length);
7742 if (grant_column)
7743 {
7744 want_access&= ~grant_column->rights;
7745 }
7746 }
7747 if (grant_table_role)
7748 {
7749 grant_column= column_hash_search(grant_table_role, name, length);
7750 if (grant_column)
7751 {
7752 want_access&= ~grant_column->rights;
7753 }
7754 }
7755 if (!want_access)
7756 {
7757 mysql_rwlock_unlock(&LOCK_grant);
7758 DBUG_RETURN(0);
7759 }
7760
7761err:
7762 mysql_rwlock_unlock(&LOCK_grant);
7763 char command[128];
7764 get_privilege_desc(command, sizeof(command), want_access);
7765 /* TODO perhaps error should print current rolename aswell */
7766 my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
7767 command,
7768 sctx->priv_user,
7769 sctx->host_or_ip,
7770 name,
7771 table_name);
7772 DBUG_RETURN(1);
7773}
7774
7775
7776/*
7777 Check the access right to a column depending on the type of table.
7778
7779 SYNOPSIS
7780 check_column_grant_in_table_ref()
7781 thd thread handler
7782 table_ref table reference where to check the field
7783 name name of field to check
7784 length length of name
7785 fld use fld object to check invisibility when it is
7786 not 0, not_found_field, view_ref_found
7787
7788 DESCRIPTION
7789 Check the access rights to a column depending on the type of table
7790 reference where the column is checked. The function provides a
7791 generic interface to check column access rights that hides the
7792 heterogeneity of the column representation - whether it is a view
7793 or a stored table colum.
7794
7795 RETURN
7796 FALSE OK
7797 TRUE access denied
7798*/
7799
7800bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
7801 const char *name, size_t length,
7802 Field *fld)
7803{
7804 GRANT_INFO *grant;
7805 const char *db_name;
7806 const char *table_name;
7807 Security_context *sctx= table_ref->security_ctx ?
7808 table_ref->security_ctx : thd->security_ctx;
7809 if (fld && fld != not_found_field && fld != view_ref_found
7810 && fld->invisible >= INVISIBLE_SYSTEM)
7811 return false;
7812
7813 if (table_ref->view || table_ref->field_translation)
7814 {
7815 /* View or derived information schema table. */
7816 ulong view_privs;
7817 grant= &(table_ref->grant);
7818 db_name= table_ref->view_db.str;
7819 table_name= table_ref->view_name.str;
7820 if (table_ref->belong_to_view &&
7821 thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
7822 {
7823 view_privs= get_column_grant(thd, grant, db_name, table_name, name);
7824 if (view_privs & VIEW_ANY_ACL)
7825 {
7826 table_ref->belong_to_view->allowed_show= TRUE;
7827 return FALSE;
7828 }
7829 table_ref->belong_to_view->allowed_show= FALSE;
7830 my_message(ER_VIEW_NO_EXPLAIN, ER_THD(thd, ER_VIEW_NO_EXPLAIN), MYF(0));
7831 return TRUE;
7832 }
7833 }
7834 else
7835 {
7836 /* Normal or temporary table. */
7837 TABLE *table= table_ref->table;
7838 grant= &(table->grant);
7839 db_name= table->s->db.str;
7840 table_name= table->s->table_name.str;
7841 }
7842
7843 if (grant->want_privilege)
7844 return check_grant_column(thd, grant, db_name, table_name, name,
7845 length, sctx);
7846 else
7847 return FALSE;
7848
7849}
7850
7851
7852/**
7853 @brief check if a query can access a set of columns
7854
7855 @param thd the current thread
7856 @param want_access_arg the privileges requested
7857 @param fields an iterator over the fields of a table reference.
7858 @return Operation status
7859 @retval 0 Success
7860 @retval 1 Falure
7861 @details This function walks over the columns of a table reference
7862 The columns may originate from different tables, depending on the kind of
7863 table reference, e.g. join, view.
7864 For each table it will retrieve the grant information and will use it
7865 to check the required access privileges for the fields requested from it.
7866*/
7867bool check_grant_all_columns(THD *thd, ulong want_access_arg,
7868 Field_iterator_table_ref *fields)
7869{
7870 Security_context *sctx= thd->security_ctx;
7871 ulong UNINIT_VAR(want_access);
7872 const char *table_name= NULL;
7873 const char* db_name;
7874 GRANT_INFO *grant;
7875 GRANT_TABLE *UNINIT_VAR(grant_table);
7876 GRANT_TABLE *UNINIT_VAR(grant_table_role);
7877 /*
7878 Flag that gets set if privilege checking has to be performed on column
7879 level.
7880 */
7881 bool using_column_privileges= FALSE;
7882
7883 mysql_rwlock_rdlock(&LOCK_grant);
7884
7885 for (; !fields->end_of_fields(); fields->next())
7886 {
7887 if (fields->field() &&
7888 fields->field()->invisible >= INVISIBLE_SYSTEM)
7889 continue;
7890 LEX_CSTRING *field_name= fields->name();
7891
7892 if (table_name != fields->get_table_name())
7893 {
7894 table_name= fields->get_table_name();
7895 db_name= fields->get_db_name();
7896 grant= fields->grant();
7897 /* get a fresh one for each table */
7898 want_access= want_access_arg & ~grant->privilege;
7899 if (want_access)
7900 {
7901 /* reload table if someone has modified any grants */
7902 if (grant->version != grant_version)
7903 {
7904 grant->grant_table_user=
7905 table_hash_search(sctx->host, sctx->ip, db_name,
7906 sctx->priv_user,
7907 table_name, 0); /* purecov: inspected */
7908 grant->grant_table_role=
7909 sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
7910 sctx->priv_role,
7911 table_name, TRUE) : NULL;
7912 grant->version= grant_version; /* purecov: inspected */
7913 }
7914
7915 grant_table= grant->grant_table_user;
7916 grant_table_role= grant->grant_table_role;
7917 DBUG_ASSERT (grant_table || grant_table_role);
7918 }
7919 }
7920
7921 if (want_access)
7922 {
7923 ulong have_access= 0;
7924 if (grant_table)
7925 {
7926 GRANT_COLUMN *grant_column=
7927 column_hash_search(grant_table, field_name->str, field_name->length);
7928 if (grant_column)
7929 have_access= grant_column->rights;
7930 }
7931 if (grant_table_role)
7932 {
7933 GRANT_COLUMN *grant_column=
7934 column_hash_search(grant_table_role, field_name->str,
7935 field_name->length);
7936 if (grant_column)
7937 have_access|= grant_column->rights;
7938 }
7939
7940 if (have_access)
7941 using_column_privileges= TRUE;
7942 if (want_access & ~have_access)
7943 goto err;
7944 }
7945 }
7946 mysql_rwlock_unlock(&LOCK_grant);
7947 return 0;
7948
7949err:
7950 mysql_rwlock_unlock(&LOCK_grant);
7951
7952 char command[128];
7953 get_privilege_desc(command, sizeof(command), want_access);
7954 /*
7955 Do not give an error message listing a column name unless the user has
7956 privilege to see all columns.
7957 */
7958 if (using_column_privileges)
7959 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
7960 command, sctx->priv_user,
7961 sctx->host_or_ip, table_name);
7962 else
7963 my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
7964 command,
7965 sctx->priv_user,
7966 sctx->host_or_ip,
7967 fields->name()->str,
7968 table_name);
7969 return 1;
7970}
7971
7972
7973static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
7974{
7975 Security_context *sctx= thd->security_ctx;
7976
7977 for (uint idx= 0; idx < hash->records; ++idx)
7978 {
7979 GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
7980
7981 if (strcmp(item->user, sctx->priv_user) == 0 &&
7982 strcmp(item->db, db) == 0 &&
7983 compare_hostname(&item->host, sctx->host, sctx->ip))
7984 {
7985 return FALSE;
7986 }
7987 if (sctx->priv_role[0] && strcmp(item->user, sctx->priv_role) == 0 &&
7988 strcmp(item->db, db) == 0 &&
7989 (!item->host.hostname || !item->host.hostname[0]))
7990 {
7991 return FALSE; /* Found current role match */
7992 }
7993 }
7994
7995 return TRUE;
7996}
7997
7998
7999/*
8000 Check if a user has the right to access a database
8001 Access is accepted if he has a grant for any table/routine in the database
8002 Return 1 if access is denied
8003*/
8004
8005bool check_grant_db(THD *thd, const char *db)
8006{
8007 Security_context *sctx= thd->security_ctx;
8008 char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end;
8009 char helping2 [SAFE_NAME_LEN + USERNAME_LENGTH+2], *tmp_db;
8010 uint len, UNINIT_VAR(len2);
8011 bool error= TRUE;
8012
8013 tmp_db= strmov(helping, sctx->priv_user) + 1;
8014 end= strnmov(tmp_db, db, helping + sizeof(helping) - tmp_db);
8015
8016 if (end >= helping + sizeof(helping)) // db name was truncated
8017 return 1; // no privileges for an invalid db name
8018
8019 if (lower_case_table_names)
8020 {
8021 end = tmp_db + my_casedn_str(files_charset_info, tmp_db);
8022 db=tmp_db;
8023 }
8024
8025 len= (uint) (end - helping) + 1;
8026
8027 /*
8028 If a role is set, we need to check for privileges here as well.
8029 */
8030 if (sctx->priv_role[0])
8031 {
8032 end= strmov(helping2, sctx->priv_role) + 1;
8033 end= strnmov(end, db, helping2 + sizeof(helping2) - end);
8034 len2= (uint) (end - helping2) + 1;
8035 }
8036
8037
8038 mysql_rwlock_rdlock(&LOCK_grant);
8039
8040 for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
8041 {
8042 GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
8043 idx);
8044 if (len < grant_table->key_length &&
8045 !memcmp(grant_table->hash_key, helping, len) &&
8046 compare_hostname(&grant_table->host, sctx->host, sctx->ip))
8047 {
8048 error= FALSE; /* Found match. */
8049 break;
8050 }
8051 if (sctx->priv_role[0] &&
8052 len2 < grant_table->key_length &&
8053 !memcmp(grant_table->hash_key, helping2, len2) &&
8054 (!grant_table->host.hostname || !grant_table->host.hostname[0]))
8055 {
8056 error= FALSE; /* Found role match */
8057 break;
8058 }
8059 }
8060
8061 if (error)
8062 error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
8063 check_grant_db_routine(thd, db, &func_priv_hash) &&
8064 check_grant_db_routine(thd, db, &package_spec_priv_hash) &&
8065 check_grant_db_routine(thd, db, &package_body_priv_hash);
8066
8067 mysql_rwlock_unlock(&LOCK_grant);
8068
8069 return error;
8070}
8071
8072
8073/****************************************************************************
8074 Check routine level grants
8075
8076 SYNPOSIS
8077 bool check_grant_routine()
8078 thd Thread handler
8079 want_access Bits of privileges user needs to have
8080 procs List of routines to check. The user should have 'want_access'
8081 sph SP handler
8082 no_errors If 0 then we write an error. The error is sent directly to
8083 the client
8084
8085 RETURN
8086 0 ok
8087 1 Error: User did not have the requested privielges
8088****************************************************************************/
8089
8090bool check_grant_routine(THD *thd, ulong want_access,
8091 TABLE_LIST *procs, const Sp_handler *sph,
8092 bool no_errors)
8093{
8094 TABLE_LIST *table;
8095 Security_context *sctx= thd->security_ctx;
8096 char *user= sctx->priv_user;
8097 char *host= sctx->priv_host;
8098 char *role= sctx->priv_role;
8099 DBUG_ENTER("check_grant_routine");
8100
8101 want_access&= ~sctx->master_access;
8102 if (!want_access)
8103 DBUG_RETURN(0); // ok
8104
8105 mysql_rwlock_rdlock(&LOCK_grant);
8106 for (table= procs; table; table= table->next_global)
8107 {
8108 GRANT_NAME *grant_proc;
8109 if ((grant_proc= routine_hash_search(host, sctx->ip, table->db.str, user,
8110 table->table_name.str, sph, 0)))
8111 table->grant.privilege|= grant_proc->privs;
8112 if (role[0]) /* current role set check */
8113 {
8114 if ((grant_proc= routine_hash_search("", NULL, table->db.str, role,
8115 table->table_name.str, sph, 0)))
8116 table->grant.privilege|= grant_proc->privs;
8117 }
8118
8119 if (want_access & ~table->grant.privilege)
8120 {
8121 want_access &= ~table->grant.privilege;
8122 goto err;
8123 }
8124 }
8125 mysql_rwlock_unlock(&LOCK_grant);
8126 DBUG_RETURN(0);
8127err:
8128 mysql_rwlock_unlock(&LOCK_grant);
8129 if (!no_errors)
8130 {
8131 char buff[1024];
8132 const char *command="";
8133 if (table)
8134 strxmov(buff, table->db.str, ".", table->table_name.str, NullS);
8135 if (want_access & EXECUTE_ACL)
8136 command= "execute";
8137 else if (want_access & ALTER_PROC_ACL)
8138 command= "alter routine";
8139 else if (want_access & GRANT_ACL)
8140 command= "grant";
8141 my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
8142 command, user, host, table ? buff : "unknown");
8143 }
8144 DBUG_RETURN(1);
8145}
8146
8147
8148/*
8149 Check if routine has any of the
8150 routine level grants
8151
8152 SYNPOSIS
8153 bool check_routine_level_acl()
8154 thd Thread handler
8155 db Database name
8156 name Routine name
8157
8158 RETURN
8159 0 Ok
8160 1 error
8161*/
8162
8163bool check_routine_level_acl(THD *thd, const char *db, const char *name,
8164 const Sp_handler *sph)
8165{
8166 bool no_routine_acl= 1;
8167 GRANT_NAME *grant_proc;
8168 Security_context *sctx= thd->security_ctx;
8169 mysql_rwlock_rdlock(&LOCK_grant);
8170 if ((grant_proc= routine_hash_search(sctx->priv_host,
8171 sctx->ip, db,
8172 sctx->priv_user,
8173 name, sph, 0)))
8174 no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
8175
8176 if (no_routine_acl && sctx->priv_role[0]) /* current set role check */
8177 {
8178 if ((grant_proc= routine_hash_search("",
8179 NULL, db,
8180 sctx->priv_role,
8181 name, sph, 0)))
8182 no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
8183 }
8184 mysql_rwlock_unlock(&LOCK_grant);
8185 return no_routine_acl;
8186}
8187
8188
8189/*****************************************************************************
8190 Functions to retrieve the grant for a table/column (for SHOW functions)
8191*****************************************************************************/
8192
8193ulong get_table_grant(THD *thd, TABLE_LIST *table)
8194{
8195 ulong privilege;
8196 Security_context *sctx= thd->security_ctx;
8197 const char *db = table->db.str ? table->db.str : thd->db.str;
8198 GRANT_TABLE *grant_table;
8199 GRANT_TABLE *grant_table_role= NULL;
8200
8201 mysql_rwlock_rdlock(&LOCK_grant);
8202#ifdef EMBEDDED_LIBRARY
8203 grant_table= NULL;
8204 grant_table_role= NULL;
8205#else
8206 grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
8207 table->table_name.str, 0);
8208 if (sctx->priv_role[0])
8209 grant_table_role= table_hash_search("", "", db, sctx->priv_role,
8210 table->table_name.str, 0);
8211#endif
8212 table->grant.grant_table_user= grant_table; // Remember for column test
8213 table->grant.grant_table_role= grant_table_role;
8214 table->grant.version=grant_version;
8215 if (grant_table)
8216 table->grant.privilege|= grant_table->privs;
8217 if (grant_table_role)
8218 table->grant.privilege|= grant_table_role->privs;
8219 privilege= table->grant.privilege;
8220 mysql_rwlock_unlock(&LOCK_grant);
8221 return privilege;
8222}
8223
8224
8225/*
8226 Determine the access priviliges for a field.
8227
8228 SYNOPSIS
8229 get_column_grant()
8230 thd thread handler
8231 grant grants table descriptor
8232 db_name name of database that the field belongs to
8233 table_name name of table that the field belongs to
8234 field_name name of field
8235
8236 DESCRIPTION
8237 The procedure may also modify: grant->grant_table and grant->version.
8238
8239 RETURN
8240 The access priviliges for the field db_name.table_name.field_name
8241*/
8242
8243ulong get_column_grant(THD *thd, GRANT_INFO *grant,
8244 const char *db_name, const char *table_name,
8245 const char *field_name)
8246{
8247 GRANT_TABLE *grant_table;
8248 GRANT_TABLE *grant_table_role;
8249 GRANT_COLUMN *grant_column;
8250 ulong priv= 0;
8251
8252 mysql_rwlock_rdlock(&LOCK_grant);
8253 /* reload table if someone has modified any grants */
8254 if (grant->version != grant_version)
8255 {
8256 Security_context *sctx= thd->security_ctx;
8257 grant->grant_table_user=
8258 table_hash_search(sctx->host, sctx->ip,
8259 db_name, sctx->priv_user,
8260 table_name, 0); /* purecov: inspected */
8261 grant->grant_table_role=
8262 sctx->priv_role[0] ? table_hash_search("", "", db_name,
8263 sctx->priv_role,
8264 table_name, TRUE) : NULL;
8265 grant->version= grant_version; /* purecov: inspected */
8266 }
8267
8268 grant_table= grant->grant_table_user;
8269 grant_table_role= grant->grant_table_role;
8270
8271 if (!grant_table && !grant_table_role)
8272 priv= grant->privilege;
8273 else
8274 {
8275 if (grant_table)
8276 {
8277 grant_column= column_hash_search(grant_table, field_name,
8278 (uint) strlen(field_name));
8279 if (!grant_column)
8280 priv= (grant->privilege | grant_table->privs);
8281 else
8282 priv= (grant->privilege | grant_table->privs | grant_column->rights);
8283 }
8284
8285 if (grant_table_role)
8286 {
8287 grant_column= column_hash_search(grant_table_role, field_name,
8288 (uint) strlen(field_name));
8289 if (!grant_column)
8290 priv|= (grant->privilege | grant_table_role->privs);
8291 else
8292 priv|= (grant->privilege | grant_table_role->privs |
8293 grant_column->rights);
8294 }
8295 }
8296 mysql_rwlock_unlock(&LOCK_grant);
8297 return priv;
8298}
8299
8300
8301/* Help function for mysql_show_grants */
8302
8303static void add_user_option(String *grant, long value, const char *name,
8304 bool is_signed)
8305{
8306 if (value)
8307 {
8308 char buff[22], *p; // just as in int2str
8309 grant->append(' ');
8310 grant->append(name, strlen(name));
8311 grant->append(' ');
8312 p=int10_to_str(value, buff, is_signed ? -10 : 10);
8313 grant->append(buff,p-buff);
8314 }
8315}
8316
8317
8318static void add_user_option(String *grant, double value, const char *name)
8319{
8320 if (value != 0.0 )
8321 {
8322 char buff[FLOATING_POINT_BUFFER];
8323 size_t len;
8324 grant->append(' ');
8325 grant->append(name, strlen(name));
8326 grant->append(' ');
8327 len= my_fcvt(value, 6, buff, NULL);
8328 grant->append(buff, len);
8329 }
8330}
8331
8332static void add_user_parameters(String *result, ACL_USER* acl_user,
8333 bool with_grant)
8334{
8335 result->append(STRING_WITH_LEN("@'"));
8336 result->append(acl_user->host.hostname, acl_user->hostname_length,
8337 system_charset_info);
8338 result->append('\'');
8339
8340 if (acl_user->plugin.str == native_password_plugin_name.str ||
8341 acl_user->plugin.str == old_password_plugin_name.str)
8342 {
8343 if (acl_user->auth_string.length)
8344 {
8345 DBUG_ASSERT(acl_user->salt_len);
8346 result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
8347 result->append(&acl_user->auth_string);
8348 result->append('\'');
8349 }
8350 }
8351 else
8352 {
8353 result->append(STRING_WITH_LEN(" IDENTIFIED VIA "));
8354 result->append(&acl_user->plugin);
8355 if (acl_user->auth_string.length)
8356 {
8357 result->append(STRING_WITH_LEN(" USING '"));
8358 result->append(&acl_user->auth_string);
8359 result->append('\'');
8360 }
8361 }
8362 /* "show grants" SSL related stuff */
8363 if (acl_user->ssl_type == SSL_TYPE_ANY)
8364 result->append(STRING_WITH_LEN(" REQUIRE SSL"));
8365 else if (acl_user->ssl_type == SSL_TYPE_X509)
8366 result->append(STRING_WITH_LEN(" REQUIRE X509"));
8367 else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
8368 {
8369 int ssl_options = 0;
8370 result->append(STRING_WITH_LEN(" REQUIRE "));
8371 if (acl_user->x509_issuer)
8372 {
8373 ssl_options++;
8374 result->append(STRING_WITH_LEN("ISSUER \'"));
8375 result->append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
8376 result->append('\'');
8377 }
8378 if (acl_user->x509_subject)
8379 {
8380 if (ssl_options++)
8381 result->append(' ');
8382 result->append(STRING_WITH_LEN("SUBJECT \'"));
8383 result->append(acl_user->x509_subject,strlen(acl_user->x509_subject),
8384 system_charset_info);
8385 result->append('\'');
8386 }
8387 if (acl_user->ssl_cipher)
8388 {
8389 if (ssl_options++)
8390 result->append(' ');
8391 result->append(STRING_WITH_LEN("CIPHER '"));
8392 result->append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
8393 system_charset_info);
8394 result->append('\'');
8395 }
8396 }
8397 if (with_grant ||
8398 (acl_user->user_resource.questions ||
8399 acl_user->user_resource.updates ||
8400 acl_user->user_resource.conn_per_hour ||
8401 acl_user->user_resource.user_conn ||
8402 acl_user->user_resource.max_statement_time != 0.0))
8403 {
8404 result->append(STRING_WITH_LEN(" WITH"));
8405 if (with_grant)
8406 result->append(STRING_WITH_LEN(" GRANT OPTION"));
8407 add_user_option(result, acl_user->user_resource.questions,
8408 "MAX_QUERIES_PER_HOUR", false);
8409 add_user_option(result, acl_user->user_resource.updates,
8410 "MAX_UPDATES_PER_HOUR", false);
8411 add_user_option(result, acl_user->user_resource.conn_per_hour,
8412 "MAX_CONNECTIONS_PER_HOUR", false);
8413 add_user_option(result, acl_user->user_resource.user_conn,
8414 "MAX_USER_CONNECTIONS", true);
8415 add_user_option(result, acl_user->user_resource.max_statement_time,
8416 "MAX_STATEMENT_TIME");
8417 }
8418}
8419
8420static const char *command_array[]=
8421{
8422 "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
8423 "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
8424 "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
8425 "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
8426 "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
8427 "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE",
8428 "DELETE VERSIONING ROWS"
8429};
8430
8431static uint command_lengths[]=
8432{
8433 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
8434 14, 13, 11, 5, 7, 17, 22,
8435};
8436
8437
8438static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
8439{
8440 char buff[1024];
8441
8442 if (show_role_grants(thd, role->user.str, "", role, buff, sizeof(buff)))
8443 return TRUE;
8444
8445 if (show_global_privileges(thd, role, TRUE, buff, sizeof(buff)))
8446 return TRUE;
8447
8448 if (show_database_privileges(thd, role->user.str, "", buff, sizeof(buff)))
8449 return TRUE;
8450
8451 if (show_table_and_column_privileges(thd, role->user.str, "", buff, sizeof(buff)))
8452 return TRUE;
8453
8454 if (show_routine_grants(thd, role->user.str, "", &sp_handler_procedure,
8455 buff, sizeof(buff)))
8456 return TRUE;
8457
8458 if (show_routine_grants(thd, role->user.str, "", &sp_handler_function,
8459 buff, sizeof(buff)))
8460 return TRUE;
8461
8462 if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_spec,
8463 buff, sizeof(buff)))
8464 return TRUE;
8465
8466 if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_body,
8467 buff, sizeof(buff)))
8468 return TRUE;
8469
8470 return FALSE;
8471
8472}
8473
8474/** checks privileges for SHOW GRANTS and SHOW CREATE USER
8475
8476 @note that in case of SHOW CREATE USER the parser guarantees
8477 that a role can never happen here, so *rolename will never
8478 be assigned to
8479*/
8480static bool check_show_access(THD *thd, LEX_USER *lex_user,
8481 const char **username,
8482 const char **hostname, const char **rolename)
8483{
8484 DBUG_ENTER("check_show_access");
8485
8486 if (lex_user->user.str == current_user.str)
8487 {
8488 *username= thd->security_ctx->priv_user;
8489 *hostname= thd->security_ctx->priv_host;
8490 }
8491 else if (lex_user->user.str == current_role.str)
8492 {
8493 *rolename= thd->security_ctx->priv_role;
8494 }
8495 else if (lex_user->user.str == current_user_and_current_role.str)
8496 {
8497 *username= thd->security_ctx->priv_user;
8498 *hostname= thd->security_ctx->priv_host;
8499 *rolename= thd->security_ctx->priv_role;
8500 }
8501 else
8502 {
8503 Security_context *sctx= thd->security_ctx;
8504 bool do_check_access;
8505
8506 lex_user= get_current_user(thd, lex_user);
8507 if (!lex_user)
8508 DBUG_RETURN(TRUE);
8509
8510 if (lex_user->is_role())
8511 {
8512 *rolename= lex_user->user.str;
8513 do_check_access= strcmp(*rolename, sctx->priv_role);
8514 }
8515 else
8516 {
8517 *username= lex_user->user.str;
8518 *hostname= lex_user->host.str;
8519 do_check_access= strcmp(*username, sctx->priv_user) ||
8520 strcmp(*hostname, sctx->priv_host);
8521 }
8522
8523 if (do_check_access && check_access(thd, SELECT_ACL, "mysql", 0, 0, 1, 0))
8524 DBUG_RETURN(TRUE);
8525 }
8526 DBUG_RETURN(FALSE);
8527}
8528
8529bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
8530{
8531 const char *username= NULL, *hostname= NULL;
8532 char buff[1024]; //Show create user should not take more than 1024 bytes.
8533 Protocol *protocol= thd->protocol;
8534 bool error= false;
8535 ACL_USER *acl_user;
8536 uint head_length;
8537 DBUG_ENTER("mysql_show_create_user");
8538
8539 if (check_show_access(thd, lex_user, &username, &hostname, NULL))
8540 DBUG_RETURN(TRUE);
8541
8542 List<Item> field_list;
8543 head_length= (uint) (strxmov(buff, "CREATE USER for ", username, "@",
8544 hostname, NullS) - buff);
8545 Item_string *field = new (thd->mem_root) Item_string_ascii(thd, "", 0);
8546 if (!field)
8547 DBUG_RETURN(true); // Error given my my_alloc()
8548
8549 field->name.str= buff;
8550 field->name.length= head_length;
8551 field->max_length= sizeof(buff);
8552 field_list.push_back(field, thd->mem_root);
8553 if (protocol->send_result_set_metadata(&field_list,
8554 Protocol::SEND_NUM_ROWS |
8555 Protocol::SEND_EOF))
8556 DBUG_RETURN(true);
8557
8558 String result(buff, sizeof(buff), system_charset_info);
8559 result.length(0);
8560 mysql_rwlock_rdlock(&LOCK_grant);
8561 mysql_mutex_lock(&acl_cache->lock);
8562
8563 acl_user= find_user_exact(hostname, username);
8564
8565 // User not found in the internal data structures.
8566 if (!acl_user)
8567 {
8568 my_error(ER_PASSWORD_NO_MATCH, MYF(0));
8569 error= true;
8570 goto end;
8571 }
8572
8573 result.append("CREATE USER '");
8574 result.append(username);
8575 result.append('\'');
8576
8577 add_user_parameters(&result, acl_user, false);
8578
8579 protocol->prepare_for_resend();
8580 protocol->store(result.ptr(), result.length(), result.charset());
8581 if (protocol->write())
8582 {
8583 error= true;
8584 }
8585 my_eof(thd);
8586
8587end:
8588 mysql_rwlock_unlock(&LOCK_grant);
8589 mysql_mutex_unlock(&acl_cache->lock);
8590
8591 DBUG_RETURN(error);
8592}
8593
8594
8595static int show_grants_callback(ACL_USER_BASE *role, void *data)
8596{
8597 THD *thd= (THD *)data;
8598 DBUG_ASSERT(role->flags & IS_ROLE);
8599 if (print_grants_for_role(thd, (ACL_ROLE *)role))
8600 return -1;
8601 return 0;
8602}
8603
8604void mysql_show_grants_get_fields(THD *thd, List<Item> *fields,
8605 const char *name, size_t length)
8606{
8607 Item_string *field=new (thd->mem_root) Item_string_ascii(thd, "", 0);
8608 /* Set name explicit to avoid character set conversions */
8609 field->name.str= name;
8610 field->name.length= length;
8611 field->max_length=1024;
8612 fields->push_back(field, thd->mem_root);
8613}
8614
8615
8616/*
8617 SHOW GRANTS; Send grants for a user to the client
8618
8619 IMPLEMENTATION
8620 Send to client grant-like strings depicting user@host privileges
8621*/
8622
8623bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
8624{
8625 int error = -1;
8626 ACL_USER *UNINIT_VAR(acl_user);
8627 ACL_ROLE *acl_role= NULL;
8628 char buff[1024];
8629 Protocol *protocol= thd->protocol;
8630 const char *username= NULL, *hostname= NULL, *rolename= NULL, *end;
8631 DBUG_ENTER("mysql_show_grants");
8632
8633 if (!initialized)
8634 {
8635 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
8636 DBUG_RETURN(TRUE);
8637 }
8638
8639 if (check_show_access(thd, lex_user, &username, &hostname, &rolename))
8640 DBUG_RETURN(TRUE);
8641 DBUG_ASSERT(rolename || username);
8642
8643 List<Item> field_list;
8644 if (!username)
8645 end= strxmov(buff,"Grants for ",rolename, NullS);
8646 else
8647 end= strxmov(buff,"Grants for ",username,"@",hostname, NullS);
8648
8649 mysql_show_grants_get_fields(thd, &field_list, buff, (uint) (end-buff));
8650
8651 if (protocol->send_result_set_metadata(&field_list,
8652 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
8653 DBUG_RETURN(TRUE);
8654
8655 mysql_rwlock_rdlock(&LOCK_grant);
8656 mysql_mutex_lock(&acl_cache->lock);
8657
8658 if (username)
8659 {
8660 acl_user= find_user_exact(hostname, username);
8661 if (!acl_user)
8662 {
8663 mysql_mutex_unlock(&acl_cache->lock);
8664 mysql_rwlock_unlock(&LOCK_grant);
8665
8666 my_error(ER_NONEXISTING_GRANT, MYF(0),
8667 username, hostname);
8668 DBUG_RETURN(TRUE);
8669 }
8670
8671 /* Show granted roles to acl_user */
8672 if (show_role_grants(thd, username, hostname, acl_user, buff, sizeof(buff)))
8673 goto end;
8674
8675 /* Add first global access grants */
8676 if (show_global_privileges(thd, acl_user, FALSE, buff, sizeof(buff)))
8677 goto end;
8678
8679 /* Add database access */
8680 if (show_database_privileges(thd, username, hostname, buff, sizeof(buff)))
8681 goto end;
8682
8683 /* Add table & column access */
8684 if (show_table_and_column_privileges(thd, username, hostname, buff, sizeof(buff)))
8685 goto end;
8686
8687 if (show_routine_grants(thd, username, hostname, &sp_handler_procedure,
8688 buff, sizeof(buff)))
8689 goto end;
8690
8691 if (show_routine_grants(thd, username, hostname, &sp_handler_function,
8692 buff, sizeof(buff)))
8693 goto end;
8694
8695 if (show_routine_grants(thd, username, hostname, &sp_handler_package_spec,
8696 buff, sizeof(buff)))
8697 goto end;
8698
8699 if (show_routine_grants(thd, username, hostname, &sp_handler_package_body,
8700 buff, sizeof(buff)))
8701 goto end;
8702
8703 if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff)))
8704 goto end;
8705 }
8706
8707 if (rolename)
8708 {
8709 acl_role= find_acl_role(rolename);
8710 if (acl_role)
8711 {
8712 /* get a list of all inherited roles */
8713 traverse_role_graph_down(acl_role, thd, show_grants_callback, NULL);
8714 }
8715 else
8716 {
8717 if (lex_user->user.str == current_role.str)
8718 {
8719 mysql_mutex_unlock(&acl_cache->lock);
8720 mysql_rwlock_unlock(&LOCK_grant);
8721 my_error(ER_NONEXISTING_GRANT, MYF(0),
8722 thd->security_ctx->priv_user,
8723 thd->security_ctx->priv_host);
8724 DBUG_RETURN(TRUE);
8725 }
8726 }
8727 }
8728
8729 error= 0;
8730end:
8731 mysql_mutex_unlock(&acl_cache->lock);
8732 mysql_rwlock_unlock(&LOCK_grant);
8733
8734 my_eof(thd);
8735 DBUG_RETURN(error);
8736}
8737
8738static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u,
8739 const LEX_CSTRING *h,
8740 const LEX_CSTRING *r)
8741{
8742 char buf[1024];
8743 String pair_key(buf, sizeof(buf), &my_charset_bin);
8744
8745 size_t key_length= u->length + h->length + r->length + 3;
8746 pair_key.alloc(key_length);
8747
8748 strmov(strmov(strmov(const_cast<char*>(pair_key.ptr()),
8749 safe_str(u->str)) + 1, h->str) + 1, r->str);
8750
8751 return (ROLE_GRANT_PAIR *)
8752 my_hash_search(&acl_roles_mappings, (uchar*)pair_key.ptr(), key_length);
8753}
8754
8755static bool show_role_grants(THD *thd, const char *username,
8756 const char *hostname, ACL_USER_BASE *acl_entry,
8757 char *buff, size_t buffsize)
8758{
8759 uint counter;
8760 Protocol *protocol= thd->protocol;
8761 LEX_CSTRING host= {const_cast<char*>(hostname), strlen(hostname)};
8762
8763 String grant(buff,sizeof(buff),system_charset_info);
8764 for (counter= 0; counter < acl_entry->role_grants.elements; counter++)
8765 {
8766 grant.length(0);
8767 grant.append(STRING_WITH_LEN("GRANT "));
8768 ACL_ROLE *acl_role= *(dynamic_element(&acl_entry->role_grants, counter,
8769 ACL_ROLE**));
8770 grant.append(acl_role->user.str, acl_role->user.length,
8771 system_charset_info);
8772 grant.append(STRING_WITH_LEN(" TO '"));
8773 grant.append(acl_entry->user.str, acl_entry->user.length,
8774 system_charset_info);
8775 if (!(acl_entry->flags & IS_ROLE))
8776 {
8777 grant.append(STRING_WITH_LEN("'@'"));
8778 grant.append(&host);
8779 }
8780 grant.append('\'');
8781
8782 ROLE_GRANT_PAIR *pair=
8783 find_role_grant_pair(&acl_entry->user, &host, &acl_role->user);
8784 DBUG_ASSERT(pair);
8785
8786 if (pair->with_admin)
8787 grant.append(STRING_WITH_LEN(" WITH ADMIN OPTION"));
8788
8789 protocol->prepare_for_resend();
8790 protocol->store(grant.ptr(),grant.length(),grant.charset());
8791 if (protocol->write())
8792 {
8793 return TRUE;
8794 }
8795 }
8796 return FALSE;
8797}
8798
8799static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
8800 bool handle_as_role,
8801 char *buff, size_t buffsize)
8802{
8803 uint counter;
8804 ulong want_access;
8805 Protocol *protocol= thd->protocol;
8806
8807 String global(buff,sizeof(buff),system_charset_info);
8808 global.length(0);
8809 global.append(STRING_WITH_LEN("GRANT "));
8810
8811 if (handle_as_role)
8812 want_access= ((ACL_ROLE *)acl_entry)->initial_role_access;
8813 else
8814 want_access= acl_entry->access;
8815 if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
8816 global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
8817 else if (!(want_access & ~GRANT_ACL))
8818 global.append(STRING_WITH_LEN("USAGE"));
8819 else
8820 {
8821 bool found=0;
8822 ulong j,test_access= want_access & ~GRANT_ACL;
8823 for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
8824 {
8825 if (test_access & j)
8826 {
8827 if (found)
8828 global.append(STRING_WITH_LEN(", "));
8829 found=1;
8830 global.append(command_array[counter],command_lengths[counter]);
8831 }
8832 }
8833 }
8834 global.append (STRING_WITH_LEN(" ON *.* TO '"));
8835 global.append(acl_entry->user.str, acl_entry->user.length,
8836 system_charset_info);
8837 global.append('\'');
8838
8839 if (!handle_as_role)
8840 add_user_parameters(&global, (ACL_USER *)acl_entry, (want_access & GRANT_ACL));
8841
8842 protocol->prepare_for_resend();
8843 protocol->store(global.ptr(),global.length(),global.charset());
8844 if (protocol->write())
8845 return TRUE;
8846
8847 return FALSE;
8848
8849}
8850
8851static bool show_database_privileges(THD *thd, const char *username,
8852 const char *hostname,
8853 char *buff, size_t buffsize)
8854{
8855 ACL_DB *acl_db;
8856 ulong want_access;
8857 uint counter;
8858 Protocol *protocol= thd->protocol;
8859
8860 for (counter=0 ; counter < acl_dbs.elements ; counter++)
8861 {
8862 const char *user, *host;
8863
8864 acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
8865 user= safe_str(acl_db->user);
8866 host=acl_db->host.hostname;
8867
8868 /*
8869 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
8870 but make it case-insensitive because that's the way they are
8871 actually applied, and showing fewer privileges than are applied
8872 would be wrong from a security point of view.
8873 */
8874
8875 if (!strcmp(username, user) &&
8876 !my_strcasecmp(system_charset_info, hostname, host))
8877 {
8878 /*
8879 do not print inherited access bits for roles,
8880 the role bits present in the table are what matters
8881 */
8882 if (*hostname) // User
8883 want_access=acl_db->access;
8884 else // Role
8885 want_access=acl_db->initial_access;
8886 if (want_access)
8887 {
8888 String db(buff,sizeof(buff),system_charset_info);
8889 db.length(0);
8890 db.append(STRING_WITH_LEN("GRANT "));
8891
8892 if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
8893 db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
8894 else if (!(want_access & ~GRANT_ACL))
8895 db.append(STRING_WITH_LEN("USAGE"));
8896 else
8897 {
8898 int found=0, cnt;
8899 ulong j,test_access= want_access & ~GRANT_ACL;
8900 for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
8901 {
8902 if (test_access & j)
8903 {
8904 if (found)
8905 db.append(STRING_WITH_LEN(", "));
8906 found = 1;
8907 db.append(command_array[cnt],command_lengths[cnt]);
8908 }
8909 }
8910 }
8911 db.append (STRING_WITH_LEN(" ON "));
8912 append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
8913 db.append (STRING_WITH_LEN(".* TO '"));
8914 db.append(username, strlen(username),
8915 system_charset_info);
8916 if (*hostname)
8917 {
8918 db.append (STRING_WITH_LEN("'@'"));
8919 // host and lex_user->host are equal except for case
8920 db.append(host, strlen(host), system_charset_info);
8921 }
8922 db.append ('\'');
8923 if (want_access & GRANT_ACL)
8924 db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
8925 protocol->prepare_for_resend();
8926 protocol->store(db.ptr(),db.length(),db.charset());
8927 if (protocol->write())
8928 {
8929 return TRUE;
8930 }
8931 }
8932 }
8933 }
8934 return FALSE;
8935
8936}
8937
8938static bool show_table_and_column_privileges(THD *thd, const char *username,
8939 const char *hostname,
8940 char *buff, size_t buffsize)
8941{
8942 uint counter, index;
8943 Protocol *protocol= thd->protocol;
8944
8945 for (index=0 ; index < column_priv_hash.records ; index++)
8946 {
8947 const char *user, *host;
8948 GRANT_TABLE *grant_table= (GRANT_TABLE*)
8949 my_hash_element(&column_priv_hash, index);
8950
8951 user= safe_str(grant_table->user);
8952 host= grant_table->host.hostname;
8953
8954 /*
8955 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
8956 but make it case-insensitive because that's the way they are
8957 actually applied, and showing fewer privileges than are applied
8958 would be wrong from a security point of view.
8959 */
8960
8961 if (!strcmp(username,user) &&
8962 !my_strcasecmp(system_charset_info, hostname, host))
8963 {
8964 ulong table_access;
8965 ulong cols_access;
8966 if (*hostname) // User
8967 {
8968 table_access= grant_table->privs;
8969 cols_access= grant_table->cols;
8970 }
8971 else // Role
8972 {
8973 table_access= grant_table->init_privs;
8974 cols_access= grant_table->init_cols;
8975 }
8976
8977 if ((table_access | cols_access) != 0)
8978 {
8979 String global(buff, sizeof(buff), system_charset_info);
8980 ulong test_access= (table_access | cols_access) & ~GRANT_ACL;
8981
8982 global.length(0);
8983 global.append(STRING_WITH_LEN("GRANT "));
8984
8985 if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
8986 global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
8987 else if (!test_access)
8988 global.append(STRING_WITH_LEN("USAGE"));
8989 else
8990 {
8991 /* Add specific column access */
8992 int found= 0;
8993 ulong j;
8994
8995 for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
8996 {
8997 if (test_access & j)
8998 {
8999 if (found)
9000 global.append(STRING_WITH_LEN(", "));
9001 found= 1;
9002 global.append(command_array[counter],command_lengths[counter]);
9003
9004 if (grant_table->cols)
9005 {
9006 uint found_col= 0;
9007 HASH *hash_columns;
9008 hash_columns= &grant_table->hash_columns;
9009
9010 for (uint col_index=0 ;
9011 col_index < hash_columns->records ;
9012 col_index++)
9013 {
9014 GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
9015 my_hash_element(hash_columns,col_index);
9016 if (j & (*hostname ? grant_column->rights // User
9017 : grant_column->init_rights)) // Role
9018 {
9019 if (!found_col)
9020 {
9021 found_col= 1;
9022 /*
9023 If we have a duplicated table level privilege, we
9024 must write the access privilege name again.
9025 */
9026 if (table_access & j)
9027 {
9028 global.append(STRING_WITH_LEN(", "));
9029 global.append(command_array[counter],
9030 command_lengths[counter]);
9031 }
9032 global.append(STRING_WITH_LEN(" ("));
9033 }
9034 else
9035 global.append(STRING_WITH_LEN(", "));
9036 global.append(grant_column->column,
9037 grant_column->key_length,
9038 system_charset_info);
9039 }
9040 }
9041 if (found_col)
9042 global.append(')');
9043 }
9044 }
9045 }
9046 }
9047 global.append(STRING_WITH_LEN(" ON "));
9048 append_identifier(thd, &global, grant_table->db,
9049 strlen(grant_table->db));
9050 global.append('.');
9051 append_identifier(thd, &global, grant_table->tname,
9052 strlen(grant_table->tname));
9053 global.append(STRING_WITH_LEN(" TO '"));
9054 global.append(username, strlen(username),
9055 system_charset_info);
9056 if (*hostname)
9057 {
9058 global.append(STRING_WITH_LEN("'@'"));
9059 // host and lex_user->host are equal except for case
9060 global.append(host, strlen(host), system_charset_info);
9061 }
9062 global.append('\'');
9063 if (table_access & GRANT_ACL)
9064 global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9065 protocol->prepare_for_resend();
9066 protocol->store(global.ptr(),global.length(),global.charset());
9067 if (protocol->write())
9068 {
9069 return TRUE;
9070 }
9071 }
9072 }
9073 }
9074 return FALSE;
9075
9076}
9077
9078static int show_routine_grants(THD* thd,
9079 const char *username, const char *hostname,
9080 const Sp_handler *sph,
9081 char *buff, int buffsize)
9082{
9083 uint counter, index;
9084 int error= 0;
9085 Protocol *protocol= thd->protocol;
9086 HASH *hash= sph->get_priv_hash();
9087 /* Add routine access */
9088 for (index=0 ; index < hash->records ; index++)
9089 {
9090 const char *user, *host;
9091 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
9092
9093 user= safe_str(grant_proc->user);
9094 host= grant_proc->host.hostname;
9095
9096 /*
9097 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
9098 but make it case-insensitive because that's the way they are
9099 actually applied, and showing fewer privileges than are applied
9100 would be wrong from a security point of view.
9101 */
9102
9103 if (!strcmp(username, user) &&
9104 !my_strcasecmp(system_charset_info, hostname, host))
9105 {
9106 ulong proc_access;
9107 if (*hostname) // User
9108 proc_access= grant_proc->privs;
9109 else // Role
9110 proc_access= grant_proc->init_privs;
9111
9112 if (proc_access != 0)
9113 {
9114 String global(buff, buffsize, system_charset_info);
9115 ulong test_access= proc_access & ~GRANT_ACL;
9116
9117 global.length(0);
9118 global.append(STRING_WITH_LEN("GRANT "));
9119
9120 if (!test_access)
9121 global.append(STRING_WITH_LEN("USAGE"));
9122 else
9123 {
9124 /* Add specific procedure access */
9125 int found= 0;
9126 ulong j;
9127
9128 for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
9129 {
9130 if (test_access & j)
9131 {
9132 if (found)
9133 global.append(STRING_WITH_LEN(", "));
9134 found= 1;
9135 global.append(command_array[counter],command_lengths[counter]);
9136 }
9137 }
9138 }
9139 global.append(STRING_WITH_LEN(" ON "));
9140 LEX_CSTRING tmp= sph->type_lex_cstring();
9141 global.append(&tmp);
9142 global.append(' ');
9143 append_identifier(thd, &global, grant_proc->db,
9144 strlen(grant_proc->db));
9145 global.append('.');
9146 append_identifier(thd, &global, grant_proc->tname,
9147 strlen(grant_proc->tname));
9148 global.append(STRING_WITH_LEN(" TO '"));
9149 global.append(username, strlen(username),
9150 system_charset_info);
9151 if (*hostname)
9152 {
9153 global.append(STRING_WITH_LEN("'@'"));
9154 // host and lex_user->host are equal except for case
9155 global.append(host, strlen(host), system_charset_info);
9156 }
9157 global.append('\'');
9158 if (proc_access & GRANT_ACL)
9159 global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9160 protocol->prepare_for_resend();
9161 protocol->store(global.ptr(),global.length(),global.charset());
9162 if (protocol->write())
9163 {
9164 error= -1;
9165 break;
9166 }
9167 }
9168 }
9169 }
9170 return error;
9171}
9172
9173
9174/*
9175 Make a clear-text version of the requested privilege.
9176*/
9177
9178void get_privilege_desc(char *to, uint max_length, ulong access)
9179{
9180 uint pos;
9181 char *start=to;
9182 DBUG_ASSERT(max_length >= 30); // For end ', ' removal
9183
9184 if (access)
9185 {
9186 max_length--; // Reserve place for end-zero
9187 for (pos=0 ; access ; pos++, access>>=1)
9188 {
9189 if ((access & 1) &&
9190 command_lengths[pos] + (uint) (to-start) < max_length)
9191 {
9192 to= strmov(to, command_array[pos]);
9193 *to++= ',';
9194 *to++= ' ';
9195 }
9196 }
9197 to--; // Remove end ' '
9198 to--; // Remove end ','
9199 }
9200 *to=0;
9201}
9202
9203
9204void get_mqh(const char *user, const char *host, USER_CONN *uc)
9205{
9206 ACL_USER *acl_user;
9207
9208 mysql_mutex_lock(&acl_cache->lock);
9209
9210 if (initialized && (acl_user= find_user_wild(host,user)))
9211 uc->user_resources= acl_user->user_resource;
9212 else
9213 bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
9214
9215 mysql_mutex_unlock(&acl_cache->lock);
9216}
9217
9218static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data)
9219{
9220 LEX_CSTRING *rolename= static_cast<LEX_CSTRING *>(data);
9221 if (rolename->length == grantee->user.length &&
9222 !strcmp(rolename->str, grantee->user.str))
9223 return -1; // End search, we've found our role.
9224
9225 /* Keep looking, we haven't found our role yet. */
9226 return 0;
9227}
9228
9229/*
9230 Modify a privilege table.
9231
9232 SYNOPSIS
9233 modify_grant_table()
9234 table The table to modify.
9235 host_field The host name field.
9236 user_field The user name field.
9237 user_to The new name for the user if to be renamed,
9238 NULL otherwise.
9239
9240 DESCRIPTION
9241 Update user/host in the current record if user_to is not NULL.
9242 Delete the current record if user_to is NULL.
9243
9244 RETURN
9245 0 OK.
9246 != 0 Error.
9247*/
9248
9249static int modify_grant_table(TABLE *table, Field *host_field,
9250 Field *user_field, LEX_USER *user_to)
9251{
9252 int error;
9253 DBUG_ENTER("modify_grant_table");
9254
9255 if (user_to)
9256 {
9257 /* rename */
9258 store_record(table, record[1]);
9259 host_field->store(user_to->host.str, user_to->host.length,
9260 system_charset_info);
9261 user_field->store(user_to->user.str, user_to->user.length,
9262 system_charset_info);
9263 if (unlikely(error= table->file->ha_update_row(table->record[1],
9264 table->record[0])) &&
9265 error != HA_ERR_RECORD_IS_THE_SAME)
9266 table->file->print_error(error, MYF(0));
9267 else
9268 error= 0;
9269 }
9270 else
9271 {
9272 /* delete */
9273 if (unlikely((error=table->file->ha_delete_row(table->record[0]))))
9274 table->file->print_error(error, MYF(0));
9275 }
9276
9277 DBUG_RETURN(error);
9278}
9279
9280/*
9281 Handle the roles_mapping privilege table
9282*/
9283static int handle_roles_mappings_table(TABLE *table, bool drop,
9284 LEX_USER *user_from, LEX_USER *user_to)
9285{
9286 /*
9287 All entries (Host, User) that match user_from will be renamed,
9288 as well as all Role entries that match if user_from.host.str == ""
9289
9290 Otherwise, only matching (Host, User) will be renamed.
9291 */
9292 DBUG_ENTER("handle_roles_mappings_table");
9293
9294 int error;
9295 int result= 0;
9296 THD *thd= table->in_use;
9297 const char *host, *user, *role;
9298 Field *host_field= table->field[0];
9299 Field *user_field= table->field[1];
9300 Field *role_field= table->field[2];
9301
9302 DBUG_PRINT("info", ("Rewriting entry in roles_mapping table: %s@%s",
9303 user_from->user.str, user_from->host.str));
9304 table->use_all_columns();
9305
9306 if (unlikely(table->file->ha_rnd_init_with_error(1)))
9307 result= -1;
9308 else
9309 {
9310 while((error= table->file->ha_rnd_next(table->record[0])) !=
9311 HA_ERR_END_OF_FILE)
9312 {
9313 if (error)
9314 {
9315 DBUG_PRINT("info", ("scan error: %d", error));
9316 continue;
9317 }
9318
9319 host= safe_str(get_field(thd->mem_root, host_field));
9320 user= safe_str(get_field(thd->mem_root, user_field));
9321
9322 if (!(strcmp(user_from->user.str, user) ||
9323 my_strcasecmp(system_charset_info, user_from->host.str, host)))
9324 result= ((drop || user_to) &&
9325 modify_grant_table(table, host_field, user_field, user_to)) ?
9326 -1 : result ? result : 1; /* Error or keep result or found. */
9327 else
9328 {
9329 role= safe_str(get_field(thd->mem_root, role_field));
9330
9331 if (!user_from->is_role() || strcmp(user_from->user.str, role))
9332 continue;
9333
9334 error= 0;
9335
9336 if (drop) /* drop if requested */
9337 {
9338 if (unlikely((error= table->file->ha_delete_row(table->record[0]))))
9339 table->file->print_error(error, MYF(0));
9340 }
9341 else if (user_to)
9342 {
9343 store_record(table, record[1]);
9344 role_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 }
9351
9352 /* Error or keep result or found. */
9353 result= error ? -1 : result ? result : 1;
9354 }
9355 }
9356 table->file->ha_rnd_end();
9357 }
9358 DBUG_RETURN(result);
9359}
9360
9361/*
9362 Handle a privilege table.
9363
9364 SYNOPSIS
9365 handle_grant_table()
9366 grant_table An open grant table handle.
9367 which_table Which grant table to handle.
9368 drop If user_from is to be dropped.
9369 user_from The the user to be searched/dropped/renamed.
9370 user_to The new name for the user if to be renamed,
9371 NULL otherwise.
9372
9373 DESCRIPTION
9374 Scan through all records in a grant table and apply the requested
9375 operation. For the "user" table, a single index access is sufficient,
9376 since there is an unique index on (host, user).
9377 Delete from grant table if drop is true.
9378 Update in grant table if drop is false and user_to is not NULL.
9379 Search in grant table if drop is false and user_to is NULL.
9380
9381 RETURN
9382 > 0 At least one record matched.
9383 0 OK, but no record matched.
9384 < 0 Error.
9385
9386 TODO(cvicentiu) refactor handle_grant_table to use
9387 Grant_table_base instead of TABLE directly.
9388*/
9389
9390static int handle_grant_table(THD *thd, const Grant_table_base& grant_table,
9391 enum enum_acl_tables which_table, bool drop,
9392 LEX_USER *user_from, LEX_USER *user_to)
9393{
9394 int result= 0;
9395 int error;
9396 TABLE *table= grant_table.table();
9397 Field *host_field= table->field[0];
9398 Field *user_field= table->field[which_table == USER_TABLE ||
9399 which_table == PROXIES_PRIV_TABLE ? 1 : 2];
9400 const char *host_str= user_from->host.str;
9401 const char *user_str= user_from->user.str;
9402 const char *host;
9403 const char *user;
9404 uchar user_key[MAX_KEY_LENGTH];
9405 uint key_prefix_length;
9406 DBUG_ENTER("handle_grant_table");
9407
9408 if (which_table == ROLES_MAPPING_TABLE)
9409 {
9410 result= handle_roles_mappings_table(table, drop, user_from, user_to);
9411 DBUG_RETURN(result);
9412 }
9413
9414 table->use_all_columns();
9415 if (which_table == USER_TABLE) // mysql.user table
9416 {
9417 /*
9418 The 'user' table has an unique index on (host, user).
9419 Thus, we can handle everything with a single index access.
9420 The host- and user fields are consecutive in the user table records.
9421 So we set host- and user fields of table->record[0] and use the
9422 pointer to the host field as key.
9423 index_read_idx() will replace table->record[0] (its first argument)
9424 by the searched record, if it exists.
9425 */
9426 DBUG_PRINT("info",("read table: '%s' search: '%s'@'%s'",
9427 table->s->table_name.str, user_str, host_str));
9428 host_field->store(host_str, user_from->host.length, system_charset_info);
9429 user_field->store(user_str, user_from->user.length, system_charset_info);
9430
9431 key_prefix_length= (table->key_info->key_part[0].store_length +
9432 table->key_info->key_part[1].store_length);
9433 key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
9434
9435 error= table->file->ha_index_read_idx_map(table->record[0], 0,
9436 user_key, (key_part_map)3,
9437 HA_READ_KEY_EXACT);
9438 if (!unlikely(error) && !*host_str)
9439 {
9440 // verify that we got a role or a user, as needed
9441 if (static_cast<const User_table&>(grant_table).check_is_role() !=
9442 user_from->is_role())
9443 error= HA_ERR_KEY_NOT_FOUND;
9444 }
9445 if (unlikely(error))
9446 {
9447 if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
9448 {
9449 table->file->print_error(error, MYF(0));
9450 result= -1;
9451 }
9452 }
9453 else
9454 {
9455 /* If requested, delete or update the record. */
9456 result= ((drop || user_to) &&
9457 modify_grant_table(table, host_field, user_field, user_to)) ?
9458 -1 : 1; /* Error or found. */
9459 }
9460 DBUG_PRINT("info",("read result: %d", result));
9461 }
9462 else
9463 {
9464 /*
9465 The non-'user' table do not have indexes on (host, user).
9466 And their host- and user fields are not consecutive.
9467 Thus, we need to do a table scan to find all matching records.
9468 */
9469 if (unlikely(table->file->ha_rnd_init_with_error(1)))
9470 result= -1;
9471 else
9472 {
9473#ifdef EXTRA_DEBUG
9474 DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'",
9475 table->s->table_name.str, user_str, host_str));
9476#endif
9477 while ((error= table->file->ha_rnd_next(table->record[0])) !=
9478 HA_ERR_END_OF_FILE)
9479 {
9480 if (error)
9481 {
9482 /* Most probable 'deleted record'. */
9483 DBUG_PRINT("info",("scan error: %d", error));
9484 continue;
9485 }
9486 host= safe_str(get_field(thd->mem_root, host_field));
9487 user= safe_str(get_field(thd->mem_root, user_field));
9488
9489#ifdef EXTRA_DEBUG
9490 if (which_table != PROXIES_PRIV_TABLE)
9491 {
9492 DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
9493 user, host,
9494 get_field(thd->mem_root, table->field[1]) /*db*/,
9495 get_field(thd->mem_root, table->field[3]) /*table*/,
9496 get_field(thd->mem_root,
9497 table->field[4]) /*column*/));
9498 }
9499#endif
9500 if (strcmp(user_str, user) ||
9501 my_strcasecmp(system_charset_info, host_str, host))
9502 continue;
9503
9504 /* If requested, delete or update the record. */
9505 result= ((drop || user_to) &&
9506 modify_grant_table(table, host_field, user_field, user_to)) ?
9507 -1 : result ? result : 1; /* Error or keep result or found. */
9508 /* If search is requested, we do not need to search further. */
9509 if (! drop && ! user_to)
9510 break ;
9511 }
9512 (void) table->file->ha_rnd_end();
9513 DBUG_PRINT("info",("scan result: %d", result));
9514 }
9515 }
9516
9517 DBUG_RETURN(result);
9518}
9519
9520
9521/**
9522 Handle an in-memory privilege structure.
9523
9524 @param struct_no The number of the structure to handle (0..6).
9525 @param drop If user_from is to be dropped.
9526 @param user_from The the user to be searched/dropped/renamed.
9527 @param user_to The new name for the user if to be renamed, NULL otherwise.
9528
9529 @note
9530 Scan through all elements in an in-memory grant structure and apply
9531 the requested operation.
9532 Delete from grant structure if drop is true.
9533 Update in grant structure if drop is false and user_to is not NULL.
9534 Search in grant structure if drop is false and user_to is NULL.
9535
9536 @retval > 0 At least one element matched.
9537 @retval 0 OK, but no element matched.
9538*/
9539
9540static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
9541 LEX_USER *user_from, LEX_USER *user_to)
9542{
9543 int result= 0;
9544 int idx;
9545 int elements;
9546 const char *UNINIT_VAR(user);
9547 const char *UNINIT_VAR(host);
9548 ACL_USER *acl_user= NULL;
9549 ACL_ROLE *acl_role= NULL;
9550 ACL_DB *acl_db= NULL;
9551 ACL_PROXY_USER *acl_proxy_user= NULL;
9552 GRANT_NAME *grant_name= NULL;
9553 ROLE_GRANT_PAIR *UNINIT_VAR(role_grant_pair);
9554 HASH *grant_name_hash= NULL;
9555 HASH *roles_mappings_hash= NULL;
9556 DBUG_ENTER("handle_grant_struct");
9557 DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
9558 struct_no, user_from->user.str, user_from->host.str));
9559
9560 mysql_mutex_assert_owner(&acl_cache->lock);
9561
9562 /* No point in querying ROLE ACL if user_from is not a role */
9563 if (struct_no == ROLE_ACL && user_from->host.length)
9564 DBUG_RETURN(0);
9565
9566 /* same. no roles in PROXY_USERS_ACL */
9567 if (struct_no == PROXY_USERS_ACL && user_from->is_role())
9568 DBUG_RETURN(0);
9569
9570 if (struct_no == ROLE_ACL) //no need to scan the structures in this case
9571 {
9572 acl_role= find_acl_role(user_from->user.str);
9573 if (!acl_role)
9574 DBUG_RETURN(0);
9575
9576 if (!drop && !user_to) //role was found
9577 DBUG_RETURN(1);
9578
9579 /* this calls for a role update */
9580 const char *old_key= acl_role->user.str;
9581 size_t old_key_length= acl_role->user.length;
9582 if (drop)
9583 {
9584 /* all grants must be revoked from this role by now. propagate this */
9585 propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL);
9586
9587 // delete the role from cross-reference arrays
9588 for (uint i=0; i < acl_role->role_grants.elements; i++)
9589 {
9590 ACL_ROLE *grant= *dynamic_element(&acl_role->role_grants,
9591 i, ACL_ROLE**);
9592 remove_ptr_from_dynarray(&grant->parent_grantee, acl_role);
9593 }
9594
9595 for (uint i=0; i < acl_role->parent_grantee.elements; i++)
9596 {
9597 ACL_USER_BASE *grantee= *dynamic_element(&acl_role->parent_grantee,
9598 i, ACL_USER_BASE**);
9599 remove_ptr_from_dynarray(&grantee->role_grants, acl_role);
9600 }
9601
9602 my_hash_delete(&acl_roles, (uchar*) acl_role);
9603 DBUG_RETURN(1);
9604 }
9605 acl_role->user.str= strdup_root(&acl_memroot, user_to->user.str);
9606 acl_role->user.length= user_to->user.length;
9607
9608 my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key,
9609 old_key_length);
9610 DBUG_RETURN(1);
9611
9612 }
9613
9614 /* Get the number of elements in the in-memory structure. */
9615 switch (struct_no) {
9616 case USER_ACL:
9617 elements= acl_users.elements;
9618 break;
9619 case DB_ACL:
9620 elements= acl_dbs.elements;
9621 break;
9622 case COLUMN_PRIVILEGES_HASH:
9623 grant_name_hash= &column_priv_hash;
9624 elements= grant_name_hash->records;
9625 break;
9626 case PROC_PRIVILEGES_HASH:
9627 grant_name_hash= &proc_priv_hash;
9628 elements= grant_name_hash->records;
9629 break;
9630 case FUNC_PRIVILEGES_HASH:
9631 grant_name_hash= &func_priv_hash;
9632 elements= grant_name_hash->records;
9633 break;
9634 case PACKAGE_SPEC_PRIVILEGES_HASH:
9635 grant_name_hash= &package_spec_priv_hash;
9636 elements= grant_name_hash->records;
9637 break;
9638 case PACKAGE_BODY_PRIVILEGES_HASH:
9639 grant_name_hash= &package_body_priv_hash;
9640 elements= grant_name_hash->records;
9641 break;
9642 case PROXY_USERS_ACL:
9643 elements= acl_proxy_users.elements;
9644 break;
9645 case ROLES_MAPPINGS_HASH:
9646 roles_mappings_hash= &acl_roles_mappings;
9647 elements= roles_mappings_hash->records;
9648 break;
9649 default:
9650 DBUG_ASSERT(0);
9651 DBUG_RETURN(-1);
9652 }
9653
9654#ifdef EXTRA_DEBUG
9655 DBUG_PRINT("loop",("scan struct: %u search user: '%s' host: '%s'",
9656 struct_no, user_from->user.str, user_from->host.str));
9657#endif
9658 /* Loop over all elements *backwards* (see the comment below). */
9659 for (idx= elements - 1; idx >= 0; idx--)
9660 {
9661 /*
9662 Get a pointer to the element.
9663 */
9664 switch (struct_no) {
9665 case USER_ACL:
9666 acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
9667 user= acl_user->user.str;
9668 host= acl_user->host.hostname;
9669 break;
9670
9671 case DB_ACL:
9672 acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
9673 user= acl_db->user;
9674 host= acl_db->host.hostname;
9675 break;
9676
9677 case COLUMN_PRIVILEGES_HASH:
9678 case PROC_PRIVILEGES_HASH:
9679 case FUNC_PRIVILEGES_HASH:
9680 case PACKAGE_SPEC_PRIVILEGES_HASH:
9681 case PACKAGE_BODY_PRIVILEGES_HASH:
9682 grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
9683 user= grant_name->user;
9684 host= grant_name->host.hostname;
9685 break;
9686
9687 case PROXY_USERS_ACL:
9688 acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
9689 user= acl_proxy_user->get_user();
9690 host= acl_proxy_user->get_host();
9691 break;
9692
9693 case ROLES_MAPPINGS_HASH:
9694 role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx);
9695 user= role_grant_pair->u_uname;
9696 host= role_grant_pair->u_hname;
9697 break;
9698
9699 default:
9700 DBUG_ASSERT(0);
9701 }
9702 if (! user)
9703 user= "";
9704 if (! host)
9705 host= "";
9706
9707#ifdef EXTRA_DEBUG
9708 DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'",
9709 struct_no, idx, user, host));
9710#endif
9711
9712 if (struct_no == ROLES_MAPPINGS_HASH)
9713 {
9714 const char* role= role_grant_pair->r_uname? role_grant_pair->r_uname: "";
9715 if (user_from->is_role())
9716 {
9717 /* When searching for roles within the ROLES_MAPPINGS_HASH, we have
9718 to check both the user field as well as the role field for a match.
9719
9720 It is possible to have a role granted to a role. If we are going
9721 to modify the mapping entry, it needs to be done on either on the
9722 "user" end (here represented by a role) or the "role" end. At least
9723 one part must match.
9724
9725 If the "user" end has a not-empty host string, it can never match
9726 as we are searching for a role here. A role always has an empty host
9727 string.
9728 */
9729 if ((*host || strcmp(user_from->user.str, user)) &&
9730 strcmp(user_from->user.str, role))
9731 continue;
9732 }
9733 else
9734 {
9735 if (strcmp(user_from->user.str, user) ||
9736 my_strcasecmp(system_charset_info, user_from->host.str, host))
9737 continue;
9738 }
9739 }
9740 else
9741 {
9742 if (strcmp(user_from->user.str, user) ||
9743 my_strcasecmp(system_charset_info, user_from->host.str, host))
9744 continue;
9745 }
9746
9747 result= 1; /* At least one element found. */
9748 if ( drop )
9749 {
9750 elements--;
9751 switch ( struct_no ) {
9752 case USER_ACL:
9753 free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*));
9754 delete_dynamic_element(&acl_users, idx);
9755 break;
9756
9757 case DB_ACL:
9758 delete_dynamic_element(&acl_dbs, idx);
9759 break;
9760
9761 case COLUMN_PRIVILEGES_HASH:
9762 case PROC_PRIVILEGES_HASH:
9763 case FUNC_PRIVILEGES_HASH:
9764 case PACKAGE_SPEC_PRIVILEGES_HASH:
9765 case PACKAGE_BODY_PRIVILEGES_HASH:
9766 my_hash_delete(grant_name_hash, (uchar*) grant_name);
9767 /*
9768 In our HASH implementation on deletion one elements
9769 is moved into a place where a deleted element was,
9770 and the last element is moved into the empty space.
9771 Thus we need to re-examine the current element, but
9772 we don't have to restart the search from the beginning.
9773 */
9774 if (idx != elements)
9775 idx++;
9776 break;
9777
9778 case PROXY_USERS_ACL:
9779 delete_dynamic_element(&acl_proxy_users, idx);
9780 break;
9781
9782 case ROLES_MAPPINGS_HASH:
9783 my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair);
9784 if (idx != elements)
9785 idx++;
9786 break;
9787
9788 default:
9789 DBUG_ASSERT(0);
9790 break;
9791 }
9792 }
9793 else if ( user_to )
9794 {
9795 switch ( struct_no ) {
9796 case USER_ACL:
9797 acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str);
9798 acl_user->user.length= user_to->user.length;
9799 update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str));
9800 acl_user->hostname_length= strlen(acl_user->host.hostname);
9801 break;
9802
9803 case DB_ACL:
9804 acl_db->user= strdup_root(&acl_memroot, user_to->user.str);
9805 update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str));
9806 break;
9807
9808 case COLUMN_PRIVILEGES_HASH:
9809 case PROC_PRIVILEGES_HASH:
9810 case FUNC_PRIVILEGES_HASH:
9811 case PACKAGE_SPEC_PRIVILEGES_HASH:
9812 case PACKAGE_BODY_PRIVILEGES_HASH:
9813 {
9814 /*
9815 Save old hash key and its length to be able to properly update
9816 element position in hash.
9817 */
9818 char *old_key= grant_name->hash_key;
9819 size_t old_key_length= grant_name->key_length;
9820
9821 /*
9822 Update the grant structure with the new user name and host name.
9823 */
9824 grant_name->set_user_details(user_to->host.str, grant_name->db,
9825 user_to->user.str, grant_name->tname,
9826 TRUE);
9827
9828 /*
9829 Since username is part of the hash key, when the user name
9830 is renamed, the hash key is changed. Update the hash to
9831 ensure that the position matches the new hash key value
9832 */
9833 my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
9834 old_key_length);
9835 /*
9836 hash_update() operation could have moved element from the tail or
9837 the head of the hash to the current position. But it can never
9838 move an element from the head to the tail or from the tail to the
9839 head over the current element.
9840 So we need to examine the current element once again, but
9841 we don't need to restart the search from the beginning.
9842 */
9843 idx++;
9844 break;
9845 }
9846
9847 case PROXY_USERS_ACL:
9848 acl_proxy_user->set_user (&acl_memroot, user_to->user.str);
9849 acl_proxy_user->set_host (&acl_memroot, user_to->host.str);
9850 break;
9851
9852 case ROLES_MAPPINGS_HASH:
9853 {
9854 /*
9855 Save old hash key and its length to be able to properly update
9856 element position in hash.
9857 */
9858 char *old_key= role_grant_pair->hashkey.str;
9859 size_t old_key_length= role_grant_pair->hashkey.length;
9860 bool oom;
9861
9862 if (user_to->is_role())
9863 oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname,
9864 role_grant_pair->u_hname,
9865 user_to->user.str, false);
9866 else
9867 oom= role_grant_pair->init(&acl_memroot, user_to->user.str,
9868 user_to->host.str,
9869 role_grant_pair->r_uname, false);
9870 if (oom)
9871 DBUG_RETURN(-1);
9872
9873 my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair,
9874 (uchar*) old_key, old_key_length);
9875 idx++; // see the comment above
9876 break;
9877 }
9878
9879 default:
9880 DBUG_ASSERT(0);
9881 break;
9882 }
9883
9884 }
9885 else
9886 {
9887 /* If search is requested, we do not need to search further. */
9888 break;
9889 }
9890 }
9891#ifdef EXTRA_DEBUG
9892 DBUG_PRINT("loop",("scan struct: %u result %d", struct_no, result));
9893#endif
9894
9895 DBUG_RETURN(result);
9896}
9897
9898
9899/*
9900 Handle all privilege tables and in-memory privilege structures.
9901
9902 SYNOPSIS
9903 handle_grant_data()
9904 tables The array with the four open tables.
9905 drop If user_from is to be dropped.
9906 user_from The the user to be searched/dropped/renamed.
9907 user_to The new name for the user if to be renamed,
9908 NULL otherwise.
9909
9910 DESCRIPTION
9911 Go through all grant tables and in-memory grant structures and apply
9912 the requested operation.
9913 Delete from grant data if drop is true.
9914 Update in grant data if drop is false and user_to is not NULL.
9915 Search in grant data if drop is false and user_to is NULL.
9916
9917 RETURN
9918 > 0 At least one element matched.
9919 0 OK, but no element matched.
9920 < 0 Error.
9921*/
9922
9923static int handle_grant_data(THD *thd, Grant_tables& tables, bool drop,
9924 LEX_USER *user_from, LEX_USER *user_to)
9925{
9926 int result= 0;
9927 int found;
9928 bool handle_as_role= user_from->is_role();
9929 bool search_only= !drop && !user_to;
9930 DBUG_ENTER("handle_grant_data");
9931
9932 if (user_to)
9933 DBUG_ASSERT(handle_as_role == user_to->is_role());
9934
9935 if (search_only)
9936 {
9937 /* quickly search in-memory structures first */
9938 if (handle_as_role && find_acl_role(user_from->user.str))
9939 DBUG_RETURN(1); // found
9940
9941 if (!handle_as_role && find_user_exact(user_from->host.str, user_from->user.str))
9942 DBUG_RETURN(1); // found
9943 }
9944
9945 /* Handle db table. */
9946 if ((found= handle_grant_table(thd, tables.db_table(),
9947 DB_TABLE, drop, user_from,
9948 user_to)) < 0)
9949 {
9950 /* Handle of table failed, don't touch the in-memory array. */
9951 result= -1;
9952 }
9953 else
9954 {
9955 /* Handle db array. */
9956 if ((handle_grant_struct(DB_ACL, drop, user_from, user_to) || found)
9957 && ! result)
9958 {
9959 result= 1; /* At least one record/element found. */
9960 /* If search is requested, we do not need to search further. */
9961 if (search_only)
9962 goto end;
9963 acl_cache->clear(1);
9964 }
9965 }
9966
9967 /* Handle stored routines table. */
9968 if ((found= handle_grant_table(thd, tables.procs_priv_table(),
9969 PROCS_PRIV_TABLE, drop,
9970 user_from, user_to)) < 0)
9971 {
9972 /* Handle of table failed, don't touch in-memory array. */
9973 result= -1;
9974 }
9975 else
9976 {
9977 /* Handle procs array. */
9978 if ((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
9979 && ! result)
9980 {
9981 result= 1; /* At least one record/element found. */
9982 /* If search is requested, we do not need to search further. */
9983 if (search_only)
9984 goto end;
9985 }
9986 /* Handle funcs array. */
9987 if ((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
9988 && ! result)
9989 {
9990 result= 1; /* At least one record/element found. */
9991 /* If search is requested, we do not need to search further. */
9992 if (search_only)
9993 goto end;
9994 }
9995 /* Handle package spec array. */
9996 if ((handle_grant_struct(PACKAGE_SPEC_PRIVILEGES_HASH,
9997 drop, user_from, user_to) || found)
9998 && ! result)
9999 {
10000 result= 1; /* At least one record/element found. */
10001 /* If search is requested, we do not need to search further. */
10002 if (search_only)
10003 goto end;
10004 }
10005 /* Handle package body array. */
10006 if ((handle_grant_struct(PACKAGE_BODY_PRIVILEGES_HASH,
10007 drop, user_from, user_to) || found)
10008 && ! result)
10009 {
10010 result= 1; /* At least one record/element found. */
10011 /* If search is requested, we do not need to search further. */
10012 if (search_only)
10013 goto end;
10014 }
10015 }
10016
10017 /* Handle tables table. */
10018 if ((found= handle_grant_table(thd, tables.tables_priv_table(),
10019 TABLES_PRIV_TABLE, drop,
10020 user_from, user_to)) < 0)
10021 {
10022 /* Handle of table failed, don't touch columns and in-memory array. */
10023 result= -1;
10024 }
10025 else
10026 {
10027 if (found && ! result)
10028 {
10029 result= 1; /* At least one record found. */
10030 /* If search is requested, we do not need to search further. */
10031 if (search_only)
10032 goto end;
10033 }
10034
10035 /* Handle columns table. */
10036 if ((found= handle_grant_table(thd, tables.columns_priv_table(),
10037 COLUMNS_PRIV_TABLE, drop,
10038 user_from, user_to)) < 0)
10039 {
10040 /* Handle of table failed, don't touch the in-memory array. */
10041 result= -1;
10042 }
10043 else
10044 {
10045 /* Handle columns hash. */
10046 if ((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10047 && ! result)
10048 result= 1; /* At least one record/element found. */
10049 if (search_only)
10050 goto end;
10051 }
10052 }
10053
10054 /* Handle proxies_priv table. */
10055 if (tables.proxies_priv_table().table_exists())
10056 {
10057 if ((found= handle_grant_table(thd, tables.proxies_priv_table(),
10058 PROXIES_PRIV_TABLE, drop,
10059 user_from, user_to)) < 0)
10060 {
10061 /* Handle of table failed, don't touch the in-memory array. */
10062 result= -1;
10063 }
10064 else
10065 {
10066 /* Handle proxies_priv array. */
10067 if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) || found)
10068 && ! result)
10069 result= 1; /* At least one record/element found. */
10070 if (search_only)
10071 goto end;
10072 }
10073 }
10074
10075 /* Handle roles_mapping table. */
10076 if (tables.roles_mapping_table().table_exists())
10077 {
10078 if ((found= handle_grant_table(thd, tables.roles_mapping_table(),
10079 ROLES_MAPPING_TABLE, drop,
10080 user_from, user_to)) < 0)
10081 {
10082 /* Handle of table failed, don't touch the in-memory array. */
10083 result= -1;
10084 }
10085 else
10086 {
10087 /* Handle acl_roles_mappings array */
10088 if ((handle_grant_struct(ROLES_MAPPINGS_HASH, drop, user_from, user_to) || found)
10089 && ! result)
10090 result= 1; /* At least one record/element found */
10091 if (search_only)
10092 goto end;
10093 }
10094 }
10095
10096 /* Handle user table. */
10097 if ((found= handle_grant_table(thd, tables.user_table(), USER_TABLE,
10098 drop, user_from, user_to)) < 0)
10099 {
10100 /* Handle of table failed, don't touch the in-memory array. */
10101 result= -1;
10102 }
10103 else
10104 {
10105 enum enum_acl_lists what= handle_as_role ? ROLE_ACL : USER_ACL;
10106 if (((handle_grant_struct(what, drop, user_from, user_to)) || found) && !result)
10107 {
10108 result= 1; /* At least one record/element found. */
10109 DBUG_ASSERT(! search_only);
10110 }
10111 }
10112
10113end:
10114 DBUG_RETURN(result);
10115}
10116
10117/*
10118 Create a list of users.
10119
10120 SYNOPSIS
10121 mysql_create_user()
10122 thd The current thread.
10123 list The users to create.
10124 handle_as_role Handle the user list as roles if true
10125
10126 RETURN
10127 FALSE OK.
10128 TRUE Error.
10129*/
10130
10131bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
10132{
10133 int result;
10134 String wrong_users;
10135 LEX_USER *user_name;
10136 List_iterator <LEX_USER> user_list(list);
10137 bool binlog= false;
10138 DBUG_ENTER("mysql_create_user");
10139 DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
10140
10141 if (handle_as_role && sp_process_definer(thd))
10142 DBUG_RETURN(TRUE);
10143
10144 /* CREATE USER may be skipped on replication client. */
10145 Grant_tables tables(Table_user | Table_db |
10146 Table_tables_priv | Table_columns_priv |
10147 Table_procs_priv | Table_proxies_priv |
10148 Table_roles_mapping, TL_WRITE);
10149 if ((result= tables.open_and_lock(thd)))
10150 DBUG_RETURN(result != 1);
10151
10152 mysql_rwlock_wrlock(&LOCK_grant);
10153 mysql_mutex_lock(&acl_cache->lock);
10154
10155 while ((user_name= user_list++))
10156 {
10157 if (user_name->user.str == current_user.str)
10158 {
10159 append_str(&wrong_users, STRING_WITH_LEN("CURRENT_USER"));
10160 result= TRUE;
10161 continue;
10162 }
10163
10164 if (user_name->user.str == current_role.str)
10165 {
10166 append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
10167 result= TRUE;
10168 continue;
10169 }
10170
10171 if (handle_as_role && is_invalid_role_name(user_name->user.str))
10172 {
10173 append_user(thd, &wrong_users, user_name);
10174 result= TRUE;
10175 continue;
10176 }
10177
10178 if (!user_name->host.str)
10179 user_name->host= host_not_specified;
10180
10181 if (fix_lex_user(thd, user_name))
10182 {
10183 append_user(thd, &wrong_users, user_name);
10184 result= TRUE;
10185 continue;
10186 }
10187
10188 /*
10189 Search all in-memory structures and grant tables
10190 for a mention of the new user/role name.
10191 */
10192 if (handle_grant_data(thd, tables, 0, user_name, NULL))
10193 {
10194 if (thd->lex->create_info.or_replace())
10195 {
10196 // Drop the existing user
10197 if (handle_grant_data(thd, tables, 1, user_name, NULL) <= 0)
10198 {
10199 // DROP failed
10200 append_user(thd, &wrong_users, user_name);
10201 result= true;
10202 continue;
10203 }
10204 // Proceed with the creation
10205 }
10206 else if (thd->lex->create_info.if_not_exists())
10207 {
10208 binlog= true;
10209 if (handle_as_role)
10210 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10211 ER_ROLE_CREATE_EXISTS,
10212 ER_THD(thd, ER_ROLE_CREATE_EXISTS),
10213 user_name->user.str);
10214 else
10215 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10216 ER_USER_CREATE_EXISTS,
10217 ER_THD(thd, ER_USER_CREATE_EXISTS),
10218 user_name->user.str, user_name->host.str);
10219 continue;
10220 }
10221 else
10222 {
10223 // "CREATE USER user1" for an existing user
10224 append_user(thd, &wrong_users, user_name);
10225 result= true;
10226 continue;
10227 }
10228 }
10229
10230 if (replace_user_table(thd, tables.user_table(), *user_name, 0, 0, 1, 0))
10231 {
10232 append_user(thd, &wrong_users, user_name);
10233 result= TRUE;
10234 continue;
10235 }
10236 binlog= true;
10237
10238 // every created role is automatically granted to its creator-admin
10239 if (handle_as_role)
10240 {
10241 ACL_USER_BASE *grantee= find_acl_user_base(thd->lex->definer->user.str,
10242 thd->lex->definer->host.str);
10243 ACL_ROLE *role= find_acl_role(user_name->user.str);
10244
10245 /*
10246 just like with routines, views, triggers, and events we allow
10247 non-existant definers here with a warning (see sp_process_definer())
10248 */
10249 if (grantee)
10250 add_role_user_mapping(grantee, role);
10251
10252 /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
10253 Roles_mapping_table instead of TABLE directly. */
10254 if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
10255 &thd->lex->definer->user,
10256 &thd->lex->definer->host,
10257 &user_name->user, true,
10258 NULL, false))
10259 {
10260 append_user(thd, &wrong_users, user_name);
10261 if (grantee)
10262 undo_add_role_user_mapping(grantee, role);
10263 result= TRUE;
10264 }
10265 else if (grantee)
10266 update_role_mapping(&thd->lex->definer->user,
10267 &thd->lex->definer->host,
10268 &user_name->user, true, NULL, false);
10269 }
10270 }
10271
10272 mysql_mutex_unlock(&acl_cache->lock);
10273
10274 if (result)
10275 my_error(ER_CANNOT_USER, MYF(0),
10276 (handle_as_role) ? "CREATE ROLE" : "CREATE USER",
10277 wrong_users.c_ptr_safe());
10278
10279 if (binlog)
10280 result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10281
10282 mysql_rwlock_unlock(&LOCK_grant);
10283 DBUG_RETURN(result);
10284}
10285
10286/*
10287 Drop a list of users and all their privileges.
10288
10289 SYNOPSIS
10290 mysql_drop_user()
10291 thd The current thread.
10292 list The users to drop.
10293
10294 RETURN
10295 FALSE OK.
10296 TRUE Error.
10297*/
10298
10299bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
10300{
10301 int result;
10302 String wrong_users;
10303 LEX_USER *user_name, *tmp_user_name;
10304 List_iterator <LEX_USER> user_list(list);
10305 bool binlog= false;
10306 sql_mode_t old_sql_mode= thd->variables.sql_mode;
10307 DBUG_ENTER("mysql_drop_user");
10308 DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
10309
10310 /* DROP USER may be skipped on replication client. */
10311 Grant_tables tables(Table_user | Table_db |
10312 Table_tables_priv | Table_columns_priv |
10313 Table_procs_priv | Table_proxies_priv |
10314 Table_roles_mapping, TL_WRITE);
10315 if ((result= tables.open_and_lock(thd)))
10316 DBUG_RETURN(result != 1);
10317
10318 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
10319
10320 mysql_rwlock_wrlock(&LOCK_grant);
10321 mysql_mutex_lock(&acl_cache->lock);
10322
10323 while ((tmp_user_name= user_list++))
10324 {
10325 int rc;
10326 user_name= get_current_user(thd, tmp_user_name, false);
10327 if (!user_name)
10328 {
10329 thd->clear_error();
10330 append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
10331 result= TRUE;
10332 continue;
10333 }
10334
10335 if (handle_as_role != user_name->is_role())
10336 {
10337 append_user(thd, &wrong_users, user_name);
10338 result= TRUE;
10339 continue;
10340 }
10341
10342 if ((rc= handle_grant_data(thd, tables, 1, user_name, NULL)) > 0)
10343 {
10344 // The user or role was successfully deleted
10345 binlog= true;
10346 continue;
10347 }
10348
10349 if (rc == 0 && thd->lex->if_exists())
10350 {
10351 // "DROP USER IF EXISTS user1" for a non-existing user or role
10352 if (handle_as_role)
10353 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10354 ER_ROLE_DROP_EXISTS,
10355 ER_THD(thd, ER_ROLE_DROP_EXISTS),
10356 user_name->user.str);
10357 else
10358 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10359 ER_USER_DROP_EXISTS,
10360 ER_THD(thd, ER_USER_DROP_EXISTS),
10361 user_name->user.str, user_name->host.str);
10362 binlog= true;
10363 continue;
10364 }
10365 // Internal error, or "DROP USER user1" for a non-existing user
10366 append_user(thd, &wrong_users, user_name);
10367 result= TRUE;
10368 }
10369
10370 if (!handle_as_role)
10371 {
10372 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
10373 rebuild_check_host();
10374
10375 /*
10376 Rebuild every user's role_grants since 'acl_users' has been sorted
10377 and old pointers to ACL_USER elements are no longer valid
10378 */
10379 rebuild_role_grants();
10380 }
10381
10382 mysql_mutex_unlock(&acl_cache->lock);
10383
10384 if (result)
10385 my_error(ER_CANNOT_USER, MYF(0),
10386 (handle_as_role) ? "DROP ROLE" : "DROP USER",
10387 wrong_users.c_ptr_safe());
10388
10389 if (binlog)
10390 result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10391
10392 mysql_rwlock_unlock(&LOCK_grant);
10393 thd->variables.sql_mode= old_sql_mode;
10394 DBUG_RETURN(result);
10395}
10396
10397/*
10398 Rename a user.
10399
10400 SYNOPSIS
10401 mysql_rename_user()
10402 thd The current thread.
10403 list The user name pairs: (from, to).
10404
10405 RETURN
10406 FALSE OK.
10407 TRUE Error.
10408*/
10409
10410bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
10411{
10412 int result;
10413 String wrong_users;
10414 LEX_USER *user_from, *tmp_user_from;
10415 LEX_USER *user_to, *tmp_user_to;
10416 List_iterator <LEX_USER> user_list(list);
10417 bool some_users_renamed= FALSE;
10418 DBUG_ENTER("mysql_rename_user");
10419
10420 /* RENAME USER may be skipped on replication client. */
10421 Grant_tables tables(Table_user | Table_db |
10422 Table_tables_priv | Table_columns_priv |
10423 Table_procs_priv | Table_proxies_priv |
10424 Table_roles_mapping, TL_WRITE);
10425 if ((result= tables.open_and_lock(thd)))
10426 DBUG_RETURN(result != 1);
10427
10428 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
10429
10430 mysql_rwlock_wrlock(&LOCK_grant);
10431 mysql_mutex_lock(&acl_cache->lock);
10432
10433 while ((tmp_user_from= user_list++))
10434 {
10435 tmp_user_to= user_list++;
10436 if (!(user_from= get_current_user(thd, tmp_user_from, false)))
10437 {
10438 append_user(thd, &wrong_users, user_from);
10439 result= TRUE;
10440 continue;
10441 }
10442 if (!(user_to= get_current_user(thd, tmp_user_to, false)))
10443 {
10444 append_user(thd, &wrong_users, user_to);
10445 result= TRUE;
10446 continue;
10447 }
10448 DBUG_ASSERT(!user_from->is_role());
10449 DBUG_ASSERT(!user_to->is_role());
10450
10451 /*
10452 Search all in-memory structures and grant tables
10453 for a mention of the new user name.
10454 */
10455 if (handle_grant_data(thd, tables, 0, user_to, NULL) ||
10456 handle_grant_data(thd, tables, 0, user_from, user_to) <= 0)
10457 {
10458 /* NOTE TODO renaming roles is not yet implemented */
10459 append_user(thd, &wrong_users, user_from);
10460 result= TRUE;
10461 continue;
10462 }
10463 some_users_renamed= TRUE;
10464 }
10465
10466 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
10467 rebuild_check_host();
10468
10469 /*
10470 Rebuild every user's role_grants since 'acl_users' has been sorted
10471 and old pointers to ACL_USER elements are no longer valid
10472 */
10473 rebuild_role_grants();
10474
10475 mysql_mutex_unlock(&acl_cache->lock);
10476
10477 if (result)
10478 my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
10479
10480 if (some_users_renamed && mysql_bin_log.is_open())
10481 result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10482
10483 mysql_rwlock_unlock(&LOCK_grant);
10484 DBUG_RETURN(result);
10485}
10486
10487/*
10488 Alter a user's connection and resource settings.
10489
10490 SYNOPSIS
10491 mysql_alter_user()
10492 thd The current thread.
10493 list The users to alter.
10494
10495 RETURN
10496 > 0 Error. Error message already sent.
10497 0 OK.
10498*/
10499int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
10500{
10501 DBUG_ENTER("mysql_alter_user");
10502 int result= 0;
10503 String wrong_users;
10504
10505 /* The only table we're altering is the user table. */
10506 Grant_tables tables(Table_user, TL_WRITE);
10507 if ((result= tables.open_and_lock(thd)))
10508 DBUG_RETURN(result != 1);
10509
10510 /* Lock ACL data structures until we finish altering all users. */
10511 mysql_rwlock_wrlock(&LOCK_grant);
10512 mysql_mutex_lock(&acl_cache->lock);
10513
10514 LEX_USER *tmp_lex_user;
10515 List_iterator<LEX_USER> users_list_iterator(users_list);
10516 while ((tmp_lex_user= users_list_iterator++))
10517 {
10518 LEX_USER* lex_user= get_current_user(thd, tmp_lex_user, false);
10519 if (!lex_user ||
10520 fix_lex_user(thd, lex_user) ||
10521 replace_user_table(thd, tables.user_table(), *lex_user, 0,
10522 false, false, true))
10523 {
10524 thd->clear_error();
10525 append_user(thd, &wrong_users, tmp_lex_user);
10526 result= TRUE;
10527 continue;
10528 }
10529 }
10530
10531 /* Unlock ACL data structures. */
10532 mysql_mutex_unlock(&acl_cache->lock);
10533 mysql_rwlock_unlock(&LOCK_grant);
10534
10535 if (result)
10536 {
10537 /* 'if exists' flag leads to warnings instead of errors. */
10538 if (thd->lex->create_info.if_exists())
10539 {
10540 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10541 ER_CANNOT_USER,
10542 ER_THD(thd, ER_CANNOT_USER),
10543 "ALTER USER", wrong_users.c_ptr_safe());
10544 result= FALSE;
10545 }
10546 else
10547 {
10548 my_error(ER_CANNOT_USER, MYF(0),
10549 "ALTER USER",
10550 wrong_users.c_ptr_safe());
10551 }
10552 }
10553 DBUG_RETURN(result);
10554}
10555
10556
10557static bool
10558mysql_revoke_sp_privs(THD *thd,
10559 Grant_tables *tables,
10560 const Sp_handler *sph,
10561 const LEX_USER *lex_user)
10562{
10563 bool rc= false;
10564 uint counter, revoked;
10565 do {
10566 HASH *hash= sph->get_priv_hash();
10567 for (counter= 0, revoked= 0 ; counter < hash->records ; )
10568 {
10569 const char *user,*host;
10570 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
10571 user= safe_str(grant_proc->user);
10572 host= safe_str(grant_proc->host.hostname);
10573
10574 if (!strcmp(lex_user->user.str, user) &&
10575 !strcmp(lex_user->host.str, host))
10576 {
10577 if (replace_routine_table(thd, grant_proc,
10578 tables->procs_priv_table().table(),
10579 *lex_user,
10580 grant_proc->db, grant_proc->tname,
10581 sph, ~(ulong)0, 1) == 0)
10582 {
10583 revoked= 1;
10584 continue;
10585 }
10586 rc= true; // Something went wrong
10587 }
10588 counter++;
10589 }
10590 } while (revoked);
10591 return rc;
10592}
10593
10594
10595/*
10596 Revoke all privileges from a list of users.
10597
10598 SYNOPSIS
10599 mysql_revoke_all()
10600 thd The current thread.
10601 list The users to revoke all privileges from.
10602
10603 RETURN
10604 > 0 Error. Error message already sent.
10605 0 OK.
10606 < 0 Error. Error message not yet sent.
10607*/
10608
10609bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
10610{
10611 uint counter, revoked;
10612 int result;
10613 ACL_DB *acl_db;
10614 DBUG_ENTER("mysql_revoke_all");
10615
10616 Grant_tables tables(Table_user | Table_db |
10617 Table_tables_priv | Table_columns_priv |
10618 Table_procs_priv | Table_proxies_priv |
10619 Table_roles_mapping, TL_WRITE);
10620 if ((result= tables.open_and_lock(thd)))
10621 DBUG_RETURN(result != 1);
10622
10623 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
10624
10625 mysql_rwlock_wrlock(&LOCK_grant);
10626 mysql_mutex_lock(&acl_cache->lock);
10627
10628 LEX_USER *lex_user, *tmp_lex_user;
10629 List_iterator <LEX_USER> user_list(list);
10630 while ((tmp_lex_user= user_list++))
10631 {
10632 if (!(lex_user= get_current_user(thd, tmp_lex_user, false)))
10633 {
10634 result= -1;
10635 continue;
10636 }
10637
10638 /* This is not a role and the user could not be found */
10639 if (!lex_user->is_role() &&
10640 !find_user_exact(lex_user->host.str, lex_user->user.str))
10641 {
10642 result= -1;
10643 continue;
10644 }
10645
10646 if (replace_user_table(thd, tables.user_table(), *lex_user,
10647 ~(ulong)0, 1, 0, 0))
10648 {
10649 result= -1;
10650 continue;
10651 }
10652
10653 /* Remove db access privileges */
10654 /*
10655 Because acl_dbs and column_priv_hash shrink and may re-order
10656 as privileges are removed, removal occurs in a repeated loop
10657 until no more privileges are revoked.
10658 */
10659 do
10660 {
10661 for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
10662 {
10663 const char *user,*host;
10664
10665 acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
10666
10667 user= safe_str(acl_db->user);
10668 host= safe_str(acl_db->host.hostname);
10669
10670 if (!strcmp(lex_user->user.str, user) &&
10671 !strcmp(lex_user->host.str, host))
10672 {
10673 /* TODO(cvicentiu) refactor replace_db_table to use
10674 Db_table instead of TABLE directly. */
10675 if (!replace_db_table(tables.db_table().table(), acl_db->db, *lex_user,
10676 ~(ulong)0, 1))
10677 {
10678 /*
10679 Don't increment counter as replace_db_table deleted the
10680 current element in acl_dbs.
10681 */
10682 revoked= 1;
10683 continue;
10684 }
10685 result= -1; // Something went wrong
10686 }
10687 counter++;
10688 }
10689 } while (revoked);
10690
10691 /* Remove column access */
10692 do
10693 {
10694 for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
10695 {
10696 const char *user,*host;
10697 GRANT_TABLE *grant_table=
10698 (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
10699 user= safe_str(grant_table->user);
10700 host= safe_str(grant_table->host.hostname);
10701
10702 if (!strcmp(lex_user->user.str,user) &&
10703 !strcmp(lex_user->host.str, host))
10704 {
10705 /* TODO(cvicentiu) refactor replace_db_table to use
10706 Db_table instead of TABLE directly. */
10707 if (replace_table_table(thd, grant_table,
10708 tables.tables_priv_table().table(),
10709 *lex_user, grant_table->db,
10710 grant_table->tname, ~(ulong)0, 0, 1))
10711 {
10712 result= -1;
10713 }
10714 else
10715 {
10716 if (!grant_table->cols)
10717 {
10718 revoked= 1;
10719 continue;
10720 }
10721 List<LEX_COLUMN> columns;
10722 /* TODO(cvicentiu) refactor replace_db_table to use
10723 Db_table instead of TABLE directly. */
10724 if (!replace_column_table(grant_table,
10725 tables.columns_priv_table().table(),
10726 *lex_user, columns, grant_table->db,
10727 grant_table->tname, ~(ulong)0, 1))
10728 {
10729 revoked= 1;
10730 continue;
10731 }
10732 result= -1;
10733 }
10734 }
10735 counter++;
10736 }
10737 } while (revoked);
10738
10739 /* Remove procedure access */
10740 if (mysql_revoke_sp_privs(thd, &tables, &sp_handler_function, lex_user) ||
10741 mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user) ||
10742 mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_spec, lex_user) ||
10743 mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_body, lex_user))
10744 result= -1;
10745
10746 ACL_USER_BASE *user_or_role;
10747 /* remove role grants */
10748 if (lex_user->is_role())
10749 {
10750 /* this can not fail due to get_current_user already having searched for it */
10751 user_or_role= find_acl_role(lex_user->user.str);
10752 }
10753 else
10754 {
10755 user_or_role= find_user_exact(lex_user->host.str, lex_user->user.str);
10756 }
10757 /*
10758 Find every role grant pair matching the role_grants array and remove it,
10759 both from the acl_roles_mappings and the roles_mapping table
10760 */
10761 for (counter= 0; counter < user_or_role->role_grants.elements; counter++)
10762 {
10763 ACL_ROLE *role_grant= *dynamic_element(&user_or_role->role_grants,
10764 counter, ACL_ROLE**);
10765 ROLE_GRANT_PAIR *pair = find_role_grant_pair(&lex_user->user,
10766 &lex_user->host,
10767 &role_grant->user);
10768 /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
10769 Roles_mapping_table instead of TABLE directly. */
10770 if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
10771 &lex_user->user, &lex_user->host,
10772 &role_grant->user, false, pair, true))
10773 {
10774 result= -1; //Something went wrong
10775 }
10776 update_role_mapping(&lex_user->user, &lex_user->host,
10777 &role_grant->user, false, pair, true);
10778 /*
10779 Delete from the parent_grantee array of the roles granted,
10780 the entry pointing to this user_or_role
10781 */
10782 remove_ptr_from_dynarray(&role_grant->parent_grantee, user_or_role);
10783 }
10784 /* TODO
10785 How to handle an error in the replace_roles_mapping_table, in
10786 regards to the privileges held in memory
10787 */
10788
10789 /* Finally, clear the role_grants array */
10790 if (counter == user_or_role->role_grants.elements)
10791 {
10792 reset_dynamic(&user_or_role->role_grants);
10793 }
10794 /*
10795 If we are revoking from a role, we need to update all the parent grantees
10796 */
10797 if (lex_user->is_role())
10798 {
10799 propagate_role_grants((ACL_ROLE *)user_or_role, PRIVS_TO_MERGE::ALL);
10800 }
10801 }
10802
10803 mysql_mutex_unlock(&acl_cache->lock);
10804
10805 if (result)
10806 my_message(ER_REVOKE_GRANTS, ER_THD(thd, ER_REVOKE_GRANTS), MYF(0));
10807
10808 result= result |
10809 write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10810
10811 mysql_rwlock_unlock(&LOCK_grant);
10812
10813 DBUG_RETURN(result);
10814}
10815
10816
10817
10818
10819/**
10820 If the defining user for a routine does not exist, then the ACL lookup
10821 code should raise two errors which we should intercept. We convert the more
10822 descriptive error into a warning, and consume the other.
10823
10824 If any other errors are raised, then we set a flag that should indicate
10825 that there was some failure we should complain at a higher level.
10826*/
10827class Silence_routine_definer_errors : public Internal_error_handler
10828{
10829public:
10830 Silence_routine_definer_errors()
10831 : is_grave(FALSE)
10832 {}
10833
10834 virtual ~Silence_routine_definer_errors()
10835 {}
10836
10837 virtual bool handle_condition(THD *thd,
10838 uint sql_errno,
10839 const char* sqlstate,
10840 Sql_condition::enum_warning_level *level,
10841 const char* msg,
10842 Sql_condition ** cond_hdl);
10843
10844 bool has_errors() { return is_grave; }
10845
10846private:
10847 bool is_grave;
10848};
10849
10850bool
10851Silence_routine_definer_errors::handle_condition(
10852 THD *thd,
10853 uint sql_errno,
10854 const char*,
10855 Sql_condition::enum_warning_level *level,
10856 const char* msg,
10857 Sql_condition ** cond_hdl)
10858{
10859 *cond_hdl= NULL;
10860 if (*level == Sql_condition::WARN_LEVEL_ERROR)
10861 {
10862 switch (sql_errno)
10863 {
10864 case ER_NONEXISTING_PROC_GRANT:
10865 /* Convert the error into a warning. */
10866 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
10867 sql_errno, msg);
10868 return TRUE;
10869 default:
10870 is_grave= TRUE;
10871 }
10872 }
10873
10874 return FALSE;
10875}
10876
10877
10878/**
10879 Revoke privileges for all users on a stored procedure. Use an error handler
10880 that converts errors about missing grants into warnings.
10881
10882 @param
10883 thd The current thread.
10884 @param
10885 db DB of the stored procedure
10886 @param
10887 name Name of the stored procedure
10888
10889 @retval
10890 0 OK.
10891 @retval
10892 < 0 Error. Error message not yet sent.
10893*/
10894
10895bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
10896 const Sp_handler *sph)
10897{
10898 uint counter, revoked;
10899 int result;
10900 HASH *hash= sph->get_priv_hash();
10901 Silence_routine_definer_errors error_handler;
10902 DBUG_ENTER("sp_revoke_privileges");
10903
10904 Grant_tables tables(Table_user | Table_db |
10905 Table_tables_priv | Table_columns_priv |
10906 Table_procs_priv | Table_proxies_priv |
10907 Table_roles_mapping, TL_WRITE);
10908 if ((result= tables.open_and_lock(thd)))
10909 DBUG_RETURN(result != 1);
10910
10911 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
10912
10913 /* Be sure to pop this before exiting this scope! */
10914 thd->push_internal_handler(&error_handler);
10915
10916 mysql_rwlock_wrlock(&LOCK_grant);
10917 mysql_mutex_lock(&acl_cache->lock);
10918
10919 /* Remove procedure access */
10920 do
10921 {
10922 for (counter= 0, revoked= 0 ; counter < hash->records ; )
10923 {
10924 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
10925 if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
10926 !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
10927 {
10928 LEX_USER lex_user;
10929 lex_user.user.str= grant_proc->user;
10930 lex_user.user.length= strlen(grant_proc->user);
10931 lex_user.host.str= safe_str(grant_proc->host.hostname);
10932 lex_user.host.length= strlen(lex_user.host.str);
10933 if (replace_routine_table(thd, grant_proc,
10934 tables.procs_priv_table().table(), lex_user,
10935 grant_proc->db, grant_proc->tname,
10936 sph, ~(ulong)0, 1) == 0)
10937 {
10938 revoked= 1;
10939 continue;
10940 }
10941 }
10942 counter++;
10943 }
10944 } while (revoked);
10945
10946 mysql_mutex_unlock(&acl_cache->lock);
10947 mysql_rwlock_unlock(&LOCK_grant);
10948
10949 thd->pop_internal_handler();
10950
10951 DBUG_RETURN(error_handler.has_errors());
10952}
10953
10954
10955/**
10956 Grant EXECUTE,ALTER privilege for a stored procedure
10957
10958 @param thd The current thread.
10959 @param sp_db
10960 @param sp_name
10961 @param sph
10962
10963 @return
10964 @retval FALSE Success
10965 @retval TRUE An error occurred. Error message not yet sent.
10966*/
10967
10968bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
10969 const Sp_handler *sph)
10970{
10971 Security_context *sctx= thd->security_ctx;
10972 LEX_USER *combo;
10973 TABLE_LIST tables[1];
10974 List<LEX_USER> user_list;
10975 bool result;
10976 ACL_USER *au;
10977 Dummy_error_handler error_handler;
10978 DBUG_ENTER("sp_grant_privileges");
10979
10980 if (!(combo=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
10981 DBUG_RETURN(TRUE);
10982
10983 combo->user.str= sctx->user;
10984
10985 mysql_mutex_lock(&acl_cache->lock);
10986
10987 if ((au= find_user_wild(combo->host.str=(char*)sctx->host_or_ip, combo->user.str)))
10988 goto found_acl;
10989 if ((au= find_user_wild(combo->host.str=(char*)sctx->host, combo->user.str)))
10990 goto found_acl;
10991 if ((au= find_user_wild(combo->host.str=(char*)sctx->ip, combo->user.str)))
10992 goto found_acl;
10993 if ((au= find_user_wild(combo->host.str=(char*)"%", combo->user.str)))
10994 goto found_acl;
10995
10996 mysql_mutex_unlock(&acl_cache->lock);
10997 DBUG_RETURN(TRUE);
10998
10999 found_acl:
11000 mysql_mutex_unlock(&acl_cache->lock);
11001
11002 bzero((char*)tables, sizeof(TABLE_LIST));
11003 user_list.empty();
11004
11005 tables->db.str= sp_db;
11006 tables->db.length= sp_db ? strlen(sp_db) : 0;
11007 tables->table_name.str= tables->alias.str= sp_name;
11008 tables->table_name.length= tables->alias.length= sp_name ? strlen(sp_name) : 0;
11009
11010 thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str));
11011 thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str));
11012
11013 combo->reset_auth();
11014
11015 if(au)
11016 {
11017 combo->plugin= au->plugin;
11018 combo->auth= au->auth_string;
11019 }
11020
11021 if (user_list.push_back(combo, thd->mem_root))
11022 DBUG_RETURN(TRUE);
11023
11024 thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
11025 thd->lex->ssl_cipher= thd->lex->x509_subject= thd->lex->x509_issuer= 0;
11026 bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
11027
11028 /*
11029 Only care about whether the operation failed or succeeded
11030 as all errors will be handled later.
11031 */
11032 thd->push_internal_handler(&error_handler);
11033 result= mysql_routine_grant(thd, tables, sph, user_list,
11034 DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
11035 thd->pop_internal_handler();
11036 DBUG_RETURN(result);
11037}
11038
11039
11040/**
11041 Validate if a user can proxy as another user
11042
11043 @thd current thread
11044 @param user the logged in user (proxy user)
11045 @param authenticated_as the effective user a plugin is trying to
11046 impersonate as (proxied user)
11047 @return proxy user definition
11048 @retval NULL proxy user definition not found or not applicable
11049 @retval non-null the proxy user data
11050*/
11051
11052static ACL_PROXY_USER *
11053acl_find_proxy_user(const char *user, const char *host, const char *ip,
11054 const char *authenticated_as, bool *proxy_used)
11055{
11056 uint i;
11057 /* if the proxied and proxy user are the same return OK */
11058 DBUG_ENTER("acl_find_proxy_user");
11059 DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
11060 user, host, ip, authenticated_as));
11061
11062 if (!strcmp(authenticated_as, user))
11063 {
11064 DBUG_PRINT ("info", ("user is the same as authenticated_as"));
11065 DBUG_RETURN (NULL);
11066 }
11067
11068 *proxy_used= TRUE;
11069 for (i=0; i < acl_proxy_users.elements; i++)
11070 {
11071 ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11072 ACL_PROXY_USER *);
11073 if (proxy->matches(host, user, ip, authenticated_as))
11074 DBUG_RETURN(proxy);
11075 }
11076
11077 DBUG_RETURN(NULL);
11078}
11079
11080
11081bool
11082acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
11083 bool with_grant)
11084{
11085 DBUG_ENTER("acl_check_proxy_grant_access");
11086 DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
11087 (int) with_grant));
11088 if (!initialized)
11089 {
11090 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
11091 DBUG_RETURN(1);
11092 }
11093
11094 /* replication slave thread can do anything */
11095 if (thd->slave_thread)
11096 {
11097 DBUG_PRINT("info", ("replication slave"));
11098 DBUG_RETURN(FALSE);
11099 }
11100
11101 /*
11102 one can grant proxy for self to others.
11103 Security context in THD contains two pairs of (user,host):
11104 1. (user,host) pair referring to inbound connection.
11105 2. (priv_user,priv_host) pair obtained from mysql.user table after doing
11106 authnetication of incoming connection.
11107 Privileges should be checked wrt (priv_user, priv_host) tuple, because
11108 (user,host) pair obtained from inbound connection may have different
11109 values than what is actually stored in mysql.user table and while granting
11110 or revoking proxy privilege, user is expected to provide entries mentioned
11111 in mysql.user table.
11112 */
11113 if (!strcmp(thd->security_ctx->priv_user, user) &&
11114 !my_strcasecmp(system_charset_info, host,
11115 thd->security_ctx->priv_host))
11116 {
11117 DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
11118 thd->security_ctx->priv_user, user,
11119 host, thd->security_ctx->priv_host));
11120 DBUG_RETURN(FALSE);
11121 }
11122
11123 mysql_mutex_lock(&acl_cache->lock);
11124
11125 /* check for matching WITH PROXY rights */
11126 for (uint i=0; i < acl_proxy_users.elements; i++)
11127 {
11128 ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11129 ACL_PROXY_USER *);
11130 if (proxy->matches(thd->security_ctx->host,
11131 thd->security_ctx->user,
11132 thd->security_ctx->ip,
11133 user) &&
11134 proxy->get_with_grant())
11135 {
11136 DBUG_PRINT("info", ("found"));
11137 mysql_mutex_unlock(&acl_cache->lock);
11138 DBUG_RETURN(FALSE);
11139 }
11140 }
11141
11142 mysql_mutex_unlock(&acl_cache->lock);
11143 my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
11144 thd->security_ctx->user,
11145 thd->security_ctx->host_or_ip);
11146 DBUG_RETURN(TRUE);
11147}
11148
11149
11150static bool
11151show_proxy_grants(THD *thd, const char *username, const char *hostname,
11152 char *buff, size_t buffsize)
11153{
11154 Protocol *protocol= thd->protocol;
11155 int error= 0;
11156
11157 for (uint 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->granted_on(hostname, username))
11162 {
11163 String global(buff, buffsize, system_charset_info);
11164 global.length(0);
11165 proxy->print_grant(&global);
11166 protocol->prepare_for_resend();
11167 protocol->store(global.ptr(), global.length(), global.charset());
11168 if (protocol->write())
11169 {
11170 error= -1;
11171 break;
11172 }
11173 }
11174 }
11175 return error;
11176}
11177
11178static int enabled_roles_insert(ACL_USER_BASE *role, void *context_data)
11179{
11180 TABLE *table= (TABLE*) context_data;
11181 DBUG_ASSERT(role->flags & IS_ROLE);
11182
11183 restore_record(table, s->default_values);
11184 table->field[0]->set_notnull();
11185 table->field[0]->store(role->user.str, role->user.length,
11186 system_charset_info);
11187 if (schema_table_store_record(table->in_use, table))
11188 return -1;
11189 return 0;
11190}
11191
11192struct APPLICABLE_ROLES_DATA
11193{
11194 TABLE *table;
11195 const LEX_CSTRING host;
11196 const LEX_CSTRING user_and_host;
11197 ACL_USER *user;
11198};
11199
11200static int
11201applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr)
11202{
11203 APPLICABLE_ROLES_DATA *data= (APPLICABLE_ROLES_DATA *)ptr;
11204 CHARSET_INFO *cs= system_charset_info;
11205 TABLE *table= data->table;
11206 bool is_role= grantee != data->user;
11207 const LEX_CSTRING *user_and_host= is_role ? &grantee->user
11208 : &data->user_and_host;
11209 const LEX_CSTRING *host= is_role ? &empty_clex_str : &data->host;
11210
11211 restore_record(table, s->default_values);
11212 table->field[0]->store(user_and_host->str, user_and_host->length, cs);
11213 table->field[1]->store(role->user.str, role->user.length, cs);
11214
11215 ROLE_GRANT_PAIR *pair=
11216 find_role_grant_pair(&grantee->user, host, &role->user);
11217 DBUG_ASSERT(pair);
11218
11219 if (pair->with_admin)
11220 table->field[2]->store(STRING_WITH_LEN("YES"), cs);
11221 else
11222 table->field[2]->store(STRING_WITH_LEN("NO"), cs);
11223
11224 /* Default role is only valid when looking at a role granted to a user. */
11225 if (!is_role)
11226 {
11227 if (data->user->default_rolename.length &&
11228 lex_string_eq(&data->user->default_rolename, &role->user))
11229 table->field[3]->store(STRING_WITH_LEN("YES"), cs);
11230 else
11231 table->field[3]->store(STRING_WITH_LEN("NO"), cs);
11232 table->field[3]->set_notnull();
11233 }
11234
11235 if (schema_table_store_record(table->in_use, table))
11236 return -1;
11237 return 0;
11238}
11239
11240/**
11241 Hash iterate function to count the number of total column privileges granted.
11242*/
11243static my_bool count_column_grants(void *grant_table,
11244 void *current_count)
11245{
11246 HASH hash_columns = ((GRANT_TABLE *)grant_table)->hash_columns;
11247 *(ulong *)current_count+= hash_columns.records;
11248 return 0;
11249}
11250
11251/**
11252 SHOW function that computes the number of column grants.
11253
11254 This must be performed under the mutex in order to make sure the
11255 iteration does not fail.
11256*/
11257static int show_column_grants(THD *thd, SHOW_VAR *var, char *buff,
11258 enum enum_var_type scope)
11259{
11260 var->type= SHOW_ULONG;
11261 var->value= buff;
11262 *(ulong *)buff= 0;
11263 if (initialized)
11264 {
11265 mysql_rwlock_rdlock(&LOCK_grant);
11266 mysql_mutex_lock(&acl_cache->lock);
11267 my_hash_iterate(&column_priv_hash, count_column_grants, buff);
11268 mysql_mutex_unlock(&acl_cache->lock);
11269 mysql_rwlock_unlock(&LOCK_grant);
11270 }
11271 return 0;
11272}
11273
11274
11275#else
11276bool check_grant(THD *, ulong, TABLE_LIST *, bool, uint, bool)
11277{
11278 return 0;
11279}
11280#endif /*NO_EMBEDDED_ACCESS_CHECKS */
11281
11282SHOW_VAR acl_statistics[] = {
11283#ifndef NO_EMBEDDED_ACCESS_CHECKS
11284 {"column_grants", (char*)show_column_grants, SHOW_SIMPLE_FUNC},
11285 {"database_grants", (char*)&acl_dbs.elements, SHOW_UINT},
11286 {"function_grants", (char*)&func_priv_hash.records, SHOW_ULONG},
11287 {"procedure_grants", (char*)&proc_priv_hash.records, SHOW_ULONG},
11288 {"package_spec_grants", (char*)&package_spec_priv_hash.records, SHOW_ULONG},
11289 {"package_body_grants", (char*)&package_body_priv_hash.records, SHOW_ULONG},
11290 {"proxy_users", (char*)&acl_proxy_users.elements, SHOW_UINT},
11291 {"role_grants", (char*)&acl_roles_mappings.records, SHOW_ULONG},
11292 {"roles", (char*)&acl_roles.records, SHOW_ULONG},
11293 {"table_grants", (char*)&column_priv_hash.records, SHOW_ULONG},
11294 {"users", (char*)&acl_users.elements, SHOW_UINT},
11295#endif
11296 {NullS, NullS, SHOW_LONG},
11297};
11298
11299/* Check if a role is granted to a user/role. We traverse the role graph
11300 and return true if we find a match.
11301
11302 hostname == NULL means we are looking for a role as a starting point,
11303 otherwise a user.
11304*/
11305bool check_role_is_granted(const char *username,
11306 const char *hostname,
11307 const char *rolename)
11308{
11309 DBUG_ENTER("check_role_is_granted");
11310 bool result= false;
11311#ifndef NO_EMBEDDED_ACCESS_CHECKS
11312 ACL_USER_BASE *root;
11313 mysql_mutex_lock(&acl_cache->lock);
11314 if (hostname)
11315 root= find_user_exact(username, hostname);
11316 else
11317 root= find_acl_role(username);
11318
11319 LEX_CSTRING role_lex;
11320 role_lex.str= rolename;
11321 role_lex.length= strlen(rolename);
11322
11323 if (root && /* No grantee, nothing to search. */
11324 traverse_role_graph_down(root, &role_lex, check_role_is_granted_callback,
11325 NULL) == -1)
11326 {
11327 /* We have found the role during our search. */
11328 result= true;
11329 }
11330
11331 /* We haven't found the role or we had no initial grantee to start from. */
11332 mysql_mutex_unlock(&acl_cache->lock);
11333#endif
11334 DBUG_RETURN(result);
11335}
11336
11337int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond)
11338{
11339 TABLE *table= tables->table;
11340#ifndef NO_EMBEDDED_ACCESS_CHECKS
11341 if (thd->security_ctx->priv_role[0])
11342 {
11343 mysql_rwlock_rdlock(&LOCK_grant);
11344 mysql_mutex_lock(&acl_cache->lock);
11345 ACL_ROLE *acl_role= find_acl_role(thd->security_ctx->priv_role);
11346 if (acl_role)
11347 traverse_role_graph_down(acl_role, table, enabled_roles_insert, NULL);
11348 mysql_mutex_unlock(&acl_cache->lock);
11349 mysql_rwlock_unlock(&LOCK_grant);
11350 if (acl_role)
11351 return 0;
11352 }
11353#endif
11354
11355 restore_record(table, s->default_values);
11356 table->field[0]->set_null();
11357 return schema_table_store_record(table->in_use, table);
11358}
11359
11360
11361/*
11362 This shows all roles granted to current user
11363 and recursively all roles granted to those roles
11364*/
11365int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond)
11366{
11367 int res= 0;
11368#ifndef NO_EMBEDDED_ACCESS_CHECKS
11369 if (initialized)
11370 {
11371 TABLE *table= tables->table;
11372 Security_context *sctx= thd->security_ctx;
11373 mysql_rwlock_rdlock(&LOCK_grant);
11374 mysql_mutex_lock(&acl_cache->lock);
11375 ACL_USER *user= find_user_exact(sctx->priv_host, sctx->priv_user);
11376 if (user)
11377 {
11378 char buff[USER_HOST_BUFF_SIZE+10];
11379 DBUG_ASSERT(user->user.length + user->hostname_length +2 < sizeof(buff));
11380 char *end= strxmov(buff, user->user.str, "@", user->host.hostname, NULL);
11381 APPLICABLE_ROLES_DATA data= { table,
11382 { user->host.hostname, user->hostname_length },
11383 { buff, (size_t)(end - buff) }, user
11384 };
11385
11386 res= traverse_role_graph_down(user, &data, 0, applicable_roles_insert);
11387 }
11388
11389 mysql_mutex_unlock(&acl_cache->lock);
11390 mysql_rwlock_unlock(&LOCK_grant);
11391 }
11392#endif
11393
11394 return res;
11395}
11396
11397
11398int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
11399{
11400 int flag;
11401 DBUG_ENTER("wild_case_compare");
11402 DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr));
11403 while (*wildstr)
11404 {
11405 while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
11406 {
11407 if (*wildstr == wild_prefix && wildstr[1])
11408 wildstr++;
11409 if (my_toupper(cs, *wildstr++) !=
11410 my_toupper(cs, *str++)) DBUG_RETURN(1);
11411 }
11412 if (! *wildstr ) DBUG_RETURN (*str != 0);
11413 if (*wildstr++ == wild_one)
11414 {
11415 if (! *str++) DBUG_RETURN (1); /* One char; skip */
11416 }
11417 else
11418 { /* Found '*' */
11419 if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */
11420 flag=(*wildstr != wild_many && *wildstr != wild_one);
11421 do
11422 {
11423 if (flag)
11424 {
11425 char cmp;
11426 if ((cmp= *wildstr) == wild_prefix && wildstr[1])
11427 cmp=wildstr[1];
11428 cmp=my_toupper(cs, cmp);
11429 while (*str && my_toupper(cs, *str) != cmp)
11430 str++;
11431 if (!*str) DBUG_RETURN (1);
11432 }
11433 if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
11434 } while (*str++);
11435 DBUG_RETURN(1);
11436 }
11437 }
11438 DBUG_RETURN (*str != '\0');
11439}
11440
11441
11442#ifndef NO_EMBEDDED_ACCESS_CHECKS
11443static bool update_schema_privilege(THD *thd, TABLE *table, char *buff,
11444 const char* db, const char* t_name,
11445 const char* column, uint col_length,
11446 const char *priv, uint priv_length,
11447 const char* is_grantable)
11448{
11449 int i= 2;
11450 CHARSET_INFO *cs= system_charset_info;
11451 restore_record(table, s->default_values);
11452 table->field[0]->store(buff, (uint) strlen(buff), cs);
11453 table->field[1]->store(STRING_WITH_LEN("def"), cs);
11454 if (db)
11455 table->field[i++]->store(db, (uint) strlen(db), cs);
11456 if (t_name)
11457 table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
11458 if (column)
11459 table->field[i++]->store(column, col_length, cs);
11460 table->field[i++]->store(priv, priv_length, cs);
11461 table->field[i]->store(is_grantable, strlen(is_grantable), cs);
11462 return schema_table_store_record(thd, table);
11463}
11464#endif
11465
11466
11467int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11468{
11469#ifndef NO_EMBEDDED_ACCESS_CHECKS
11470 int error= 0;
11471 uint counter;
11472 ACL_USER *acl_user;
11473 ulong want_access;
11474 char buff[100];
11475 TABLE *table= tables->table;
11476 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11477 NULL, NULL, 1, 1);
11478 char *curr_host= thd->security_ctx->priv_host_name();
11479 DBUG_ENTER("fill_schema_user_privileges");
11480
11481 if (!initialized)
11482 DBUG_RETURN(0);
11483 mysql_mutex_lock(&acl_cache->lock);
11484
11485 for (counter=0 ; counter < acl_users.elements ; counter++)
11486 {
11487 const char *user,*host, *is_grantable="YES";
11488 acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
11489 user= safe_str(acl_user->user.str);
11490 host= safe_str(acl_user->host.hostname);
11491
11492 if (no_global_access &&
11493 (strcmp(thd->security_ctx->priv_user, user) ||
11494 my_strcasecmp(system_charset_info, curr_host, host)))
11495 continue;
11496
11497 want_access= acl_user->access;
11498 if (!(want_access & GRANT_ACL))
11499 is_grantable= "NO";
11500
11501 strxmov(buff,"'",user,"'@'",host,"'",NullS);
11502 if (!(want_access & ~GRANT_ACL))
11503 {
11504 if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
11505 STRING_WITH_LEN("USAGE"), is_grantable))
11506 {
11507 error= 1;
11508 goto err;
11509 }
11510 }
11511 else
11512 {
11513 uint priv_id;
11514 ulong j,test_access= want_access & ~GRANT_ACL;
11515 for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
11516 {
11517 if (test_access & j)
11518 {
11519 if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
11520 command_array[priv_id],
11521 command_lengths[priv_id], is_grantable))
11522 {
11523 error= 1;
11524 goto err;
11525 }
11526 }
11527 }
11528 }
11529 }
11530err:
11531 mysql_mutex_unlock(&acl_cache->lock);
11532
11533 DBUG_RETURN(error);
11534#else
11535 return(0);
11536#endif
11537}
11538
11539
11540int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11541{
11542#ifndef NO_EMBEDDED_ACCESS_CHECKS
11543 int error= 0;
11544 uint counter;
11545 ACL_DB *acl_db;
11546 ulong want_access;
11547 char buff[100];
11548 TABLE *table= tables->table;
11549 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11550 NULL, NULL, 1, 1);
11551 char *curr_host= thd->security_ctx->priv_host_name();
11552 DBUG_ENTER("fill_schema_schema_privileges");
11553
11554 if (!initialized)
11555 DBUG_RETURN(0);
11556 mysql_mutex_lock(&acl_cache->lock);
11557
11558 for (counter=0 ; counter < acl_dbs.elements ; counter++)
11559 {
11560 const char *user, *host, *is_grantable="YES";
11561
11562 acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
11563 user= safe_str(acl_db->user);
11564 host= safe_str(acl_db->host.hostname);
11565
11566 if (no_global_access &&
11567 (strcmp(thd->security_ctx->priv_user, user) ||
11568 my_strcasecmp(system_charset_info, curr_host, host)))
11569 continue;
11570
11571 want_access=acl_db->access;
11572 if (want_access)
11573 {
11574 if (!(want_access & GRANT_ACL))
11575 {
11576 is_grantable= "NO";
11577 }
11578 strxmov(buff,"'",user,"'@'",host,"'",NullS);
11579 if (!(want_access & ~GRANT_ACL))
11580 {
11581 if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0,
11582 0, STRING_WITH_LEN("USAGE"), is_grantable))
11583 {
11584 error= 1;
11585 goto err;
11586 }
11587 }
11588 else
11589 {
11590 int cnt;
11591 ulong j,test_access= want_access & ~GRANT_ACL;
11592 for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
11593 if (test_access & j)
11594 {
11595 if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0, 0,
11596 command_array[cnt], command_lengths[cnt],
11597 is_grantable))
11598 {
11599 error= 1;
11600 goto err;
11601 }
11602 }
11603 }
11604 }
11605 }
11606err:
11607 mysql_mutex_unlock(&acl_cache->lock);
11608
11609 DBUG_RETURN(error);
11610#else
11611 return (0);
11612#endif
11613}
11614
11615
11616int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11617{
11618#ifndef NO_EMBEDDED_ACCESS_CHECKS
11619 int error= 0;
11620 uint index;
11621 char buff[100];
11622 TABLE *table= tables->table;
11623 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11624 NULL, NULL, 1, 1);
11625 char *curr_host= thd->security_ctx->priv_host_name();
11626 DBUG_ENTER("fill_schema_table_privileges");
11627
11628 mysql_rwlock_rdlock(&LOCK_grant);
11629
11630 for (index=0 ; index < column_priv_hash.records ; index++)
11631 {
11632 const char *user, *host, *is_grantable= "YES";
11633 GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
11634 index);
11635 user= safe_str(grant_table->user);
11636 host= safe_str(grant_table->host.hostname);
11637
11638 if (no_global_access &&
11639 (strcmp(thd->security_ctx->priv_user, user) ||
11640 my_strcasecmp(system_charset_info, curr_host, host)))
11641 continue;
11642
11643 ulong table_access= grant_table->privs;
11644 if (table_access)
11645 {
11646 ulong test_access= table_access & ~GRANT_ACL;
11647 /*
11648 We should skip 'usage' privilege on table if
11649 we have any privileges on column(s) of this table
11650 */
11651 if (!test_access && grant_table->cols)
11652 continue;
11653 if (!(table_access & GRANT_ACL))
11654 is_grantable= "NO";
11655
11656 strxmov(buff, "'", user, "'@'", host, "'", NullS);
11657 if (!test_access)
11658 {
11659 if (update_schema_privilege(thd, table, buff, grant_table->db,
11660 grant_table->tname, 0, 0,
11661 STRING_WITH_LEN("USAGE"), is_grantable))
11662 {
11663 error= 1;
11664 goto err;
11665 }
11666 }
11667 else
11668 {
11669 ulong j;
11670 int cnt;
11671 for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
11672 {
11673 if (test_access & j)
11674 {
11675 if (update_schema_privilege(thd, table, buff, grant_table->db,
11676 grant_table->tname, 0, 0,
11677 command_array[cnt],
11678 command_lengths[cnt], is_grantable))
11679 {
11680 error= 1;
11681 goto err;
11682 }
11683 }
11684 }
11685 }
11686 }
11687 }
11688err:
11689 mysql_rwlock_unlock(&LOCK_grant);
11690
11691 DBUG_RETURN(error);
11692#else
11693 return (0);
11694#endif
11695}
11696
11697
11698int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11699{
11700#ifndef NO_EMBEDDED_ACCESS_CHECKS
11701 int error= 0;
11702 uint index;
11703 char buff[100];
11704 TABLE *table= tables->table;
11705 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11706 NULL, NULL, 1, 1);
11707 char *curr_host= thd->security_ctx->priv_host_name();
11708 DBUG_ENTER("fill_schema_table_privileges");
11709
11710 mysql_rwlock_rdlock(&LOCK_grant);
11711
11712 for (index=0 ; index < column_priv_hash.records ; index++)
11713 {
11714 const char *user, *host, *is_grantable= "YES";
11715 GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
11716 index);
11717 user= safe_str(grant_table->user);
11718 host= safe_str(grant_table->host.hostname);
11719
11720 if (no_global_access &&
11721 (strcmp(thd->security_ctx->priv_user, user) ||
11722 my_strcasecmp(system_charset_info, curr_host, host)))
11723 continue;
11724
11725 ulong table_access= grant_table->cols;
11726 if (table_access != 0)
11727 {
11728 if (!(grant_table->privs & GRANT_ACL))
11729 is_grantable= "NO";
11730
11731 ulong test_access= table_access & ~GRANT_ACL;
11732 strxmov(buff, "'", user, "'@'", host, "'", NullS);
11733 if (!test_access)
11734 continue;
11735 else
11736 {
11737 ulong j;
11738 int cnt;
11739 for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
11740 {
11741 if (test_access & j)
11742 {
11743 for (uint col_index=0 ;
11744 col_index < grant_table->hash_columns.records ;
11745 col_index++)
11746 {
11747 GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
11748 my_hash_element(&grant_table->hash_columns,col_index);
11749 if ((grant_column->rights & j) && (table_access & j))
11750 {
11751 if (update_schema_privilege(thd, table, buff, grant_table->db,
11752 grant_table->tname,
11753 grant_column->column,
11754 grant_column->key_length,
11755 command_array[cnt],
11756 command_lengths[cnt], is_grantable))
11757 {
11758 error= 1;
11759 goto err;
11760 }
11761 }
11762 }
11763 }
11764 }
11765 }
11766 }
11767 }
11768err:
11769 mysql_rwlock_unlock(&LOCK_grant);
11770
11771 DBUG_RETURN(error);
11772#else
11773 return (0);
11774#endif
11775}
11776
11777
11778#ifndef NO_EMBEDDED_ACCESS_CHECKS
11779/*
11780 fill effective privileges for table
11781
11782 SYNOPSIS
11783 fill_effective_table_privileges()
11784 thd thread handler
11785 grant grants table descriptor
11786 db db name
11787 table table name
11788*/
11789
11790void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
11791 const char *db, const char *table)
11792{
11793 Security_context *sctx= thd->security_ctx;
11794 DBUG_ENTER("fill_effective_table_privileges");
11795 DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
11796 sctx->priv_host, sctx->ip, sctx->priv_user, db, table));
11797 /* --skip-grants */
11798 if (!initialized)
11799 {
11800 DBUG_PRINT("info", ("skip grants"));
11801 grant->privilege= ~NO_ACCESS; // everything is allowed
11802 DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
11803 DBUG_VOID_RETURN;
11804 }
11805
11806 /* global privileges */
11807 grant->privilege= sctx->master_access;
11808
11809 if (!thd->db.str || strcmp(db, thd->db.str))
11810 {
11811 /* db privileges */
11812 grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
11813 /* db privileges for role */
11814 if (sctx->priv_role[0])
11815 grant->privilege|= acl_get("", "", sctx->priv_role, db, 0);
11816 }
11817 else
11818 {
11819 grant->privilege|= sctx->db_access;
11820 }
11821
11822 /* table privileges */
11823 mysql_rwlock_rdlock(&LOCK_grant);
11824 if (grant->version != grant_version)
11825 {
11826 grant->grant_table_user=
11827 table_hash_search(sctx->host, sctx->ip, db,
11828 sctx->priv_user,
11829 table, 0); /* purecov: inspected */
11830 grant->grant_table_role=
11831 sctx->priv_role[0] ? table_hash_search("", "", db,
11832 sctx->priv_role,
11833 table, TRUE) : NULL;
11834 grant->version= grant_version; /* purecov: inspected */
11835 }
11836 if (grant->grant_table_user != 0)
11837 {
11838 grant->privilege|= grant->grant_table_user->privs;
11839 }
11840 if (grant->grant_table_role != 0)
11841 {
11842 grant->privilege|= grant->grant_table_role->privs;
11843 }
11844 mysql_rwlock_unlock(&LOCK_grant);
11845
11846 DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
11847 DBUG_VOID_RETURN;
11848}
11849
11850#else /* NO_EMBEDDED_ACCESS_CHECKS */
11851
11852/****************************************************************************
11853 Dummy wrappers when we don't have any access checks
11854****************************************************************************/
11855
11856bool check_routine_level_acl(THD *thd, const char *db, const char *name,
11857 const Sp_handler *sph)
11858{
11859 return FALSE;
11860}
11861
11862#endif
11863
11864/**
11865 Return information about user or current user.
11866
11867 @param[in] thd thread handler
11868 @param[in] user user
11869 @param[in] lock whether &acl_cache->lock mutex needs to be locked
11870
11871 @return
11872 - On success, return a valid pointer to initialized
11873 LEX_USER, which contains user information.
11874 - On error, return 0.
11875*/
11876
11877LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock)
11878{
11879 if (user->user.str == current_user.str) // current_user
11880 return create_default_definer(thd, false);
11881
11882 if (user->user.str == current_role.str) // current_role
11883 return create_default_definer(thd, true);
11884
11885 if (user->host.str == NULL) // Possibly a role
11886 {
11887 // to be reexecution friendly we have to make a copy
11888 LEX_USER *dup= (LEX_USER*) thd->memdup(user, sizeof(*user));
11889 if (!dup)
11890 return 0;
11891
11892#ifndef NO_EMBEDDED_ACCESS_CHECKS
11893 if (has_auth(user, thd->lex))
11894 {
11895 dup->host= host_not_specified;
11896 return dup;
11897 }
11898
11899 if (is_invalid_role_name(user->user.str))
11900 return 0;
11901
11902 if (lock)
11903 mysql_mutex_lock(&acl_cache->lock);
11904 if (find_acl_role(dup->user.str))
11905 dup->host= empty_clex_str;
11906 else
11907 dup->host= host_not_specified;
11908 if (lock)
11909 mysql_mutex_unlock(&acl_cache->lock);
11910#endif
11911
11912 return dup;
11913 }
11914
11915 return user;
11916}
11917
11918struct ACL_internal_schema_registry_entry
11919{
11920 const LEX_CSTRING *m_name;
11921 const ACL_internal_schema_access *m_access;
11922};
11923
11924/**
11925 Internal schema registered.
11926 Currently, this is only:
11927 - performance_schema
11928 - information_schema,
11929 This can be reused later for:
11930 - mysql
11931*/
11932static ACL_internal_schema_registry_entry registry_array[2];
11933static uint m_registry_array_size= 0;
11934
11935/**
11936 Add an internal schema to the registry.
11937 @param name the schema name
11938 @param access the schema ACL specific rules
11939*/
11940void ACL_internal_schema_registry::register_schema
11941 (const LEX_CSTRING *name, const ACL_internal_schema_access *access)
11942{
11943 DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
11944
11945 /* Not thread safe, and does not need to be. */
11946 registry_array[m_registry_array_size].m_name= name;
11947 registry_array[m_registry_array_size].m_access= access;
11948 m_registry_array_size++;
11949}
11950
11951/**
11952 Search per internal schema ACL by name.
11953 @param name a schema name
11954 @return per schema rules, or NULL
11955*/
11956const ACL_internal_schema_access *
11957ACL_internal_schema_registry::lookup(const char *name)
11958{
11959 DBUG_ASSERT(name != NULL);
11960
11961 uint i;
11962
11963 for (i= 0; i<m_registry_array_size; i++)
11964 {
11965 if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
11966 name) == 0)
11967 return registry_array[i].m_access;
11968 }
11969 return NULL;
11970}
11971
11972/**
11973 Get a cached internal schema access.
11974 @param grant_internal_info the cache
11975 @param schema_name the name of the internal schema
11976*/
11977const ACL_internal_schema_access *
11978get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
11979 const char *schema_name)
11980{
11981 if (grant_internal_info)
11982 {
11983 if (! grant_internal_info->m_schema_lookup_done)
11984 {
11985 grant_internal_info->m_schema_access=
11986 ACL_internal_schema_registry::lookup(schema_name);
11987 grant_internal_info->m_schema_lookup_done= TRUE;
11988 }
11989 return grant_internal_info->m_schema_access;
11990 }
11991 return ACL_internal_schema_registry::lookup(schema_name);
11992}
11993
11994/**
11995 Get a cached internal table access.
11996 @param grant_internal_info the cache
11997 @param schema_name the name of the internal schema
11998 @param table_name the name of the internal table
11999*/
12000const ACL_internal_table_access *
12001get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
12002 const char *schema_name,
12003 const char *table_name)
12004{
12005 DBUG_ASSERT(grant_internal_info);
12006 if (! grant_internal_info->m_table_lookup_done)
12007 {
12008 const ACL_internal_schema_access *schema_access;
12009 schema_access= get_cached_schema_access(grant_internal_info, schema_name);
12010 if (schema_access)
12011 grant_internal_info->m_table_access= schema_access->lookup(table_name);
12012 grant_internal_info->m_table_lookup_done= TRUE;
12013 }
12014 return grant_internal_info->m_table_access;
12015}
12016
12017
12018/****************************************************************************
12019 AUTHENTICATION CODE
12020 including initial connect handshake, invoking appropriate plugins,
12021 client-server plugin negotiation, COM_CHANGE_USER, and native
12022 MySQL authentication plugins.
12023****************************************************************************/
12024
12025/* few defines to have less ifdef's in the code below */
12026#ifdef EMBEDDED_LIBRARY
12027#undef HAVE_OPENSSL
12028#ifdef NO_EMBEDDED_ACCESS_CHECKS
12029#define initialized 0
12030#define check_for_max_user_connections(X,Y) 0
12031#define get_or_create_user_conn(A,B,C,D) 0
12032#endif
12033#endif
12034#ifndef HAVE_OPENSSL
12035#define ssl_acceptor_fd 0
12036#define sslaccept(A,B,C,D) 1
12037#endif
12038
12039/**
12040 The internal version of what plugins know as MYSQL_PLUGIN_VIO,
12041 basically the context of the authentication session
12042*/
12043struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
12044{
12045 MYSQL_SERVER_AUTH_INFO auth_info;
12046 ACL_USER *acl_user; ///< a copy, independent from acl_users array
12047 plugin_ref plugin; ///< what plugin we're under
12048 LEX_CSTRING db; ///< db name from the handshake packet
12049 /** when restarting a plugin this caches the last client reply */
12050 struct {
12051 const char *plugin;
12052 char *pkt; ///< pointer into NET::buff
12053 uint pkt_len;
12054 } cached_client_reply;
12055 /** this caches the first plugin packet for restart request on the client */
12056 struct {
12057 char *pkt;
12058 uint pkt_len;
12059 } cached_server_packet;
12060 int packets_read, packets_written; ///< counters for send/received packets
12061 bool make_it_fail;
12062 /** when plugin returns a failure this tells us what really happened */
12063 enum { SUCCESS, FAILURE, RESTART } status;
12064};
12065
12066/**
12067 a helper function to report an access denied error in all the proper places
12068*/
12069static void login_failed_error(THD *thd)
12070{
12071 my_error(access_denied_error_code(thd->password), MYF(0),
12072 thd->main_security_ctx.user,
12073 thd->main_security_ctx.host_or_ip,
12074 thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12075 general_log_print(thd, COM_CONNECT,
12076 ER_THD(thd, access_denied_error_code(thd->password)),
12077 thd->main_security_ctx.user,
12078 thd->main_security_ctx.host_or_ip,
12079 thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12080 status_var_increment(thd->status_var.access_denied_errors);
12081 /*
12082 Log access denied messages to the error log when log-warnings = 2
12083 so that the overhead of the general query log is not required to track
12084 failed connections.
12085 */
12086 if (global_system_variables.log_warnings > 1)
12087 {
12088 sql_print_warning(ER_THD(thd, access_denied_error_code(thd->password)),
12089 thd->main_security_ctx.user,
12090 thd->main_security_ctx.host_or_ip,
12091 thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12092 }
12093}
12094
12095/**
12096 sends a server handshake initialization packet, the very first packet
12097 after the connection was established
12098
12099 Packet format:
12100
12101 Bytes Content
12102 ----- ----
12103 1 protocol version (always 10)
12104 n server version string, \0-terminated
12105 4 thread id
12106 8 first 8 bytes of the plugin provided data (scramble)
12107 1 \0 byte, terminating the first part of a scramble
12108 2 server capabilities (two lower bytes)
12109 1 server character set
12110 2 server status
12111 2 server capabilities (two upper bytes)
12112 1 length of the scramble
12113 10 reserved, always 0
12114 n rest of the plugin provided data (at least 12 bytes)
12115 1 \0 byte, terminating the second part of a scramble
12116
12117 @retval 0 ok
12118 @retval 1 error
12119*/
12120static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
12121 const char *data, uint data_len)
12122{
12123 DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
12124 DBUG_ASSERT(data_len <= 255);
12125
12126 THD *thd= mpvio->auth_info.thd;
12127 char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + 1 + data_len + 64);
12128 char scramble_buf[SCRAMBLE_LENGTH];
12129 char *end= buff;
12130 DBUG_ENTER("send_server_handshake_packet");
12131
12132 *end++= protocol_version;
12133
12134 thd->client_capabilities= CLIENT_BASIC_FLAGS;
12135
12136 if (opt_using_transactions)
12137 thd->client_capabilities|= CLIENT_TRANSACTIONS;
12138
12139 thd->client_capabilities|= CAN_CLIENT_COMPRESS;
12140
12141 if (ssl_acceptor_fd)
12142 {
12143 thd->client_capabilities |= CLIENT_SSL;
12144 thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
12145 }
12146
12147 if (data_len)
12148 {
12149 mpvio->cached_server_packet.pkt= (char*)thd->memdup(data, data_len);
12150 mpvio->cached_server_packet.pkt_len= data_len;
12151 }
12152
12153 if (data_len < SCRAMBLE_LENGTH)
12154 {
12155 if (data_len)
12156 {
12157 /*
12158 the first packet *must* have at least 20 bytes of a scramble.
12159 if a plugin provided less, we pad it to 20 with zeros
12160 */
12161 memcpy(scramble_buf, data, data_len);
12162 bzero(scramble_buf + data_len, SCRAMBLE_LENGTH - data_len);
12163 data= scramble_buf;
12164 }
12165 else
12166 {
12167 /*
12168 if the default plugin does not provide the data for the scramble at
12169 all, we generate a scramble internally anyway, just in case the
12170 user account (that will be known only later) uses a
12171 native_password_plugin (which needs a scramble). If we don't send a
12172 scramble now - wasting 20 bytes in the packet -
12173 native_password_plugin will have to send it in a separate packet,
12174 adding one more round trip.
12175 */
12176 thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
12177 data= thd->scramble;
12178 }
12179 data_len= SCRAMBLE_LENGTH;
12180 }
12181
12182 /* When server version is specified in config file, don't include
12183 the replication hack prefix. */
12184 if (using_custom_server_version)
12185 end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
12186 else
12187 end= strxnmov(end, SERVER_VERSION_LENGTH, RPL_VERSION_HACK, server_version, NullS) + 1;
12188
12189 int4store((uchar*) end, mpvio->auth_info.thd->thread_id);
12190 end+= 4;
12191
12192 /*
12193 Old clients does not understand long scrambles, but can ignore packet
12194 tail: that's why first part of the scramble is placed here, and second
12195 part at the end of packet.
12196 */
12197 end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
12198 end+= SCRAMBLE_LENGTH_323;
12199 *end++= 0;
12200
12201 int2store(end, thd->client_capabilities);
12202 /* write server characteristics: up to 16 bytes allowed */
12203 end[2]= (char) default_charset_info->number;
12204 int2store(end+3, mpvio->auth_info.thd->server_status);
12205 int2store(end+5, thd->client_capabilities >> 16);
12206 end[7]= data_len;
12207 DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
12208 bzero(end + 8, 6);
12209 int4store(end + 14, thd->client_capabilities >> 32);
12210 end+= 18;
12211 /* write scramble tail */
12212 end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
12213 data_len - SCRAMBLE_LENGTH_323);
12214 end+= data_len - SCRAMBLE_LENGTH_323;
12215 end= strmake(end, plugin_name(mpvio->plugin)->str,
12216 plugin_name(mpvio->plugin)->length);
12217
12218 int res= my_net_write(&mpvio->auth_info.thd->net, (uchar*) buff,
12219 (size_t) (end - buff + 1)) ||
12220 net_flush(&mpvio->auth_info.thd->net);
12221 my_afree(buff);
12222 DBUG_RETURN (res);
12223}
12224
12225static bool secure_auth(THD *thd)
12226{
12227 if (!opt_secure_auth)
12228 return 0;
12229
12230 /*
12231 If the server is running in secure auth mode, short scrambles are
12232 forbidden. Extra juggling to report the same error as the old code.
12233 */
12234 if (thd->client_capabilities & CLIENT_PROTOCOL_41)
12235 {
12236 my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
12237 thd->security_ctx->user,
12238 thd->security_ctx->host_or_ip);
12239 general_log_print(thd, COM_CONNECT,
12240 ER_THD(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE),
12241 thd->security_ctx->user,
12242 thd->security_ctx->host_or_ip);
12243 }
12244 else
12245 {
12246 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
12247 general_log_print(thd, COM_CONNECT,
12248 ER_THD(thd, ER_NOT_SUPPORTED_AUTH_MODE));
12249 }
12250 return 1;
12251}
12252
12253/**
12254 sends a "change plugin" packet, requesting a client to restart authentication
12255 using a different authentication plugin
12256
12257 Packet format:
12258
12259 Bytes Content
12260 ----- ----
12261 1 byte with the value 254
12262 n client plugin to use, \0-terminated
12263 n plugin provided data
12264
12265 In a special case of switching from native_password_plugin to
12266 old_password_plugin, the packet contains only one - the first - byte,
12267 plugin name is omitted, plugin data aren't needed as the scramble was
12268 already sent. This one-byte packet is identical to the "use the short
12269 scramble" packet in the protocol before plugins were introduced.
12270
12271 @retval 0 ok
12272 @retval 1 error
12273*/
12274static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
12275 const uchar *data, uint data_len)
12276{
12277 DBUG_ASSERT(mpvio->packets_written == 1);
12278 DBUG_ASSERT(mpvio->packets_read == 1);
12279 NET *net= &mpvio->auth_info.thd->net;
12280 static uchar switch_plugin_request_buf[]= { 254 };
12281 DBUG_ENTER("send_plugin_request_packet");
12282
12283 mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
12284
12285 const char *client_auth_plugin=
12286 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
12287
12288 DBUG_EXECUTE_IF("auth_disconnect", { vio_close(net->vio); DBUG_RETURN(1); });
12289 DBUG_ASSERT(client_auth_plugin);
12290
12291 /*
12292 we send an old "short 4.0 scramble request", if we need to request a
12293 client to use 4.0 auth plugin (short scramble) and the scramble was
12294 already sent to the client
12295
12296 below, cached_client_reply.plugin is the plugin name that client has used,
12297 client_auth_plugin is derived from mysql.user table, for the given
12298 user account, it's the plugin that the client need to use to login.
12299 */
12300 bool switch_from_long_to_short_scramble=
12301 native_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
12302 client_auth_plugin == old_password_plugin_name.str;
12303
12304 if (switch_from_long_to_short_scramble)
12305 DBUG_RETURN (secure_auth(mpvio->auth_info.thd) ||
12306 my_net_write(net, switch_plugin_request_buf, 1) ||
12307 net_flush(net));
12308
12309 /*
12310 We never request a client to switch from a short to long scramble.
12311 Plugin-aware clients can do that, but traditionally it meant to
12312 ask an old 4.0 client to use the new 4.1 authentication protocol.
12313 */
12314 bool switch_from_short_to_long_scramble=
12315 old_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
12316 client_auth_plugin == native_password_plugin_name.str;
12317
12318 if (switch_from_short_to_long_scramble)
12319 {
12320 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
12321 general_log_print(mpvio->auth_info.thd, COM_CONNECT,
12322 ER_THD(mpvio->auth_info.thd, ER_NOT_SUPPORTED_AUTH_MODE));
12323 DBUG_RETURN (1);
12324 }
12325
12326 DBUG_PRINT("info", ("requesting client to use the %s plugin",
12327 client_auth_plugin));
12328 DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
12329 (uchar*) client_auth_plugin,
12330 strlen(client_auth_plugin) + 1,
12331 (uchar*) data, data_len));
12332}
12333
12334#ifndef NO_EMBEDDED_ACCESS_CHECKS
12335/**
12336 Finds acl entry in user database for authentication purposes.
12337
12338 Finds a user and copies it into mpvio. Creates a fake user
12339 if no matching user account is found.
12340
12341 @retval 0 found
12342 @retval 1 error
12343*/
12344static bool find_mpvio_user(MPVIO_EXT *mpvio)
12345{
12346 Security_context *sctx= mpvio->auth_info.thd->security_ctx;
12347 DBUG_ENTER("find_mpvio_user");
12348 DBUG_ASSERT(mpvio->acl_user == 0);
12349
12350 mysql_mutex_lock(&acl_cache->lock);
12351
12352 ACL_USER *user= find_user_or_anon(sctx->host, sctx->user, sctx->ip);
12353 if (user)
12354 mpvio->acl_user= user->copy(mpvio->auth_info.thd->mem_root);
12355
12356 mysql_mutex_unlock(&acl_cache->lock);
12357
12358 if (!mpvio->acl_user)
12359 {
12360 /*
12361 A matching user was not found. Fake it. Take any user, make the
12362 authentication fail later.
12363 This way we get a realistically looking failure, with occasional
12364 "change auth plugin" requests even for nonexistent users. The ratio
12365 of "change auth plugin" request will be the same for real and
12366 nonexistent users.
12367 Note, that we cannot pick any user at random, it must always be
12368 the same user account for the incoming sctx->user name.
12369 */
12370 ulong nr1=1, nr2=4;
12371 CHARSET_INFO *cs= &my_charset_latin1;
12372 cs->coll->hash_sort(cs, (uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2);
12373
12374 mysql_mutex_lock(&acl_cache->lock);
12375 if (!acl_users.elements)
12376 {
12377 mysql_mutex_unlock(&acl_cache->lock);
12378 login_failed_error(mpvio->auth_info.thd);
12379 DBUG_RETURN(1);
12380 }
12381 uint i= nr1 % acl_users.elements;
12382 ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
12383 mpvio->acl_user= acl_user_tmp->copy(mpvio->auth_info.thd->mem_root);
12384 mysql_mutex_unlock(&acl_cache->lock);
12385
12386 mpvio->make_it_fail= true;
12387 }
12388
12389 /* user account requires non-default plugin and the client is too old */
12390 if (mpvio->acl_user->plugin.str != native_password_plugin_name.str &&
12391 mpvio->acl_user->plugin.str != old_password_plugin_name.str &&
12392 !(mpvio->auth_info.thd->client_capabilities & CLIENT_PLUGIN_AUTH))
12393 {
12394 DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
12395 native_password_plugin_name.str));
12396 DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
12397 old_password_plugin_name.str));
12398 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
12399 general_log_print(mpvio->auth_info.thd, COM_CONNECT,
12400 ER_THD(mpvio->auth_info.thd, ER_NOT_SUPPORTED_AUTH_MODE));
12401 DBUG_RETURN (1);
12402 }
12403
12404 mpvio->auth_info.user_name= sctx->user;
12405 mpvio->auth_info.user_name_length= (uint)strlen(sctx->user);
12406 mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
12407 mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length;
12408 strmake_buf(mpvio->auth_info.authenticated_as, safe_str(mpvio->acl_user->user.str));
12409
12410 DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
12411 "plugin=%s",
12412 mpvio->auth_info.user_name,
12413 mpvio->auth_info.auth_string,
12414 mpvio->auth_info.authenticated_as,
12415 mpvio->acl_user->plugin.str));
12416 DBUG_RETURN(0);
12417}
12418
12419static bool
12420read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs)
12421{
12422 ulonglong length;
12423 char *ptr_save= *ptr;
12424
12425 /* not enough bytes to hold the length */
12426 if (ptr_save >= end)
12427 return true;
12428
12429 length= safe_net_field_length_ll((uchar **) ptr, end - ptr_save);
12430
12431 /* cannot even read the length */
12432 if (*ptr == NULL)
12433 return true;
12434
12435 /* length says there're more data than can fit into the packet */
12436 if (*ptr + length > end)
12437 return true;
12438
12439 /* impose an artificial length limit of 64k */
12440 if (length > 65535)
12441 return true;
12442
12443 if (PSI_CALL_set_thread_connect_attrs(*ptr, (uint)length, from_cs) &&
12444 current_thd->variables.log_warnings)
12445 sql_print_warning("Connection attributes of length %llu were truncated",
12446 length);
12447 return false;
12448}
12449
12450#endif
12451
12452/* the packet format is described in send_change_user_packet() */
12453static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
12454{
12455 THD *thd= mpvio->auth_info.thd;
12456 NET *net= &thd->net;
12457 Security_context *sctx= thd->security_ctx;
12458
12459 char *user= (char*) net->read_pos;
12460 char *end= user + packet_length;
12461 /* Safe because there is always a trailing \0 at the end of the packet */
12462 char *passwd= strend(user) + 1;
12463 uint user_len= (uint)(passwd - user - 1);
12464 char *db= passwd;
12465 char db_buff[SAFE_NAME_LEN + 1]; // buffer to store db in utf8
12466 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
12467 uint dummy_errors;
12468 DBUG_ENTER ("parse_com_change_user_packet");
12469
12470 if (passwd >= end)
12471 {
12472 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12473 MYF(0));
12474 DBUG_RETURN (1);
12475 }
12476
12477 /*
12478 Old clients send null-terminated string as password; new clients send
12479 the size (1 byte) + string (not null-terminated). Hence in case of empty
12480 password both send '\0'.
12481
12482 This strlen() can't be easily deleted without changing protocol.
12483
12484 Cast *passwd to an unsigned char, so that it doesn't extend the sign for
12485 *passwd > 127 and become 2**32-127+ after casting to uint.
12486 */
12487 uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
12488 (uchar) (*passwd++) : (uint)strlen(passwd));
12489
12490 db+= passwd_len + 1;
12491 /*
12492 Database name is always NUL-terminated, so in case of empty database
12493 the packet must contain at least the trailing '\0'.
12494 */
12495 if (db >= end)
12496 {
12497 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12498 MYF(0));
12499 DBUG_RETURN (1);
12500 }
12501
12502 size_t db_len= strlen(db);
12503
12504 char *next_field= db + db_len + 1;
12505
12506 if (next_field + 1 < end)
12507 {
12508 if (thd_init_client_charset(thd, uint2korr(next_field)))
12509 DBUG_RETURN(1);
12510 next_field+= 2;
12511 }
12512
12513 /* Convert database and user names to utf8 */
12514 db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
12515 db, db_len, thd->charset(), &dummy_errors);
12516
12517 user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
12518 system_charset_info, user, user_len,
12519 thd->charset(), &dummy_errors);
12520
12521 if (!(sctx->user= my_strndup(user_buff, user_len, MYF(MY_WME))))
12522 DBUG_RETURN(1);
12523
12524 /* Clear variables that are allocated */
12525 thd->user_connect= 0;
12526 strmake_buf(sctx->priv_user, sctx->user);
12527
12528 if (thd->make_lex_string(&mpvio->db, db_buff, db_len) == 0)
12529 DBUG_RETURN(1); /* The error is set by make_lex_string(). */
12530
12531 /*
12532 Clear thd->db as it points to something, that will be freed when
12533 connection is closed. We don't want to accidentally free a wrong
12534 pointer if connect failed.
12535 */
12536 thd->reset_db(&null_clex_str);
12537
12538 if (!initialized)
12539 {
12540 // if mysqld's been started with --skip-grant-tables option
12541 mpvio->status= MPVIO_EXT::SUCCESS;
12542 DBUG_RETURN(0);
12543 }
12544
12545#ifndef NO_EMBEDDED_ACCESS_CHECKS
12546 thd->password= passwd_len > 0;
12547 if (find_mpvio_user(mpvio))
12548 DBUG_RETURN(1);
12549
12550 const char *client_plugin;
12551 if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
12552 {
12553 if (next_field >= end)
12554 {
12555 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12556 MYF(0));
12557 DBUG_RETURN(1);
12558 }
12559 client_plugin= fix_plugin_ptr(next_field);
12560 next_field+= strlen(next_field) + 1;
12561 }
12562 else
12563 {
12564 if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
12565 client_plugin= native_password_plugin_name.str;
12566 else
12567 {
12568 client_plugin= old_password_plugin_name.str;
12569 /*
12570 For a passwordless accounts we use native_password_plugin.
12571 But when an old 4.0 client connects to it, we change it to
12572 old_password_plugin, otherwise MySQL will think that server
12573 and client plugins don't match.
12574 */
12575 if (mpvio->acl_user->auth_string.length == 0)
12576 mpvio->acl_user->plugin= old_password_plugin_name;
12577 }
12578 }
12579
12580 if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
12581 read_client_connect_attrs(&next_field, end,
12582 thd->charset()))
12583 {
12584 my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12585 MYF(0));
12586 DBUG_RETURN(1);
12587 }
12588
12589 DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
12590 /*
12591 Remember the data part of the packet, to present it to plugin in
12592 read_packet()
12593 */
12594 mpvio->cached_client_reply.pkt= passwd;
12595 mpvio->cached_client_reply.pkt_len= passwd_len;
12596 mpvio->cached_client_reply.plugin= client_plugin;
12597 mpvio->status= MPVIO_EXT::RESTART;
12598#endif
12599
12600 DBUG_RETURN (0);
12601}
12602
12603
12604/* the packet format is described in send_client_reply_packet() */
12605static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
12606 uchar **buff, ulong pkt_len)
12607{
12608#ifndef EMBEDDED_LIBRARY
12609 THD *thd= mpvio->auth_info.thd;
12610 NET *net= &thd->net;
12611 char *end;
12612 DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
12613
12614 if (pkt_len < MIN_HANDSHAKE_SIZE)
12615 return packet_error;
12616
12617 /*
12618 Protocol buffer is guaranteed to always end with \0. (see my_net_read())
12619 As the code below depends on this, lets check that.
12620 */
12621 DBUG_ASSERT(net->read_pos[pkt_len] == 0);
12622
12623 ulonglong client_capabilities= uint2korr(net->read_pos);
12624 compile_time_assert(sizeof(client_capabilities) >= 8);
12625 if (client_capabilities & CLIENT_PROTOCOL_41)
12626 {
12627 if (pkt_len < 32)
12628 return packet_error;
12629 client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
12630 if (!(client_capabilities & CLIENT_MYSQL))
12631 {
12632 // it is client with mariadb extensions
12633 ulonglong ext_client_capabilities=
12634 (((ulonglong)uint4korr(net->read_pos + 28)) << 32);
12635 client_capabilities|= ext_client_capabilities;
12636 }
12637 }
12638
12639 /* Disable those bits which are not supported by the client. */
12640 compile_time_assert(sizeof(thd->client_capabilities) >= 8);
12641 thd->client_capabilities&= client_capabilities;
12642
12643 DBUG_PRINT("info", ("client capabilities: %llu", thd->client_capabilities));
12644 if (thd->client_capabilities & CLIENT_SSL)
12645 {
12646 unsigned long errptr __attribute__((unused));
12647
12648 /* Do the SSL layering. */
12649 if (!ssl_acceptor_fd)
12650 return packet_error;
12651
12652 DBUG_PRINT("info", ("IO layer change in progress..."));
12653 if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr))
12654 {
12655 DBUG_PRINT("error", ("Failed to accept new SSL connection"));
12656 return packet_error;
12657 }
12658
12659 DBUG_PRINT("info", ("Reading user information over SSL layer"));
12660 pkt_len= my_net_read(net);
12661 if (unlikely(pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE))
12662 {
12663 DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
12664 pkt_len));
12665 return packet_error;
12666 }
12667 }
12668
12669 if (client_capabilities & CLIENT_PROTOCOL_41)
12670 {
12671 thd->max_client_packet_length= uint4korr(net->read_pos+4);
12672 DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
12673 if (thd_init_client_charset(thd, (uint) net->read_pos[8]))
12674 return packet_error;
12675 end= (char*) net->read_pos+32;
12676 }
12677 else
12678 {
12679 if (pkt_len < 5)
12680 return packet_error;
12681 thd->max_client_packet_length= uint3korr(net->read_pos+2);
12682 end= (char*) net->read_pos+5;
12683 }
12684
12685 if (end >= (char*) net->read_pos+ pkt_len +2)
12686 return packet_error;
12687
12688 if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
12689 thd->variables.sql_mode|= MODE_IGNORE_SPACE;
12690 if (thd->client_capabilities & CLIENT_INTERACTIVE)
12691 thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
12692
12693 if (end >= (char*) net->read_pos+ pkt_len +2)
12694 return packet_error;
12695
12696 if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
12697 opt_using_transactions)
12698 net->return_status= &thd->server_status;
12699
12700 char *user= end;
12701 char *passwd= strend(user)+1;
12702 size_t user_len= (size_t)(passwd - user - 1), db_len;
12703 char *db= passwd;
12704 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
12705 uint dummy_errors;
12706
12707 /*
12708 Old clients send null-terminated string as password; new clients send
12709 the size (1 byte) + string (not null-terminated). Hence in case of empty
12710 password both send '\0'.
12711
12712 This strlen() can't be easily deleted without changing protocol.
12713
12714 Cast *passwd to an unsigned char, so that it doesn't extend the sign for
12715 *passwd > 127 and become 2**32-127+ after casting to uint.
12716 */
12717 ulonglong len;
12718 size_t passwd_len;
12719
12720 if (!(thd->client_capabilities & CLIENT_SECURE_CONNECTION))
12721 len= strlen(passwd);
12722 else if (!(thd->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA))
12723 len= (uchar)(*passwd++);
12724 else
12725 {
12726 len= safe_net_field_length_ll((uchar**)&passwd,
12727 net->read_pos + pkt_len - (uchar*)passwd);
12728 if (len > pkt_len)
12729 return packet_error;
12730 }
12731
12732 passwd_len= (size_t)len;
12733 db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
12734 db + passwd_len + 1 : 0;
12735
12736 if (passwd == NULL ||
12737 passwd + passwd_len + MY_TEST(db) > (char*) net->read_pos + pkt_len)
12738 return packet_error;
12739
12740 /* strlen() can't be easily deleted without changing protocol */
12741 db_len= safe_strlen(db);
12742
12743 char *next_field;
12744 const char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0);
12745
12746 /*
12747 Since 4.1 all database names are stored in utf8
12748 The cast is ok as copy_with_error will create a new area for db
12749 */
12750 if (unlikely(thd->copy_with_error(system_charset_info,
12751 (LEX_STRING*) &mpvio->db,
12752 thd->charset(), db, db_len)))
12753 return packet_error;
12754
12755 user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
12756 system_charset_info, user, user_len,
12757 thd->charset(), &dummy_errors);
12758 user= user_buff;
12759
12760 /* If username starts and ends in "'", chop them off */
12761 if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
12762 {
12763 user++;
12764 user_len-= 2;
12765 }
12766
12767 /*
12768 Clip username to allowed length in characters (not bytes). This is
12769 mostly for backward compatibility (to truncate long usernames, as
12770 old 5.1 did)
12771 */
12772 user_len= Well_formed_prefix(system_charset_info, user, user_len,
12773 username_char_length).length();
12774 user[user_len]= '\0';
12775
12776 Security_context *sctx= thd->security_ctx;
12777
12778 my_free((char*) sctx->user);
12779 if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME))))
12780 return packet_error; /* The error is set by my_strdup(). */
12781
12782
12783 /*
12784 Clear thd->db as it points to something, that will be freed when
12785 connection is closed. We don't want to accidentally free a wrong
12786 pointer if connect failed.
12787 */
12788 thd->reset_db(&null_clex_str);
12789
12790 if (!initialized)
12791 {
12792 // if mysqld's been started with --skip-grant-tables option
12793 mpvio->status= MPVIO_EXT::SUCCESS;
12794 return packet_error;
12795 }
12796
12797 thd->password= passwd_len > 0;
12798 if (find_mpvio_user(mpvio))
12799 return packet_error;
12800
12801 if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) &&
12802 (client_plugin < (char *)net->read_pos + pkt_len))
12803 {
12804 client_plugin= fix_plugin_ptr(client_plugin);
12805 next_field+= strlen(next_field) + 1;
12806 }
12807 else
12808 {
12809 /* Some clients lie. Sad, but true */
12810 thd->client_capabilities &= ~CLIENT_PLUGIN_AUTH;
12811
12812 if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
12813 client_plugin= native_password_plugin_name.str;
12814 else
12815 {
12816 client_plugin= old_password_plugin_name.str;
12817 /*
12818 For a passwordless accounts we use native_password_plugin.
12819 But when an old 4.0 client connects to it, we change it to
12820 old_password_plugin, otherwise MySQL will think that server
12821 and client plugins don't match.
12822 */
12823 if (mpvio->acl_user->auth_string.length == 0)
12824 mpvio->acl_user->plugin= old_password_plugin_name;
12825 }
12826 }
12827
12828 if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
12829 read_client_connect_attrs(&next_field, ((char *)net->read_pos) + pkt_len,
12830 mpvio->auth_info.thd->charset()))
12831 return packet_error;
12832
12833 /*
12834 if the acl_user needs a different plugin to authenticate
12835 (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
12836 we need to restart the authentication in the server.
12837 But perhaps the client has already used the correct plugin -
12838 in that case the authentication on the client may not need to be
12839 restarted and a server auth plugin will read the data that the client
12840 has just send. Cache them to return in the next server_mpvio_read_packet().
12841 */
12842 if (!lex_string_eq(&mpvio->acl_user->plugin, plugin_name(mpvio->plugin)))
12843 {
12844 mpvio->cached_client_reply.pkt= passwd;
12845 mpvio->cached_client_reply.pkt_len= (uint)passwd_len;
12846 mpvio->cached_client_reply.plugin= client_plugin;
12847 mpvio->status= MPVIO_EXT::RESTART;
12848 return packet_error;
12849 }
12850
12851 /*
12852 ok, we don't need to restart the authentication on the server.
12853 but if the client used the wrong plugin, we need to restart
12854 the authentication on the client. Do it here, the server plugin
12855 doesn't need to know.
12856 */
12857 const char *client_auth_plugin=
12858 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
12859
12860 if (client_auth_plugin &&
12861 my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
12862 {
12863 mpvio->cached_client_reply.plugin= client_plugin;
12864 if (send_plugin_request_packet(mpvio,
12865 (uchar*) mpvio->cached_server_packet.pkt,
12866 mpvio->cached_server_packet.pkt_len))
12867 return packet_error;
12868
12869 passwd_len= my_net_read(&thd->net);
12870 passwd= (char*)thd->net.read_pos;
12871 }
12872
12873 *buff= (uchar*) passwd;
12874 return (ulong)passwd_len;
12875#else
12876 return 0;
12877#endif
12878}
12879
12880
12881/**
12882 vio->write_packet() callback method for server authentication plugins
12883
12884 This function is called by a server authentication plugin, when it wants
12885 to send data to the client.
12886
12887 It transparently wraps the data into a handshake packet,
12888 and handles plugin negotiation with the client. If necessary,
12889 it escapes the plugin data, if it starts with a mysql protocol packet byte.
12890*/
12891static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
12892 const uchar *packet, int packet_len)
12893{
12894 MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
12895 int res;
12896 DBUG_ENTER("server_mpvio_write_packet");
12897
12898 /* reset cached_client_reply */
12899 mpvio->cached_client_reply.pkt= 0;
12900
12901 /* for the 1st packet we wrap plugin data into the handshake packet */
12902 if (mpvio->packets_written == 0)
12903 res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
12904 else if (mpvio->status == MPVIO_EXT::RESTART)
12905 res= send_plugin_request_packet(mpvio, packet, packet_len);
12906 else if (packet_len > 0 && (*packet == 1 || *packet == 255 || *packet == 254))
12907 {
12908 /*
12909 we cannot allow plugin data packet to start from 255 or 254 -
12910 as the client will treat it as an error or "change plugin" packet.
12911 We'll escape these bytes with \1. Consequently, we
12912 have to escape \1 byte too.
12913 */
12914 res= net_write_command(&mpvio->auth_info.thd->net, 1, (uchar*)"", 0,
12915 packet, packet_len);
12916 }
12917 else
12918 {
12919 res= my_net_write(&mpvio->auth_info.thd->net, packet, packet_len) ||
12920 net_flush(&mpvio->auth_info.thd->net);
12921 }
12922 mpvio->packets_written++;
12923 DBUG_RETURN(res);
12924}
12925
12926/**
12927 vio->read_packet() callback method for server authentication plugins
12928
12929 This function is called by a server authentication plugin, when it wants
12930 to read data from the client.
12931
12932 It transparently extracts the client plugin data, if embedded into
12933 a client authentication handshake packet, and handles plugin negotiation
12934 with the client, if necessary.
12935*/
12936static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
12937{
12938 MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
12939 ulong pkt_len;
12940 DBUG_ENTER("server_mpvio_read_packet");
12941 if (mpvio->packets_written == 0)
12942 {
12943 /*
12944 plugin wants to read the data without sending anything first.
12945 send an empty packet to force a server handshake packet to be sent
12946 */
12947 if (server_mpvio_write_packet(mpvio, 0, 0))
12948 pkt_len= packet_error;
12949 else
12950 pkt_len= my_net_read(&mpvio->auth_info.thd->net);
12951 }
12952 else if (mpvio->cached_client_reply.pkt)
12953 {
12954 DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
12955 DBUG_ASSERT(mpvio->packets_read > 0);
12956 /*
12957 if the have the data cached from the last server_mpvio_read_packet
12958 (which can be the case if it's a restarted authentication)
12959 and a client has used the correct plugin, then we can return the
12960 cached data straight away and avoid one round trip.
12961 */
12962 const char *client_auth_plugin=
12963 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
12964 if (client_auth_plugin == 0 ||
12965 my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
12966 client_auth_plugin) == 0)
12967 {
12968 mpvio->status= MPVIO_EXT::FAILURE;
12969 *buf= (uchar*) mpvio->cached_client_reply.pkt;
12970 mpvio->cached_client_reply.pkt= 0;
12971 mpvio->packets_read++;
12972
12973 DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
12974 }
12975
12976 /*
12977 But if the client has used the wrong plugin, the cached data are
12978 useless. Furthermore, we have to send a "change plugin" request
12979 to the client.
12980 */
12981 if (server_mpvio_write_packet(mpvio, 0, 0))
12982 pkt_len= packet_error;
12983 else
12984 pkt_len= my_net_read(&mpvio->auth_info.thd->net);
12985 }
12986 else
12987 pkt_len= my_net_read(&mpvio->auth_info.thd->net);
12988
12989 if (unlikely(pkt_len == packet_error))
12990 goto err;
12991
12992 mpvio->packets_read++;
12993
12994 /*
12995 the 1st packet has the plugin data wrapped into the client authentication
12996 handshake packet
12997 */
12998 if (mpvio->packets_read == 1)
12999 {
13000 pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
13001 if (unlikely(pkt_len == packet_error))
13002 goto err;
13003 }
13004 else
13005 *buf= mpvio->auth_info.thd->net.read_pos;
13006
13007 DBUG_RETURN((int)pkt_len);
13008
13009err:
13010 if (mpvio->status == MPVIO_EXT::FAILURE)
13011 {
13012 if (!mpvio->auth_info.thd->is_error())
13013 my_error(ER_HANDSHAKE_ERROR, MYF(0));
13014 }
13015 DBUG_RETURN(-1);
13016}
13017
13018/**
13019 fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
13020 connection
13021*/
13022static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
13023 MYSQL_PLUGIN_VIO_INFO *info)
13024{
13025 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
13026 mpvio_info(mpvio->auth_info.thd->net.vio, info);
13027}
13028
13029static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
13030{
13031#ifdef HAVE_OPENSSL
13032 Vio *vio= thd->net.vio;
13033 SSL *ssl= (SSL *) vio->ssl_arg;
13034 X509 *cert;
13035#endif
13036
13037 /*
13038 At this point we know that user is allowed to connect
13039 from given host by given username/password pair. Now
13040 we check if SSL is required, if user is using SSL and
13041 if X509 certificate attributes are OK
13042 */
13043 switch (acl_user->ssl_type) {
13044 case SSL_TYPE_NOT_SPECIFIED: // Impossible
13045 case SSL_TYPE_NONE: // SSL is not required
13046 return 0;
13047#ifdef HAVE_OPENSSL
13048 case SSL_TYPE_ANY: // Any kind of SSL is ok
13049 return vio_type(vio) != VIO_TYPE_SSL;
13050 case SSL_TYPE_X509: /* Client should have any valid certificate. */
13051 /*
13052 Connections with non-valid certificates are dropped already
13053 in sslaccept() anyway, so we do not check validity here.
13054
13055 We need to check for absence of SSL because without SSL
13056 we should reject connection.
13057 */
13058 if (vio_type(vio) == VIO_TYPE_SSL &&
13059 SSL_get_verify_result(ssl) == X509_V_OK &&
13060 (cert= SSL_get_peer_certificate(ssl)))
13061 {
13062 X509_free(cert);
13063 return 0;
13064 }
13065 return 1;
13066 case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
13067 /* If a cipher name is specified, we compare it to actual cipher in use. */
13068 if (vio_type(vio) != VIO_TYPE_SSL ||
13069 SSL_get_verify_result(ssl) != X509_V_OK)
13070 return 1;
13071 if (acl_user->ssl_cipher)
13072 {
13073 DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
13074 acl_user->ssl_cipher, SSL_get_cipher(ssl)));
13075 if (strcmp(acl_user->ssl_cipher, SSL_get_cipher(ssl)))
13076 {
13077 if (global_system_variables.log_warnings)
13078 sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
13079 acl_user->ssl_cipher, SSL_get_cipher(ssl));
13080 return 1;
13081 }
13082 }
13083 if (!acl_user->x509_issuer && !acl_user->x509_subject)
13084 return 0; // all done
13085
13086 /* Prepare certificate (if exists) */
13087 if (!(cert= SSL_get_peer_certificate(ssl)))
13088 return 1;
13089 /* If X509 issuer is specified, we check it... */
13090 if (acl_user->x509_issuer)
13091 {
13092 char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
13093 DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
13094 acl_user->x509_issuer, ptr));
13095 if (strcmp(acl_user->x509_issuer, ptr))
13096 {
13097 if (global_system_variables.log_warnings)
13098 sql_print_information("X509 issuer mismatch: should be '%s' "
13099 "but is '%s'", acl_user->x509_issuer, ptr);
13100 free(ptr);
13101 X509_free(cert);
13102 return 1;
13103 }
13104 free(ptr);
13105 }
13106 /* X509 subject is specified, we check it .. */
13107 if (acl_user->x509_subject)
13108 {
13109 char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
13110 DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
13111 acl_user->x509_subject, ptr));
13112 if (strcmp(acl_user->x509_subject, ptr))
13113 {
13114 if (global_system_variables.log_warnings)
13115 sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
13116 acl_user->x509_subject, ptr);
13117 free(ptr);
13118 X509_free(cert);
13119 return 1;
13120 }
13121 free(ptr);
13122 }
13123 X509_free(cert);
13124 return 0;
13125#else /* HAVE_OPENSSL */
13126 default:
13127 /*
13128 If we don't have SSL but SSL is required for this user the
13129 authentication should fail.
13130 */
13131 return 1;
13132#endif /* HAVE_OPENSSL */
13133 }
13134 return 1;
13135}
13136
13137
13138static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name,
13139 MPVIO_EXT *mpvio)
13140{
13141 int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
13142 bool unlock_plugin= false;
13143 plugin_ref plugin= NULL;
13144
13145 if (auth_plugin_name->str == native_password_plugin_name.str)
13146 plugin= native_password_plugin;
13147#ifndef EMBEDDED_LIBRARY
13148 else if (auth_plugin_name->str == old_password_plugin_name.str)
13149 plugin= old_password_plugin;
13150 else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
13151 MYSQL_AUTHENTICATION_PLUGIN)))
13152 unlock_plugin= true;
13153#endif
13154
13155 mpvio->plugin= plugin;
13156 old_status= mpvio->status;
13157
13158 if (plugin)
13159 {
13160 st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
13161 switch (auth->interface_version >> 8) {
13162 case 0x02:
13163 res= auth->authenticate_user(mpvio, &mpvio->auth_info);
13164 break;
13165 case 0x01:
13166 {
13167 MYSQL_SERVER_AUTH_INFO_0x0100 compat;
13168 compat.downgrade(&mpvio->auth_info);
13169 res= auth->authenticate_user(mpvio, (MYSQL_SERVER_AUTH_INFO *)&compat);
13170 compat.upgrade(&mpvio->auth_info);
13171 }
13172 break;
13173 default: DBUG_ASSERT(0);
13174 }
13175
13176 if (unlock_plugin)
13177 plugin_unlock(thd, plugin);
13178 }
13179 else
13180 {
13181 /* Server cannot load the required plugin. */
13182 Host_errors errors;
13183 errors.m_no_auth_plugin= 1;
13184 inc_host_errors(mpvio->auth_info.thd->security_ctx->ip, &errors);
13185 my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
13186 res= CR_ERROR;
13187 }
13188
13189 /*
13190 If the status was MPVIO_EXT::RESTART before the authenticate_user() call
13191 it can never be MPVIO_EXT::RESTART after the call, because any call
13192 to write_packet() or read_packet() will reset the status.
13193
13194 But (!) if a plugin never called a read_packet() or write_packet(), the
13195 status will stay unchanged. We'll fix it, by resetting the status here.
13196 */
13197 if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART)
13198 mpvio->status= MPVIO_EXT::FAILURE; // reset to the default
13199
13200 return res;
13201}
13202
13203
13204/**
13205 Perform the handshake, authorize the client and update thd sctx variables.
13206
13207 @param thd thread handle
13208 @param com_change_user_pkt_len size of the COM_CHANGE_USER packet
13209 (without the first, command, byte) or 0
13210 if it's not a COM_CHANGE_USER (that is, if
13211 it's a new connection)
13212
13213 @retval 0 success, thd is updated.
13214 @retval 1 error
13215*/
13216bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
13217{
13218 int res= CR_OK;
13219 MPVIO_EXT mpvio;
13220 const LEX_CSTRING *auth_plugin_name= default_auth_plugin_name;
13221 enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
13222 : COM_CONNECT;
13223 DBUG_ENTER("acl_authenticate");
13224
13225 bzero(&mpvio, sizeof(mpvio));
13226 mpvio.read_packet= server_mpvio_read_packet;
13227 mpvio.write_packet= server_mpvio_write_packet;
13228 mpvio.info= server_mpvio_info;
13229 mpvio.status= MPVIO_EXT::FAILURE;
13230 mpvio.make_it_fail= false;
13231 mpvio.auth_info.thd= thd;
13232 mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
13233 mpvio.auth_info.host_or_ip_length=
13234 (unsigned int) strlen(thd->security_ctx->host_or_ip);
13235
13236 DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
13237
13238 if (command == COM_CHANGE_USER)
13239 {
13240 mpvio.packets_written++; // pretend that a server handshake packet was sent
13241 mpvio.packets_read++; // take COM_CHANGE_USER packet into account
13242
13243 if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
13244 DBUG_RETURN(1);
13245
13246 DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
13247 mpvio.status == MPVIO_EXT::SUCCESS);
13248 }
13249 else
13250 {
13251 /* mark the thd as having no scramble yet */
13252 thd->scramble[SCRAMBLE_LENGTH]= 1;
13253
13254 /*
13255 perform the first authentication attempt, with the default plugin.
13256 This sends the server handshake packet, reads the client reply
13257 with a user name, and performs the authentication if everyone has used
13258 the correct plugin.
13259 */
13260
13261 res= do_auth_once(thd, auth_plugin_name, &mpvio);
13262 }
13263
13264 /*
13265 retry the authentication, if - after receiving the user name -
13266 we found that we need to switch to a non-default plugin
13267 */
13268 if (mpvio.status == MPVIO_EXT::RESTART)
13269 {
13270 DBUG_ASSERT(mpvio.acl_user);
13271 DBUG_ASSERT(command == COM_CHANGE_USER ||
13272 !lex_string_eq(auth_plugin_name, &mpvio.acl_user->plugin));
13273 auth_plugin_name= &mpvio.acl_user->plugin;
13274 res= do_auth_once(thd, auth_plugin_name, &mpvio);
13275 }
13276 if (mpvio.make_it_fail && res == CR_OK)
13277 {
13278 mpvio.status= MPVIO_EXT::FAILURE;
13279 res= CR_ERROR;
13280 }
13281
13282 Security_context *sctx= thd->security_ctx;
13283 const ACL_USER *acl_user= mpvio.acl_user;
13284
13285 thd->password= mpvio.auth_info.password_used; // remember for error messages
13286
13287 /*
13288 Log the command here so that the user can check the log
13289 for the tried logins and also to detect break-in attempts.
13290
13291 if sctx->user is unset it's protocol failure, bad packet.
13292 */
13293 if (sctx->user)
13294 {
13295 if (strcmp(sctx->priv_user, sctx->user))
13296 {
13297 general_log_print(thd, command, "%s@%s as %s on %s",
13298 sctx->user, sctx->host_or_ip,
13299 sctx->priv_user[0] ? sctx->priv_user : "anonymous",
13300 safe_str(mpvio.db.str));
13301 }
13302 else
13303 general_log_print(thd, command, (char*) "%s@%s on %s",
13304 sctx->user, sctx->host_or_ip,
13305 safe_str(mpvio.db.str));
13306 }
13307
13308 if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
13309 {
13310 Host_errors errors;
13311 DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
13312 switch (res)
13313 {
13314 case CR_AUTH_PLUGIN_ERROR:
13315 errors.m_auth_plugin= 1;
13316 break;
13317 case CR_AUTH_HANDSHAKE:
13318 errors.m_handshake= 1;
13319 break;
13320 case CR_AUTH_USER_CREDENTIALS:
13321 errors.m_authentication= 1;
13322 break;
13323 case CR_ERROR:
13324 default:
13325 /* Unknown of unspecified auth plugin error. */
13326 errors.m_auth_plugin= 1;
13327 break;
13328 }
13329 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13330 if (!thd->is_error())
13331 login_failed_error(thd);
13332 DBUG_RETURN(1);
13333 }
13334
13335 sctx->proxy_user[0]= 0;
13336
13337 if (initialized) // if not --skip-grant-tables
13338 {
13339#ifndef NO_EMBEDDED_ACCESS_CHECKS
13340 bool is_proxy_user= FALSE;
13341 const char *auth_user = safe_str(acl_user->user.str);
13342 ACL_PROXY_USER *proxy_user;
13343 /* check if the user is allowed to proxy as another user */
13344 proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
13345 mpvio.auth_info.authenticated_as,
13346 &is_proxy_user);
13347 if (is_proxy_user)
13348 {
13349 ACL_USER *acl_proxy_user;
13350
13351 /* we need to find the proxy user, but there was none */
13352 if (!proxy_user)
13353 {
13354 Host_errors errors;
13355 errors.m_proxy_user= 1;
13356 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13357 if (!thd->is_error())
13358 login_failed_error(thd);
13359 DBUG_RETURN(1);
13360 }
13361
13362 my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
13363 "'%s'@'%s'", auth_user,
13364 safe_str(acl_user->host.hostname));
13365
13366 /* we're proxying : find the proxy user definition */
13367 mysql_mutex_lock(&acl_cache->lock);
13368 acl_proxy_user= find_user_exact(safe_str(proxy_user->get_proxied_host()),
13369 mpvio.auth_info.authenticated_as);
13370 if (!acl_proxy_user)
13371 {
13372 mysql_mutex_unlock(&acl_cache->lock);
13373
13374 Host_errors errors;
13375 errors.m_proxy_user_acl= 1;
13376 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13377 if (!thd->is_error())
13378 login_failed_error(thd);
13379 DBUG_RETURN(1);
13380 }
13381 acl_user= acl_proxy_user->copy(thd->mem_root);
13382 mysql_mutex_unlock(&acl_cache->lock);
13383 }
13384#endif
13385
13386 sctx->master_access= acl_user->access;
13387 if (acl_user->user.str)
13388 strmake_buf(sctx->priv_user, acl_user->user.str);
13389 else
13390 *sctx->priv_user= 0;
13391
13392 if (acl_user->host.hostname)
13393 strmake_buf(sctx->priv_host, acl_user->host.hostname);
13394 else
13395 *sctx->priv_host= 0;
13396
13397 /*
13398 OK. Let's check the SSL. Historically it was checked after the password,
13399 as an additional layer, not instead of the password
13400 (in which case it would've been a plugin too).
13401 */
13402 if (acl_check_ssl(thd, acl_user))
13403 {
13404 Host_errors errors;
13405 errors.m_ssl= 1;
13406 inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13407 login_failed_error(thd);
13408 DBUG_RETURN(1);
13409 }
13410
13411 /*
13412 Don't allow the user to connect if he has done too many queries.
13413 As we are testing max_user_connections == 0 here, it means that we
13414 can't let the user change max_user_connections from 0 in the server
13415 without a restart as it would lead to wrong connect counting.
13416 */
13417 if ((acl_user->user_resource.questions ||
13418 acl_user->user_resource.updates ||
13419 acl_user->user_resource.conn_per_hour ||
13420 acl_user->user_resource.user_conn ||
13421 acl_user->user_resource.max_statement_time != 0.0 ||
13422 max_user_connections_checking) &&
13423 get_or_create_user_conn(thd,
13424 (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
13425 (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
13426 &acl_user->user_resource))
13427 DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
13428
13429 if (acl_user->user_resource.max_statement_time != 0.0)
13430 {
13431 thd->variables.max_statement_time_double=
13432 acl_user->user_resource.max_statement_time;
13433 thd->variables.max_statement_time=
13434 (ulonglong) (thd->variables.max_statement_time_double * 1e6 + 0.1);
13435 }
13436 }
13437 else
13438 sctx->skip_grants();
13439
13440 if (thd->user_connect &&
13441 (thd->user_connect->user_resources.conn_per_hour ||
13442 thd->user_connect->user_resources.user_conn ||
13443 max_user_connections_checking) &&
13444 check_for_max_user_connections(thd, thd->user_connect))
13445 {
13446 /* Ensure we don't decrement thd->user_connections->connections twice */
13447 thd->user_connect= 0;
13448 status_var_increment(denied_connections);
13449 DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
13450 }
13451
13452 DBUG_PRINT("info",
13453 ("Capabilities: %llu packet_length: %ld Host: '%s' "
13454 "Login user: '%s' Priv_user: '%s' Using password: %s "
13455 "Access: %lu db: '%s'",
13456 thd->client_capabilities, thd->max_client_packet_length,
13457 sctx->host_or_ip, sctx->user, sctx->priv_user,
13458 thd->password ? "yes": "no",
13459 sctx->master_access, mpvio.db.str));
13460
13461 if (command == COM_CONNECT &&
13462 !(thd->main_security_ctx.master_access & SUPER_ACL))
13463 {
13464 mysql_mutex_lock(&LOCK_connection_count);
13465 bool count_ok= (*thd->scheduler->connection_count <=
13466 *thd->scheduler->max_connections);
13467 mysql_mutex_unlock(&LOCK_connection_count);
13468 if (!count_ok)
13469 { // too many connections
13470 my_error(ER_CON_COUNT_ERROR, MYF(0));
13471 DBUG_RETURN(1);
13472 }
13473 }
13474
13475 /*
13476 This is the default access rights for the current database. It's
13477 set to 0 here because we don't have an active database yet (and we
13478 may not have an active database to set.
13479 */
13480 sctx->db_access=0;
13481
13482#ifndef NO_EMBEDDED_ACCESS_CHECKS
13483 /*
13484 In case the user has a default role set, attempt to set that role
13485 */
13486 if (initialized && acl_user->default_rolename.length) {
13487 ulonglong access= 0;
13488 int result;
13489 result= acl_check_setrole(thd, acl_user->default_rolename.str, &access);
13490 if (!result)
13491 result= acl_setrole(thd, acl_user->default_rolename.str, access);
13492 if (result)
13493 thd->clear_error(); // even if the default role was not granted, do not
13494 // close the connection
13495 }
13496#endif
13497
13498 /* Change a database if necessary */
13499 if (mpvio.db.length)
13500 {
13501 if (mysql_change_db(thd, &mpvio.db, FALSE))
13502 {
13503 /* mysql_change_db() has pushed the error message. */
13504 status_var_increment(thd->status_var.access_denied_errors);
13505 DBUG_RETURN(1);
13506 }
13507 }
13508
13509 thd->net.net_skip_rest_factor= 2; // skip at most 2*max_packet_size
13510
13511 if (mpvio.auth_info.external_user[0])
13512 sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));
13513
13514 if (res == CR_OK_HANDSHAKE_COMPLETE)
13515 thd->get_stmt_da()->disable_status();
13516 else
13517 my_ok(thd);
13518
13519 PSI_CALL_set_thread_user_host
13520 (thd->main_security_ctx.user, (uint)strlen(thd->main_security_ctx.user),
13521 thd->main_security_ctx.host_or_ip, (uint)strlen(thd->main_security_ctx.host_or_ip));
13522
13523 /* Ready to handle queries */
13524 DBUG_RETURN(0);
13525}
13526
13527/**
13528 MySQL Server Password Authentication Plugin
13529
13530 In the MySQL authentication protocol:
13531 1. the server sends the random scramble to the client
13532 2. client sends the encrypted password back to the server
13533 3. the server checks the password.
13534*/
13535static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
13536 MYSQL_SERVER_AUTH_INFO *info)
13537{
13538 uchar *pkt;
13539 int pkt_len;
13540 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
13541 THD *thd=info->thd;
13542 DBUG_ENTER("native_password_authenticate");
13543
13544 /* generate the scramble, or reuse the old one */
13545 if (thd->scramble[SCRAMBLE_LENGTH])
13546 {
13547 thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
13548 /* and send it to the client */
13549 if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
13550 DBUG_RETURN(CR_AUTH_HANDSHAKE);
13551 }
13552
13553 /* reply and authenticate */
13554
13555 /*
13556 <digression>
13557 This is more complex than it looks.
13558
13559 The plugin (we) may be called right after the client was connected -
13560 and will need to send a scramble, read reply, authenticate.
13561
13562 Or the plugin may be called after another plugin has sent a scramble,
13563 and read the reply. If the client has used the correct client-plugin,
13564 we won't need to read anything here from the client, the client
13565 has already sent a reply with everything we need for authentication.
13566
13567 Or the plugin may be called after another plugin has sent a scramble,
13568 and read the reply, but the client has used the wrong client-plugin.
13569 We'll need to sent a "switch to another plugin" packet to the
13570 client and read the reply. "Use the short scramble" packet is a special
13571 case of "switch to another plugin" packet.
13572
13573 Or, perhaps, the plugin may be called after another plugin has
13574 done the handshake but did not send a useful scramble. We'll need
13575 to send a scramble (and perhaps a "switch to another plugin" packet)
13576 and read the reply.
13577
13578 Besides, a client may be an old one, that doesn't understand plugins.
13579 Or doesn't even understand 4.0 scramble.
13580
13581 And we want to keep the same protocol on the wire unless non-native
13582 plugins are involved.
13583
13584 Anyway, it still looks simple from a plugin point of view:
13585 "send the scramble, read the reply and authenticate".
13586 All the magic is transparently handled by the server.
13587 </digression>
13588 */
13589
13590 /* read the reply with the encrypted password */
13591 if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
13592 DBUG_RETURN(CR_AUTH_HANDSHAKE);
13593 DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
13594
13595#ifdef NO_EMBEDDED_ACCESS_CHECKS
13596 DBUG_RETURN(CR_OK);
13597#endif
13598
13599 DBUG_EXECUTE_IF("native_password_bad_reply", { pkt_len= 12; });
13600
13601 if (pkt_len == 0) /* no password */
13602 DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK);
13603
13604 info->password_used= PASSWORD_USED_YES;
13605 if (pkt_len == SCRAMBLE_LENGTH)
13606 {
13607 if (!mpvio->acl_user->salt_len)
13608 DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
13609
13610 if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt))
13611 DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
13612 else
13613 DBUG_RETURN(CR_OK);
13614 }
13615
13616 my_error(ER_HANDSHAKE_ERROR, MYF(0));
13617 DBUG_RETURN(CR_AUTH_HANDSHAKE);
13618}
13619
13620static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
13621 MYSQL_SERVER_AUTH_INFO *info)
13622{
13623 uchar *pkt;
13624 int pkt_len;
13625 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
13626 THD *thd=info->thd;
13627
13628 /* generate the scramble, or reuse the old one */
13629 if (thd->scramble[SCRAMBLE_LENGTH])
13630 {
13631 thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
13632 /* and send it to the client */
13633 if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
13634 return CR_AUTH_HANDSHAKE;
13635 }
13636
13637 /* read the reply and authenticate */
13638 if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
13639 return CR_AUTH_HANDSHAKE;
13640
13641#ifdef NO_EMBEDDED_ACCESS_CHECKS
13642 return CR_OK;
13643#endif
13644
13645 /*
13646 legacy: if switch_from_long_to_short_scramble,
13647 the password is sent \0-terminated, the pkt_len is always 9 bytes.
13648 We need to figure out the correct scramble length here.
13649 */
13650 if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
13651 pkt_len= (int)strnlen((char*)pkt, pkt_len);
13652
13653 if (pkt_len == 0) /* no password */
13654 return info->auth_string[0] ? CR_AUTH_USER_CREDENTIALS : CR_OK;
13655
13656 if (secure_auth(thd))
13657 return CR_AUTH_HANDSHAKE;
13658
13659 info->password_used= PASSWORD_USED_YES;
13660
13661 if (pkt_len == SCRAMBLE_LENGTH_323)
13662 {
13663 if (!mpvio->acl_user->salt_len)
13664 return CR_AUTH_USER_CREDENTIALS;
13665
13666 return check_scramble_323(pkt, thd->scramble,
13667 (ulong *) mpvio->acl_user->salt) ?
13668 CR_AUTH_USER_CREDENTIALS : CR_OK;
13669 }
13670
13671 my_error(ER_HANDSHAKE_ERROR, MYF(0));
13672 return CR_AUTH_HANDSHAKE;
13673}
13674
13675static struct st_mysql_auth native_password_handler=
13676{
13677 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
13678 native_password_plugin_name.str,
13679 native_password_authenticate
13680};
13681
13682static struct st_mysql_auth old_password_handler=
13683{
13684 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
13685 old_password_plugin_name.str,
13686 old_password_authenticate
13687};
13688
13689maria_declare_plugin(mysql_password)
13690{
13691 MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
13692 &native_password_handler, /* type descriptor */
13693 native_password_plugin_name.str, /* Name */
13694 "R.J.Silk, Sergei Golubchik", /* Author */
13695 "Native MySQL authentication", /* Description */
13696 PLUGIN_LICENSE_GPL, /* License */
13697 NULL, /* Init function */
13698 NULL, /* Deinit function */
13699 0x0100, /* Version (1.0) */
13700 NULL, /* status variables */
13701 NULL, /* system variables */
13702 "1.0", /* String version */
13703 MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */
13704},
13705{
13706 MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
13707 &old_password_handler, /* type descriptor */
13708 old_password_plugin_name.str, /* Name */
13709 "R.J.Silk, Sergei Golubchik", /* Author */
13710 "Old MySQL-4.0 authentication", /* Description */
13711 PLUGIN_LICENSE_GPL, /* License */
13712 NULL, /* Init function */
13713 NULL, /* Deinit function */
13714 0x0100, /* Version (1.0) */
13715 NULL, /* status variables */
13716 NULL, /* system variables */
13717 "1.0", /* String version */
13718 MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */
13719}
13720maria_declare_plugin_end;
13721