1 /* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
2    Copyright (c) 2009, 2020, MariaDB
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA */
16 
17 
18 /*
19   The privileges are saved in the following tables:
20   mysql/user	 ; super user who are allowed to do almost anything
21   mysql/host	 ; host privileges. This is used if host is empty in mysql/db.
22   mysql/db	 ; database privileges / user
23 
24   data in tables is sorted according to how many not-wild-cards there is
25   in the relevant fields. Empty strings comes last.
26 */
27 
28 #include "mariadb.h"                          /* NO_EMBEDDED_ACCESS_CHECKS */
29 #include "sql_priv.h"
30 #include "sql_acl.h"         // MYSQL_DB_FIELD_COUNT, ACL_ACCESS
31 #include "sql_base.h"                           // close_mysql_tables
32 #include "key.h"             // key_copy, key_cmp_if_same, key_restore
33 #include "sql_show.h"        // append_identifier
34 #include "sql_table.h"                         // write_bin_log
35 #include "hash_filo.h"
36 #include "sql_parse.h"                          // check_access
37 #include "sql_view.h"                           // VIEW_ANY_ACL
38 #include "records.h"              // READ_RECORD, read_record_info,
39                                   // init_read_record, end_read_record
40 #include "rpl_filter.h"           // rpl_filter
41 #include "rpl_rli.h"
42 #include <m_ctype.h>
43 #include <stdarg.h>
44 #include "sp_head.h"
45 #include "sp.h"
46 #include "transaction.h"
47 #include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
48 #include <sql_common.h>
49 #include <mysql/plugin_auth.h>
50 #include <mysql/plugin_password_validation.h>
51 #include "sql_connect.h"
52 #include "hostname.h"
53 #include "sql_db.h"
54 #include "sql_array.h"
55 #include "sql_hset.h"
56 #include "password.h"
57 
58 #include "sql_plugin_compat.h"
59 
60 bool mysql_user_table_is_in_short_password_format= false;
61 
62 static LEX_CSTRING native_password_plugin_name= {
63   STRING_WITH_LEN("mysql_native_password")
64 };
65 
66 static LEX_CSTRING old_password_plugin_name= {
67   STRING_WITH_LEN("mysql_old_password")
68 };
69 
70 /// @todo make it configurable
71 LEX_CSTRING *default_auth_plugin_name= &native_password_plugin_name;
72 
73 /*
74   Wildcard host, matches any hostname
75 */
76 LEX_CSTRING host_not_specified= { STRING_WITH_LEN("%") };
77 
78 /*
79   Constants, used in the SHOW GRANTS command.
80   Their actual string values are irrelevant, they're always compared
81   as pointers to these string constants.
82 */
83 LEX_CSTRING current_user= { STRING_WITH_LEN("*current_user") };
84 LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role") };
85 LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role") };
86 
87 #ifndef NO_EMBEDDED_ACCESS_CHECKS
88 static plugin_ref old_password_plugin;
89 #endif
90 static plugin_ref native_password_plugin;
91 
92 /* Classes */
93 
94 struct acl_host_and_ip
95 {
96   char *hostname;
97   long ip, ip_mask;                      // Used with masked ip:s
98 };
99 
100 #ifndef NO_EMBEDDED_ACCESS_CHECKS
101 static bool compare_hostname(const acl_host_and_ip *, const char *, const char *);
102 #else
103 #define compare_hostname(X,Y,Z) 0
104 #endif
105 
106 class ACL_ACCESS {
107 public:
108   ulong sort;
109   ulong access;
110 };
111 
112 /* ACL_HOST is used if no host is specified */
113 
114 class ACL_HOST :public ACL_ACCESS
115 {
116 public:
117   acl_host_and_ip host;
118   char *db;
119 };
120 
121 class ACL_USER_BASE :public ACL_ACCESS
122 {
123 
124 public:
operator new(size_t size,MEM_ROOT * mem_root)125   static void *operator new(size_t size, MEM_ROOT *mem_root)
126   { return (void*) alloc_root(mem_root, size); }
operator delete(void *,MEM_ROOT *)127   static void operator delete(void *, MEM_ROOT *){}
128   uchar flags;           // field used to store various state information
129   LEX_CSTRING user;
130   /* list to hold references to granted roles (ACL_ROLE instances) */
131   DYNAMIC_ARRAY role_grants;
132 };
133 
134 class ACL_USER :public ACL_USER_BASE
135 {
136 public:
137   acl_host_and_ip host;
138   size_t hostname_length;
139   USER_RESOURCES user_resource;
140   uint8 salt[SCRAMBLE_LENGTH + 1];       // scrambled password in binary form
141   uint8 salt_len;        // 0 - no password, 4 - 3.20, 8 - 4.0,  20 - 4.1.1
142   enum SSL_type ssl_type;
143   const char *ssl_cipher, *x509_issuer, *x509_subject;
144   LEX_CSTRING plugin;
145   LEX_CSTRING auth_string;
146   LEX_CSTRING default_rolename;
147 
copy(MEM_ROOT * root)148   ACL_USER *copy(MEM_ROOT *root)
149   {
150     ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
151     if (!dst)
152       return 0;
153     *dst= *this;
154     dst->user.str= safe_strdup_root(root, user.str);
155     dst->user.length= user.length;
156     dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
157     dst->x509_issuer= safe_strdup_root(root, x509_issuer);
158     dst->x509_subject= safe_strdup_root(root, x509_subject);
159     if (plugin.str == native_password_plugin_name.str ||
160         plugin.str == old_password_plugin_name.str)
161       dst->plugin= plugin;
162     else
163       dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
164     dst->auth_string.str= safe_strdup_root(root, auth_string.str);
165     dst->host.hostname= safe_strdup_root(root, host.hostname);
166     dst->default_rolename.str= safe_strdup_root(root, default_rolename.str);
167     dst->default_rolename.length= default_rolename.length;
168     bzero(&dst->role_grants, sizeof(role_grants));
169     return dst;
170   }
171 
cmp(const char * user2,const char * host2)172   int cmp(const char *user2, const char *host2)
173   {
174     CHARSET_INFO *cs= system_charset_info;
175     int res;
176     res= strcmp(safe_str(user.str), safe_str(user2));
177     if (!res)
178       res= my_strcasecmp(cs, host.hostname, host2);
179     return res;
180   }
181 
eq(const char * user2,const char * host2)182   bool eq(const char *user2, const char *host2) { return !cmp(user2, host2); }
183 
wild_eq(const char * user2,const char * host2,const char * ip2)184   bool wild_eq(const char *user2, const char *host2, const char *ip2)
185   {
186     if (strcmp(safe_str(user.str), safe_str(user2)))
187       return false;
188 
189     return compare_hostname(&host, host2, ip2 ? ip2 : host2);
190   }
191 };
192 
193 class ACL_ROLE :public ACL_USER_BASE
194 {
195 public:
196   /*
197     In case of granting a role to a role, the access bits are merged together
198     via a bit OR operation and placed in the ACL_USER::access field.
199 
200     When rebuilding role_grants via the rebuild_role_grant function,
201     the ACL_USER::access field needs to be reset first. The field
202     initial_role_access holds initial grants, as granted directly to the role
203   */
204   ulong initial_role_access;
205   /*
206     In subgraph traversal, when we need to traverse only a part of the graph
207     (e.g. all direct and indirect grantees of a role X), the counter holds the
208     number of affected neighbour nodes.
209     See also propagate_role_grants()
210   */
211   uint  counter;
212   DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted
213 
214   ACL_ROLE(ACL_USER * user, MEM_ROOT *mem);
215   ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *mem);
216 
217 };
218 
219 class ACL_DB :public ACL_ACCESS
220 {
221 public:
222   acl_host_and_ip host;
223   const char *user,*db;
224   ulong initial_access; /* access bits present in the table */
225 };
226 
227 #ifndef DBUG_OFF
228 /* status variables, only visible in SHOW STATUS after -#d,role_merge_stats */
229 ulong role_global_merges= 0, role_db_merges= 0, role_table_merges= 0,
230       role_column_merges= 0, role_routine_merges= 0;
231 #endif
232 
233 #ifndef NO_EMBEDDED_ACCESS_CHECKS
234 static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd);
235 static void update_hostname(acl_host_and_ip *host, const char *hostname);
236 static ulong get_sort(uint count,...);
237 static bool show_proxy_grants (THD *, const char *, const char *,
238                                char *, size_t);
239 static bool show_role_grants(THD *, const char *,
240                              ACL_USER_BASE *, char *, size_t);
241 static bool show_default_role(THD *, ACL_USER *, char *, size_t);
242 static bool show_global_privileges(THD *, ACL_USER_BASE *,
243                                    bool, char *, size_t);
244 static bool show_database_privileges(THD *, const char *, const char *,
245                                      char *, size_t);
246 static bool show_table_and_column_privileges(THD *, const char *, const char *,
247                                              char *, size_t);
248 static int show_routine_grants(THD *, const char *, const char *,
249                                const Sp_handler *sph, char *, int);
250 
251 class Grant_tables;
252 class User_table;
253 class Proxies_priv_table;
254 
255 class ACL_PROXY_USER :public ACL_ACCESS
256 {
257   acl_host_and_ip host;
258   const char *user;
259   acl_host_and_ip proxied_host;
260   const char *proxied_user;
261   bool with_grant;
262 
263   typedef enum {
264     MYSQL_PROXIES_PRIV_HOST,
265     MYSQL_PROXIES_PRIV_USER,
266     MYSQL_PROXIES_PRIV_PROXIED_HOST,
267     MYSQL_PROXIES_PRIV_PROXIED_USER,
268     MYSQL_PROXIES_PRIV_WITH_GRANT,
269     MYSQL_PROXIES_PRIV_GRANTOR,
270     MYSQL_PROXIES_PRIV_TIMESTAMP } proxy_table_fields;
271 public:
ACL_PROXY_USER()272   ACL_PROXY_USER () {};
273 
init(const char * host_arg,const char * user_arg,const char * proxied_host_arg,const char * proxied_user_arg,bool with_grant_arg)274   void init(const char *host_arg, const char *user_arg,
275        const char *proxied_host_arg, const char *proxied_user_arg,
276        bool with_grant_arg)
277   {
278     user= (user_arg && *user_arg) ? user_arg : NULL;
279     update_hostname (&host, (host_arg && *host_arg) ? host_arg : NULL);
280     proxied_user= (proxied_user_arg && *proxied_user_arg) ?
281       proxied_user_arg : NULL;
282     update_hostname (&proxied_host,
283                      (proxied_host_arg && *proxied_host_arg) ?
284                      proxied_host_arg : NULL);
285     with_grant= with_grant_arg;
286     sort= get_sort(4, host.hostname, user, proxied_host.hostname, proxied_user);
287   }
288 
init(MEM_ROOT * mem,const char * host_arg,const char * user_arg,const char * proxied_host_arg,const char * proxied_user_arg,bool with_grant_arg)289   void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
290        const char *proxied_host_arg, const char *proxied_user_arg,
291        bool with_grant_arg)
292   {
293     init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
294           (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
295           (proxied_host_arg && *proxied_host_arg) ?
296             strdup_root (mem, proxied_host_arg) : NULL,
297           (proxied_user_arg && *proxied_user_arg) ?
298             strdup_root (mem, proxied_user_arg) : NULL,
299           with_grant_arg);
300   }
301 
302   void init(const Proxies_priv_table& proxies_priv_table, MEM_ROOT *mem);
303 
get_with_grant()304   bool get_with_grant() { return with_grant; }
get_user()305   const char *get_user() { return user; }
get_host()306   const char *get_host() { return host.hostname; }
get_proxied_user()307   const char *get_proxied_user() { return proxied_user; }
get_proxied_host()308   const char *get_proxied_host() { return proxied_host.hostname; }
set_user(MEM_ROOT * mem,const char * user_arg)309   void set_user(MEM_ROOT *mem, const char *user_arg)
310   {
311     user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
312   }
set_host(MEM_ROOT * mem,const char * host_arg)313   void set_host(MEM_ROOT *mem, const char *host_arg)
314   {
315     update_hostname(&host, safe_strdup_root(mem, host_arg));
316   }
317 
check_validity(bool check_no_resolve)318   bool check_validity(bool check_no_resolve)
319   {
320     if (check_no_resolve &&
321         (hostname_requires_resolving(host.hostname) ||
322          hostname_requires_resolving(proxied_host.hostname)))
323     {
324       sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
325                         "ignored in --skip-name-resolve mode.",
326                         safe_str(proxied_user),
327                         safe_str(proxied_host.hostname),
328                         safe_str(user),
329                         safe_str(host.hostname));
330       return TRUE;
331     }
332     return FALSE;
333   }
334 
matches(const char * host_arg,const char * user_arg,const char * ip_arg,const char * proxied_user_arg)335   bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
336                 const char *proxied_user_arg)
337   {
338     DBUG_ENTER("ACL_PROXY_USER::matches");
339     DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
340                         "compare_hostname(%s,%s,%s) &&"
341                         "wild_compare (%s,%s) &&"
342                         "wild_compare (%s,%s)",
343                         host.hostname, host_arg, ip_arg, proxied_host.hostname,
344                         host_arg, ip_arg, user_arg, user,
345                         proxied_user_arg, proxied_user));
346     DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
347                 compare_hostname(&proxied_host, host_arg, ip_arg) &&
348                 (!user ||
349                  (user_arg && !wild_compare(user_arg, user, TRUE))) &&
350                 (!proxied_user ||
351                  (proxied_user && !wild_compare(proxied_user_arg,
352                                                 proxied_user, TRUE))));
353   }
354 
355 
auth_element_equals(const char * a,const char * b)356   inline static bool auth_element_equals(const char *a, const char *b)
357   {
358     return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
359   }
360 
361 
pk_equals(ACL_PROXY_USER * grant)362   bool pk_equals(ACL_PROXY_USER *grant)
363   {
364     DBUG_ENTER("pk_equals");
365     DBUG_PRINT("info", ("strcmp(%s,%s) &&"
366                         "strcmp(%s,%s) &&"
367                         "wild_compare (%s,%s) &&"
368                         "wild_compare (%s,%s)",
369                         user, grant->user, proxied_user, grant->proxied_user,
370                         host.hostname, grant->host.hostname,
371                         proxied_host.hostname, grant->proxied_host.hostname));
372 
373     bool res= auth_element_equals(user, grant->user) &&
374               auth_element_equals(proxied_user, grant->proxied_user) &&
375               auth_element_equals(host.hostname, grant->host.hostname) &&
376               auth_element_equals(proxied_host.hostname,
377                                   grant->proxied_host.hostname);
378     DBUG_RETURN(res);
379   }
380 
381 
granted_on(const char * host_arg,const char * user_arg)382   bool granted_on(const char *host_arg, const char *user_arg)
383   {
384     return (((!user && (!user_arg || !user_arg[0])) ||
385              (user && user_arg && !strcmp(user, user_arg))) &&
386             ((!host.hostname && (!host_arg || !host_arg[0])) ||
387              (host.hostname && host_arg && !strcmp(host.hostname, host_arg))));
388   }
389 
390 
print_grant(String * str)391   void print_grant(String *str)
392   {
393     str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
394     if (proxied_user)
395       str->append(proxied_user, strlen(proxied_user));
396     str->append(STRING_WITH_LEN("'@'"));
397     if (proxied_host.hostname)
398       str->append(proxied_host.hostname, strlen(proxied_host.hostname));
399     str->append(STRING_WITH_LEN("' TO '"));
400     if (user)
401       str->append(user, strlen(user));
402     str->append(STRING_WITH_LEN("'@'"));
403     if (host.hostname)
404       str->append(host.hostname, strlen(host.hostname));
405     str->append(STRING_WITH_LEN("'"));
406     if (with_grant)
407       str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
408   }
409 
set_data(ACL_PROXY_USER * grant)410   void set_data(ACL_PROXY_USER *grant)
411   {
412     with_grant= grant->with_grant;
413   }
414 
store_pk(TABLE * table,const LEX_CSTRING * host,const LEX_CSTRING * user,const LEX_CSTRING * proxied_host,const LEX_CSTRING * proxied_user)415   static int store_pk(TABLE *table,
416                       const LEX_CSTRING *host,
417                       const LEX_CSTRING *user,
418                       const LEX_CSTRING *proxied_host,
419                       const LEX_CSTRING *proxied_user)
420   {
421     DBUG_ENTER("ACL_PROXY_USER::store_pk");
422     DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
423                         host->str, user->str,
424                         proxied_host->str, proxied_user->str));
425     if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
426                                                    host->length,
427                                                    system_charset_info))
428       DBUG_RETURN(TRUE);
429     if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
430                                                    user->length,
431                                                    system_charset_info))
432       DBUG_RETURN(TRUE);
433     if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str,
434                                                            proxied_host->length,
435                                                            system_charset_info))
436       DBUG_RETURN(TRUE);
437     if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str,
438                                                            proxied_user->length,
439                                                            system_charset_info))
440       DBUG_RETURN(TRUE);
441 
442     DBUG_RETURN(FALSE);
443   }
444 
store_data_record(TABLE * table,const LEX_CSTRING * host,const LEX_CSTRING * user,const LEX_CSTRING * proxied_host,const LEX_CSTRING * proxied_user,bool with_grant,const char * grantor)445   static int store_data_record(TABLE *table,
446                                const LEX_CSTRING *host,
447                                const LEX_CSTRING *user,
448                                const LEX_CSTRING *proxied_host,
449                                const LEX_CSTRING *proxied_user,
450                                bool with_grant,
451                                const char *grantor)
452   {
453     DBUG_ENTER("ACL_PROXY_USER::store_pk");
454     if (store_pk(table,  host, user, proxied_host, proxied_user))
455       DBUG_RETURN(TRUE);
456     DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
457     if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
458                                                            TRUE))
459       DBUG_RETURN(TRUE);
460     if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
461                                                         strlen(grantor),
462                                                         system_charset_info))
463       DBUG_RETURN(TRUE);
464 
465     DBUG_RETURN(FALSE);
466   }
467 };
468 
469 #define FIRST_NON_YN_FIELD 26
470 
471 class acl_entry :public hash_filo_element
472 {
473 public:
474   ulong access;
475   uint16 length;
476   char key[1];					// Key will be stored here
477 };
478 
479 
acl_entry_get_key(acl_entry * entry,size_t * length,my_bool not_used)480 static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
481                                 my_bool not_used __attribute__((unused)))
482 {
483   *length=(uint) entry->length;
484   return (uchar*) entry->key;
485 }
486 
acl_role_get_key(ACL_ROLE * entry,size_t * length,my_bool not_used)487 static uchar* acl_role_get_key(ACL_ROLE *entry, size_t *length,
488                                my_bool not_used __attribute__((unused)))
489 {
490   *length=(uint) entry->user.length;
491   return (uchar*) entry->user.str;
492 }
493 
494 struct ROLE_GRANT_PAIR : public Sql_alloc
495 {
496   char *u_uname;
497   char *u_hname;
498   char *r_uname;
499   LEX_STRING hashkey;
500   bool with_admin;
501 
502   bool init(MEM_ROOT *mem, const char *username, const char *hostname,
503             const char *rolename, bool with_admin_option);
504 };
505 
acl_role_map_get_key(ROLE_GRANT_PAIR * entry,size_t * length,my_bool not_used)506 static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length,
507                                   my_bool not_used __attribute__((unused)))
508 {
509   *length=(uint) entry->hashkey.length;
510   return (uchar*) entry->hashkey.str;
511 }
512 
init(MEM_ROOT * mem,const char * username,const char * hostname,const char * rolename,bool with_admin_option)513 bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, const char *username,
514                            const char *hostname, const char *rolename,
515                            bool with_admin_option)
516 {
517   size_t uname_l = safe_strlen(username);
518   size_t hname_l = safe_strlen(hostname);
519   size_t rname_l = safe_strlen(rolename);
520   /*
521     Create a buffer that holds all 3 NULL terminated strings in succession
522     To save memory space, the same buffer is used as the hashkey
523   */
524   size_t bufflen = uname_l + hname_l + rname_l + 3; //add the '\0' aswell
525   char *buff= (char *)alloc_root(mem, bufflen);
526   if (!buff)
527     return true;
528 
529   /*
530     Offsets in the buffer for all 3 strings
531   */
532   char *username_pos= buff;
533   char *hostname_pos= buff + uname_l + 1;
534   char *rolename_pos= buff + uname_l + hname_l + 2;
535 
536   if (username) //prevent undefined behaviour
537     memcpy(username_pos, username, uname_l);
538   username_pos[uname_l]= '\0';         //#1 string terminator
539   u_uname= username_pos;
540 
541   if (hostname) //prevent undefined behaviour
542     memcpy(hostname_pos, hostname, hname_l);
543   hostname_pos[hname_l]= '\0';         //#2 string terminator
544   u_hname= hostname_pos;
545 
546   if (rolename) //prevent undefined behaviour
547     memcpy(rolename_pos, rolename, rname_l);
548   rolename_pos[rname_l]= '\0';         //#3 string terminator
549   r_uname= rolename_pos;
550 
551   hashkey.str = buff;
552   hashkey.length = bufflen;
553 
554   with_admin= with_admin_option;
555 
556   return false;
557 }
558 
559 #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
560 #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
561                         1 + USERNAME_LENGTH + 1)
562 
563 #if defined(HAVE_OPENSSL)
564 /*
565   Without SSL the handshake consists of one packet. This packet
566   has both client capabilities and scrambled password.
567   With SSL the handshake might consist of two packets. If the first
568   packet (client capabilities) has CLIENT_SSL flag set, we have to
569   switch to SSL and read the second packet. The scrambled password
570   is in the second packet and client_capabilities field will be ignored.
571   Maybe it is better to accept flags other than CLIENT_SSL from the
572   second packet?
573 */
574 #define SSL_HANDSHAKE_SIZE      2
575 #define MIN_HANDSHAKE_SIZE      2
576 #else
577 #define MIN_HANDSHAKE_SIZE      6
578 #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
579 #define NORMAL_HANDSHAKE_SIZE   6
580 
581 #define ROLE_ASSIGN_COLUMN_IDX  44
582 #define DEFAULT_ROLE_COLUMN_IDX 45
583 #define MAX_STATEMENT_TIME_COLUMN_IDX 46
584 
585 /* various flags valid for ACL_USER */
586 #define IS_ROLE                 (1L << 0)
587 /* Flag to mark that a ROLE is on the recursive DEPTH_FIRST_SEARCH stack */
588 #define ROLE_ON_STACK            (1L << 1)
589 /*
590   Flag to mark that a ROLE and all it's neighbours have
591   been visited
592 */
593 #define ROLE_EXPLORED           (1L << 2)
594 /* Flag to mark that on_node was already called for this role */
595 #define ROLE_OPENED             (1L << 3)
596 
597 static DYNAMIC_ARRAY acl_hosts, acl_users, acl_proxy_users;
598 static Dynamic_array<ACL_DB> acl_dbs(0U,50U);
599 typedef Dynamic_array<ACL_DB>::CMP_FUNC acl_dbs_cmp;
600 static HASH acl_roles;
601 /*
602   An hash containing mappings user <--> role
603 
604   A hash is used so as to make updates quickly
605   The hashkey used represents all the entries combined
606 */
607 static HASH acl_roles_mappings;
608 static MEM_ROOT acl_memroot, grant_memroot;
609 static bool initialized=0;
610 static bool allow_all_hosts=1;
611 static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
612 static HASH package_spec_priv_hash, package_body_priv_hash;
613 static DYNAMIC_ARRAY acl_wild_hosts;
614 static Hash_filo<acl_entry> *acl_cache;
615 static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
616 static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
617 static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
618 static ulong get_sort(uint count,...);
619 static void init_check_host(void);
620 static void rebuild_check_host(void);
621 static void rebuild_role_grants(void);
622 static ACL_USER *find_user_exact(const char *host, const char *user);
623 static ACL_USER *find_user_wild(const char *host, const char *user, const char *ip= 0);
624 static ACL_ROLE *find_acl_role(const char *user);
625 static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, const LEX_CSTRING *h, const LEX_CSTRING *r);
626 static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host);
627 static bool update_user_table(THD *, const User_table &, const char *, const char *, const
628                               char *, size_t new_password_len);
629 static bool acl_load(THD *thd, const Grant_tables& grant_tables);
630 static inline void get_grantor(THD *thd, char* grantor);
631 static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname);
632 static bool get_YN_as_bool(Field *field);
633 
634 #define ROLE_CYCLE_FOUND 2
635 static int traverse_role_graph_up(ACL_ROLE *, void *,
636                                   int (*) (ACL_ROLE *, void *),
637                                   int (*) (ACL_ROLE *, ACL_ROLE *, void *));
638 
639 static int traverse_role_graph_down(ACL_USER_BASE *, void *,
640                              int (*) (ACL_USER_BASE *, void *),
641                              int (*) (ACL_USER_BASE *, ACL_ROLE *, void *));
642 
643 
get_priv_hash() const644 HASH *Sp_handler_procedure::get_priv_hash() const
645 {
646   return &proc_priv_hash;
647 }
648 
649 
get_priv_hash() const650 HASH *Sp_handler_function::get_priv_hash() const
651 {
652   return &func_priv_hash;
653 }
654 
655 
get_priv_hash() const656 HASH *Sp_handler_package_spec::get_priv_hash() const
657 {
658   return &package_spec_priv_hash;
659 }
660 
661 
get_priv_hash() const662 HASH *Sp_handler_package_body::get_priv_hash() const
663 {
664   return &package_body_priv_hash;
665 }
666 
667 
668 /*
669  Enumeration of ACL/GRANT tables in the mysql database
670 */
671 enum enum_acl_tables
672 {
673   USER_TABLE,
674   DB_TABLE,
675   TABLES_PRIV_TABLE,
676   COLUMNS_PRIV_TABLE,
677 #define FIRST_OPTIONAL_TABLE HOST_TABLE
678   HOST_TABLE,
679   PROCS_PRIV_TABLE,
680   PROXIES_PRIV_TABLE,
681   ROLES_MAPPING_TABLE,
682   TABLES_MAX // <== always the last
683 };
684 
685 static const int Table_user= 1 << USER_TABLE;
686 static const int Table_db= 1 << DB_TABLE;
687 static const int Table_tables_priv= 1 << TABLES_PRIV_TABLE;
688 static const int Table_columns_priv= 1 << COLUMNS_PRIV_TABLE;
689 static const int Table_host= 1 << HOST_TABLE;
690 static const int Table_procs_priv= 1 << PROCS_PRIV_TABLE;
691 static const int Table_proxies_priv= 1 << PROXIES_PRIV_TABLE;
692 static const int Table_roles_mapping= 1 << ROLES_MAPPING_TABLE;
693 
694 /**
695   Base class representing a generic grant table from the mysql database.
696 
697   The potential tables that this class can represent are:
698   user, db, columns_priv, tables_priv, host, procs_priv, proxies_priv,
699   roles_mapping
700 
701   Objects belonging to this parent class can only be constructed by the
702   Grants_table class. This ensures the correct initialization of the objects.
703 */
704 class Grant_table_base
705 {
706  public:
707   /* Number of fields for this Grant Table. */
num_fields() const708   uint num_fields() const { return tl.table->s->fields; }
709   /* Check if the table exists after an attempt to open it was made.
710      Some tables, such as the host table in MySQL 5.6.7+ are missing. */
table_exists() const711   bool table_exists() const { return tl.table; };
712   /* Initializes the READ_RECORD structure provided as a parameter
713      to read through the whole table, with all columns available. Cleaning up
714      is the caller's job. */
init_read_record(READ_RECORD * info,THD * thd) const715   bool init_read_record(READ_RECORD* info, THD* thd) const
716   {
717     DBUG_ASSERT(tl.table);
718     bool result= ::init_read_record(info, thd, tl.table, NULL, NULL, 1,
719                                     true, false);
720     if (!result)
721       tl.table->use_all_columns();
722     return result;
723   }
724 
725   /* Return the number of privilege columns for this table. */
num_privileges() const726   uint num_privileges() const { return num_privilege_cols; }
727   /* Return a privilege column by index. */
priv_field(uint privilege_idx) const728   Field* priv_field(uint privilege_idx) const
729   {
730     DBUG_ASSERT(privilege_idx < num_privileges());
731     return tl.table->field[start_privilege_column + privilege_idx];
732   }
733 
734   /* Fetch the privileges from the table as a set of bits. The first column
735      is represented by the first bit in the result, the second column by the
736      second bit, etc. */
get_access() const737   ulong get_access() const
738   {
739     return get_access(start_privilege_column,
740                       start_privilege_column + num_privileges() - 1);
741   }
742 
743   /* Return the underlying TABLE handle. */
table() const744   TABLE* table() const
745   {
746     return tl.table;
747   }
748 
749   /** Check if the table was opened, issue an error otherwise. */
no_such_table() const750   int no_such_table() const
751   {
752     if (table_exists())
753       return 0;
754 
755     my_error(ER_NO_SUCH_TABLE, MYF(0), tl.db.str, tl.alias.str);
756     return 1;
757   }
758 
759 
760  protected:
761   friend class Grant_tables;
762 
Grant_table_base()763   Grant_table_base() : start_privilege_column(0), num_privilege_cols(0)
764   {
765     tl.reset();
766   };
767 
768   /* Initialization sequence common for all grant tables. This should be called
769      after all table-specific initialization is performed. */
init(enum thr_lock_type lock_type,bool is_optional)770   void init(enum thr_lock_type lock_type, bool is_optional)
771   {
772     tl.open_type= OT_BASE_ONLY;
773     tl.i_s_requested_object= OPEN_TABLE_ONLY;
774     if (is_optional)
775       tl.open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
776   }
777 
778   /*
779     Get all access bits from table between start_field and end_field indices.
780 
781     IMPLEMENTATION
782     The record should be already read in table->record[0]. All privileges
783     are specified as an ENUM(Y,N).
784 
785     SYNOPSIS
786       get_access()
787       start_field_idx   The field index at which the first privilege
788                         specification begins.
789       end_field_idx     The field index at which the last privilege
790                         specification is located.
791 
792     RETURN VALUE
793       privilege mask
794   */
get_access(uint start_field_idx,uint end_field_idx) const795   ulong get_access(uint start_field_idx, uint end_field_idx) const
796   {
797     ulong access_bits= 0, bit= 1;
798     for (uint i = start_field_idx; i <= end_field_idx; i++, bit<<=1)
799     {
800       if (get_YN_as_bool(tl.table->field[i]))
801         access_bits|= bit;
802     }
803     return access_bits;
804   }
805 
806   /* Compute how many privilege columns this table has. This method
807      can only be called after the table has been opened.
808 
809      IMPLEMENTATION
810      A privilege column is of type enum('Y', 'N'). Privilege columns are
811      expected to be one after another.
812   */
compute_num_privilege_cols()813   void compute_num_privilege_cols()
814   {
815     if (!table_exists()) // Table does not exist or not opened.
816       return;
817 
818     num_privilege_cols= 0;
819     for (uint i= 0; i < num_fields(); i++)
820     {
821       Field *field= tl.table->field[i];
822       if (num_privilege_cols > 0 && field->real_type() != MYSQL_TYPE_ENUM)
823         return;
824       if (field->real_type() == MYSQL_TYPE_ENUM &&
825           static_cast<Field_enum*>(field)->typelib->count == 2)
826       {
827         num_privilege_cols++;
828         if (num_privilege_cols == 1)
829           start_privilege_column= i;
830       }
831     }
832   }
833 
834   /* The index at which privilege columns start. */
835   uint start_privilege_column;
836   /* The number of privilege columns in the table. */
837   uint num_privilege_cols;
838 
839   TABLE_LIST tl;
840 };
841 
842 class User_table: public Grant_table_base
843 {
844  public:
845   /* Field getters return NULL if the column is not present in the table.
846      This is consistent only if the table is in a supported version. We do
847      not guard against corrupt tables. (yet) */
host() const848   Field* host() const
849   { return get_field(0); }
user() const850   Field* user() const
851   { return get_field(1); }
password() const852   Field* password() const
853   { return have_password() ? NULL : tl.table->field[2]; }
854   /* Columns after privilege columns. */
ssl_type() const855   Field* ssl_type() const
856   { return get_field(start_privilege_column + num_privileges()); }
ssl_cipher() const857   Field* ssl_cipher() const
858   { return get_field(start_privilege_column + num_privileges() + 1); }
x509_issuer() const859   Field* x509_issuer() const
860   { return get_field(start_privilege_column + num_privileges() + 2); }
x509_subject() const861   Field* x509_subject() const
862   { return get_field(start_privilege_column + num_privileges() + 3); }
max_questions() const863   Field* max_questions() const
864   { return get_field(start_privilege_column + num_privileges() + 4); }
max_updates() const865   Field* max_updates() const
866   { return get_field(start_privilege_column + num_privileges() + 5); }
max_connections() const867   Field* max_connections() const
868   { return get_field(start_privilege_column + num_privileges() + 6); }
max_user_connections() const869   Field* max_user_connections() const
870   { return get_field(start_privilege_column + num_privileges() + 7); }
plugin() const871   Field* plugin() const
872   { return get_field(start_privilege_column + num_privileges() + 8); }
authentication_string() const873   Field* authentication_string() const
874   { return get_field(start_privilege_column + num_privileges() + 9); }
password_expired() const875   Field* password_expired() const
876   { return get_field(start_privilege_column + num_privileges() + 10); }
is_role() const877   Field* is_role() const
878   { return get_field(start_privilege_column + num_privileges() + 11); }
default_role() const879   Field* default_role() const
880   { return get_field(start_privilege_column + num_privileges() + 12); }
max_statement_time() const881   Field* max_statement_time() const
882   { return get_field(start_privilege_column + num_privileges() + 13); }
883 
884   /*
885     Check if a user entry in the user table is marked as being a role entry
886 
887     IMPLEMENTATION
888     Access the coresponding column and check the coresponding ENUM of the form
889     ENUM('N', 'Y')
890 
891     SYNOPSIS
892       check_is_role()
893       form      an open table to read the entry from.
894                 The record should be already read in table->record[0]
895 
896     RETURN VALUE
897       TRUE      if the user is marked as a role
898       FALSE     otherwise
899   */
check_is_role() const900   bool check_is_role() const
901   {
902     /* Table version does not support roles */
903     if (!is_role())
904       return false;
905 
906     return get_YN_as_bool(is_role());
907   }
908 
909 
get_access() const910   ulong get_access() const
911   {
912     ulong access= Grant_table_base::get_access();
913     if ((num_fields() <= 13) && (access & CREATE_ACL))
914       access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
915 
916     if (num_fields() <= 18)
917     {
918       access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
919       if (access & FILE_ACL)
920         access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
921       if (access & PROCESS_ACL)
922         access|= SUPER_ACL | EXECUTE_ACL;
923     }
924     /*
925       If it is pre 5.0.1 privilege table then map CREATE privilege on
926       CREATE VIEW & SHOW VIEW privileges.
927     */
928     if (num_fields() <= 31 && (access & CREATE_ACL))
929       access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
930     /*
931       If it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
932       CREATE PROCEDURE & ALTER PROCEDURE privileges.
933     */
934     if (num_fields() <= 33)
935     {
936       if (access & CREATE_ACL)
937         access|= CREATE_PROC_ACL;
938       if (access & ALTER_ACL)
939         access|= ALTER_PROC_ACL;
940     }
941     /*
942       Pre 5.0.3 did not have CREATE_USER_ACL.
943     */
944     if (num_fields() <= 36 && (access & GRANT_ACL))
945       access|= CREATE_USER_ACL;
946     /*
947       If it is pre 5.1.6 privilege table then map CREATE privilege on
948       CREATE|ALTER|DROP|EXECUTE EVENT.
949     */
950     if (num_fields() <= 37 && (access & SUPER_ACL))
951       access|= EVENT_ACL;
952     /*
953       If it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
954     */
955     if (num_fields() <= 38 && (access & SUPER_ACL))
956       access|= TRIGGER_ACL;
957 
958     if (num_fields() <= 46 && (access & DELETE_ACL))
959       access|= DELETE_HISTORY_ACL;
960 
961     return access & GLOBAL_ACLS;
962   }
963 
964  private:
965   friend class Grant_tables;
966 
967   /* Only Grant_tables can instantiate this class. */
User_table()968   User_table() {};
969 
init(enum thr_lock_type lock_type)970   void init(enum thr_lock_type lock_type)
971   {
972     /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
973     tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, lock_type);
974     Grant_table_base::init(lock_type, false);
975   }
976 
977   /* The user table is a bit different compared to the other Grant tables.
978      Usually, we only add columns to the grant tables when adding functionality.
979      This makes it easy to test which version of the table we are using, by
980      just looking at the number of fields present in the table.
981 
982      In MySQL 5.7.6 the Password column was removed. We need to guard for that.
983      The field-fetching methods for the User table return NULL if the field
984      doesn't exist. This simplifies checking of table "version", as we don't
985      have to make use of num_fields() any more.
986   */
get_field(uint field_num) const987   inline Field* get_field(uint field_num) const
988   {
989     if (field_num >= num_fields())
990       return NULL;
991 
992     return tl.table->field[field_num];
993   }
994 
995   /* Normally password column is the third column in the table. If privileges
996      start on the third column instead, we are missing the password column.
997      This means we are using a MySQL 5.7.6+ data directory. */
have_password() const998   bool have_password() const { return start_privilege_column == 2; }
999 
1000 };
1001 
1002 class Db_table: public Grant_table_base
1003 {
1004  public:
host() const1005   Field* host() const { return tl.table->field[0]; }
db() const1006   Field* db() const { return tl.table->field[1]; }
user() const1007   Field* user() const { return tl.table->field[2]; }
1008 
1009  private:
1010   friend class Grant_tables;
1011 
Db_table()1012   Db_table() {};
1013 
init(enum thr_lock_type lock_type)1014   void init(enum thr_lock_type lock_type)
1015   {
1016     /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1017     tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_DB_NAME, NULL, lock_type);
1018     Grant_table_base::init(lock_type, false);
1019   }
1020 };
1021 
1022 class Tables_priv_table: public Grant_table_base
1023 {
1024  public:
host() const1025   Field* host() const { return tl.table->field[0]; }
db() const1026   Field* db() const { return tl.table->field[1]; }
user() const1027   Field* user() const { return tl.table->field[2]; }
table_name() const1028   Field* table_name() const { return tl.table->field[3]; }
grantor() const1029   Field* grantor() const { return tl.table->field[4]; }
timestamp() const1030   Field* timestamp() const { return tl.table->field[5]; }
table_priv() const1031   Field* table_priv() const { return tl.table->field[6]; }
column_priv() const1032   Field* column_priv() const { return tl.table->field[7]; }
1033 
1034  private:
1035   friend class Grant_tables;
1036 
Tables_priv_table()1037   Tables_priv_table() {};
1038 
init(enum thr_lock_type lock_type,Grant_table_base * next_table=NULL)1039   void init(enum thr_lock_type lock_type, Grant_table_base *next_table= NULL)
1040   {
1041     /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1042     LEX_CSTRING MYSQL_TABLES_PRIV_NAME={STRING_WITH_LEN("tables_priv") };
1043     tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLES_PRIV_NAME, NULL, lock_type);
1044     Grant_table_base::init(lock_type, false);
1045   }
1046 };
1047 
1048 class Columns_priv_table: public Grant_table_base
1049 {
1050  public:
host() const1051   Field* host() const { return tl.table->field[0]; }
db() const1052   Field* db() const { return tl.table->field[1]; }
user() const1053   Field* user() const { return tl.table->field[2]; }
table_name() const1054   Field* table_name() const { return tl.table->field[3]; }
column_name() const1055   Field* column_name() const { return tl.table->field[4]; }
timestamp() const1056   Field* timestamp() const { return tl.table->field[5]; }
column_priv() const1057   Field* column_priv() const { return tl.table->field[6]; }
1058 
1059  private:
1060   friend class Grant_tables;
1061 
Columns_priv_table()1062   Columns_priv_table() {};
1063 
init(enum thr_lock_type lock_type)1064   void init(enum thr_lock_type lock_type)
1065   {
1066     /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1067     LEX_CSTRING MYSQL_COLUMNS_PRIV_NAME={ STRING_WITH_LEN("columns_priv") };
1068     tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_COLUMNS_PRIV_NAME, NULL, lock_type);
1069     Grant_table_base::init(lock_type, false);
1070   }
1071 };
1072 
1073 class Host_table: public Grant_table_base
1074 {
1075  public:
host() const1076   Field* host() const { return tl.table->field[0]; }
db() const1077   Field* db() const { return tl.table->field[1]; }
1078 
1079  private:
1080   friend class Grant_tables;
1081 
Host_table()1082   Host_table() {}
1083 
init(enum thr_lock_type lock_type)1084   void init(enum thr_lock_type lock_type)
1085   {
1086     /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1087     LEX_CSTRING MYSQL_HOST_NAME={STRING_WITH_LEN("host") };
1088     tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HOST_NAME, NULL, lock_type);
1089     Grant_table_base::init(lock_type, true);
1090   }
1091 };
1092 
1093 class Procs_priv_table: public Grant_table_base
1094 {
1095  public:
host() const1096   Field* host() const { return tl.table->field[0]; }
db() const1097   Field* db() const { return tl.table->field[1]; }
user() const1098   Field* user() const { return tl.table->field[2]; }
routine_name() const1099   Field* routine_name() const { return tl.table->field[3]; }
routine_type() const1100   Field* routine_type() const { return tl.table->field[4]; }
grantor() const1101   Field* grantor() const { return tl.table->field[5]; }
proc_priv() const1102   Field* proc_priv() const { return tl.table->field[6]; }
timestamp() const1103   Field* timestamp() const { return tl.table->field[7]; }
1104 
1105  private:
1106   friend class Grant_tables;
1107 
Procs_priv_table()1108   Procs_priv_table() {}
1109 
init(enum thr_lock_type lock_type)1110   void init(enum thr_lock_type lock_type)
1111   {
1112     /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1113     LEX_CSTRING MYSQL_PROCS_PRIV_NAME={STRING_WITH_LEN("procs_priv") };
1114     tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROCS_PRIV_NAME, NULL, lock_type);
1115     Grant_table_base::init(lock_type, true);
1116   }
1117 };
1118 
1119 class Proxies_priv_table: public Grant_table_base
1120 {
1121  public:
host() const1122   Field* host() const { return tl.table->field[0]; }
user() const1123   Field* user() const { return tl.table->field[1]; }
proxied_host() const1124   Field* proxied_host() const { return tl.table->field[2]; }
proxied_user() const1125   Field* proxied_user() const { return tl.table->field[3]; }
with_grant() const1126   Field* with_grant() const { return tl.table->field[4]; }
grantor() const1127   Field* grantor() const { return tl.table->field[5]; }
timestamp() const1128   Field* timestamp() const { return tl.table->field[6]; }
1129 
1130  private:
1131   friend class Grant_tables;
1132 
Proxies_priv_table()1133   Proxies_priv_table() {}
1134 
init(enum thr_lock_type lock_type)1135   void init(enum thr_lock_type lock_type)
1136   {
1137     /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1138     LEX_CSTRING MYSQL_PROXIES_PRIV_NAME={STRING_WITH_LEN("proxies_priv") };
1139     tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROXIES_PRIV_NAME, NULL, lock_type);
1140     Grant_table_base::init(lock_type, true);
1141   }
1142 };
1143 
1144 class Roles_mapping_table: public Grant_table_base
1145 {
1146  public:
host() const1147   Field* host() const { return tl.table->field[0]; }
user() const1148   Field* user() const { return tl.table->field[1]; }
role() const1149   Field* role() const { return tl.table->field[2]; }
admin_option() const1150   Field* admin_option() const { return tl.table->field[3]; }
1151 
1152  private:
1153   friend class Grant_tables;
1154 
Roles_mapping_table()1155   Roles_mapping_table() {}
1156 
init(enum thr_lock_type lock_type)1157   void init(enum thr_lock_type lock_type)
1158   {
1159     /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */
1160     LEX_CSTRING MYSQL_ROLES_MAPPING_NAME={STRING_WITH_LEN("roles_mapping") };
1161     tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_ROLES_MAPPING_NAME, NULL, lock_type);
1162     Grant_table_base::init(lock_type, true);
1163   }
1164 };
1165 
1166 /**
1167   Class that represents a collection of grant tables.
1168 */
1169 class Grant_tables
1170 {
1171  public:
1172   /* When constructing the Grant_tables object, we initialize only
1173      the tables which are going to be opened.
1174      @param which_tables   Bitmap of which tables to open.
1175      @param lock_type      Lock type to use when opening tables.
1176   */
Grant_tables(int which_tables,enum thr_lock_type lock_type)1177   Grant_tables(int which_tables, enum thr_lock_type lock_type)
1178   {
1179     DBUG_ENTER("Grant_tables::Grant_tables");
1180     DBUG_PRINT("info", ("which_tables: %x, lock_type: %u",
1181                         which_tables, lock_type));
1182     DBUG_ASSERT(which_tables); /* At least one table must be opened. */
1183     Grant_table_base* prev= NULL;
1184     /* We start from the last table, Table_roles_mapping, such that
1185        the first one in the linked list is Table_user. */
1186     if (which_tables & Table_roles_mapping)
1187     {
1188       m_roles_mapping_table.init(lock_type);
1189       prev= &m_roles_mapping_table;
1190     }
1191     if (which_tables & Table_proxies_priv)
1192     {
1193       m_proxies_priv_table.init(lock_type);
1194       link_tables(&m_proxies_priv_table, prev);
1195       prev= &m_proxies_priv_table;
1196     }
1197     if (which_tables & Table_procs_priv)
1198     {
1199       m_procs_priv_table.init(lock_type);
1200       link_tables(&m_procs_priv_table, prev);
1201       prev= &m_procs_priv_table;
1202     }
1203     if (which_tables & Table_host)
1204     {
1205       m_host_table.init(lock_type);
1206       link_tables(&m_host_table, prev);
1207       prev= &m_host_table;
1208     }
1209     if (which_tables & Table_columns_priv)
1210     {
1211       m_columns_priv_table.init(lock_type);
1212       link_tables(&m_columns_priv_table, prev);
1213       prev= &m_columns_priv_table;
1214     }
1215     if (which_tables & Table_tables_priv)
1216     {
1217       m_tables_priv_table.init(lock_type);
1218       link_tables(&m_tables_priv_table, prev);
1219       prev= &m_tables_priv_table;
1220     }
1221     if (which_tables & Table_db)
1222     {
1223       m_db_table.init(lock_type);
1224       link_tables(&m_db_table, prev);
1225       prev= &m_db_table;
1226     }
1227     if (which_tables & Table_user)
1228     {
1229       m_user_table.init(lock_type);
1230       link_tables(&m_user_table, prev);
1231       prev= &m_user_table;
1232     }
1233 
1234     first_table_in_list= prev;
1235     DBUG_VOID_RETURN;
1236   }
1237 
1238   /* Before any operation is possible on grant tables, they must be opened.
1239      This opens the tables according to the lock type specified during
1240      construction.
1241 
1242      @retval  1 replication filters matched. Abort the operation,
1243                 but return OK (!)
1244      @retval  0 tables were opened successfully
1245      @retval -1 error, tables could not be opened
1246   */
open_and_lock(THD * thd)1247   int open_and_lock(THD *thd)
1248   {
1249     DBUG_ENTER("Grant_tables::open_and_lock");
1250     DBUG_ASSERT(first_table_in_list);
1251 #ifdef HAVE_REPLICATION
1252     if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE &&
1253         thd->slave_thread && !thd->spcont)
1254     {
1255       /*
1256         GRANT and REVOKE are applied the slave in/exclusion rules as they are
1257         some kind of updates to the mysql.% tables.
1258       */
1259       Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
1260       if (rpl_filter->is_on() &&
1261           !rpl_filter->tables_ok(0, &first_table_in_list->tl))
1262         DBUG_RETURN(1);
1263     }
1264 #endif
1265     if (open_and_lock_tables(thd, &first_table_in_list->tl, FALSE,
1266                              MYSQL_LOCK_IGNORE_TIMEOUT))
1267       DBUG_RETURN(-1);
1268 
1269     /*
1270        We can read privilege tables even when !initialized.
1271        This can be acl_load() - server startup or FLUSH PRIVILEGES
1272        */
1273     if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE &&
1274         !initialized)
1275     {
1276       my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1277       DBUG_RETURN(-1);
1278     }
1279 
1280     /* The privilge columns vary based on MariaDB version. Figure out
1281        how many we have after we've opened the table. */
1282     m_user_table.compute_num_privilege_cols();
1283     m_db_table.compute_num_privilege_cols();
1284     m_tables_priv_table.compute_num_privilege_cols();
1285     m_columns_priv_table.compute_num_privilege_cols();
1286     m_host_table.compute_num_privilege_cols();
1287     m_procs_priv_table.compute_num_privilege_cols();
1288     m_proxies_priv_table.compute_num_privilege_cols();
1289     m_roles_mapping_table.compute_num_privilege_cols();
1290     DBUG_RETURN(0);
1291   }
1292 
user_table() const1293   inline const User_table& user_table() const
1294   {
1295     return m_user_table;
1296   }
1297 
db_table() const1298   inline const Db_table& db_table() const
1299   {
1300     return m_db_table;
1301   }
1302 
1303 
tables_priv_table() const1304   inline const Tables_priv_table& tables_priv_table() const
1305   {
1306     return m_tables_priv_table;
1307   }
1308 
columns_priv_table() const1309   inline const Columns_priv_table& columns_priv_table() const
1310   {
1311     return m_columns_priv_table;
1312   }
1313 
host_table() const1314   inline const Host_table& host_table() const
1315   {
1316     return m_host_table;
1317   }
1318 
procs_priv_table() const1319   inline const Procs_priv_table& procs_priv_table() const
1320   {
1321     return m_procs_priv_table;
1322   }
1323 
proxies_priv_table() const1324   inline const Proxies_priv_table& proxies_priv_table() const
1325   {
1326     return m_proxies_priv_table;
1327   }
1328 
roles_mapping_table() const1329   inline const Roles_mapping_table& roles_mapping_table() const
1330   {
1331     return m_roles_mapping_table;
1332   }
1333 
1334  private:
1335   User_table m_user_table;
1336   Db_table m_db_table;
1337   Tables_priv_table m_tables_priv_table;
1338   Columns_priv_table m_columns_priv_table;
1339   Host_table m_host_table;
1340   Procs_priv_table m_procs_priv_table;
1341   Proxies_priv_table m_proxies_priv_table;
1342   Roles_mapping_table m_roles_mapping_table;
1343 
1344   /* The grant tables are set-up in a linked list. We keep the head of it. */
1345   Grant_table_base *first_table_in_list;
1346   /**
1347     Chain two grant tables' TABLE_LIST members.
1348   */
link_tables(Grant_table_base * from,Grant_table_base * to)1349   static void link_tables(Grant_table_base *from, Grant_table_base *to)
1350   {
1351     DBUG_ASSERT(from);
1352     if (to)
1353       from->tl.next_local= from->tl.next_global= &to->tl;
1354     else
1355       from->tl.next_local= from->tl.next_global= NULL;
1356   }
1357 };
1358 
1359 
init(const Proxies_priv_table & proxies_priv_table,MEM_ROOT * mem)1360 void ACL_PROXY_USER::init(const Proxies_priv_table& proxies_priv_table,
1361                           MEM_ROOT *mem)
1362 {
1363   init(get_field(mem, proxies_priv_table.host()),
1364        get_field(mem, proxies_priv_table.user()),
1365        get_field(mem, proxies_priv_table.proxied_host()),
1366        get_field(mem, proxies_priv_table.proxied_user()),
1367        proxies_priv_table.with_grant()->val_int() != 0);
1368 }
1369 
1370 
1371 
1372 /*
1373  Enumeration of various ACL's and Hashes used in handle_grant_struct()
1374 */
1375 enum enum_acl_lists
1376 {
1377   USER_ACL= 0,
1378   ROLE_ACL,
1379   DB_ACL,
1380   COLUMN_PRIVILEGES_HASH,
1381   PROC_PRIVILEGES_HASH,
1382   FUNC_PRIVILEGES_HASH,
1383   PACKAGE_SPEC_PRIVILEGES_HASH,
1384   PACKAGE_BODY_PRIVILEGES_HASH,
1385   PROXY_USERS_ACL,
1386   ROLES_MAPPINGS_HASH
1387 };
1388 
ACL_ROLE(ACL_USER * user,MEM_ROOT * root)1389 ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) : counter(0)
1390 {
1391 
1392   access= user->access;
1393   /* set initial role access the same as the table row privileges */
1394   initial_role_access= user->access;
1395   this->user.str= safe_strdup_root(root, user->user.str);
1396   this->user.length= user->user.length;
1397   bzero(&role_grants, sizeof(role_grants));
1398   bzero(&parent_grantee, sizeof(parent_grantee));
1399   flags= IS_ROLE;
1400 }
1401 
ACL_ROLE(const char * rolename,ulong privileges,MEM_ROOT * root)1402 ACL_ROLE::ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *root) :
1403   initial_role_access(privileges), counter(0)
1404 {
1405   this->access= initial_role_access;
1406   this->user.str= safe_strdup_root(root, rolename);
1407   this->user.length= strlen(rolename);
1408   bzero(&role_grants, sizeof(role_grants));
1409   bzero(&parent_grantee, sizeof(parent_grantee));
1410   flags= IS_ROLE;
1411 }
1412 
1413 
is_invalid_role_name(const char * str)1414 static bool is_invalid_role_name(const char *str)
1415 {
1416   if (*str && strcasecmp(str, "PUBLIC") && strcasecmp(str, "NONE"))
1417     return false;
1418 
1419   my_error(ER_INVALID_ROLE, MYF(0), str);
1420   return true;
1421 }
1422 
1423 
free_acl_user(ACL_USER * user)1424 static void free_acl_user(ACL_USER *user)
1425 {
1426   delete_dynamic(&(user->role_grants));
1427 }
1428 
free_acl_role(ACL_ROLE * role)1429 static void free_acl_role(ACL_ROLE *role)
1430 {
1431   delete_dynamic(&(role->role_grants));
1432   delete_dynamic(&(role->parent_grantee));
1433 }
1434 
check_if_exists(THD *,plugin_ref,void *)1435 static my_bool check_if_exists(THD *, plugin_ref, void *)
1436 {
1437   return TRUE;
1438 }
1439 
has_validation_plugins()1440 static bool has_validation_plugins()
1441 {
1442   return plugin_foreach(NULL, check_if_exists,
1443                         MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL);
1444 }
1445 
1446 struct validation_data { LEX_CSTRING *user, *password; };
1447 
do_validate(THD *,plugin_ref plugin,void * arg)1448 static my_bool do_validate(THD *, plugin_ref plugin, void *arg)
1449 {
1450   struct validation_data *data= (struct validation_data *)arg;
1451   struct st_mariadb_password_validation *handler=
1452     (st_mariadb_password_validation *)plugin_decl(plugin)->info;
1453   return handler->validate_password(data->user, data->password);
1454 }
1455 
1456 
validate_password(LEX_USER * user,THD * thd)1457 static bool validate_password(LEX_USER *user, THD *thd)
1458 {
1459   if (user->pwtext.length || !user->pwhash.length)
1460   {
1461     struct validation_data data= { &user->user,
1462                                    user->pwtext.str ? &user->pwtext :
1463                                    const_cast<LEX_CSTRING *>(&empty_clex_str) };
1464     if (plugin_foreach(NULL, do_validate,
1465                        MariaDB_PASSWORD_VALIDATION_PLUGIN, &data))
1466     {
1467       my_error(ER_NOT_VALID_PASSWORD, MYF(0));
1468       return true;
1469     }
1470   }
1471   else
1472   {
1473     if (!thd->slave_thread &&
1474         strict_password_validation && has_validation_plugins()
1475 #ifdef WITH_WSREP
1476         && !thd->wsrep_applier
1477 #endif
1478        )
1479     {
1480       my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--strict-password-validation");
1481       return true;
1482     }
1483   }
1484   return false;
1485 }
1486 
1487 /**
1488   Convert scrambled password to binary form, according to scramble type,
1489   Binary form is stored in user.salt.
1490 
1491   @param acl_user The object where to store the salt
1492   @param password The password hash containing the salt
1493   @param password_len The length of the password hash
1494 
1495   Despite the name of the function it is used when loading ACLs from disk
1496   to store the password hash in the ACL_USER object.
1497 */
1498 
1499 static void
set_user_salt(ACL_USER * acl_user,const char * password,size_t password_len)1500 set_user_salt(ACL_USER *acl_user, const char *password, size_t password_len)
1501 {
1502   if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
1503   {
1504     get_salt_from_password(acl_user->salt, password);
1505     acl_user->salt_len= SCRAMBLE_LENGTH;
1506   }
1507   else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1508   {
1509     get_salt_from_password_323((ulong *) acl_user->salt, password);
1510     acl_user->salt_len= SCRAMBLE_LENGTH_323;
1511   }
1512   else
1513     acl_user->salt_len= 0;
1514 }
1515 
fix_plugin_ptr(const char * name)1516 static const char *fix_plugin_ptr(const char *name)
1517 {
1518   if (my_strcasecmp(system_charset_info, name,
1519                     native_password_plugin_name.str) == 0)
1520     return native_password_plugin_name.str;
1521   else
1522   if (my_strcasecmp(system_charset_info, name,
1523                     old_password_plugin_name.str) == 0)
1524     return old_password_plugin_name.str;
1525   else
1526     return name;
1527 }
1528 
1529 /**
1530   Fix ACL::plugin pointer to point to a hard-coded string, if appropriate
1531 
1532   Make sure that if ACL_USER's plugin is a built-in, then it points
1533   to a hard coded string, not to an allocated copy. Run-time, for
1534   authentication, we want to be able to detect built-ins by comparing
1535   pointers, not strings.
1536 
1537   Additionally - update the salt if the plugin is built-in.
1538 
1539   @retval 0 the pointers were fixed
1540   @retval 1 this ACL_USER uses a not built-in plugin
1541 */
fix_user_plugin_ptr(ACL_USER * user)1542 static bool fix_user_plugin_ptr(ACL_USER *user)
1543 {
1544   if (lex_string_eq(&user->plugin, &native_password_plugin_name))
1545     user->plugin= native_password_plugin_name;
1546   else
1547   if (lex_string_eq(&user->plugin, &old_password_plugin_name))
1548     user->plugin= old_password_plugin_name;
1549   else
1550     return true;
1551 
1552   set_user_salt(user, user->auth_string.str, user->auth_string.length);
1553   return false;
1554 }
1555 
1556 
1557 /*
1558   Validates the password, calculates password hash, transforms
1559   equivalent LEX_USER representations.
1560 
1561   Upon entering this function:
1562 
1563   - if user->plugin is specified, user->auth is the plugin auth data.
1564   - if user->plugin is mysql_native_password or mysql_old_password,
1565     user->auth is the password hash, and LEX_USER is transformed
1566     to match the next case (that is, user->plugin is cleared).
1567   - if user->plugin is NOT specified, built-in auth is assumed, that is
1568     mysql_native_password or mysql_old_password. In that case,
1569     user->pwhash is the password hash. And user->pwtext is the original
1570     plain-text password. Either one can be set or both.
1571 
1572   Upon exiting this function:
1573 
1574   - user->pwtext is left untouched
1575   - user->pwhash is the password hash, as the mysql.user.password column
1576   - user->plugin is the plugin name, as the mysql.user.plugin column
1577   - user->auth is the plugin auth data, as the mysql.user.authentication_string column
1578 */
fix_lex_user(THD * thd,LEX_USER * user)1579 static bool fix_lex_user(THD *thd, LEX_USER *user)
1580 {
1581   size_t check_length;
1582 
1583   DBUG_ASSERT(user->plugin.length || !user->auth.length);
1584   DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length)));
1585 
1586   if (lex_string_eq(&user->plugin, &native_password_plugin_name))
1587     check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
1588   else
1589   if (lex_string_eq(&user->plugin, &old_password_plugin_name))
1590     check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
1591   else
1592   if (user->plugin.length)
1593     return false; // nothing else to do
1594   else if (thd->variables.old_passwords == 1 ||
1595            user->pwhash.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1596     check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
1597   else
1598     check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
1599 
1600   if (user->plugin.length)
1601   {
1602     user->pwhash= user->auth;
1603     user->plugin= empty_clex_str;
1604     user->auth= empty_clex_str;
1605   }
1606 
1607   if (user->pwhash.length && user->pwhash.length != check_length)
1608   {
1609     my_error(ER_PASSWD_LENGTH, MYF(0), (int) check_length);
1610     return true;
1611   }
1612 
1613   if (user->pwtext.length && !user->pwhash.length)
1614   {
1615     size_t scramble_length;
1616     void (*make_scramble)(char *, const char *, size_t);
1617 
1618     if (thd->variables.old_passwords == 1)
1619     {
1620       scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
1621       make_scramble= my_make_scrambled_password_323;
1622     }
1623     else
1624     {
1625       scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
1626       make_scramble= my_make_scrambled_password;
1627     }
1628 
1629     Query_arena *arena, backup;
1630     arena= thd->activate_stmt_arena_if_needed(&backup);
1631     char *buff= (char *) thd->alloc(scramble_length + 1);
1632     if (arena)
1633       thd->restore_active_arena(arena, &backup);
1634 
1635     if (buff == NULL)
1636       return true;
1637     make_scramble(buff, user->pwtext.str, user->pwtext.length);
1638     user->pwhash.str= buff;
1639     user->pwhash.length= scramble_length;
1640   }
1641 
1642   return false;
1643 }
1644 
1645 
get_YN_as_bool(Field * field)1646 static bool get_YN_as_bool(Field *field)
1647 {
1648   char buff[2];
1649   String res(buff,sizeof(buff),&my_charset_latin1);
1650   field->val_str(&res);
1651   return res[0] == 'Y' || res[0] == 'y';
1652 }
1653 
1654 
1655 /*
1656   Initialize structures responsible for user/db-level privilege checking and
1657   load privilege information for them from tables in the 'mysql' database.
1658 
1659   SYNOPSIS
1660     acl_init()
1661       dont_read_acl_tables  TRUE if we want to skip loading data from
1662                             privilege tables and disable privilege checking.
1663 
1664   NOTES
1665     This function is mostly responsible for preparatory steps, main work
1666     on initialization and grants loading is done in acl_reload().
1667 
1668   RETURN VALUES
1669     0	ok
1670     1	Could not initialize grant's
1671 */
1672 
acl_init(bool dont_read_acl_tables)1673 bool acl_init(bool dont_read_acl_tables)
1674 {
1675   THD  *thd;
1676   bool return_val;
1677   DBUG_ENTER("acl_init");
1678 
1679   acl_cache= new Hash_filo<acl_entry>(ACL_CACHE_SIZE, 0, 0,
1680                            (my_hash_get_key) acl_entry_get_key,
1681                            (my_hash_free_key) free,
1682                            &my_charset_utf8_bin);
1683 
1684   /*
1685     cache built-in native authentication plugins,
1686     to avoid hash searches and a global mutex lock on every connect
1687   */
1688   native_password_plugin= my_plugin_lock_by_name(0,
1689            &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
1690   old_password_plugin= my_plugin_lock_by_name(0,
1691            &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
1692 
1693   if (!native_password_plugin || !old_password_plugin)
1694     DBUG_RETURN(1);
1695 
1696   if (dont_read_acl_tables)
1697   {
1698     DBUG_RETURN(0); /* purecov: tested */
1699   }
1700 
1701   /*
1702     To be able to run this from boot, we allocate a temporary THD
1703   */
1704   if (!(thd=new THD(0)))
1705     DBUG_RETURN(1); /* purecov: inspected */
1706   thd->thread_stack= (char*) &thd;
1707   thd->store_globals();
1708   /*
1709     It is safe to call acl_reload() since acl_* arrays and hashes which
1710     will be freed there are global static objects and thus are initialized
1711     by zeros at startup.
1712   */
1713   return_val= acl_reload(thd);
1714   delete thd;
1715   DBUG_RETURN(return_val);
1716 }
1717 
1718 /**
1719   Choose from either native or old password plugins when assigning a password
1720 */
1721 
set_user_plugin(ACL_USER * user,size_t password_len)1722 static bool set_user_plugin (ACL_USER *user, size_t password_len)
1723 {
1724   switch (password_len)
1725   {
1726   case 0: /* no password */
1727   case SCRAMBLED_PASSWORD_CHAR_LENGTH:
1728     user->plugin= native_password_plugin_name;
1729     return FALSE;
1730   case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
1731     user->plugin= old_password_plugin_name;
1732     return FALSE;
1733   default:
1734     sql_print_warning("Found invalid password for user: '%s@%s'; "
1735                       "Ignoring user", safe_str(user->user.str),
1736                       safe_str(user->host.hostname));
1737     return TRUE;
1738   }
1739 }
1740 
1741 
1742 /*
1743   Initialize structures responsible for user/db-level privilege checking
1744   and load information about grants from open privilege tables.
1745 
1746   SYNOPSIS
1747     acl_load()
1748       thd     Current thread
1749       tables  List containing open "mysql.host", "mysql.user",
1750               "mysql.db", "mysql.proxies_priv" and "mysql.roles_mapping"
1751               tables.
1752 
1753   RETURN VALUES
1754     FALSE  Success
1755     TRUE   Error
1756 */
1757 
acl_load(THD * thd,const Grant_tables & tables)1758 static bool acl_load(THD *thd, const Grant_tables& tables)
1759 {
1760   READ_RECORD read_record_info;
1761   bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
1762   char tmp_name[SAFE_NAME_LEN+1];
1763   int password_length;
1764   Sql_mode_save old_mode_save(thd);
1765   DBUG_ENTER("acl_load");
1766 
1767   thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
1768 
1769   grant_version++; /* Privileges updated */
1770 
1771   const Host_table& host_table= tables.host_table();
1772   init_sql_alloc(&acl_memroot, "ACL", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
1773   if (host_table.table_exists()) // "host" table may not exist (e.g. in MySQL 5.6.7+)
1774   {
1775     if (host_table.init_read_record(&read_record_info, thd))
1776       DBUG_RETURN(true);
1777     while (!(read_record_info.read_record()))
1778     {
1779       ACL_HOST host;
1780       update_hostname(&host.host, get_field(&acl_memroot, host_table.host()));
1781       host.db= get_field(&acl_memroot, host_table.db());
1782       if (lower_case_table_names && host.db)
1783       {
1784         /*
1785           convert db to lower case and give a warning if the db wasn't
1786           already in lower case
1787         */
1788         char *end = strnmov(tmp_name, host.db, sizeof(tmp_name));
1789         if (end >= tmp_name + sizeof(tmp_name))
1790         {
1791           sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), host.db);
1792           continue;
1793         }
1794         my_casedn_str(files_charset_info, host.db);
1795         if (strcmp(host.db, tmp_name) != 0)
1796           sql_print_warning("'host' entry '%s|%s' had database in mixed "
1797                             "case that has been forced to lowercase because "
1798                             "lower_case_table_names is set. It will not be "
1799                             "possible to remove this privilege using REVOKE.",
1800                             host.host.hostname, host.db);
1801       }
1802       host.access= host_table.get_access();
1803       host.access= fix_rights_for_db(host.access);
1804       host.sort= get_sort(2, host.host.hostname, host.db);
1805       if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
1806       {
1807         sql_print_warning("'host' entry '%s|%s' "
1808                         "ignored in --skip-name-resolve mode.",
1809                          safe_str(host.host.hostname),
1810                          safe_str(host.db));
1811         continue;
1812       }
1813 #ifndef TO_BE_REMOVED
1814       if (host_table.num_fields() == 8)
1815       {						// Without grant
1816         if (host.access & CREATE_ACL)
1817           host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
1818       }
1819 #endif
1820       (void) push_dynamic(&acl_hosts,(uchar*) &host);
1821     }
1822     my_qsort((uchar*) dynamic_element(&acl_hosts, 0, ACL_HOST*),
1823              acl_hosts.elements, sizeof(ACL_HOST),(qsort_cmp) acl_compare);
1824     end_read_record(&read_record_info);
1825   }
1826   freeze_size(&acl_hosts);
1827 
1828   const User_table& user_table= tables.user_table();
1829   if (user_table.init_read_record(&read_record_info, thd))
1830     DBUG_RETURN(true);
1831 
1832   if (user_table.num_fields() < 13) // number of columns in 3.21
1833   {
1834     sql_print_error("Fatal error: mysql.user table is damaged or in "
1835                     "unsupported 3.20 format.");
1836     DBUG_RETURN(true);
1837   }
1838   username_char_length= MY_MIN(user_table.user()->char_length(),
1839                                USERNAME_CHAR_LENGTH);
1840   if (user_table.password()) // Password column might be missing. (MySQL 5.7.6+)
1841   {
1842     password_length= user_table.password()->field_length /
1843                      user_table.password()->charset()->mbmaxlen;
1844     if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1845     {
1846       sql_print_error("Fatal error: mysql.user table is damaged or in "
1847                       "unsupported 3.20 format.");
1848       DBUG_RETURN(TRUE);
1849     }
1850 
1851     DBUG_PRINT("info",("user table fields: %d, password length: %d",
1852                        user_table.num_fields(), password_length));
1853 
1854     mysql_mutex_lock(&LOCK_global_system_variables);
1855     if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
1856     {
1857       if (opt_secure_auth)
1858       {
1859         mysql_mutex_unlock(&LOCK_global_system_variables);
1860         sql_print_error("Fatal error: mysql.user table is in old format, "
1861                         "but server started with --secure-auth option.");
1862         DBUG_RETURN(TRUE);
1863       }
1864       mysql_user_table_is_in_short_password_format= true;
1865       if (global_system_variables.old_passwords)
1866         mysql_mutex_unlock(&LOCK_global_system_variables);
1867       else
1868       {
1869         extern sys_var *Sys_old_passwords_ptr;
1870         Sys_old_passwords_ptr->value_origin= sys_var::AUTO;
1871         global_system_variables.old_passwords= 1;
1872         mysql_mutex_unlock(&LOCK_global_system_variables);
1873         sql_print_warning("mysql.user table is not updated to new password format; "
1874                           "Disabling new password usage until "
1875                           "mysql_fix_privilege_tables is run");
1876       }
1877       thd->variables.old_passwords= 1;
1878     }
1879     else
1880     {
1881       mysql_user_table_is_in_short_password_format= false;
1882       mysql_mutex_unlock(&LOCK_global_system_variables);
1883     }
1884   }
1885 
1886   allow_all_hosts=0;
1887   while (!(read_record_info.read_record()))
1888   {
1889     ACL_USER user;
1890     bool is_role= FALSE;
1891     bzero(&user, sizeof(user));
1892     update_hostname(&user.host, get_field(&acl_memroot, user_table.host()));
1893     char *username= get_field(&acl_memroot, user_table.user());
1894     user.user.str= username;
1895     user.user.length= safe_strlen(username);
1896 
1897     /*
1898        If the user entry is a role, skip password and hostname checks
1899        A user can not log in with a role so some checks are not necessary
1900     */
1901     is_role= user_table.check_is_role();
1902 
1903     if (is_role && is_invalid_role_name(username))
1904     {
1905       thd->clear_error(); // the warning is still issued
1906       continue;
1907     }
1908 
1909     if (!is_role && check_no_resolve &&
1910         hostname_requires_resolving(user.host.hostname))
1911     {
1912       sql_print_warning("'user' entry '%s@%s' "
1913                         "ignored in --skip-name-resolve mode.",
1914                         safe_str(user.user.str),
1915                         safe_str(user.host.hostname));
1916       continue;
1917     }
1918 
1919     char *password= const_cast<char*>("");
1920     if (user_table.password())
1921       password= get_field(&acl_memroot, user_table.password());
1922     size_t password_len= safe_strlen(password);
1923     user.auth_string.str= safe_str(password);
1924     user.auth_string.length= password_len;
1925     set_user_salt(&user, password, password_len);
1926 
1927     if (!is_role && set_user_plugin(&user, password_len))
1928       continue;
1929 
1930     {
1931       user.access= user_table.get_access();
1932       user.sort= get_sort(2, user.host.hostname, user.user.str);
1933       user.hostname_length= safe_strlen(user.host.hostname);
1934       user.user_resource.user_conn= 0;
1935       user.user_resource.max_statement_time= 0.0;
1936 
1937       /* Starting from 4.0.2 we have more fields */
1938       if (user_table.ssl_type())
1939       {
1940         char *ssl_type=get_field(thd->mem_root, user_table.ssl_type());
1941         if (!ssl_type)
1942           user.ssl_type=SSL_TYPE_NONE;
1943         else if (!strcmp(ssl_type, "ANY"))
1944           user.ssl_type=SSL_TYPE_ANY;
1945         else if (!strcmp(ssl_type, "X509"))
1946           user.ssl_type=SSL_TYPE_X509;
1947         else  /* !strcmp(ssl_type, "SPECIFIED") */
1948           user.ssl_type=SSL_TYPE_SPECIFIED;
1949 
1950         user.ssl_cipher=   get_field(&acl_memroot, user_table.ssl_cipher());
1951         user.x509_issuer=  get_field(&acl_memroot, user_table.x509_issuer());
1952         user.x509_subject= get_field(&acl_memroot, user_table.x509_subject());
1953 
1954         char *ptr = get_field(thd->mem_root, user_table.max_questions());
1955         user.user_resource.questions=ptr ? atoi(ptr) : 0;
1956         ptr = get_field(thd->mem_root, user_table.max_updates());
1957         user.user_resource.updates=ptr ? atoi(ptr) : 0;
1958         ptr = get_field(thd->mem_root, user_table.max_connections());
1959         user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
1960         if (user.user_resource.questions || user.user_resource.updates ||
1961             user.user_resource.conn_per_hour)
1962           mqh_used=1;
1963 
1964         if (user_table.max_user_connections())
1965         {
1966           /* Starting from 5.0.3 we have max_user_connections field */
1967           ptr= get_field(thd->mem_root, user_table.max_user_connections());
1968           user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
1969         }
1970 
1971         if (!is_role && user_table.plugin())
1972         {
1973           /* We may have plugin & auth_String fields */
1974           char *tmpstr= get_field(&acl_memroot, user_table.plugin());
1975           if (tmpstr)
1976           {
1977             user.plugin.str= tmpstr;
1978             user.plugin.length= strlen(user.plugin.str);
1979             user.auth_string.str=
1980               safe_str(get_field(&acl_memroot,
1981                                  user_table.authentication_string()));
1982             user.auth_string.length= strlen(user.auth_string.str);
1983 
1984             if (user.auth_string.length && password_len &&
1985                 (user.auth_string.length != password_len ||
1986                  memcmp(user.auth_string.str, password, password_len)))
1987             {
1988               sql_print_warning("'user' entry '%s@%s' has both a password "
1989                                 "and an authentication plugin specified. The "
1990                                 "password will be ignored.",
1991                                 safe_str(user.user.str),
1992                                 safe_str(user.host.hostname));
1993             }
1994             else if (password_len)
1995             {
1996               user.auth_string.str= password;
1997               user.auth_string.length= password_len;
1998             }
1999 
2000             fix_user_plugin_ptr(&user);
2001           }
2002         }
2003 
2004         if (user_table.max_statement_time())
2005         {
2006           /* Starting from 10.1.1 we can have max_statement_time */
2007           ptr= get_field(thd->mem_root,
2008                          user_table.max_statement_time());
2009           user.user_resource.max_statement_time= ptr ? atof(ptr) : 0.0;
2010         }
2011       }
2012       else
2013       {
2014         user.ssl_type=SSL_TYPE_NONE;
2015 #ifndef TO_BE_REMOVED
2016         if (user_table.num_fields() <= 13)
2017         {						// Without grant
2018           if (user.access & CREATE_ACL)
2019             user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
2020         }
2021         /* Convert old privileges */
2022         user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
2023         if (user.access & FILE_ACL)
2024           user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
2025         if (user.access & PROCESS_ACL)
2026           user.access|= SUPER_ACL | EXECUTE_ACL;
2027 #endif
2028       }
2029 
2030       (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *),
2031                                    8, 8, MYF(0));
2032 
2033       /* check default role, if any */
2034       if (!is_role && user_table.default_role())
2035       {
2036         user.default_rolename.str=
2037           get_field(&acl_memroot, user_table.default_role());
2038         user.default_rolename.length= safe_strlen(user.default_rolename.str);
2039       }
2040 
2041       if (is_role)
2042       {
2043         DBUG_PRINT("info", ("Found role %s", user.user.str));
2044         ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot);
2045         entry->role_grants = user.role_grants;
2046         (void) my_init_dynamic_array(&entry->parent_grantee,
2047                                      sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
2048         my_hash_insert(&acl_roles, (uchar *)entry);
2049 
2050         continue;
2051       }
2052       else
2053       {
2054         DBUG_PRINT("info", ("Found user %s", user.user.str));
2055         (void) push_dynamic(&acl_users,(uchar*) &user);
2056       }
2057       if (!user.host.hostname ||
2058 	  (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
2059         allow_all_hosts=1;			// Anyone can connect
2060     }
2061   }
2062   my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
2063 	   sizeof(ACL_USER),(qsort_cmp) acl_compare);
2064   end_read_record(&read_record_info);
2065   freeze_size(&acl_users);
2066 
2067   const Db_table& db_table= tables.db_table();
2068   if (db_table.init_read_record(&read_record_info, thd))
2069     DBUG_RETURN(TRUE);
2070   while (!(read_record_info.read_record()))
2071   {
2072     ACL_DB db;
2073     char *db_name;
2074     db.user=get_field(&acl_memroot, db_table.user());
2075     const char *hostname= get_field(&acl_memroot, db_table.host());
2076     if (!hostname && find_acl_role(db.user))
2077       hostname= "";
2078     update_hostname(&db.host, hostname);
2079     db.db= db_name= get_field(&acl_memroot, db_table.db());
2080     if (!db.db)
2081     {
2082       sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
2083       continue;
2084     }
2085     if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
2086     {
2087       sql_print_warning("'db' entry '%s %s@%s' "
2088                         "ignored in --skip-name-resolve mode.",
2089 		        db.db, safe_str(db.user), safe_str(db.host.hostname));
2090       continue;
2091     }
2092     db.access= db_table.get_access();
2093     db.access=fix_rights_for_db(db.access);
2094     db.initial_access= db.access;
2095     if (lower_case_table_names)
2096     {
2097       /*
2098         convert db to lower case and give a warning if the db wasn't
2099         already in lower case
2100       */
2101       char *end = strnmov(tmp_name, db.db, sizeof(tmp_name));
2102       if (end >= tmp_name + sizeof(tmp_name))
2103       {
2104         sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), db.db);
2105         continue;
2106       }
2107       my_casedn_str(files_charset_info, db_name);
2108       if (strcmp(db_name, tmp_name) != 0)
2109       {
2110         sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
2111                           "case that has been forced to lowercase because "
2112                           "lower_case_table_names is set. It will not be "
2113                           "possible to remove this privilege using REVOKE.",
2114 		          db.db, safe_str(db.user), safe_str(db.host.hostname));
2115       }
2116     }
2117     db.sort=get_sort(3,db.host.hostname,db.db,db.user);
2118 #ifndef TO_BE_REMOVED
2119     if (db_table.num_fields() <=  9)
2120     {						// Without grant
2121       if (db.access & CREATE_ACL)
2122 	db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
2123     }
2124 #endif
2125     acl_dbs.push(db);
2126   }
2127   end_read_record(&read_record_info);
2128   acl_dbs.sort((acl_dbs_cmp)acl_compare);
2129   acl_dbs.freeze();
2130 
2131   const Proxies_priv_table& proxies_priv_table= tables.proxies_priv_table();
2132   if (proxies_priv_table.table_exists())
2133   {
2134     if (proxies_priv_table.init_read_record(&read_record_info, thd))
2135       DBUG_RETURN(TRUE);
2136     while (!(read_record_info.read_record()))
2137     {
2138       ACL_PROXY_USER proxy;
2139       proxy.init(proxies_priv_table, &acl_memroot);
2140       if (proxy.check_validity(check_no_resolve))
2141         continue;
2142       if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
2143         DBUG_RETURN(TRUE);
2144     }
2145     my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
2146              acl_proxy_users.elements,
2147              sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
2148     end_read_record(&read_record_info);
2149   }
2150   else
2151   {
2152     sql_print_error("Missing system table mysql.proxies_priv; "
2153                     "please run mysql_upgrade to create it");
2154   }
2155   freeze_size(&acl_proxy_users);
2156 
2157   const Roles_mapping_table& roles_mapping_table= tables.roles_mapping_table();
2158   if (roles_mapping_table.table_exists())
2159   {
2160     if (roles_mapping_table.init_read_record(&read_record_info, thd))
2161       DBUG_RETURN(TRUE);
2162 
2163     MEM_ROOT temp_root;
2164     init_alloc_root(&temp_root, "ACL_tmp", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
2165     while (!(read_record_info.read_record()))
2166     {
2167       char *hostname= safe_str(get_field(&temp_root, roles_mapping_table.host()));
2168       char *username= safe_str(get_field(&temp_root, roles_mapping_table.user()));
2169       char *rolename= safe_str(get_field(&temp_root, roles_mapping_table.role()));
2170       bool with_grant_option= get_YN_as_bool(roles_mapping_table.admin_option());
2171 
2172       if (add_role_user_mapping(username, hostname, rolename)) {
2173         sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'",
2174                         username, hostname, rolename);
2175         continue;
2176       }
2177 
2178       ROLE_GRANT_PAIR *mapping= new (&acl_memroot) ROLE_GRANT_PAIR;
2179 
2180       if (mapping->init(&acl_memroot, username, hostname, rolename, with_grant_option))
2181         continue;
2182 
2183       my_hash_insert(&acl_roles_mappings, (uchar*) mapping);
2184     }
2185 
2186     free_root(&temp_root, MYF(0));
2187     end_read_record(&read_record_info);
2188   }
2189   else
2190   {
2191     sql_print_error("Missing system table mysql.roles_mapping; "
2192                     "please run mysql_upgrade to create it");
2193   }
2194 
2195   init_check_host();
2196 
2197   initialized=1;
2198   DBUG_RETURN(FALSE);
2199 }
2200 
2201 
acl_free(bool end)2202 void acl_free(bool end)
2203 {
2204   my_hash_free(&acl_roles);
2205   free_root(&acl_memroot,MYF(0));
2206   delete_dynamic(&acl_hosts);
2207   delete_dynamic_with_callback(&acl_users, (FREE_FUNC) free_acl_user);
2208   acl_dbs.free_memory();
2209   delete_dynamic(&acl_wild_hosts);
2210   delete_dynamic(&acl_proxy_users);
2211   my_hash_free(&acl_check_hosts);
2212   my_hash_free(&acl_roles_mappings);
2213   if (!end)
2214     acl_cache->clear(1); /* purecov: inspected */
2215   else
2216   {
2217     plugin_unlock(0, native_password_plugin);
2218     plugin_unlock(0, old_password_plugin);
2219     delete acl_cache;
2220     acl_cache=0;
2221   }
2222 }
2223 
2224 
2225 /*
2226   Forget current user/db-level privileges and read new privileges
2227   from the privilege tables.
2228 
2229   SYNOPSIS
2230     acl_reload()
2231       thd  Current thread
2232 
2233   NOTE
2234     All tables of calling thread which were open and locked by LOCK TABLES
2235     statement will be unlocked and closed.
2236     This function is also used for initialization of structures responsible
2237     for user/db-level privilege checking.
2238 
2239   RETURN VALUE
2240     FALSE  Success
2241     TRUE   Failure
2242 */
2243 
acl_reload(THD * thd)2244 bool acl_reload(THD *thd)
2245 {
2246   DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_proxy_users;
2247   Dynamic_array<ACL_DB> old_acl_dbs(0U,0U);
2248   HASH old_acl_roles, old_acl_roles_mappings;
2249   MEM_ROOT old_mem;
2250   int result;
2251   DBUG_ENTER("acl_reload");
2252 
2253   Grant_tables tables(Table_host | Table_user | Table_db | Table_proxies_priv |
2254                       Table_roles_mapping, TL_READ);
2255   /*
2256     To avoid deadlocks we should obtain table locks before
2257     obtaining acl_cache->lock mutex.
2258   */
2259   if (unlikely((result= tables.open_and_lock(thd))))
2260   {
2261     DBUG_ASSERT(result <= 0);
2262     /*
2263       Execution might have been interrupted; only print the error message
2264       if an error condition has been raised.
2265     */
2266     if (thd->get_stmt_da()->is_error())
2267       sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
2268                       thd->get_stmt_da()->message());
2269     goto end;
2270   }
2271 
2272   acl_cache->clear(0);
2273   mysql_mutex_lock(&acl_cache->lock);
2274 
2275   old_acl_hosts= acl_hosts;
2276   old_acl_users= acl_users;
2277   old_acl_roles= acl_roles;
2278   old_acl_roles_mappings= acl_roles_mappings;
2279   old_acl_proxy_users= acl_proxy_users;
2280   old_acl_dbs= acl_dbs;
2281   my_init_dynamic_array(&acl_hosts, sizeof(ACL_HOST), 20, 50, MYF(0));
2282   my_init_dynamic_array(&acl_users, sizeof(ACL_USER), 50, 100, MYF(0));
2283   acl_dbs.init(50, 100);
2284   my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), 50, 100, MYF(0));
2285   my_hash_init2(&acl_roles,50, &my_charset_utf8_bin,
2286                 0, 0, 0, (my_hash_get_key) acl_role_get_key, 0,
2287                 (void (*)(void *))free_acl_role, 0);
2288   my_hash_init2(&acl_roles_mappings, 50, &my_charset_utf8_bin, 0, 0, 0,
2289                 (my_hash_get_key) acl_role_map_get_key, 0, 0, 0);
2290   old_mem= acl_memroot;
2291   delete_dynamic(&acl_wild_hosts);
2292   my_hash_free(&acl_check_hosts);
2293 
2294   if ((result= acl_load(thd, tables)))
2295   {					// Error. Revert to old list
2296     DBUG_PRINT("error",("Reverting to old privileges"));
2297     acl_free();				/* purecov: inspected */
2298     acl_hosts= old_acl_hosts;
2299     acl_users= old_acl_users;
2300     acl_roles= old_acl_roles;
2301     acl_roles_mappings= old_acl_roles_mappings;
2302     acl_proxy_users= old_acl_proxy_users;
2303     acl_dbs= old_acl_dbs;
2304     old_acl_dbs.init(0,0);
2305     acl_memroot= old_mem;
2306     init_check_host();
2307   }
2308   else
2309   {
2310     my_hash_free(&old_acl_roles);
2311     free_root(&old_mem,MYF(0));
2312     delete_dynamic(&old_acl_hosts);
2313     delete_dynamic_with_callback(&old_acl_users, (FREE_FUNC) free_acl_user);
2314     delete_dynamic(&old_acl_proxy_users);
2315     my_hash_free(&old_acl_roles_mappings);
2316   }
2317   mysql_mutex_unlock(&acl_cache->lock);
2318 end:
2319   close_mysql_tables(thd);
2320   DBUG_RETURN(result);
2321 }
2322 
2323 /*
2324   Get all access bits from table after fieldnr
2325 
2326   IMPLEMENTATION
2327   We know that the access privileges ends when there is no more fields
2328   or the field is not an enum with two elements.
2329 
2330   SYNOPSIS
2331     get_access()
2332     form        an open table to read privileges from.
2333                 The record should be already read in table->record[0]
2334     fieldnr     number of the first privilege (that is ENUM('N','Y') field
2335     next_field  on return - number of the field next to the last ENUM
2336                 (unless next_field == 0)
2337 
2338   RETURN VALUE
2339     privilege mask
2340 */
2341 
get_access(TABLE * form,uint fieldnr,uint * next_field)2342 static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
2343 {
2344   ulong access_bits=0,bit;
2345   char buff[2];
2346   String res(buff,sizeof(buff),&my_charset_latin1);
2347   Field **pos;
2348 
2349   for (pos=form->field+fieldnr, bit=1;
2350        *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
2351 	 ((Field_enum*) (*pos))->typelib->count == 2 ;
2352        pos++, fieldnr++, bit<<=1)
2353   {
2354     if (get_YN_as_bool(*pos))
2355       access_bits|= bit;
2356   }
2357   if (next_field)
2358     *next_field=fieldnr;
2359   return access_bits;
2360 }
2361 
2362 
2363 /*
2364   Return a number which, if sorted 'desc', puts strings in this order:
2365     no wildcards
2366     wildcards
2367     empty string
2368 */
2369 
get_sort(uint count,...)2370 static ulong get_sort(uint count,...)
2371 {
2372   va_list args;
2373   va_start(args,count);
2374   ulong sort=0;
2375 
2376   /* Should not use this function with more than 4 arguments for compare. */
2377   DBUG_ASSERT(count <= 4);
2378 
2379   while (count--)
2380   {
2381     char *start, *str= va_arg(args,char*);
2382     uint chars= 0;
2383     uint wild_pos= 0;           /* first wildcard position */
2384 
2385     if ((start= str))
2386     {
2387       for (; *str ; str++)
2388       {
2389         if (*str == wild_prefix && str[1])
2390           str++;
2391         else if (*str == wild_many || *str == wild_one)
2392         {
2393           wild_pos= (uint) (str - start) + 1;
2394           break;
2395         }
2396         chars= 128;                             // Marker that chars existed
2397       }
2398     }
2399     sort= (sort << 8) + (wild_pos ? MY_MIN(wild_pos, 127U) : chars);
2400   }
2401   va_end(args);
2402   return sort;
2403 }
2404 
2405 
acl_compare(ACL_ACCESS * a,ACL_ACCESS * b)2406 static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
2407 {
2408   if (a->sort > b->sort)
2409     return -1;
2410   if (a->sort < b->sort)
2411     return 1;
2412   return 0;
2413 }
2414 
2415 
2416 /*
2417   Gets user credentials without authentication and resource limit checks.
2418 
2419   SYNOPSIS
2420     acl_getroot()
2421       sctx               Context which should be initialized
2422       user               user name
2423       host               host name
2424       ip                 IP
2425       db                 current data base name
2426 
2427   RETURN
2428     FALSE  OK
2429     TRUE   Error
2430 */
2431 
acl_getroot(Security_context * sctx,const char * user,const char * host,const char * ip,const char * db)2432 bool acl_getroot(Security_context *sctx, const char *user, const char *host,
2433                  const char *ip, const char *db)
2434 {
2435   int res= 1;
2436   uint i;
2437   ACL_USER *acl_user= 0;
2438   DBUG_ENTER("acl_getroot");
2439 
2440   DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
2441                        host, ip, user, db));
2442   sctx->user= user;
2443   sctx->host= host;
2444   sctx->ip= ip;
2445   sctx->host_or_ip= host ? host : (safe_str(ip));
2446 
2447   if (!initialized)
2448   {
2449     /*
2450       here if mysqld's been started with --skip-grant-tables option.
2451     */
2452     sctx->skip_grants();
2453     DBUG_RETURN(FALSE);
2454   }
2455 
2456   mysql_mutex_lock(&acl_cache->lock);
2457 
2458   sctx->master_access= 0;
2459   sctx->db_access= 0;
2460   *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0;
2461 
2462   if (host[0]) // User, not Role
2463   {
2464     acl_user= find_user_wild(host, user, ip);
2465 
2466     if (acl_user)
2467     {
2468       res= 0;
2469       for (i=0 ; i < acl_dbs.elements() ; i++)
2470       {
2471         ACL_DB *acl_db= &acl_dbs.at(i);
2472         if (!acl_db->user ||
2473             (user && user[0] && !strcmp(user, acl_db->user)))
2474         {
2475           if (compare_hostname(&acl_db->host, host, ip))
2476           {
2477             if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
2478             {
2479               sctx->db_access= acl_db->access;
2480               break;
2481             }
2482           }
2483         }
2484       }
2485       sctx->master_access= acl_user->access;
2486 
2487       if (acl_user->user.str)
2488         strmake_buf(sctx->priv_user, user);
2489 
2490       if (acl_user->host.hostname)
2491         strmake_buf(sctx->priv_host, acl_user->host.hostname);
2492     }
2493   }
2494   else // Role, not User
2495   {
2496     ACL_ROLE *acl_role= find_acl_role(user);
2497     if (acl_role)
2498     {
2499       res= 0;
2500       for (i=0 ; i < acl_dbs.elements() ; i++)
2501       {
2502         ACL_DB *acl_db= &acl_dbs.at(i);
2503         if (!acl_db->user ||
2504             (user && user[0] && !strcmp(user, acl_db->user)))
2505         {
2506           if (compare_hostname(&acl_db->host, "", ""))
2507           {
2508             if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
2509             {
2510               sctx->db_access= acl_db->access;
2511               break;
2512             }
2513           }
2514         }
2515       }
2516       sctx->master_access= acl_role->access;
2517 
2518       if (acl_role->user.str)
2519         strmake_buf(sctx->priv_role, user);
2520     }
2521   }
2522 
2523   mysql_mutex_unlock(&acl_cache->lock);
2524   DBUG_RETURN(res);
2525 }
2526 
check_role_is_granted_callback(ACL_USER_BASE * grantee,void * data)2527 static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data)
2528 {
2529   LEX_CSTRING *rolename= static_cast<LEX_CSTRING *>(data);
2530   if (rolename->length == grantee->user.length &&
2531       !strcmp(rolename->str, grantee->user.str))
2532     return -1; // End search, we've found our role.
2533 
2534   /* Keep looking, we haven't found our role yet. */
2535   return 0;
2536 }
2537 
2538 /*
2539   unlike find_user_exact and find_user_wild,
2540   this function finds anonymous users too, it's when a
2541   user is not empty, but priv_user (acl_user->user) is empty.
2542 */
find_user_or_anon(const char * host,const char * user,const char * ip)2543 static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip)
2544 {
2545   ACL_USER *result= NULL;
2546   mysql_mutex_assert_owner(&acl_cache->lock);
2547   for (uint i=0; i < acl_users.elements; i++)
2548   {
2549     ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
2550     if ((!acl_user_tmp->user.str ||
2551          !strcmp(user, acl_user_tmp->user.str)) &&
2552          compare_hostname(&acl_user_tmp->host, host, ip))
2553     {
2554       result= acl_user_tmp;
2555       break;
2556     }
2557   }
2558   return result;
2559 }
2560 
check_user_can_set_role(THD * thd,const char * user,const char * host,const char * ip,const char * rolename,ulonglong * access)2561 static int check_user_can_set_role(THD *thd, const char *user, const char *host,
2562                                    const char *ip, const char *rolename,
2563                                    ulonglong *access)
2564 {
2565   ACL_ROLE *role;
2566   ACL_USER_BASE *acl_user_base;
2567   ACL_USER *UNINIT_VAR(acl_user);
2568   bool is_granted= FALSE;
2569   int result= 0;
2570 
2571   /* clear role privileges */
2572   mysql_mutex_lock(&acl_cache->lock);
2573 
2574   if (!strcasecmp(rolename, "NONE"))
2575   {
2576     /* have to clear the privileges */
2577     /* get the current user */
2578     acl_user= find_user_wild(host, user, ip);
2579     if (acl_user == NULL)
2580       result= ER_INVALID_CURRENT_USER;
2581     else if (access)
2582       *access= acl_user->access;
2583 
2584     goto end;
2585   }
2586 
2587   role= find_acl_role(rolename);
2588 
2589   /* According to SQL standard, the same error message must be presented */
2590   if (role == NULL)
2591   {
2592     result= ER_INVALID_ROLE;
2593     goto end;
2594   }
2595 
2596   for (uint i=0 ; i < role->parent_grantee.elements ; i++)
2597   {
2598     acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**));
2599     if (acl_user_base->flags & IS_ROLE)
2600       continue;
2601 
2602     acl_user= (ACL_USER *)acl_user_base;
2603     if (acl_user->wild_eq(user, host, ip))
2604     {
2605       is_granted= TRUE;
2606       break;
2607     }
2608   }
2609 
2610   /* According to SQL standard, the same error message must be presented */
2611   if (!is_granted)
2612   {
2613     result= 1;
2614     goto end;
2615   }
2616 
2617   if (access)
2618   {
2619     *access = acl_user->access | role->access;
2620   }
2621 
2622 end:
2623   mysql_mutex_unlock(&acl_cache->lock);
2624 
2625   /* We present different error messages depending if the user has sufficient
2626      privileges to know if the INVALID_ROLE exists. */
2627   switch (result)
2628   {
2629     case ER_INVALID_CURRENT_USER:
2630       my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename);
2631       break;
2632     case ER_INVALID_ROLE:
2633       /* Role doesn't exist at all */
2634       my_error(ER_INVALID_ROLE, MYF(0), rolename);
2635       break;
2636     case 1:
2637       LEX_CSTRING role_lex;
2638       /* First, check if current user can see mysql database. */
2639       bool read_access= !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 1);
2640 
2641       role_lex.str= rolename;
2642       role_lex.length= strlen(rolename);
2643       mysql_mutex_lock(&acl_cache->lock);
2644       ACL_USER *cur_user= find_user_or_anon(thd->security_ctx->priv_host,
2645                                             thd->security_ctx->priv_user,
2646                                             thd->security_ctx->ip);
2647 
2648       /* If the current user does not have select priv to mysql database,
2649          see if the current user can discover the role if it was granted to him.
2650       */
2651       if (cur_user && (read_access ||
2652                        traverse_role_graph_down(cur_user, &role_lex,
2653                                                 check_role_is_granted_callback,
2654                                                 NULL) == -1))
2655       {
2656         /* Role is not granted but current user can see the role */
2657         my_printf_error(ER_INVALID_ROLE, "User %`s@%`s has not been granted role %`s",
2658                         MYF(0), thd->security_ctx->priv_user,
2659                         thd->security_ctx->priv_host, rolename);
2660       }
2661       else
2662       {
2663         /* Role is not granted and current user cannot see the role */
2664         my_error(ER_INVALID_ROLE, MYF(0), rolename);
2665       }
2666       mysql_mutex_unlock(&acl_cache->lock);
2667       break;
2668   }
2669 
2670   return result;
2671 }
2672 
2673 
acl_check_setrole(THD * thd,const char * rolename,ulonglong * access)2674 int acl_check_setrole(THD *thd, const char *rolename, ulonglong *access)
2675 {
2676   if (!initialized)
2677   {
2678     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2679     return 1;
2680   }
2681 
2682   return check_user_can_set_role(thd, thd->security_ctx->priv_user,
2683            thd->security_ctx->host, thd->security_ctx->ip, rolename, access);
2684 }
2685 
2686 
acl_setrole(THD * thd,const char * rolename,ulonglong access)2687 int acl_setrole(THD *thd, const char *rolename, ulonglong access)
2688 {
2689   /* merge the privileges */
2690   Security_context *sctx= thd->security_ctx;
2691   sctx->master_access= static_cast<ulong>(access);
2692   if (thd->db.str)
2693     sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db.str, FALSE);
2694 
2695   if (!strcasecmp(rolename, "NONE"))
2696   {
2697     thd->security_ctx->priv_role[0]= 0;
2698   }
2699   else
2700   {
2701     if (thd->db.str)
2702       sctx->db_access|= acl_get("", "", rolename, thd->db.str, FALSE);
2703     /* mark the current role */
2704     strmake_buf(thd->security_ctx->priv_role, rolename);
2705   }
2706   return 0;
2707 }
2708 
check_get_key(ACL_USER * buff,size_t * length,my_bool not_used)2709 static uchar* check_get_key(ACL_USER *buff, size_t *length,
2710                             my_bool not_used __attribute__((unused)))
2711 {
2712   *length=buff->hostname_length;
2713   return (uchar*) buff->host.hostname;
2714 }
2715 
2716 
acl_update_role(const char * rolename,ulong privileges)2717 static void acl_update_role(const char *rolename, ulong privileges)
2718 {
2719   ACL_ROLE *role= find_acl_role(rolename);
2720   if (role)
2721     role->initial_role_access= role->access= privileges;
2722 }
2723 
2724 
acl_update_user(const char * user,const char * host,const char * password,size_t password_len,enum SSL_type ssl_type,const char * ssl_cipher,const char * x509_issuer,const char * x509_subject,USER_RESOURCES * mqh,ulong privileges,const LEX_CSTRING * plugin,const LEX_CSTRING * auth)2725 static void acl_update_user(const char *user, const char *host,
2726 			    const char *password, size_t password_len,
2727 			    enum SSL_type ssl_type,
2728 			    const char *ssl_cipher,
2729 			    const char *x509_issuer,
2730 			    const char *x509_subject,
2731 			    USER_RESOURCES  *mqh,
2732 			    ulong privileges,
2733 			    const LEX_CSTRING *plugin,
2734 			    const LEX_CSTRING *auth)
2735 {
2736   mysql_mutex_assert_owner(&acl_cache->lock);
2737 
2738   for (uint i=0 ; i < acl_users.elements ; i++)
2739   {
2740     ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
2741     if (acl_user->eq(user, host))
2742     {
2743       if (plugin->str[0])
2744       {
2745         acl_user->plugin= *plugin;
2746         acl_user->auth_string.str= auth->str ?
2747           strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
2748         acl_user->auth_string.length= auth->length;
2749         if (fix_user_plugin_ptr(acl_user))
2750           acl_user->plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
2751       }
2752       else
2753         if (password[0])
2754         {
2755           acl_user->auth_string.str= strmake_root(&acl_memroot, password, password_len);
2756           acl_user->auth_string.length= password_len;
2757           set_user_salt(acl_user, password, password_len);
2758           set_user_plugin(acl_user, password_len);
2759         }
2760       acl_user->access=privileges;
2761       if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
2762         acl_user->user_resource.questions=mqh->questions;
2763       if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
2764         acl_user->user_resource.updates=mqh->updates;
2765       if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
2766         acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
2767       if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
2768         acl_user->user_resource.user_conn= mqh->user_conn;
2769       if (mqh->specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
2770         acl_user->user_resource.max_statement_time= mqh->max_statement_time;
2771       if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
2772       {
2773         acl_user->ssl_type= ssl_type;
2774         acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) :
2775                                0);
2776         acl_user->x509_issuer= (x509_issuer ? strdup_root(&acl_memroot,x509_issuer) :
2777                                 0);
2778         acl_user->x509_subject= (x509_subject ?
2779                                  strdup_root(&acl_memroot,x509_subject) : 0);
2780       }
2781       /* search complete: */
2782       break;
2783     }
2784   }
2785 }
2786 
2787 
acl_insert_role(const char * rolename,ulong privileges)2788 static void acl_insert_role(const char *rolename, ulong privileges)
2789 {
2790   ACL_ROLE *entry;
2791 
2792   mysql_mutex_assert_owner(&acl_cache->lock);
2793   entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot);
2794   (void) my_init_dynamic_array(&entry->parent_grantee,
2795                                sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
2796   (void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *),
2797                                8, 8, MYF(0));
2798 
2799   my_hash_insert(&acl_roles, (uchar *)entry);
2800 }
2801 
2802 
acl_insert_user(const char * user,const char * host,const char * password,size_t password_len,enum SSL_type ssl_type,const char * ssl_cipher,const char * x509_issuer,const char * x509_subject,USER_RESOURCES * mqh,ulong privileges,const LEX_CSTRING * plugin,const LEX_CSTRING * auth)2803 static void acl_insert_user(const char *user, const char *host,
2804 			    const char *password, size_t password_len,
2805 			    enum SSL_type ssl_type,
2806 			    const char *ssl_cipher,
2807 			    const char *x509_issuer,
2808 			    const char *x509_subject,
2809 			    USER_RESOURCES *mqh,
2810 			    ulong privileges,
2811 			    const LEX_CSTRING *plugin,
2812 			    const LEX_CSTRING *auth)
2813 {
2814   ACL_USER acl_user;
2815 
2816   mysql_mutex_assert_owner(&acl_cache->lock);
2817 
2818   bzero(&acl_user, sizeof(acl_user));
2819   acl_user.user.str=*user ? strdup_root(&acl_memroot,user) : 0;
2820   acl_user.user.length= strlen(user);
2821   update_hostname(&acl_user.host, safe_strdup_root(&acl_memroot, host));
2822   if (plugin->str[0])
2823   {
2824     acl_user.plugin= *plugin;
2825     acl_user.auth_string.str= auth->str ?
2826       strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
2827     acl_user.auth_string.length= auth->length;
2828     if (fix_user_plugin_ptr(&acl_user))
2829       acl_user.plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
2830   }
2831   else
2832   {
2833     acl_user.auth_string.str= strmake_root(&acl_memroot, password, password_len);
2834     acl_user.auth_string.length= password_len;
2835     set_user_salt(&acl_user, password, password_len);
2836     set_user_plugin(&acl_user, password_len);
2837   }
2838 
2839   acl_user.flags= 0;
2840   acl_user.access=privileges;
2841   acl_user.user_resource = *mqh;
2842   acl_user.sort=get_sort(2, acl_user.host.hostname, acl_user.user.str);
2843   acl_user.hostname_length=(uint) strlen(host);
2844   acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ?
2845 		      ssl_type : SSL_TYPE_NONE);
2846   acl_user.ssl_cipher=	ssl_cipher   ? strdup_root(&acl_memroot,ssl_cipher) : 0;
2847   acl_user.x509_issuer= x509_issuer  ? strdup_root(&acl_memroot,x509_issuer) : 0;
2848   acl_user.x509_subject=x509_subject ? strdup_root(&acl_memroot,x509_subject) : 0;
2849   (void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *),
2850                                8, 8, MYF(0));
2851 
2852   (void) push_dynamic(&acl_users,(uchar*) &acl_user);
2853   if (!acl_user.host.hostname ||
2854       (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
2855     allow_all_hosts=1;		// Anyone can connect /* purecov: tested */
2856   my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
2857 	   sizeof(ACL_USER),(qsort_cmp) acl_compare);
2858 
2859   /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
2860   rebuild_check_host();
2861 
2862   /*
2863     Rebuild every user's role_grants since 'acl_users' has been sorted
2864     and old pointers to ACL_USER elements are no longer valid
2865   */
2866   rebuild_role_grants();
2867 }
2868 
2869 
acl_update_db(const char * user,const char * host,const char * db,ulong privileges)2870 static bool acl_update_db(const char *user, const char *host, const char *db,
2871                           ulong privileges)
2872 {
2873   mysql_mutex_assert_owner(&acl_cache->lock);
2874 
2875   bool updated= false;
2876 
2877   for (uint i=0 ; i < acl_dbs.elements() ; i++)
2878   {
2879     ACL_DB *acl_db= &acl_dbs.at(i);
2880     if ((!acl_db->user && !user[0]) ||
2881         (acl_db->user &&
2882          !strcmp(user,acl_db->user)))
2883     {
2884       if ((!acl_db->host.hostname && !host[0]) ||
2885           (acl_db->host.hostname &&
2886            !strcmp(host, acl_db->host.hostname)))
2887       {
2888         if ((!acl_db->db && !db[0]) ||
2889             (acl_db->db && !strcmp(db,acl_db->db)))
2890 
2891         {
2892           if (privileges)
2893           {
2894             acl_db->access= privileges;
2895             acl_db->initial_access= acl_db->access;
2896           }
2897           else
2898             acl_dbs.del(i);
2899           updated= true;
2900         }
2901       }
2902     }
2903   }
2904 
2905   return updated;
2906 }
2907 
2908 
2909 /*
2910   Insert a user/db/host combination into the global acl_cache
2911 
2912   SYNOPSIS
2913     acl_insert_db()
2914     user		User name
2915     host		Host name
2916     db			Database name
2917     privileges		Bitmap of privileges
2918 
2919   NOTES
2920     acl_cache->lock must be locked when calling this
2921 */
2922 
acl_insert_db(const char * user,const char * host,const char * db,ulong privileges)2923 static void acl_insert_db(const char *user, const char *host, const char *db,
2924                           ulong privileges)
2925 {
2926   ACL_DB acl_db;
2927   mysql_mutex_assert_owner(&acl_cache->lock);
2928   acl_db.user=strdup_root(&acl_memroot,user);
2929   update_hostname(&acl_db.host, safe_strdup_root(&acl_memroot, host));
2930   acl_db.db=strdup_root(&acl_memroot,db);
2931   acl_db.initial_access= acl_db.access= privileges;
2932   acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
2933   acl_dbs.push(acl_db);
2934   acl_dbs.sort((acl_dbs_cmp)acl_compare);
2935 }
2936 
2937 
2938 /*
2939   Get privilege for a host, user and db combination
2940 
2941   as db_is_pattern changes the semantics of comparison,
2942   acl_cache is not used if db_is_pattern is set.
2943 */
2944 
acl_get(const char * host,const char * ip,const char * user,const char * db,my_bool db_is_pattern)2945 ulong acl_get(const char *host, const char *ip,
2946               const char *user, const char *db, my_bool db_is_pattern)
2947 {
2948   ulong host_access= ~(ulong)0, db_access= 0;
2949   uint i;
2950   size_t key_length;
2951   char key[ACL_KEY_LENGTH],*tmp_db,*end;
2952   acl_entry *entry;
2953   DBUG_ENTER("acl_get");
2954 
2955   tmp_db= strmov(strmov(key, safe_str(ip)) + 1, user) + 1;
2956   end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db);
2957 
2958   if (end >= key + sizeof(key)) // db name was truncated
2959     DBUG_RETURN(0);             // no privileges for an invalid db name
2960 
2961   if (lower_case_table_names)
2962   {
2963     my_casedn_str(files_charset_info, tmp_db);
2964     db=tmp_db;
2965   }
2966   key_length= (size_t) (end-key);
2967 
2968   mysql_mutex_lock(&acl_cache->lock);
2969   if (!db_is_pattern && (entry=acl_cache->search((uchar*) key, key_length)))
2970   {
2971     db_access=entry->access;
2972     mysql_mutex_unlock(&acl_cache->lock);
2973     DBUG_PRINT("exit", ("access: 0x%lx", db_access));
2974     DBUG_RETURN(db_access);
2975   }
2976 
2977   /*
2978     Check if there are some access rights for database and user
2979   */
2980   for (i=0 ; i < acl_dbs.elements() ; i++)
2981   {
2982     ACL_DB *acl_db= &acl_dbs.at(i);
2983     if (!acl_db->user || !strcmp(user,acl_db->user))
2984     {
2985       if (compare_hostname(&acl_db->host,host,ip))
2986       {
2987         if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
2988         {
2989           db_access=acl_db->access;
2990           if (acl_db->host.hostname)
2991             goto exit;                          // Fully specified. Take it
2992           /* the host table is not used for roles */
2993           if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user))
2994             goto exit;
2995           break; /* purecov: tested */
2996 	}
2997       }
2998     }
2999   }
3000   if (!db_access)
3001     goto exit;					// Can't be better
3002 
3003   /*
3004     No host specified for user. Get hostdata from host table
3005   */
3006   host_access=0;				// Host must be found
3007   for (i=0 ; i < acl_hosts.elements ; i++)
3008   {
3009     ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
3010     if (compare_hostname(&acl_host->host,host,ip))
3011     {
3012       if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
3013       {
3014 	host_access=acl_host->access;		// Fully specified. Take it
3015 	break;
3016       }
3017     }
3018   }
3019 exit:
3020   /* Save entry in cache for quick retrieval */
3021   if (!db_is_pattern &&
3022       (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
3023   {
3024     entry->access=(db_access & host_access);
3025     DBUG_ASSERT(key_length < 0xffff);
3026     entry->length=(uint16)key_length;
3027     memcpy((uchar*) entry->key,key,key_length);
3028     acl_cache->add(entry);
3029   }
3030   mysql_mutex_unlock(&acl_cache->lock);
3031   DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
3032   DBUG_RETURN(db_access & host_access);
3033 }
3034 
3035 /*
3036   Check if there are any possible matching entries for this host
3037 
3038   NOTES
3039     All host names without wild cards are stored in a hash table,
3040     entries with wildcards are stored in a dynamic array
3041 */
3042 
init_check_host(void)3043 static void init_check_host(void)
3044 {
3045   DBUG_ENTER("init_check_host");
3046   (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
3047                                acl_users.elements, 1, MYF(0));
3048   (void) my_hash_init(&acl_check_hosts,system_charset_info,
3049                       acl_users.elements, 0, 0,
3050                       (my_hash_get_key) check_get_key, 0, 0);
3051   if (!allow_all_hosts)
3052   {
3053     for (uint i=0 ; i < acl_users.elements ; i++)
3054     {
3055       ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
3056       if (strchr(acl_user->host.hostname,wild_many) ||
3057 	  strchr(acl_user->host.hostname,wild_one) ||
3058 	  acl_user->host.ip_mask)
3059       {						// Has wildcard
3060 	uint j;
3061 	for (j=0 ; j < acl_wild_hosts.elements ; j++)
3062 	{					// Check if host already exists
3063 	  acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j,
3064 					       acl_host_and_ip *);
3065 	  if (!my_strcasecmp(system_charset_info,
3066                              acl_user->host.hostname, acl->hostname))
3067 	    break;				// already stored
3068 	}
3069 	if (j == acl_wild_hosts.elements)	// If new
3070 	  (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
3071       }
3072       else if (!my_hash_search(&acl_check_hosts,(uchar*)
3073                                acl_user->host.hostname,
3074                                strlen(acl_user->host.hostname)))
3075       {
3076 	if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
3077 	{					// End of memory
3078 	  allow_all_hosts=1;			// Should never happen
3079 	  DBUG_VOID_RETURN;
3080 	}
3081       }
3082     }
3083   }
3084   freeze_size(&acl_wild_hosts);
3085   freeze_size(&acl_check_hosts.array);
3086   DBUG_VOID_RETURN;
3087 }
3088 
3089 
3090 /*
3091   Rebuild lists used for checking of allowed hosts
3092 
3093   We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
3094   dropping or renaming user, since they contain pointers to elements of
3095   'acl_user' array, which are invalidated by drop operation, and use
3096   ACL_USER::host::hostname as a key, which is changed by rename.
3097 */
rebuild_check_host(void)3098 static void rebuild_check_host(void)
3099 {
3100   delete_dynamic(&acl_wild_hosts);
3101   my_hash_free(&acl_check_hosts);
3102   init_check_host();
3103 }
3104 
3105 /*
3106   Reset a role role_grants dynamic array.
3107   Also, the role's access bits are reset to the ones present in the table.
3108 */
acl_role_reset_role_arrays(void * ptr,void * not_used)3109 static my_bool acl_role_reset_role_arrays(void *ptr,
3110                                     void * not_used __attribute__((unused)))
3111 {
3112   ACL_ROLE *role= (ACL_ROLE *)ptr;
3113   reset_dynamic(&role->role_grants);
3114   reset_dynamic(&role->parent_grantee);
3115   role->counter= 0;
3116   return 0;
3117 }
3118 
3119 /*
3120    Add a the coresponding pointers present in the mapping to the entries in
3121    acl_users and acl_roles
3122 */
add_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role)3123 static bool add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
3124 {
3125   return push_dynamic(&grantee->role_grants, (uchar*) &role)
3126       || push_dynamic(&role->parent_grantee, (uchar*) &grantee);
3127 
3128 }
3129 
3130 /*
3131   Revert the last add_role_user_mapping() action
3132 */
undo_add_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role)3133 static void undo_add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
3134 {
3135   void *pop __attribute__((unused));
3136 
3137   pop= pop_dynamic(&grantee->role_grants);
3138   DBUG_ASSERT(role == *(ACL_ROLE**)pop);
3139 
3140   pop= pop_dynamic(&role->parent_grantee);
3141   DBUG_ASSERT(grantee == *(ACL_USER_BASE**)pop);
3142 }
3143 
3144 /*
3145   this helper is used when building role_grants and parent_grantee arrays
3146   from scratch.
3147 
3148   this happens either on initial loading of data from tables, in acl_load().
3149   or in rebuild_role_grants after acl_role_reset_role_arrays().
3150 */
add_role_user_mapping(const char * uname,const char * hname,const char * rname)3151 static bool add_role_user_mapping(const char *uname, const char *hname,
3152                                   const char *rname)
3153 {
3154   ACL_USER_BASE *grantee= find_acl_user_base(uname, hname);
3155   ACL_ROLE *role= find_acl_role(rname);
3156 
3157   if (grantee == NULL || role == NULL)
3158     return 1;
3159 
3160   /*
3161     because all arrays are rebuilt completely, and counters were also reset,
3162     we can increment them here, and after the rebuild all counters will
3163     have correct values (equal to the number of roles granted).
3164   */
3165   if (grantee->flags & IS_ROLE)
3166     ((ACL_ROLE*)grantee)->counter++;
3167   return add_role_user_mapping(grantee, role);
3168 }
3169 
3170 /*
3171   This helper function is used to removes roles and grantees
3172   from the corresponding cross-reference arrays. see remove_role_user_mapping().
3173   as such, it asserts that an element to delete is present in the array,
3174   and is present only once.
3175 */
remove_ptr_from_dynarray(DYNAMIC_ARRAY * array,void * ptr)3176 static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr)
3177 {
3178   bool found __attribute__((unused))= false;
3179   for (uint i= 0; i < array->elements; i++)
3180   {
3181     if (ptr == *dynamic_element(array, i, void**))
3182     {
3183       DBUG_ASSERT(!found);
3184       delete_dynamic_element(array, i);
3185       IF_DBUG_ASSERT(found= true, break);
3186     }
3187   }
3188   DBUG_ASSERT(found);
3189 }
3190 
remove_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role,int grantee_idx=-1,int role_idx=-1)3191 static void remove_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role,
3192                                      int grantee_idx=-1, int role_idx=-1)
3193 {
3194   remove_ptr_from_dynarray(&grantee->role_grants, role);
3195   remove_ptr_from_dynarray(&role->parent_grantee, grantee);
3196 }
3197 
3198 
add_role_user_mapping_action(void * ptr,void * unused)3199 static my_bool add_role_user_mapping_action(void *ptr, void *unused __attribute__((unused)))
3200 {
3201   ROLE_GRANT_PAIR *pair= (ROLE_GRANT_PAIR*)ptr;
3202   bool status __attribute__((unused));
3203   status= add_role_user_mapping(pair->u_uname, pair->u_hname, pair->r_uname);
3204   /*
3205      The invariant chosen is that acl_roles_mappings should _always_
3206      only contain valid entries, referencing correct user and role grants.
3207      If add_role_user_mapping detects an invalid entry, it will not add
3208      the mapping into the ACL_USER::role_grants array.
3209   */
3210   DBUG_ASSERT(status == 0);
3211   return 0;
3212 }
3213 
3214 
3215 /*
3216   Rebuild the role grants every time the acl_users is modified
3217 
3218   The role grants in the ACL_USER class need to be rebuilt, as they contain
3219   pointers to elements of the acl_users array.
3220 */
3221 
rebuild_role_grants(void)3222 static void rebuild_role_grants(void)
3223 {
3224   DBUG_ENTER("rebuild_role_grants");
3225   /*
3226     Reset every user's and role's role_grants array
3227   */
3228   for (uint i=0; i < acl_users.elements; i++) {
3229     ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *);
3230     reset_dynamic(&user->role_grants);
3231   }
3232   my_hash_iterate(&acl_roles, acl_role_reset_role_arrays, NULL);
3233 
3234   /* Rebuild the direct links between users and roles in ACL_USER::role_grants */
3235   my_hash_iterate(&acl_roles_mappings, add_role_user_mapping_action, NULL);
3236 
3237   DBUG_VOID_RETURN;
3238 }
3239 
3240 
3241 /* Return true if there is no users that can match the given host */
acl_check_host(const char * host,const char * ip)3242 bool acl_check_host(const char *host, const char *ip)
3243 {
3244   if (allow_all_hosts)
3245     return 0;
3246   mysql_mutex_lock(&acl_cache->lock);
3247 
3248   if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
3249       (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
3250   {
3251     mysql_mutex_unlock(&acl_cache->lock);
3252     return 0;					// Found host
3253   }
3254   for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
3255   {
3256     acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
3257     if (compare_hostname(acl, host, ip))
3258     {
3259       mysql_mutex_unlock(&acl_cache->lock);
3260       return 0;					// Host ok
3261     }
3262   }
3263   mysql_mutex_unlock(&acl_cache->lock);
3264   if (ip != NULL)
3265   {
3266     /* Increment HOST_CACHE.COUNT_HOST_ACL_ERRORS. */
3267     Host_errors errors;
3268     errors.m_host_acl= 1;
3269     inc_host_errors(ip, &errors);
3270   }
3271   return 1;					// Host is not allowed
3272 }
3273 
3274 /**
3275   Check if the user is allowed to alter the mysql.user table
3276 
3277  @param thd              THD
3278  @param host             Hostname for the user
3279  @param user             User name
3280 
3281  @return Error status
3282    @retval 0 OK
3283    @retval 1 Error
3284 */
3285 
check_alter_user(THD * thd,const char * host,const char * user)3286 static int check_alter_user(THD *thd, const char *host, const char *user)
3287 {
3288   int error = 1;
3289   if (!initialized)
3290   {
3291     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3292     goto end;
3293   }
3294 
3295   if (IF_WSREP((!WSREP(thd) || !thd->wsrep_applier), 1) &&
3296       !thd->slave_thread && !thd->security_ctx->priv_user[0] &&
3297       !in_bootstrap)
3298   {
3299     my_message(ER_PASSWORD_ANONYMOUS_USER,
3300                ER_THD(thd, ER_PASSWORD_ANONYMOUS_USER),
3301                MYF(0));
3302     goto end;
3303   }
3304   if (!host) // Role
3305   {
3306     my_error(ER_PASSWORD_NO_MATCH, MYF(0));
3307     goto end;
3308   }
3309 
3310   if (!thd->slave_thread &&
3311       IF_WSREP((!WSREP(thd) || !thd->wsrep_applier),1) &&
3312       (strcmp(thd->security_ctx->priv_user, user) ||
3313        my_strcasecmp(system_charset_info, host,
3314                      thd->security_ctx->priv_host)))
3315   {
3316     if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
3317       goto end;
3318   }
3319 
3320   error = 0;
3321 
3322 end:
3323   return error;
3324 }
3325 /**
3326   Check if the user is allowed to change password
3327 
3328  @param thd              THD
3329  @param user             User, hostname, new password or password hash
3330 
3331  @return Error status
3332    @retval 0 OK
3333    @retval 1 ERROR; In this case the error is sent to the client.
3334 */
3335 
check_change_password(THD * thd,LEX_USER * user)3336 bool check_change_password(THD *thd, LEX_USER *user)
3337 {
3338   LEX_USER *real_user= get_current_user(thd, user);
3339 
3340   if (fix_and_copy_user(real_user, user, thd) ||
3341       validate_password(real_user, thd))
3342     return true;
3343 
3344   *user= *real_user;
3345 
3346   return check_alter_user(thd, user->host.str, user->user.str);
3347 }
3348 
3349 
3350 /**
3351   Change a password for a user.
3352 
3353   @param thd            THD
3354   @param user           User, hostname, new password hash
3355 
3356   @return Error code
3357    @retval 0 ok
3358    @retval 1 ERROR; In this case the error is sent to the client.
3359 */
change_password(THD * thd,LEX_USER * user)3360 bool change_password(THD *thd, LEX_USER *user)
3361 {
3362   Grant_tables tables(Table_user, TL_WRITE);
3363   /* Buffer should be extended when password length is extended. */
3364   char buff[512];
3365   ulong query_length= 0;
3366   enum_binlog_format save_binlog_format;
3367   int result=0;
3368   const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
3369   DBUG_ENTER("change_password");
3370   DBUG_PRINT("enter",("host: '%s'  user: '%s'  new_password: '%s'",
3371 		      user->host.str, user->user.str, user->pwhash.str));
3372   DBUG_ASSERT(user->host.str != 0);                     // Ensured by parent
3373 
3374   /*
3375     This statement will be replicated as a statement, even when using
3376     row-based replication.  The flag will be reset at the end of the
3377     statement.
3378     This has to be handled here as it's called by set_var.cc, which is
3379     not automaticly handled by sql_parse.cc
3380   */
3381   save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
3382 
3383   if (mysql_bin_log.is_open() ||
3384       (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)))
3385   {
3386     query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
3387               safe_str(user->user.str), safe_str(user->host.str),
3388               safe_str(user->pwhash.str));
3389   }
3390 
3391   if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
3392   {
3393     thd->set_query(buff, query_length, system_charset_info);
3394     WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL);
3395   }
3396 
3397   if ((result= tables.open_and_lock(thd)))
3398     DBUG_RETURN(result != 1);
3399 
3400   result= 1;
3401 
3402   mysql_mutex_lock(&acl_cache->lock);
3403   ACL_USER *acl_user;
3404   if (!(acl_user= find_user_exact(user->host.str, user->user.str)))
3405   {
3406     mysql_mutex_unlock(&acl_cache->lock);
3407     my_message(ER_PASSWORD_NO_MATCH,
3408                ER_THD(thd, ER_PASSWORD_NO_MATCH), MYF(0));
3409     goto end;
3410   }
3411 
3412   /* update loaded acl entry: */
3413   if (acl_user->plugin.str == native_password_plugin_name.str ||
3414       acl_user->plugin.str == old_password_plugin_name.str)
3415   {
3416     acl_user->auth_string.str= strmake_root(&acl_memroot, user->pwhash.str, user->pwhash.length);
3417     acl_user->auth_string.length= user->pwhash.length;
3418     set_user_salt(acl_user, user->pwhash.str, user->pwhash.length);
3419 
3420     set_user_plugin(acl_user, user->pwhash.length);
3421   }
3422   else
3423     push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
3424                  ER_SET_PASSWORD_AUTH_PLUGIN,
3425                  ER_THD(thd, ER_SET_PASSWORD_AUTH_PLUGIN));
3426 
3427   if (update_user_table(thd, tables.user_table(),
3428                         safe_str(acl_user->host.hostname),
3429                         safe_str(acl_user->user.str),
3430                         user->pwhash.str, user->pwhash.length))
3431   {
3432     mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
3433     goto end;
3434   }
3435 
3436   acl_cache->clear(1);				// Clear locked hostname cache
3437   mysql_mutex_unlock(&acl_cache->lock);
3438   result= 0;
3439   if (mysql_bin_log.is_open())
3440   {
3441     DBUG_ASSERT(query_length);
3442     thd->clear_error();
3443     result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
3444                               FALSE, FALSE, FALSE, 0) > 0;
3445   }
3446 end:
3447   close_mysql_tables(thd);
3448 
3449 #ifdef WITH_WSREP
3450 WSREP_ERROR_LABEL:
3451   if (WSREP(thd) && !thd->wsrep_applier)
3452   {
3453     WSREP_TO_ISOLATION_END;
3454 
3455     thd->set_query(query_save);
3456     thd->wsrep_exec_mode  = LOCAL_STATE;
3457   }
3458 #endif /* WITH_WSREP */
3459   thd->restore_stmt_binlog_format(save_binlog_format);
3460 
3461   DBUG_RETURN(result);
3462 }
3463 
acl_check_set_default_role(THD * thd,const char * host,const char * user,const char * role)3464 int acl_check_set_default_role(THD *thd, const char *host, const char *user,
3465                                const char *role)
3466 {
3467   DBUG_ENTER("acl_check_set_default_role");
3468   DBUG_RETURN(check_alter_user(thd, host, user) ||
3469               check_user_can_set_role(thd, user, host, NULL, role, NULL));
3470 }
3471 
acl_set_default_role(THD * thd,const char * host,const char * user,const char * rolename)3472 int acl_set_default_role(THD *thd, const char *host, const char *user,
3473                          const char *rolename)
3474 {
3475   Grant_tables tables(Table_user, TL_WRITE);
3476   char user_key[MAX_KEY_LENGTH];
3477   int result= 1;
3478   int error;
3479   ulong query_length= 0;
3480   bool clear_role= FALSE;
3481   char buff[512];
3482   enum_binlog_format save_binlog_format=
3483     thd->get_current_stmt_binlog_format();
3484   const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
3485 
3486   DBUG_ENTER("acl_set_default_role");
3487   DBUG_PRINT("enter",("host: '%s'  user: '%s'  rolename: '%s'",
3488                       safe_str(user), safe_str(host), safe_str(rolename)));
3489 
3490   if (!strcasecmp(rolename, "NONE"))
3491     clear_role= TRUE;
3492 
3493   if (mysql_bin_log.is_open() ||
3494       (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)))
3495   {
3496     query_length=
3497       sprintf(buff,"SET DEFAULT ROLE '%-.120s' FOR '%-.120s'@'%-.120s'",
3498               safe_str(rolename), safe_str(user), safe_str(host));
3499   }
3500 
3501   /*
3502     This statement will be replicated as a statement, even when using
3503     row-based replication.  The flag will be reset at the end of the
3504     statement.
3505     This has to be handled here as it's called by set_var.cc, which is
3506     not automaticly handled by sql_parse.cc
3507   */
3508   save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
3509 
3510   if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
3511   {
3512     thd->set_query(buff, query_length, system_charset_info);
3513     // Attention!!! here is implicit goto error;
3514     WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL);
3515   }
3516 
3517   /*
3518     Extra block due to WSREP_TO_ISOLATION_BEGIN using goto.
3519     TODO(cvicentiu) Should move  this block out in a new function.
3520   */
3521   {
3522     if ((result= tables.open_and_lock(thd)))
3523       DBUG_RETURN(result != 1);
3524 
3525     const User_table& user_table= tables.user_table();
3526     TABLE *table= user_table.table();
3527 
3528     result= 1;
3529 
3530     mysql_mutex_lock(&acl_cache->lock);
3531     ACL_USER *acl_user;
3532     if (!(acl_user= find_user_exact(host, user)))
3533     {
3534       mysql_mutex_unlock(&acl_cache->lock);
3535       my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
3536                  MYF(0));
3537       goto end;
3538     }
3539 
3540     if (!clear_role)
3541     {
3542       /* set new default_rolename */
3543       acl_user->default_rolename.str= safe_strdup_root(&acl_memroot, rolename);
3544       acl_user->default_rolename.length= strlen(rolename);
3545     }
3546     else
3547     {
3548       /* clear the default_rolename */
3549       acl_user->default_rolename.str = NULL;
3550       acl_user->default_rolename.length = 0;
3551     }
3552 
3553     /* update the mysql.user table with the new default role */
3554     tables.user_table().table()->use_all_columns();
3555     if (!tables.user_table().default_role())
3556     {
3557       my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
3558                table->alias.c_ptr(), DEFAULT_ROLE_COLUMN_IDX + 1,
3559                tables.user_table().num_fields(),
3560                static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
3561       mysql_mutex_unlock(&acl_cache->lock);
3562       goto end;
3563     }
3564     user_table.host()->store(host,(uint) strlen(host), system_charset_info);
3565     user_table.user()->store(user,(uint) strlen(user), system_charset_info);
3566     key_copy((uchar *) user_key, table->record[0], table->key_info,
3567              table->key_info->key_length);
3568 
3569     if (table->file->ha_index_read_idx_map(table->record[0], 0,
3570                                            (uchar *) user_key, HA_WHOLE_KEY,
3571                                            HA_READ_KEY_EXACT))
3572     {
3573       mysql_mutex_unlock(&acl_cache->lock);
3574       my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
3575                  MYF(0));
3576       goto end;
3577     }
3578     store_record(table, record[1]);
3579     user_table.default_role()->store(acl_user->default_rolename.str,
3580                                      acl_user->default_rolename.length,
3581                                      system_charset_info);
3582     if (unlikely(error= table->file->ha_update_row(table->record[1],
3583                                                    table->record[0])) &&
3584         error != HA_ERR_RECORD_IS_THE_SAME)
3585     {
3586       mysql_mutex_unlock(&acl_cache->lock);
3587       table->file->print_error(error,MYF(0));	/* purecov: deadcode */
3588       goto end;
3589     }
3590 
3591     acl_cache->clear(1);
3592     mysql_mutex_unlock(&acl_cache->lock);
3593     result= 0;
3594     if (mysql_bin_log.is_open())
3595     {
3596       DBUG_ASSERT(query_length);
3597       thd->clear_error();
3598       result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
3599                                 FALSE, FALSE, FALSE, 0) > 0;
3600     }
3601   end:
3602     close_mysql_tables(thd);
3603   }
3604 
3605 #ifdef WITH_WSREP
3606 WSREP_ERROR_LABEL:
3607   if (WSREP(thd) && !thd->wsrep_applier)
3608   {
3609     WSREP_TO_ISOLATION_END;
3610 
3611     thd->set_query(query_save);
3612     thd->wsrep_exec_mode  = LOCAL_STATE;
3613   }
3614 #endif /* WITH_WSREP */
3615 
3616   thd->restore_stmt_binlog_format(save_binlog_format);
3617 
3618   DBUG_RETURN(result);
3619 }
3620 
3621 
3622 /*
3623   Find user in ACL
3624 
3625   SYNOPSIS
3626     is_acl_user()
3627     host                 host name
3628     user                 user name
3629 
3630   RETURN
3631    FALSE  user not fond
3632    TRUE   there is such user
3633 */
3634 
is_acl_user(const char * host,const char * user)3635 bool is_acl_user(const char *host, const char *user)
3636 {
3637   bool res;
3638 
3639   /* --skip-grants */
3640   if (!initialized)
3641     return TRUE;
3642 
3643   mysql_mutex_lock(&acl_cache->lock);
3644 
3645   if (*host) // User
3646     res= find_user_exact(host, user) != NULL;
3647   else // Role
3648     res= find_acl_role(user) != NULL;
3649 
3650   mysql_mutex_unlock(&acl_cache->lock);
3651   return res;
3652 }
3653 
3654 /*
3655   Find first entry that matches the specified user@host pair
3656 */
find_user_exact(const char * host,const char * user)3657 static ACL_USER * find_user_exact(const char *host, const char *user)
3658 {
3659   mysql_mutex_assert_owner(&acl_cache->lock);
3660 
3661   for (uint i=0 ; i < acl_users.elements ; i++)
3662   {
3663     ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
3664     if (acl_user->eq(user, host))
3665       return acl_user;
3666   }
3667   return 0;
3668 }
3669 
3670 /*
3671   Find first entry that matches the specified user@host pair
3672 */
find_user_wild(const char * host,const char * user,const char * ip)3673 static ACL_USER * find_user_wild(const char *host, const char *user, const char *ip)
3674 {
3675   mysql_mutex_assert_owner(&acl_cache->lock);
3676 
3677   for (uint i=0 ; i < acl_users.elements ; i++)
3678   {
3679     ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
3680     if (acl_user->wild_eq(user, host, ip))
3681       return acl_user;
3682   }
3683   return 0;
3684 }
3685 
3686 /*
3687   Find a role with the specified name
3688 */
find_acl_role(const char * role)3689 static ACL_ROLE *find_acl_role(const char *role)
3690 {
3691   DBUG_ENTER("find_acl_role");
3692   DBUG_PRINT("enter",("role: '%s'", role));
3693   DBUG_PRINT("info", ("Hash elements: %ld", acl_roles.records));
3694 
3695   mysql_mutex_assert_owner(&acl_cache->lock);
3696 
3697   ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role,
3698                                           safe_strlen(role));
3699   DBUG_RETURN(r);
3700 }
3701 
3702 
find_acl_user_base(const char * user,const char * host)3703 static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host)
3704 {
3705   if (*host)
3706     return find_user_exact(host, user);
3707 
3708   return find_acl_role(user);
3709 }
3710 
3711 
3712 /*
3713   Comparing of hostnames
3714 
3715   NOTES
3716   A hostname may be of type:
3717   hostname   (May include wildcards);   monty.pp.sci.fi
3718   ip	   (May include wildcards);   192.168.0.0
3719   ip/netmask			      192.168.0.0/255.255.255.0
3720 
3721   A net mask of 0.0.0.0 is not allowed.
3722 */
3723 
calc_ip(const char * ip,long * val,char end)3724 static const char *calc_ip(const char *ip, long *val, char end)
3725 {
3726   long ip_val,tmp;
3727   if (!(ip=str2int(ip,10,0,255,&ip_val)) || *ip != '.')
3728     return 0;
3729   ip_val<<=24;
3730   if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
3731     return 0;
3732   ip_val+=tmp<<16;
3733   if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
3734     return 0;
3735   ip_val+=tmp<<8;
3736   if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != end)
3737     return 0;
3738   *val=ip_val+tmp;
3739   return ip;
3740 }
3741 
3742 
update_hostname(acl_host_and_ip * host,const char * hostname)3743 static void update_hostname(acl_host_and_ip *host, const char *hostname)
3744 {
3745   // fix historical undocumented convention that empty host is the same as '%'
3746   hostname=const_cast<char*>(hostname ? hostname : host_not_specified.str);
3747   host->hostname=(char*) hostname;             // This will not be modified!
3748   if (!(hostname= calc_ip(hostname,&host->ip,'/')) ||
3749       !(hostname= calc_ip(hostname+1,&host->ip_mask,'\0')))
3750   {
3751     host->ip= host->ip_mask=0;			// Not a masked ip
3752   }
3753 }
3754 
3755 
compare_hostname(const acl_host_and_ip * host,const char * hostname,const char * ip)3756 static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
3757 			     const char *ip)
3758 {
3759   long tmp;
3760   if (host->ip_mask && ip && calc_ip(ip,&tmp,'\0'))
3761   {
3762     return (tmp & host->ip_mask) == host->ip;
3763   }
3764   return (!host->hostname ||
3765 	  (hostname && !wild_case_compare(system_charset_info,
3766                                           hostname, host->hostname)) ||
3767 	  (ip && !wild_compare(ip, host->hostname, 0)));
3768 }
3769 
3770 /**
3771   Check if the given host name needs to be resolved or not.
3772   Host name has to be resolved if it actually contains *name*.
3773 
3774   For example:
3775     192.168.1.1               --> FALSE
3776     192.168.1.0/255.255.255.0 --> FALSE
3777     %                         --> FALSE
3778     192.168.1.%               --> FALSE
3779     AB%                       --> FALSE
3780 
3781     AAAAFFFF                  --> TRUE (Hostname)
3782     AAAA:FFFF:1234:5678       --> FALSE
3783     ::1                       --> FALSE
3784 
3785   This function does not check if the given string is a valid host name or
3786   not. It assumes that the argument is a valid host name.
3787 
3788   @param hostname   the string to check.
3789 
3790   @return a flag telling if the argument needs to be resolved or not.
3791   @retval TRUE the argument is a host name and needs to be resolved.
3792   @retval FALSE the argument is either an IP address, or a patter and
3793           should not be resolved.
3794 */
3795 
hostname_requires_resolving(const char * hostname)3796 bool hostname_requires_resolving(const char *hostname)
3797 {
3798   if (!hostname)
3799     return FALSE;
3800 
3801   /* Check if hostname is the localhost. */
3802 
3803   size_t hostname_len= strlen(hostname);
3804   size_t localhost_len= strlen(my_localhost);
3805 
3806   if (hostname == my_localhost ||
3807       (hostname_len == localhost_len &&
3808        !my_strnncoll(system_charset_info,
3809                      (const uchar *) hostname,  hostname_len,
3810                      (const uchar *) my_localhost, strlen(my_localhost))))
3811   {
3812     return FALSE;
3813   }
3814 
3815   /*
3816     If the string contains any of {':', '%', '_', '/'}, it is definitely
3817     not a host name:
3818       - ':' means that the string is an IPv6 address;
3819       - '%' or '_' means that the string is a pattern;
3820       - '/' means that the string is an IPv4 network address;
3821   */
3822 
3823   for (const char *p= hostname; *p; ++p)
3824   {
3825     switch (*p) {
3826       case ':':
3827       case '%':
3828       case '_':
3829       case '/':
3830         return FALSE;
3831     }
3832   }
3833 
3834   /*
3835     Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
3836     (12.34.56.78). The assumption is that if the string contains only
3837     digits and dots, it is an IPv4 address. Otherwise -- a host name.
3838   */
3839 
3840   for (const char *p= hostname; *p; ++p)
3841   {
3842     if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
3843       return TRUE; /* a "letter" has been found. */
3844   }
3845 
3846   return FALSE; /* all characters are either dots or digits. */
3847 }
3848 
3849 
set_authentication_plugin_from_password(const User_table & user_table,const char * password,size_t password_length)3850 void set_authentication_plugin_from_password(const User_table& user_table,
3851                                              const char* password, size_t password_length)
3852 {
3853   if (password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH ||
3854       password_length == 0)
3855   {
3856     user_table.plugin()->store(native_password_plugin_name.str,
3857                                native_password_plugin_name.length,
3858                                system_charset_info);
3859   }
3860   else
3861   {
3862     DBUG_ASSERT(password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
3863     user_table.plugin()->store(old_password_plugin_name.str,
3864                                old_password_plugin_name.length,
3865                                system_charset_info);
3866   }
3867   user_table.authentication_string()->store(password,
3868                                             password_length,
3869                                             system_charset_info);
3870 }
3871 /**
3872   Update record for user in mysql.user privilege table with new password.
3873 
3874   @param thd              THD
3875   @param table            Pointer to TABLE object for open mysql.user table
3876   @param host             Hostname
3877   @param user             Username
3878   @param new_password     New password hash
3879   @param new_password_len Length of new password hash
3880 
3881   @see change_password
3882 */
3883 
update_user_table(THD * thd,const User_table & user_table,const char * host,const char * user,const char * new_password,size_t new_password_len)3884 static bool update_user_table(THD *thd, const User_table& user_table,
3885                               const char *host, const char *user,
3886                               const char *new_password, size_t new_password_len)
3887 {
3888   char user_key[MAX_KEY_LENGTH];
3889   int error;
3890   DBUG_ENTER("update_user_table");
3891   DBUG_PRINT("enter",("user: %s  host: %s",user,host));
3892 
3893   TABLE *table= user_table.table();
3894   table->use_all_columns();
3895   user_table.host()->store(host,(uint) strlen(host), system_charset_info);
3896   user_table.user()->store(user,(uint) strlen(user), system_charset_info);
3897   key_copy((uchar *) user_key, table->record[0], table->key_info,
3898            table->key_info->key_length);
3899 
3900   if (table->file->ha_index_read_idx_map(table->record[0], 0,
3901                                          (uchar *) user_key, HA_WHOLE_KEY,
3902                                          HA_READ_KEY_EXACT))
3903   {
3904     my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
3905                MYF(0));  /* purecov: deadcode */
3906     DBUG_RETURN(1);      /* purecov: deadcode */
3907   }
3908   store_record(table,record[1]);
3909 
3910   if (user_table.plugin())
3911   {
3912     set_authentication_plugin_from_password(user_table, new_password,
3913                                             new_password_len);
3914   }
3915 
3916   if (user_table.password())
3917     user_table.password()->store(new_password, new_password_len, system_charset_info);
3918 
3919 
3920   if (unlikely(error= table->file->ha_update_row(table->record[1],
3921                                                  table->record[0])) &&
3922       error != HA_ERR_RECORD_IS_THE_SAME)
3923   {
3924     table->file->print_error(error,MYF(0));  /* purecov: deadcode */
3925     DBUG_RETURN(1);
3926   }
3927   DBUG_RETURN(0);
3928 }
3929 
3930 
3931 /*
3932   Return 1 if we are allowed to create new users
3933   the logic here is: INSERT_ACL is sufficient.
3934   It's also a requirement in opt_safe_user_create,
3935   otherwise CREATE_USER_ACL is enough.
3936 */
3937 
test_if_create_new_users(THD * thd)3938 static bool test_if_create_new_users(THD *thd)
3939 {
3940   Security_context *sctx= thd->security_ctx;
3941   bool create_new_users= MY_TEST(sctx->master_access & INSERT_ACL) ||
3942                          (!opt_safe_user_create &&
3943                           MY_TEST(sctx->master_access & CREATE_USER_ACL));
3944   if (!create_new_users)
3945   {
3946     TABLE_LIST tl;
3947     ulong db_access;
3948     tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, TL_WRITE);
3949     create_new_users= 1;
3950 
3951     db_access=acl_get(sctx->host, sctx->ip,
3952 		      sctx->priv_user, tl.db.str, 0);
3953     if (sctx->priv_role[0])
3954       db_access|= acl_get("", "", sctx->priv_role, tl.db.str, 0);
3955     if (!(db_access & INSERT_ACL))
3956     {
3957       if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
3958         create_new_users=0;
3959     }
3960   }
3961   return create_new_users;
3962 }
3963 
3964 
3965 /****************************************************************************
3966   Handle GRANT commands
3967 ****************************************************************************/
3968 
replace_user_table(THD * thd,const User_table & user_table,LEX_USER & combo,ulong rights,bool revoke_grant,bool can_create_user,bool no_auto_create)3969 static int replace_user_table(THD *thd, const User_table &user_table,
3970                               LEX_USER &combo,
3971                               ulong rights, bool revoke_grant,
3972                               bool can_create_user, bool no_auto_create)
3973 {
3974   int error = -1;
3975   bool old_row_exists=0;
3976   char what= (revoke_grant) ? 'N' : 'Y';
3977   uchar user_key[MAX_KEY_LENGTH];
3978   bool handle_as_role= combo.is_role();
3979   LEX *lex= thd->lex;
3980   TABLE *table= user_table.table();
3981   DBUG_ENTER("replace_user_table");
3982 
3983   mysql_mutex_assert_owner(&acl_cache->lock);
3984 
3985   if (combo.pwhash.str && combo.pwhash.str[0])
3986   {
3987     if (combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
3988         combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
3989     {
3990       DBUG_ASSERT(0);
3991       my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
3992       DBUG_RETURN(-1);
3993     }
3994   }
3995   else
3996     combo.pwhash= empty_clex_str;
3997 
3998   /* if the user table is not up to date, we can't handle role updates */
3999   if (!user_table.is_role() && handle_as_role)
4000   {
4001     my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
4002              "user", ROLE_ASSIGN_COLUMN_IDX + 1, user_table.num_fields(),
4003              static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
4004     DBUG_RETURN(-1);
4005   }
4006 
4007   table->use_all_columns();
4008   user_table.host()->store(combo.host.str,combo.host.length,
4009                          system_charset_info);
4010   user_table.user()->store(combo.user.str,combo.user.length,
4011                          system_charset_info);
4012   key_copy(user_key, table->record[0], table->key_info,
4013            table->key_info->key_length);
4014 
4015   if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
4016                                          HA_WHOLE_KEY, HA_READ_KEY_EXACT))
4017   {
4018     /* what == 'N' means revoke */
4019     if (what == 'N')
4020     {
4021       if (combo.host.length)
4022         my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
4023       else
4024         my_error(ER_INVALID_ROLE, MYF(0), combo.user.str);
4025       goto end;
4026     }
4027     /*
4028       There are four options which affect the process of creation of
4029       a new user (mysqld option --safe-create-user, 'insert' privilege
4030       on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
4031       SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
4032       how it should work.
4033       if (safe-user-create && ! INSERT_priv) => reject
4034       else if (identified_by) => create
4035       else if (no_auto_create_user) => reject
4036       else create
4037 
4038       see also test_if_create_new_users()
4039     */
4040     else if (!combo.pwhash.length && !combo.plugin.length && no_auto_create)
4041     {
4042       my_error(ER_PASSWORD_NO_MATCH, MYF(0));
4043       goto end;
4044     }
4045     else if (!can_create_user)
4046     {
4047       my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
4048       goto end;
4049     }
4050     else if (combo.plugin.str[0])
4051     {
4052       if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN))
4053       {
4054         my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str);
4055         goto end;
4056       }
4057     }
4058 
4059     old_row_exists = 0;
4060     restore_record(table,s->default_values);
4061     user_table.host()->store(combo.host.str,combo.host.length,
4062                            system_charset_info);
4063     user_table.user()->store(combo.user.str,combo.user.length,
4064                            system_charset_info);
4065   }
4066   else
4067   {
4068     old_row_exists = 1;
4069     store_record(table,record[1]);			// Save copy for update
4070   }
4071 
4072   if (!old_row_exists || combo.pwtext.length || combo.pwhash.length)
4073     if (!handle_as_role && validate_password(&combo, thd))
4074       goto end;
4075 
4076   /* Update table columns with new privileges */
4077 
4078   ulong priv;
4079   priv = SELECT_ACL;
4080   for (uint i= 0; i < user_table.num_privileges(); i++, priv <<= 1)
4081   {
4082     if (priv & rights)
4083       user_table.priv_field(i)->store(&what, 1, &my_charset_latin1);
4084   }
4085 
4086   rights= user_table.get_access();
4087 
4088   DBUG_PRINT("info",("table fields: %d", user_table.num_fields()));
4089   /* If we don't have a password column, we'll use the authentication_string
4090      column later. */
4091   if (combo.pwhash.str[0] && user_table.password())
4092     user_table.password()->store(combo.pwhash.str, combo.pwhash.length,
4093                                  system_charset_info);
4094   /* We either have the password column, the plugin column, or both. Otherwise
4095      we have a corrupt user table. */
4096   DBUG_ASSERT(user_table.password() || user_table.plugin());
4097   if (user_table.ssl_type())    /* From 4.0.0 we have more fields */
4098   {
4099     /* We write down SSL related ACL stuff */
4100     switch (lex->ssl_type) {
4101     case SSL_TYPE_ANY:
4102       user_table.ssl_type()->store(STRING_WITH_LEN("ANY"),
4103                                    &my_charset_latin1);
4104       user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4105       user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4106       user_table.x509_subject()->store("", 0, &my_charset_latin1);
4107       break;
4108     case SSL_TYPE_X509:
4109       user_table.ssl_type()->store(STRING_WITH_LEN("X509"),
4110                                    &my_charset_latin1);
4111       user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4112       user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4113       user_table.x509_subject()->store("", 0, &my_charset_latin1);
4114       break;
4115     case SSL_TYPE_SPECIFIED:
4116       user_table.ssl_type()->store(STRING_WITH_LEN("SPECIFIED"),
4117                                    &my_charset_latin1);
4118       user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4119       user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4120       user_table.x509_subject()->store("", 0, &my_charset_latin1);
4121       if (lex->ssl_cipher)
4122         user_table.ssl_cipher()->store(lex->ssl_cipher,
4123                                        strlen(lex->ssl_cipher),
4124                                        system_charset_info);
4125       if (lex->x509_issuer)
4126         user_table.x509_issuer()->store(lex->x509_issuer,
4127                                         strlen(lex->x509_issuer),
4128                                         system_charset_info);
4129       if (lex->x509_subject)
4130         user_table.x509_subject()->store(lex->x509_subject,
4131                                          strlen(lex->x509_subject),
4132                                          system_charset_info);
4133       break;
4134     case SSL_TYPE_NOT_SPECIFIED:
4135       break;
4136     case SSL_TYPE_NONE:
4137       user_table.ssl_type()->store("", 0, &my_charset_latin1);
4138       user_table.ssl_cipher()->store("", 0, &my_charset_latin1);
4139       user_table.x509_issuer()->store("", 0, &my_charset_latin1);
4140       user_table.x509_subject()->store("", 0, &my_charset_latin1);
4141       break;
4142     }
4143 
4144     USER_RESOURCES mqh= lex->mqh;
4145     if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
4146       user_table.max_questions()->store((longlong) mqh.questions, TRUE);
4147     if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
4148       user_table.max_updates()->store((longlong) mqh.updates, TRUE);
4149     if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
4150       user_table.max_connections()->store((longlong) mqh.conn_per_hour, TRUE);
4151     if (user_table.max_user_connections() &&
4152         (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
4153       user_table.max_user_connections()->store((longlong) mqh.user_conn, FALSE);
4154     if (user_table.plugin())
4155     {
4156       user_table.plugin()->set_notnull();
4157       user_table.authentication_string()->set_notnull();
4158       if (combo.plugin.str[0])
4159       {
4160         DBUG_ASSERT(combo.pwhash.str[0] == 0);
4161         if (user_table.password())
4162           user_table.password()->reset();
4163         user_table.plugin()->store(combo.plugin.str, combo.plugin.length,
4164                                    system_charset_info);
4165         user_table.authentication_string()->store(combo.auth.str, combo.auth.length,
4166                                                   system_charset_info);
4167       }
4168       if (combo.pwhash.str[0])
4169       {
4170         DBUG_ASSERT(combo.plugin.str[0] == 0);
4171         /* We have Password column. */
4172         if (user_table.password())
4173         {
4174           user_table.plugin()->reset();
4175           user_table.authentication_string()->reset();
4176         }
4177         else
4178         {
4179           /* We do not have Password column. Use PLUGIN && Authentication_string
4180              columns instead. */
4181           set_authentication_plugin_from_password(user_table,
4182                                                   combo.pwhash.str,
4183                                                   combo.pwhash.length);
4184         }
4185       }
4186 
4187       if (user_table.max_statement_time())
4188       {
4189         if (mqh.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
4190           user_table.max_statement_time()->store(mqh.max_statement_time);
4191       }
4192     }
4193     mqh_used= (mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour ||
4194                mqh.user_conn || mqh.max_statement_time != 0.0);
4195 
4196     /* table format checked earlier */
4197     if (handle_as_role)
4198     {
4199       if (old_row_exists && !user_table.check_is_role())
4200       {
4201         goto end;
4202       }
4203       user_table.is_role()->store("Y", 1, system_charset_info);
4204     }
4205   }
4206 
4207   if (old_row_exists)
4208   {
4209     /*
4210       We should NEVER delete from the user table, as a uses can still
4211       use mysqld even if he doesn't have any privileges in the user table!
4212     */
4213     if (cmp_record(table, record[1]))
4214     {
4215       if (unlikely(error= table->file->ha_update_row(table->record[1],
4216                                                      table->record[0])) &&
4217           error != HA_ERR_RECORD_IS_THE_SAME)
4218       {                                         // This should never happen
4219         table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4220         error= -1;                              /* purecov: deadcode */
4221         goto end;                               /* purecov: deadcode */
4222       }
4223       else
4224         error= 0;
4225     }
4226   }
4227   else if (unlikely(error=table->file->ha_write_row(table->record[0])))
4228   {
4229     // This should never happen
4230     if (table->file->is_fatal_error(error, HA_CHECK_DUP))
4231     {
4232       table->file->print_error(error,MYF(0));	/* purecov: deadcode */
4233       error= -1;				/* purecov: deadcode */
4234       goto end;					/* purecov: deadcode */
4235     }
4236   }
4237   error=0;					// Privileges granted / revoked
4238 
4239 end:
4240   if (likely(!error))
4241   {
4242     acl_cache->clear(1);			// Clear privilege cache
4243     if (old_row_exists)
4244     {
4245       if (handle_as_role)
4246         acl_update_role(combo.user.str, rights);
4247       else
4248         acl_update_user(combo.user.str, combo.host.str,
4249                         combo.pwhash.str, combo.pwhash.length,
4250                         lex->ssl_type,
4251                         lex->ssl_cipher,
4252                         lex->x509_issuer,
4253                         lex->x509_subject,
4254                         &lex->mqh,
4255                         rights,
4256                         &combo.plugin,
4257                         &combo.auth);
4258     }
4259     else
4260     {
4261       if (handle_as_role)
4262         acl_insert_role(combo.user.str, rights);
4263       else
4264         acl_insert_user(combo.user.str, combo.host.str,
4265                         combo.pwhash.str, combo.pwhash.length,
4266                         lex->ssl_type,
4267                         lex->ssl_cipher,
4268                         lex->x509_issuer,
4269                         lex->x509_subject,
4270                         &lex->mqh,
4271                         rights,
4272                         &combo.plugin,
4273                         &combo.auth);
4274     }
4275   }
4276   DBUG_RETURN(error);
4277 }
4278 
4279 
4280 /*
4281   change grants in the mysql.db table
4282 */
4283 
replace_db_table(TABLE * table,const char * db,const LEX_USER & combo,ulong rights,bool revoke_grant)4284 static int replace_db_table(TABLE *table, const char *db,
4285 			    const LEX_USER &combo,
4286 			    ulong rights, bool revoke_grant)
4287 {
4288   uint i;
4289   ulong priv,store_rights;
4290   bool old_row_exists=0;
4291   int error;
4292   char what= (revoke_grant) ? 'N' : 'Y';
4293   uchar user_key[MAX_KEY_LENGTH];
4294   DBUG_ENTER("replace_db_table");
4295 
4296   /* Check if there is such a user in user table in memory? */
4297   if (!find_user_wild(combo.host.str,combo.user.str))
4298   {
4299     /* The user could be a role, check if the user is registered as a role */
4300     if (!combo.host.length && !find_acl_role(combo.user.str))
4301     {
4302       my_message(ER_PASSWORD_NO_MATCH, ER_THD(table->in_use,
4303                                               ER_PASSWORD_NO_MATCH), MYF(0));
4304       DBUG_RETURN(-1);
4305     }
4306   }
4307 
4308   table->use_all_columns();
4309   table->field[0]->store(combo.host.str,combo.host.length,
4310                          system_charset_info);
4311   table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4312   table->field[2]->store(combo.user.str,combo.user.length,
4313                          system_charset_info);
4314   key_copy(user_key, table->record[0], table->key_info,
4315            table->key_info->key_length);
4316 
4317   if (table->file->ha_index_read_idx_map(table->record[0],0, user_key,
4318                                          HA_WHOLE_KEY,
4319                                          HA_READ_KEY_EXACT))
4320   {
4321     if (what == 'N')
4322     { // no row, no revoke
4323       my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
4324       goto abort;
4325     }
4326     old_row_exists = 0;
4327     restore_record(table, s->default_values);
4328     table->field[0]->store(combo.host.str,combo.host.length,
4329                            system_charset_info);
4330     table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4331     table->field[2]->store(combo.user.str,combo.user.length,
4332                            system_charset_info);
4333   }
4334   else
4335   {
4336     old_row_exists = 1;
4337     store_record(table,record[1]);
4338   }
4339 
4340   store_rights=get_rights_for_db(rights);
4341   for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
4342   {
4343     if (priv & store_rights)			// do it if priv is chosen
4344       table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
4345   }
4346   rights=get_access(table,3);
4347   rights=fix_rights_for_db(rights);
4348 
4349   if (old_row_exists)
4350   {
4351     /* update old existing row */
4352     if (rights)
4353     {
4354       if (unlikely((error= table->file->ha_update_row(table->record[1],
4355                                                       table->record[0]))) &&
4356           error != HA_ERR_RECORD_IS_THE_SAME)
4357 	goto table_error;			/* purecov: deadcode */
4358     }
4359     else	/* must have been a revoke of all privileges */
4360     {
4361       if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4362 	goto table_error;			/* purecov: deadcode */
4363     }
4364   }
4365   else if (rights &&
4366            (unlikely(error= table->file->ha_write_row(table->record[0]))))
4367   {
4368     if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4369       goto table_error; /* purecov: deadcode */
4370   }
4371 
4372   acl_cache->clear(1);				// Clear privilege cache
4373   if (old_row_exists)
4374     acl_update_db(combo.user.str,combo.host.str,db,rights);
4375   else if (rights)
4376   {
4377     /*
4378        If we did not have an already existing row, for users, we must always
4379        insert an ACL_DB entry. For roles however, it is possible that one was
4380        already created when DB privileges were propagated from other granted
4381        roles onto the current role. For this case, first try to update the
4382        existing entry, otherwise insert a new one.
4383     */
4384     if (!combo.is_role() ||
4385         !acl_update_db(combo.user.str, combo.host.str, db, rights))
4386     {
4387       acl_insert_db(combo.user.str,combo.host.str,db,rights);
4388     }
4389   }
4390   DBUG_RETURN(0);
4391 
4392   /* This could only happen if the grant tables got corrupted */
4393 table_error:
4394   table->file->print_error(error,MYF(0));	/* purecov: deadcode */
4395 
4396 abort:
4397   DBUG_RETURN(-1);
4398 }
4399 
4400 /**
4401   Updates the mysql.roles_mapping table
4402 
4403   @param table          TABLE to update
4404   @param user           user name of the grantee
4405   @param host           host name of the grantee
4406   @param role           role name to grant
4407   @param with_admin     WITH ADMIN OPTION flag
4408   @param existing       the entry in the acl_roles_mappings hash or NULL.
4409                         it is never NULL if revoke_grant is true.
4410                         it is NULL when a new pair is added, it's not NULL
4411                         when an existing pair is updated.
4412   @param revoke_grant   true for REVOKE, false for GRANT
4413 */
4414 static int
replace_roles_mapping_table(TABLE * table,LEX_CSTRING * user,LEX_CSTRING * host,LEX_CSTRING * role,bool with_admin,ROLE_GRANT_PAIR * existing,bool revoke_grant)4415 replace_roles_mapping_table(TABLE *table, LEX_CSTRING *user, LEX_CSTRING *host,
4416                             LEX_CSTRING *role, bool with_admin,
4417                             ROLE_GRANT_PAIR *existing, bool revoke_grant)
4418 {
4419   DBUG_ENTER("replace_roles_mapping_table");
4420 
4421   uchar row_key[MAX_KEY_LENGTH];
4422   int error;
4423   table->use_all_columns();
4424   restore_record(table, s->default_values);
4425   table->field[0]->store(host->str, host->length, system_charset_info);
4426   table->field[1]->store(user->str, user->length, system_charset_info);
4427   table->field[2]->store(role->str, role->length, system_charset_info);
4428 
4429   DBUG_ASSERT(!revoke_grant || existing);
4430 
4431   if (existing) // delete or update
4432   {
4433     key_copy(row_key, table->record[0], table->key_info,
4434              table->key_info->key_length);
4435     if (table->file->ha_index_read_idx_map(table->record[1], 0, row_key,
4436                                            HA_WHOLE_KEY, HA_READ_KEY_EXACT))
4437     {
4438       /* No match */
4439       DBUG_RETURN(1);
4440     }
4441     if (revoke_grant && !with_admin)
4442     {
4443       if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4444       {
4445         DBUG_PRINT("info", ("error deleting row '%s' '%s' '%s'",
4446                             host->str, user->str, role->str));
4447         goto table_error;
4448       }
4449     }
4450     else if (with_admin)
4451     {
4452       table->field[3]->store(!revoke_grant + 1);
4453 
4454       if (unlikely((error= table->file->ha_update_row(table->record[1],
4455                                                       table->record[0]))))
4456       {
4457         DBUG_PRINT("info", ("error updating row '%s' '%s' '%s'",
4458                             host->str, user->str, role->str));
4459         goto table_error;
4460       }
4461     }
4462     DBUG_RETURN(0);
4463   }
4464 
4465   table->field[3]->store(with_admin + 1);
4466 
4467   if (unlikely((error= table->file->ha_write_row(table->record[0]))))
4468   {
4469     DBUG_PRINT("info", ("error inserting row '%s' '%s' '%s'",
4470                         host->str, user->str, role->str));
4471     goto table_error;
4472   }
4473 
4474   /* all ok */
4475   DBUG_RETURN(0);
4476 
4477 table_error:
4478   DBUG_PRINT("info", ("table error"));
4479   table->file->print_error(error, MYF(0));
4480   DBUG_RETURN(1);
4481 }
4482 
4483 
4484 /**
4485   Updates the acl_roles_mappings hash
4486 
4487   @param user           user name of the grantee
4488   @param host           host name of the grantee
4489   @param role           role name to grant
4490   @param with_admin     WITH ADMIN OPTION flag
4491   @param existing       the entry in the acl_roles_mappings hash or NULL.
4492                         it is never NULL if revoke_grant is true.
4493                         it is NULL when a new pair is added, it's not NULL
4494                         when an existing pair is updated.
4495   @param revoke_grant   true for REVOKE, false for GRANT
4496 */
4497 static int
update_role_mapping(LEX_CSTRING * user,LEX_CSTRING * host,LEX_CSTRING * role,bool with_admin,ROLE_GRANT_PAIR * existing,bool revoke_grant)4498 update_role_mapping(LEX_CSTRING *user, LEX_CSTRING *host, LEX_CSTRING *role,
4499                     bool with_admin, ROLE_GRANT_PAIR *existing, bool revoke_grant)
4500 {
4501   if (revoke_grant)
4502   {
4503     if (with_admin)
4504     {
4505       existing->with_admin= false;
4506       return 0;
4507     }
4508     return my_hash_delete(&acl_roles_mappings, (uchar*)existing);
4509   }
4510 
4511   if (existing)
4512   {
4513     existing->with_admin|= with_admin;
4514     return 0;
4515   }
4516 
4517   /* allocate a new entry that will go in the hash */
4518   ROLE_GRANT_PAIR *hash_entry= new (&acl_memroot) ROLE_GRANT_PAIR;
4519   if (hash_entry->init(&acl_memroot, user->str, host->str,
4520                        role->str, with_admin))
4521     return 1;
4522   return my_hash_insert(&acl_roles_mappings, (uchar*) hash_entry);
4523 }
4524 
4525 static void
acl_update_proxy_user(ACL_PROXY_USER * new_value,bool is_revoke)4526 acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
4527 {
4528   mysql_mutex_assert_owner(&acl_cache->lock);
4529 
4530   DBUG_ENTER("acl_update_proxy_user");
4531   for (uint i= 0; i < acl_proxy_users.elements; i++)
4532   {
4533     ACL_PROXY_USER *acl_user=
4534       dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
4535 
4536     if (acl_user->pk_equals(new_value))
4537     {
4538       if (is_revoke)
4539       {
4540         DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
4541         delete_dynamic_element(&acl_proxy_users, i);
4542       }
4543       else
4544       {
4545         DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
4546         acl_user->set_data(new_value);
4547       }
4548       break;
4549     }
4550   }
4551   DBUG_VOID_RETURN;
4552 }
4553 
4554 
4555 static void
acl_insert_proxy_user(ACL_PROXY_USER * new_value)4556 acl_insert_proxy_user(ACL_PROXY_USER *new_value)
4557 {
4558   DBUG_ENTER("acl_insert_proxy_user");
4559   mysql_mutex_assert_owner(&acl_cache->lock);
4560   (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
4561   my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
4562            acl_proxy_users.elements,
4563            sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
4564   DBUG_VOID_RETURN;
4565 }
4566 
4567 
4568 static int
replace_proxies_priv_table(THD * thd,TABLE * table,const LEX_USER * user,const LEX_USER * proxied_user,bool with_grant_arg,bool revoke_grant)4569 replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
4570                          const LEX_USER *proxied_user, bool with_grant_arg,
4571                          bool revoke_grant)
4572 {
4573   bool old_row_exists= 0;
4574   int error;
4575   uchar user_key[MAX_KEY_LENGTH];
4576   ACL_PROXY_USER new_grant;
4577   char grantor[USER_HOST_BUFF_SIZE];
4578 
4579   DBUG_ENTER("replace_proxies_priv_table");
4580 
4581   /* Check if there is such a user in user table in memory? */
4582   if (!find_user_wild(user->host.str,user->user.str))
4583   {
4584     my_message(ER_PASSWORD_NO_MATCH,
4585                ER_THD(thd, ER_PASSWORD_NO_MATCH), MYF(0));
4586     DBUG_RETURN(-1);
4587   }
4588 
4589   table->use_all_columns();
4590   ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
4591                             &proxied_user->host, &proxied_user->user);
4592 
4593   key_copy(user_key, table->record[0], table->key_info,
4594            table->key_info->key_length);
4595 
4596   get_grantor(thd, grantor);
4597 
4598   if (unlikely((error= table->file->ha_index_init(0, 1))))
4599   {
4600     table->file->print_error(error, MYF(0));
4601     DBUG_PRINT("info", ("ha_index_init error"));
4602     DBUG_RETURN(-1);
4603   }
4604 
4605   if (table->file->ha_index_read_map(table->record[0], user_key,
4606                                      HA_WHOLE_KEY,
4607                                      HA_READ_KEY_EXACT))
4608   {
4609     DBUG_PRINT ("info", ("Row not found"));
4610     if (revoke_grant)
4611     { // no row, no revoke
4612       my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
4613       goto abort;
4614     }
4615     old_row_exists= 0;
4616     restore_record(table, s->default_values);
4617     ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
4618                                       &proxied_user->host,
4619                                       &proxied_user->user,
4620                                       with_grant_arg,
4621                                       grantor);
4622   }
4623   else
4624   {
4625     DBUG_PRINT("info", ("Row found"));
4626     old_row_exists= 1;
4627     store_record(table, record[1]);
4628   }
4629 
4630   if (old_row_exists)
4631   {
4632     /* update old existing row */
4633     if (!revoke_grant)
4634     {
4635       if (unlikely(error= table->file->ha_update_row(table->record[1],
4636                                                      table->record[0])) &&
4637           error != HA_ERR_RECORD_IS_THE_SAME)
4638 	goto table_error;			/* purecov: inspected */
4639     }
4640     else
4641     {
4642       if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4643 	goto table_error;			/* purecov: inspected */
4644     }
4645   }
4646   else if (unlikely((error= table->file->ha_write_row(table->record[0]))))
4647   {
4648     DBUG_PRINT("info", ("error inserting the row"));
4649     if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4650       goto table_error; /* purecov: inspected */
4651   }
4652 
4653   acl_cache->clear(1);				// Clear privilege cache
4654   if (old_row_exists)
4655   {
4656     new_grant.init(user->host.str, user->user.str,
4657                    proxied_user->host.str, proxied_user->user.str,
4658                    with_grant_arg);
4659     acl_update_proxy_user(&new_grant, revoke_grant);
4660   }
4661   else
4662   {
4663     new_grant.init(&acl_memroot, user->host.str, user->user.str,
4664                    proxied_user->host.str, proxied_user->user.str,
4665                    with_grant_arg);
4666     acl_insert_proxy_user(&new_grant);
4667   }
4668 
4669   table->file->ha_index_end();
4670   DBUG_RETURN(0);
4671 
4672   /* This could only happen if the grant tables got corrupted */
4673 table_error:
4674   DBUG_PRINT("info", ("table error"));
4675   table->file->print_error(error, MYF(0));	/* purecov: inspected */
4676 
4677 abort:
4678   DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
4679   table->file->ha_index_end();
4680   DBUG_RETURN(-1);
4681 }
4682 
4683 
4684 class GRANT_COLUMN :public Sql_alloc
4685 {
4686 public:
4687   char *column;
4688   ulong rights;
4689   ulong init_rights;
4690   uint key_length;
GRANT_COLUMN(String & c,ulong y)4691   GRANT_COLUMN(String &c,  ulong y) :rights (y), init_rights(y)
4692   {
4693     column= (char*) memdup_root(&grant_memroot,c.ptr(), key_length=c.length());
4694   }
4695 
4696   /* this constructor assumes thas source->column is allocated in grant_memroot */
GRANT_COLUMN(GRANT_COLUMN * source)4697   GRANT_COLUMN(GRANT_COLUMN *source) : column(source->column),
4698     rights (source->rights), init_rights(0), key_length(source->key_length) { }
4699 };
4700 
4701 
get_key_column(GRANT_COLUMN * buff,size_t * length,my_bool not_used)4702 static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
4703 			    my_bool not_used __attribute__((unused)))
4704 {
4705   *length=buff->key_length;
4706   return (uchar*) buff->column;
4707 }
4708 
4709 class GRANT_NAME :public Sql_alloc
4710 {
4711 public:
4712   acl_host_and_ip host;
4713   char *db, *user, *tname, *hash_key;
4714   ulong privs;
4715   ulong init_privs; /* privileges found in physical table */
4716   ulong sort;
4717   size_t key_length;
4718   GRANT_NAME(const char *h, const char *d,const char *u,
4719              const char *t, ulong p, bool is_routine);
4720   GRANT_NAME (TABLE *form, bool is_routine);
~GRANT_NAME()4721   virtual ~GRANT_NAME() {};
ok()4722   virtual bool ok() { return privs != 0; }
4723   void set_user_details(const char *h, const char *d,
4724                         const char *u, const char *t,
4725                         bool is_routine);
4726 };
4727 
4728 
4729 class GRANT_TABLE :public GRANT_NAME
4730 {
4731 public:
4732   ulong cols;
4733   ulong init_cols; /* privileges found in physical table */
4734   HASH hash_columns;
4735 
4736   GRANT_TABLE(const char *h, const char *d,const char *u,
4737               const char *t, ulong p, ulong c);
4738   GRANT_TABLE (TABLE *form, TABLE *col_privs);
4739   ~GRANT_TABLE();
ok()4740   bool ok() { return privs != 0 || cols != 0; }
init_hash()4741   void init_hash()
4742   {
4743     my_hash_init2(&hash_columns, 4, system_charset_info, 0, 0, 0,
4744                   (my_hash_get_key) get_key_column, 0, 0, 0);
4745   }
4746 };
4747 
4748 
set_user_details(const char * h,const char * d,const char * u,const char * t,bool is_routine)4749 void GRANT_NAME::set_user_details(const char *h, const char *d,
4750                                   const char *u, const char *t,
4751                                   bool is_routine)
4752 {
4753   /* Host given by user */
4754   update_hostname(&host, strdup_root(&grant_memroot, h));
4755   if (db != d)
4756   {
4757     db= strdup_root(&grant_memroot, d);
4758     if (lower_case_table_names)
4759       my_casedn_str(files_charset_info, db);
4760   }
4761   user = strdup_root(&grant_memroot,u);
4762   sort=  get_sort(3,host.hostname,db,user);
4763   if (tname != t)
4764   {
4765     tname= strdup_root(&grant_memroot, t);
4766     if (lower_case_table_names || is_routine)
4767       my_casedn_str(files_charset_info, tname);
4768   }
4769   key_length= strlen(d) + strlen(u)+ strlen(t)+3;
4770   hash_key=   (char*) alloc_root(&grant_memroot,key_length);
4771   strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
4772 }
4773 
GRANT_NAME(const char * h,const char * d,const char * u,const char * t,ulong p,bool is_routine)4774 GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
4775                        const char *t, ulong p, bool is_routine)
4776   :db(0), tname(0), privs(p), init_privs(p)
4777 {
4778   set_user_details(h, d, u, t, is_routine);
4779 }
4780 
GRANT_TABLE(const char * h,const char * d,const char * u,const char * t,ulong p,ulong c)4781 GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
4782                 	 const char *t, ulong p, ulong c)
4783   :GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
4784 {
4785   init_hash();
4786 }
4787 
4788 /*
4789   create a new GRANT_TABLE entry for role inheritance. init_* fields are set
4790   to 0
4791 */
GRANT_NAME(TABLE * form,bool is_routine)4792 GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
4793 {
4794   user= safe_str(get_field(&grant_memroot,form->field[2]));
4795 
4796   const char *hostname= get_field(&grant_memroot, form->field[0]);
4797   mysql_mutex_lock(&acl_cache->lock);
4798   if (!hostname && find_acl_role(user))
4799     hostname= "";
4800   mysql_mutex_unlock(&acl_cache->lock);
4801   update_hostname(&host, hostname);
4802 
4803   db=    get_field(&grant_memroot,form->field[1]);
4804   sort=  get_sort(3, host.hostname, db, user);
4805   tname= get_field(&grant_memroot,form->field[3]);
4806   if (!db || !tname)
4807   {
4808     /* Wrong table row; Ignore it */
4809     privs= 0;
4810     return;					/* purecov: inspected */
4811   }
4812   if (lower_case_table_names)
4813   {
4814     my_casedn_str(files_charset_info, db);
4815   }
4816   if (lower_case_table_names || is_routine)
4817   {
4818     my_casedn_str(files_charset_info, tname);
4819   }
4820   key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
4821   hash_key=   (char*) alloc_root(&grant_memroot, key_length);
4822   strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
4823   privs = (ulong) form->field[6]->val_int();
4824   privs = fix_rights_for_table(privs);
4825   init_privs= privs;
4826 }
4827 
4828 
GRANT_TABLE(TABLE * form,TABLE * col_privs)4829 GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
4830   :GRANT_NAME(form, FALSE)
4831 {
4832   uchar key[MAX_KEY_LENGTH];
4833 
4834   if (!db || !tname)
4835   {
4836     /* Wrong table row; Ignore it */
4837     my_hash_clear(&hash_columns);               /* allow for destruction */
4838     cols= 0;
4839     return;
4840   }
4841   cols= (ulong) form->field[7]->val_int();
4842   cols= fix_rights_for_column(cols);
4843   /*
4844     Initial columns privileges are the same as column privileges on creation.
4845     In case of roles, the cols privilege bits can get inherited and thus
4846     cause the cols field to change. The init_cols field is always the same
4847     as the physical table entry
4848   */
4849   init_cols= cols;
4850 
4851   init_hash();
4852 
4853   if (cols)
4854   {
4855     uint key_prefix_len;
4856     KEY_PART_INFO *key_part= col_privs->key_info->key_part;
4857     col_privs->field[0]->store(host.hostname,
4858                                (uint) safe_strlen(host.hostname),
4859                                system_charset_info);
4860     col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
4861     col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
4862     col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
4863 
4864     key_prefix_len= (key_part[0].store_length +
4865                      key_part[1].store_length +
4866                      key_part[2].store_length +
4867                      key_part[3].store_length);
4868     key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
4869     col_privs->field[4]->store("",0, &my_charset_latin1);
4870 
4871     if (col_privs->file->ha_index_init(0, 1))
4872     {
4873       cols= 0;
4874       init_cols= 0;
4875       return;
4876     }
4877 
4878     if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
4879                                            (key_part_map)15,
4880                                            HA_READ_KEY_EXACT))
4881     {
4882       cols= 0; /* purecov: deadcode */
4883       init_cols= 0;
4884       col_privs->file->ha_index_end();
4885       return;
4886     }
4887     do
4888     {
4889       String *res,column_name;
4890       GRANT_COLUMN *mem_check;
4891       /* As column name is a string, we don't have to supply a buffer */
4892       res=col_privs->field[4]->val_str(&column_name);
4893       ulong priv= (ulong) col_privs->field[6]->val_int();
4894       if (!(mem_check = new GRANT_COLUMN(*res,
4895                                          fix_rights_for_column(priv))))
4896       {
4897         /* Don't use this entry */
4898         privs= cols= init_privs= init_cols=0;   /* purecov: deadcode */
4899         return;				/* purecov: deadcode */
4900       }
4901       if (my_hash_insert(&hash_columns, (uchar *) mem_check))
4902       {
4903         /* Invalidate this entry */
4904         privs= cols= init_privs= init_cols=0;
4905         return;
4906       }
4907     } while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
4908              !key_cmp_if_same(col_privs,key,0,key_prefix_len));
4909     col_privs->file->ha_index_end();
4910   }
4911 }
4912 
4913 
~GRANT_TABLE()4914 GRANT_TABLE::~GRANT_TABLE()
4915 {
4916   my_hash_free(&hash_columns);
4917 }
4918 
4919 
get_grant_table(GRANT_NAME * buff,size_t * length,my_bool not_used)4920 static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
4921 			     my_bool not_used __attribute__((unused)))
4922 {
4923   *length=buff->key_length;
4924   return (uchar*) buff->hash_key;
4925 }
4926 
4927 
free_grant_table(GRANT_TABLE * grant_table)4928 static void free_grant_table(GRANT_TABLE *grant_table)
4929 {
4930   grant_table->~GRANT_TABLE();
4931 }
4932 
4933 
4934 /* Search after a matching grant. Prefer exact grants before not exact ones */
4935 
name_hash_search(HASH * name_hash,const char * host,const char * ip,const char * db,const char * user,const char * tname,bool exact,bool name_tolower)4936 static GRANT_NAME *name_hash_search(HASH *name_hash,
4937                                     const char *host,const char* ip,
4938                                     const char *db,
4939                                     const char *user, const char *tname,
4940                                     bool exact, bool name_tolower)
4941 {
4942   char helping[SAFE_NAME_LEN*2+USERNAME_LENGTH+3];
4943   char *hend = helping + sizeof(helping);
4944   uint len;
4945   GRANT_NAME *grant_name,*found=0;
4946   HASH_SEARCH_STATE state;
4947 
4948   char *db_ptr= strmov(helping, user) + 1;
4949   char *tname_ptr= strnmov(db_ptr, db, hend - db_ptr) + 1;
4950   if (tname_ptr > hend)
4951     return 0; // invalid name = not found
4952   char *end= strnmov(tname_ptr, tname, hend - tname_ptr) + 1;
4953   if (end > hend)
4954     return 0; // invalid name = not found
4955 
4956   len  = (uint) (end - helping);
4957   if (name_tolower)
4958     my_casedn_str(files_charset_info, tname_ptr);
4959   for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
4960                                                len, &state);
4961        grant_name ;
4962        grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
4963                                               len, &state))
4964   {
4965     if (exact)
4966     {
4967       if (!grant_name->host.hostname ||
4968           (host &&
4969 	   !my_strcasecmp(system_charset_info, host,
4970                           grant_name->host.hostname)) ||
4971 	  (ip && !strcmp(ip, grant_name->host.hostname)))
4972 	return grant_name;
4973     }
4974     else
4975     {
4976       if (compare_hostname(&grant_name->host, host, ip) &&
4977           (!found || found->sort < grant_name->sort))
4978 	found=grant_name;					// Host ok
4979     }
4980   }
4981   return found;
4982 }
4983 
4984 
4985 static GRANT_NAME *
routine_hash_search(const char * host,const char * ip,const char * db,const char * user,const char * tname,const Sp_handler * sph,bool exact)4986 routine_hash_search(const char *host, const char *ip, const char *db,
4987                     const char *user, const char *tname, const Sp_handler *sph,
4988                     bool exact)
4989 {
4990   return (GRANT_TABLE*)
4991     name_hash_search(sph->get_priv_hash(),
4992 		     host, ip, db, user, tname, exact, TRUE);
4993 }
4994 
4995 
4996 static GRANT_TABLE *
table_hash_search(const char * host,const char * ip,const char * db,const char * user,const char * tname,bool exact)4997 table_hash_search(const char *host, const char *ip, const char *db,
4998 		  const char *user, const char *tname, bool exact)
4999 {
5000   return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
5001 					 user, tname, exact, FALSE);
5002 }
5003 
5004 
5005 static GRANT_COLUMN *
column_hash_search(GRANT_TABLE * t,const char * cname,size_t length)5006 column_hash_search(GRANT_TABLE *t, const char *cname, size_t length)
5007 {
5008   if (!my_hash_inited(&t->hash_columns))
5009     return (GRANT_COLUMN*) 0;
5010   return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
5011                                         (uchar*) cname, length);
5012 }
5013 
5014 
replace_column_table(GRANT_TABLE * g_t,TABLE * table,const LEX_USER & combo,List<LEX_COLUMN> & columns,const char * db,const char * table_name,ulong rights,bool revoke_grant)5015 static int replace_column_table(GRANT_TABLE *g_t,
5016 				TABLE *table, const LEX_USER &combo,
5017 				List <LEX_COLUMN> &columns,
5018 				const char *db, const char *table_name,
5019 				ulong rights, bool revoke_grant)
5020 {
5021   int result=0;
5022   uchar key[MAX_KEY_LENGTH];
5023   uint key_prefix_length;
5024   KEY_PART_INFO *key_part= table->key_info->key_part;
5025   DBUG_ENTER("replace_column_table");
5026 
5027   table->use_all_columns();
5028   table->field[0]->store(combo.host.str,combo.host.length,
5029                          system_charset_info);
5030   table->field[1]->store(db,(uint) strlen(db),
5031                          system_charset_info);
5032   table->field[2]->store(combo.user.str,combo.user.length,
5033                          system_charset_info);
5034   table->field[3]->store(table_name,(uint) strlen(table_name),
5035                          system_charset_info);
5036 
5037   /* Get length of 4 first key parts */
5038   key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
5039                       key_part[2].store_length + key_part[3].store_length);
5040   key_copy(key, table->record[0], table->key_info, key_prefix_length);
5041 
5042   rights&= COL_ACLS;				// Only ACL for columns
5043 
5044   /* first fix privileges for all columns in column list */
5045 
5046   List_iterator <LEX_COLUMN> iter(columns);
5047   class LEX_COLUMN *column;
5048   int error= table->file->ha_index_init(0, 1);
5049   if (unlikely(error))
5050   {
5051     table->file->print_error(error, MYF(0));
5052     DBUG_RETURN(-1);
5053   }
5054 
5055   while ((column= iter++))
5056   {
5057     ulong privileges= column->rights;
5058     bool old_row_exists=0;
5059     uchar user_key[MAX_KEY_LENGTH];
5060 
5061     key_restore(table->record[0],key,table->key_info,
5062                 key_prefix_length);
5063     table->field[4]->store(column->column.ptr(), column->column.length(),
5064                            system_charset_info);
5065     /* Get key for the first 4 columns */
5066     key_copy(user_key, table->record[0], table->key_info,
5067              table->key_info->key_length);
5068 
5069     if (table->file->ha_index_read_map(table->record[0], user_key,
5070                                        HA_WHOLE_KEY, HA_READ_KEY_EXACT))
5071     {
5072       if (revoke_grant)
5073       {
5074 	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
5075                  combo.user.str, combo.host.str,
5076                  table_name);                   /* purecov: inspected */
5077 	result= -1;                             /* purecov: inspected */
5078 	continue;                               /* purecov: inspected */
5079       }
5080       old_row_exists = 0;
5081       restore_record(table, s->default_values);		// Get empty record
5082       key_restore(table->record[0],key,table->key_info,
5083                   key_prefix_length);
5084       table->field[4]->store(column->column.ptr(),column->column.length(),
5085                              system_charset_info);
5086     }
5087     else
5088     {
5089       ulong tmp= (ulong) table->field[6]->val_int();
5090       tmp=fix_rights_for_column(tmp);
5091 
5092       if (revoke_grant)
5093 	privileges = tmp & ~(privileges | rights);
5094       else
5095 	privileges |= tmp;
5096       old_row_exists = 1;
5097       store_record(table,record[1]);			// copy original row
5098     }
5099 
5100     table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
5101 
5102     if (old_row_exists)
5103     {
5104       GRANT_COLUMN *grant_column;
5105       if (privileges)
5106 	error=table->file->ha_update_row(table->record[1],table->record[0]);
5107       else
5108 	error=table->file->ha_delete_row(table->record[1]);
5109       if (unlikely(error) && error != HA_ERR_RECORD_IS_THE_SAME)
5110       {
5111 	table->file->print_error(error,MYF(0)); /* purecov: inspected */
5112 	result= -1;				/* purecov: inspected */
5113 	goto end;				/* purecov: inspected */
5114       }
5115       else
5116         error= 0;
5117       grant_column= column_hash_search(g_t, column->column.ptr(),
5118                                        column->column.length());
5119       if (grant_column)				// Should always be true
5120 	grant_column->rights= privileges;	// Update hash
5121     }
5122     else					// new grant
5123     {
5124       GRANT_COLUMN *grant_column;
5125       if (unlikely((error=table->file->ha_write_row(table->record[0]))))
5126       {
5127 	table->file->print_error(error,MYF(0)); /* purecov: inspected */
5128 	result= -1;				/* purecov: inspected */
5129 	goto end;				/* purecov: inspected */
5130       }
5131       grant_column= new GRANT_COLUMN(column->column,privileges);
5132       if (my_hash_insert(&g_t->hash_columns,(uchar*) grant_column))
5133       {
5134         result= -1;
5135         goto end;
5136       }
5137     }
5138   }
5139 
5140   /*
5141     If revoke of privileges on the table level, remove all such privileges
5142     for all columns
5143   */
5144 
5145   if (revoke_grant)
5146   {
5147     uchar user_key[MAX_KEY_LENGTH];
5148     key_copy(user_key, table->record[0], table->key_info,
5149              key_prefix_length);
5150 
5151     if (table->file->ha_index_read_map(table->record[0], user_key,
5152                                        (key_part_map)15,
5153                                        HA_READ_KEY_EXACT))
5154       goto end;
5155 
5156     /* Scan through all rows with the same host,db,user and table */
5157     do
5158     {
5159       ulong privileges = (ulong) table->field[6]->val_int();
5160       privileges=fix_rights_for_column(privileges);
5161       store_record(table,record[1]);
5162 
5163       if (privileges & rights)	// is in this record the priv to be revoked ??
5164       {
5165 	GRANT_COLUMN *grant_column = NULL;
5166 	char  colum_name_buf[HOSTNAME_LENGTH+1];
5167 	String column_name(colum_name_buf,sizeof(colum_name_buf),
5168                            system_charset_info);
5169 
5170 	privileges&= ~rights;
5171 	table->field[6]->store((longlong)
5172 			       get_rights_for_column(privileges), TRUE);
5173 	table->field[4]->val_str(&column_name);
5174 	grant_column = column_hash_search(g_t,
5175 					  column_name.ptr(),
5176 					  column_name.length());
5177 	if (privileges)
5178 	{
5179 	  int tmp_error;
5180 	  if (unlikely(tmp_error=
5181                        table->file->ha_update_row(table->record[1],
5182                                                   table->record[0])) &&
5183               tmp_error != HA_ERR_RECORD_IS_THE_SAME)
5184 	  {					/* purecov: deadcode */
5185 	    table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
5186 	    result= -1;				/* purecov: deadcode */
5187 	    goto end;				/* purecov: deadcode */
5188 	  }
5189 	  if (grant_column)
5190           {
5191             grant_column->rights  = privileges; // Update hash
5192             grant_column->init_rights = privileges;
5193           }
5194 	}
5195 	else
5196 	{
5197 	  int tmp_error;
5198 	  if (unlikely((tmp_error=
5199                         table->file->ha_delete_row(table->record[1]))))
5200 	  {					/* purecov: deadcode */
5201 	    table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
5202 	    result= -1;				/* purecov: deadcode */
5203 	    goto end;				/* purecov: deadcode */
5204 	  }
5205 	  if (grant_column)
5206 	    my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
5207 	}
5208       }
5209     } while (!table->file->ha_index_next(table->record[0]) &&
5210 	     !key_cmp_if_same(table, key, 0, key_prefix_length));
5211   }
5212 
5213 end:
5214   table->file->ha_index_end();
5215   DBUG_RETURN(result);
5216 }
5217 
get_grantor(THD * thd,char * grantor)5218 static inline void get_grantor(THD *thd, char *grantor)
5219 {
5220   const char *user= thd->security_ctx->user;
5221   const char *host= thd->security_ctx->host_or_ip;
5222 
5223 #if defined(HAVE_REPLICATION)
5224   if (thd->slave_thread && thd->has_invoker())
5225   {
5226     user= thd->get_invoker_user().str;
5227     host= thd->get_invoker_host().str;
5228   }
5229 #endif
5230   strxmov(grantor, user, "@", host, NullS);
5231 }
5232 
replace_table_table(THD * thd,GRANT_TABLE * grant_table,TABLE * table,const LEX_USER & combo,const char * db,const char * table_name,ulong rights,ulong col_rights,bool revoke_grant)5233 static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
5234 			       TABLE *table, const LEX_USER &combo,
5235 			       const char *db, const char *table_name,
5236 			       ulong rights, ulong col_rights,
5237 			       bool revoke_grant)
5238 {
5239   char grantor[USER_HOST_BUFF_SIZE];
5240   int old_row_exists = 1;
5241   int error=0;
5242   ulong store_table_rights, store_col_rights;
5243   uchar user_key[MAX_KEY_LENGTH];
5244   DBUG_ENTER("replace_table_table");
5245 
5246   get_grantor(thd, grantor);
5247   /*
5248     The following should always succeed as new users are created before
5249     this function is called!
5250   */
5251   if (!find_user_wild(combo.host.str,combo.user.str))
5252   {
5253     if (!combo.host.length && !find_acl_role(combo.user.str))
5254     {
5255       my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
5256                  MYF(0)); /* purecov: deadcode */
5257       DBUG_RETURN(-1);                            /* purecov: deadcode */
5258     }
5259   }
5260 
5261   table->use_all_columns();
5262   restore_record(table, s->default_values);     // Get empty record
5263   table->field[0]->store(combo.host.str,combo.host.length,
5264                          system_charset_info);
5265   table->field[1]->store(db,(uint) strlen(db), system_charset_info);
5266   table->field[2]->store(combo.user.str,combo.user.length,
5267                          system_charset_info);
5268   table->field[3]->store(table_name,(uint) strlen(table_name),
5269                          system_charset_info);
5270   store_record(table,record[1]);			// store at pos 1
5271   key_copy(user_key, table->record[0], table->key_info,
5272            table->key_info->key_length);
5273 
5274   if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
5275                                          HA_WHOLE_KEY,
5276                                          HA_READ_KEY_EXACT))
5277   {
5278     /*
5279       The following should never happen as we first check the in memory
5280       grant tables for the user.  There is however always a small change that
5281       the user has modified the grant tables directly.
5282     */
5283     if (revoke_grant)
5284     { // no row, no revoke
5285       my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
5286                combo.user.str, combo.host.str,
5287                table_name);		        /* purecov: deadcode */
5288       DBUG_RETURN(-1);				/* purecov: deadcode */
5289     }
5290     old_row_exists = 0;
5291     restore_record(table,record[1]);			// Get saved record
5292   }
5293 
5294   store_table_rights= get_rights_for_table(rights);
5295   store_col_rights=   get_rights_for_column(col_rights);
5296   if (old_row_exists)
5297   {
5298     ulong j,k;
5299     store_record(table,record[1]);
5300     j = (ulong) table->field[6]->val_int();
5301     k = (ulong) table->field[7]->val_int();
5302 
5303     if (revoke_grant)
5304     {
5305       /* column rights are already fixed in mysql_table_grant */
5306       store_table_rights=j & ~store_table_rights;
5307     }
5308     else
5309     {
5310       store_table_rights|= j;
5311       store_col_rights|=   k;
5312     }
5313   }
5314 
5315   table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
5316   table->field[6]->store((longlong) store_table_rights, TRUE);
5317   table->field[7]->store((longlong) store_col_rights, TRUE);
5318   rights=fix_rights_for_table(store_table_rights);
5319   col_rights=fix_rights_for_column(store_col_rights);
5320 
5321   if (old_row_exists)
5322   {
5323     if (store_table_rights || store_col_rights)
5324     {
5325       if (unlikely(error=table->file->ha_update_row(table->record[1],
5326                                                     table->record[0])) &&
5327           error != HA_ERR_RECORD_IS_THE_SAME)
5328 	goto table_error;			/* purecov: deadcode */
5329     }
5330     else if (unlikely((error = table->file->ha_delete_row(table->record[1]))))
5331       goto table_error;				/* purecov: deadcode */
5332   }
5333   else
5334   {
5335     error=table->file->ha_write_row(table->record[0]);
5336     if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
5337       goto table_error;				/* purecov: deadcode */
5338   }
5339 
5340   if (rights | col_rights)
5341   {
5342     grant_table->init_privs= rights;
5343     grant_table->init_cols=  col_rights;
5344 
5345     grant_table->privs= rights;
5346     grant_table->cols=	col_rights;
5347   }
5348   else
5349   {
5350     my_hash_delete(&column_priv_hash,(uchar*) grant_table);
5351   }
5352   DBUG_RETURN(0);
5353 
5354   /* This should never happen */
5355 table_error:
5356   table->file->print_error(error,MYF(0)); /* purecov: deadcode */
5357   DBUG_RETURN(-1); /* purecov: deadcode */
5358 }
5359 
5360 
5361 /**
5362   @retval       0  success
5363   @retval      -1  error
5364 */
replace_routine_table(THD * thd,GRANT_NAME * grant_name,TABLE * table,const LEX_USER & combo,const char * db,const char * routine_name,const Sp_handler * sph,ulong rights,bool revoke_grant)5365 static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
5366 			      TABLE *table, const LEX_USER &combo,
5367 			      const char *db, const char *routine_name,
5368 			      const Sp_handler *sph,
5369 			      ulong rights, bool revoke_grant)
5370 {
5371   char grantor[USER_HOST_BUFF_SIZE];
5372   int old_row_exists= 1;
5373   int error=0;
5374   ulong store_proc_rights;
5375   HASH *hash= sph->get_priv_hash();
5376   DBUG_ENTER("replace_routine_table");
5377 
5378   if (revoke_grant && !grant_name->init_privs) // only inherited role privs
5379   {
5380     my_hash_delete(hash, (uchar*) grant_name);
5381     DBUG_RETURN(0);
5382   }
5383 
5384   get_grantor(thd, grantor);
5385   /*
5386     New users are created before this function is called.
5387 
5388     There may be some cases where a routine's definer is removed but the
5389     routine remains.
5390   */
5391 
5392   table->use_all_columns();
5393   restore_record(table, s->default_values);		// Get empty record
5394   table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
5395   table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
5396   table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
5397   table->field[3]->store(routine_name,(uint) strlen(routine_name),
5398                          &my_charset_latin1);
5399   table->field[4]->store((longlong) sph->type(), true);
5400   store_record(table,record[1]);			// store at pos 1
5401 
5402   if (table->file->ha_index_read_idx_map(table->record[0], 0,
5403                                          (uchar*) table->field[0]->ptr,
5404                                          HA_WHOLE_KEY,
5405                                          HA_READ_KEY_EXACT))
5406   {
5407     /*
5408       The following should never happen as we first check the in memory
5409       grant tables for the user.  There is however always a small change that
5410       the user has modified the grant tables directly.
5411 
5412       Also, there is also a second posibility that this routine entry
5413       is created for a role by being inherited from a granted role.
5414     */
5415     if (revoke_grant)
5416     { // no row, no revoke
5417       my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
5418                combo.user.str, combo.host.str, routine_name);
5419       DBUG_RETURN(-1);
5420     }
5421     old_row_exists= 0;
5422     restore_record(table,record[1]);			// Get saved record
5423   }
5424 
5425   store_proc_rights= get_rights_for_procedure(rights);
5426   if (old_row_exists)
5427   {
5428     ulong j;
5429     store_record(table,record[1]);
5430     j= (ulong) table->field[6]->val_int();
5431 
5432     if (revoke_grant)
5433     {
5434       /* column rights are already fixed in mysql_table_grant */
5435       store_proc_rights=j & ~store_proc_rights;
5436     }
5437     else
5438     {
5439       store_proc_rights|= j;
5440     }
5441   }
5442 
5443   table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
5444   table->field[6]->store((longlong) store_proc_rights, TRUE);
5445   rights=fix_rights_for_procedure(store_proc_rights);
5446 
5447   if (old_row_exists)
5448   {
5449     if (store_proc_rights)
5450     {
5451       if (unlikely(error=table->file->ha_update_row(table->record[1],
5452                                                     table->record[0])) &&
5453                    error != HA_ERR_RECORD_IS_THE_SAME)
5454 	goto table_error;
5455     }
5456     else if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
5457       goto table_error;
5458   }
5459   else
5460   {
5461     error=table->file->ha_write_row(table->record[0]);
5462     if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
5463       goto table_error;
5464   }
5465 
5466   if (rights)
5467   {
5468     grant_name->init_privs= rights;
5469     grant_name->privs= rights;
5470   }
5471   else
5472   {
5473     my_hash_delete(hash, (uchar*) grant_name);
5474   }
5475   DBUG_RETURN(0);
5476 
5477   /* This should never happen */
5478 table_error:
5479   table->file->print_error(error,MYF(0));
5480   DBUG_RETURN(-1);
5481 }
5482 
5483 
5484 /*****************************************************************
5485   Role privilege propagation and graph traversal functionality
5486 
5487   According to the SQL standard, a role can be granted to a role,
5488   thus role grants can create an arbitrarily complex directed acyclic
5489   graph (a standard explicitly specifies that cycles are not allowed).
5490 
5491   When a privilege is granted to a role, it becomes available to all grantees.
5492   The code below recursively traverses a DAG of role grants, propagating
5493   privilege changes.
5494 
5495   The traversal function can work both ways, from roles to grantees or
5496   from grantees to roles. The first is used for privilege propagation,
5497   the second - for SHOW GRANTS and I_S.APPLICABLE_ROLES
5498 
5499   The role propagation code is smart enough to propagate only privilege
5500   changes to one specific database, table, or routine, if only they
5501   were changed (like in GRANT ... ON ... TO ...) or it can propagate
5502   everything (on startup or after FLUSH PRIVILEGES).
5503 
5504   It traverses only a subgraph that's accessible from the modified role,
5505   only visiting roles that can be possibly affected by the GRANT statement.
5506 
5507   Additionally, it stops traversal early, if this particular GRANT statement
5508   didn't result in any changes of privileges (e.g. both role1 and role2
5509   are granted to the role3, both role1 and role2 have SELECT privilege.
5510   if SELECT is revoked from role1 it won't change role3 privileges,
5511   so we won't traverse from role3 to its grantees).
5512 ******************************************************************/
5513 struct PRIVS_TO_MERGE
5514 {
5515   enum what
5516   {
5517     ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC, PACKAGE_SPEC, PACKAGE_BODY
5518   } what;
5519   const char *db, *name;
5520 };
5521 
5522 
sp_privs_to_merge(stored_procedure_type type)5523 static enum PRIVS_TO_MERGE::what sp_privs_to_merge(stored_procedure_type type)
5524 {
5525   switch (type) {
5526   case TYPE_ENUM_FUNCTION:
5527     return PRIVS_TO_MERGE::FUNC;
5528   case TYPE_ENUM_PROCEDURE:
5529     return PRIVS_TO_MERGE::PROC;
5530   case TYPE_ENUM_PACKAGE:
5531     return PRIVS_TO_MERGE::PACKAGE_SPEC;
5532   case TYPE_ENUM_PACKAGE_BODY:
5533     return PRIVS_TO_MERGE::PACKAGE_BODY;
5534   case TYPE_ENUM_TRIGGER:
5535   case TYPE_ENUM_PROXY:
5536     break;
5537   }
5538   DBUG_ASSERT(0);
5539   return PRIVS_TO_MERGE::PROC;
5540 }
5541 
5542 
init_role_for_merging(ACL_ROLE * role,void * context)5543 static int init_role_for_merging(ACL_ROLE *role, void *context)
5544 {
5545   role->counter= 0;
5546   return 0;
5547 }
5548 
count_subgraph_nodes(ACL_ROLE * role,ACL_ROLE * grantee,void * context)5549 static int count_subgraph_nodes(ACL_ROLE *role, ACL_ROLE *grantee, void *context)
5550 {
5551   grantee->counter++;
5552   return 0;
5553 }
5554 
5555 static int merge_role_privileges(ACL_ROLE *, ACL_ROLE *, void *);
5556 
5557 /**
5558   rebuild privileges of all affected roles
5559 
5560   entry point into role privilege propagation. after privileges of the
5561   'role' were changed, this function rebuilds privileges of all affected roles
5562   as necessary.
5563 */
propagate_role_grants(ACL_ROLE * role,enum PRIVS_TO_MERGE::what what,const char * db=0,const char * name=0)5564 static void propagate_role_grants(ACL_ROLE *role,
5565                                   enum PRIVS_TO_MERGE::what what,
5566                                   const char *db= 0, const char *name= 0)
5567 {
5568   if (!role)
5569     return;
5570 
5571   mysql_mutex_assert_owner(&acl_cache->lock);
5572   PRIVS_TO_MERGE data= { what, db, name };
5573 
5574   /*
5575      Changing privileges of a role causes all other roles that had
5576      this role granted to them to have their rights invalidated.
5577 
5578      We need to rebuild all roles' related access bits.
5579 
5580      This cannot be a simple depth-first search, instead we have to merge
5581      privieges for all roles granted to a specific grantee, *before*
5582      merging privileges for this grantee. In other words, we must visit all
5583      parent nodes of a specific node, before descencing into this node.
5584 
5585      For example, if role1 is granted to role2 and role3, and role3 is
5586      granted to role2, after "GRANT ... role1", we cannot merge privileges
5587      for role2, until role3 is merged.  The counter will be 0 for role1, 2
5588      for role2, 1 for role3. Traversal will start from role1, go to role2,
5589      decrement the counter, backtrack, go to role3, merge it, go to role2
5590      again, merge it.
5591 
5592      And the counter is not just "all parent nodes", but only parent nodes
5593      that are part of the subgraph we're interested in. For example, if
5594      both roleA and roleB are granted to roleC, then roleC has two parent
5595      nodes. But when granting a privilege to roleA, we're only looking at a
5596      subgraph that includes roleA and roleC (roleB cannot be possibly
5597      affected by that grant statement). In this subgraph roleC has only one
5598      parent.
5599 
5600      (on the other hand, in acl_load we want to update all roles, and
5601      the counter is exactly equal to the number of all parent nodes)
5602 
5603      Thus, we do two graph traversals here. First we only count parents
5604      that are part of the subgraph. On the second traversal we decrement
5605      the counter and actually merge privileges for a node when a counter
5606      drops to zero.
5607   */
5608   traverse_role_graph_up(role, &data, init_role_for_merging, count_subgraph_nodes);
5609   traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
5610 }
5611 
5612 
5613 // State of a node during a Depth First Search exploration
5614 struct NODE_STATE
5615 {
5616   ACL_USER_BASE *node_data; /* pointer to the node data */
5617   uint neigh_idx;           /* the neighbour that needs to be evaluated next */
5618 };
5619 
5620 /**
5621   Traverse the role grant graph and invoke callbacks at the specified points.
5622 
5623   @param user           user or role to start traversal from
5624   @param context        opaque parameter to pass to callbacks
5625   @param offset         offset to ACL_ROLE::parent_grantee or to
5626                         ACL_USER_BASE::role_grants. Depending on this value,
5627                         traversal will go from roles to grantees or from
5628                         grantees to roles.
5629   @param on_node        called when a node is visited for the first time.
5630                         Returning a value <0 will abort the traversal.
5631   @param on_edge        called for every edge in the graph, when traversal
5632                         goes from a node to a neighbour node.
5633                         Returning <0 will abort the traversal. Returning >0
5634                         will make the traversal not to follow this edge.
5635 
5636   @note
5637   The traverse method is a DEPTH FIRST SEARCH, but callbacks can influence
5638   that (on_edge returning >0 value).
5639 
5640   @note
5641   This function should not be called directly, use
5642   traverse_role_graph_up() and traverse_role_graph_down() instead.
5643 
5644   @retval 0                 traversal finished successfully
5645   @retval ROLE_CYCLE_FOUND  traversal aborted, cycle detected
5646   @retval <0                traversal was aborted, because a callback returned
5647                             this error code
5648 */
traverse_role_graph_impl(ACL_USER_BASE * user,void * context,off_t offset,int (* on_node)(ACL_USER_BASE * role,void * context),int (* on_edge)(ACL_USER_BASE * current,ACL_ROLE * neighbour,void * context))5649 static int traverse_role_graph_impl(ACL_USER_BASE *user, void *context,
5650        off_t offset,
5651        int (*on_node) (ACL_USER_BASE *role, void *context),
5652        int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
5653 {
5654   DBUG_ENTER("traverse_role_graph_impl");
5655   DBUG_ASSERT(user);
5656   DBUG_PRINT("enter",("role: '%s'", user->user.str));
5657   /*
5658      The search operation should always leave the ROLE_ON_STACK and
5659      ROLE_EXPLORED flags clean for all nodes involved in the search
5660   */
5661   DBUG_ASSERT(!(user->flags & ROLE_ON_STACK));
5662   DBUG_ASSERT(!(user->flags & ROLE_EXPLORED));
5663   mysql_mutex_assert_owner(&acl_cache->lock);
5664 
5665   /*
5666      Stack used to simulate the recursive calls of DFS.
5667      It uses a Dynamic_array to reduce the number of
5668      malloc calls to a minimum
5669   */
5670   Dynamic_array<NODE_STATE> stack(20,50);
5671   Dynamic_array<ACL_USER_BASE *> to_clear(20,50);
5672   NODE_STATE state;     /* variable used to insert elements in the stack */
5673   int result= 0;
5674 
5675   state.neigh_idx= 0;
5676   state.node_data= user;
5677   user->flags|= ROLE_ON_STACK;
5678 
5679   stack.push(state);
5680   to_clear.push(user);
5681 
5682   user->flags|= ROLE_OPENED;
5683   if (on_node && ((result= on_node(user, context)) < 0))
5684     goto end;
5685 
5686   while (stack.elements())
5687   {
5688     NODE_STATE *curr_state= stack.back();
5689 
5690     DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
5691 
5692     ACL_USER_BASE *current= curr_state->node_data;
5693     ACL_USER_BASE *neighbour= NULL;
5694     DBUG_PRINT("info", ("Examining role %s", current->user.str));
5695     /*
5696       Iterate through the neighbours until a first valid jump-to
5697       neighbour is found
5698     */
5699     bool found= FALSE;
5700     uint i;
5701     DYNAMIC_ARRAY *array= (DYNAMIC_ARRAY *)(((char*)current) + offset);
5702 
5703     DBUG_ASSERT(array == &current->role_grants || current->flags & IS_ROLE);
5704     for (i= curr_state->neigh_idx; i < array->elements; i++)
5705     {
5706       neighbour= *(dynamic_element(array, i, ACL_ROLE**));
5707       if (!(neighbour->flags & IS_ROLE))
5708         continue;
5709 
5710       DBUG_PRINT("info", ("Examining neighbour role %s", neighbour->user.str));
5711 
5712       /* check if it forms a cycle */
5713       if (neighbour->flags & ROLE_ON_STACK)
5714       {
5715         DBUG_PRINT("info", ("Found cycle"));
5716         result= ROLE_CYCLE_FOUND;
5717         goto end;
5718       }
5719 
5720       if (!(neighbour->flags & ROLE_OPENED))
5721       {
5722         neighbour->flags|= ROLE_OPENED;
5723         to_clear.push(neighbour);
5724         if (on_node && ((result= on_node(neighbour, context)) < 0))
5725           goto end;
5726       }
5727 
5728       if (on_edge)
5729       {
5730         result= on_edge(current, (ACL_ROLE*)neighbour, context);
5731         if (result < 0)
5732           goto end;
5733         if (result > 0)
5734           continue;
5735       }
5736 
5737       /* Check if it was already explored, in that case, move on */
5738       if (neighbour->flags & ROLE_EXPLORED)
5739         continue;
5740 
5741       found= TRUE;
5742       break;
5743     }
5744 
5745     /* found states that we have found a node to jump next into */
5746     if (found)
5747     {
5748       curr_state->neigh_idx= i + 1;
5749 
5750       /* some sanity checks */
5751       DBUG_ASSERT(!(neighbour->flags & ROLE_ON_STACK));
5752 
5753       /* add the neighbour on the stack */
5754       neighbour->flags|= ROLE_ON_STACK;
5755       state.neigh_idx= 0;
5756       state.node_data= neighbour;
5757       stack.push(state);
5758     }
5759     else
5760     {
5761       /* Make sure we got a correct node */
5762       DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
5763       /* Finished with exploring the current node, pop it off the stack */
5764       curr_state= &stack.pop();
5765       curr_state->node_data->flags&= ~ROLE_ON_STACK; /* clear the on-stack bit */
5766       curr_state->node_data->flags|= ROLE_EXPLORED;
5767     }
5768   }
5769 
5770 end:
5771   /* Cleanup */
5772   for (uint i= 0; i < to_clear.elements(); i++)
5773   {
5774     ACL_USER_BASE *current= to_clear.at(i);
5775     DBUG_ASSERT(current->flags & (ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED));
5776     current->flags&= ~(ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED);
5777   }
5778   DBUG_RETURN(result);
5779 }
5780 
5781 /**
5782   Traverse the role grant graph, going from a role to its grantees.
5783 
5784   This is used to propagate changes in privileges, for example,
5785   when GRANT or REVOKE is issued for a role.
5786 */
5787 
traverse_role_graph_up(ACL_ROLE * role,void * context,int (* on_node)(ACL_ROLE * role,void * context),int (* on_edge)(ACL_ROLE * current,ACL_ROLE * neighbour,void * context))5788 static int traverse_role_graph_up(ACL_ROLE *role, void *context,
5789        int (*on_node) (ACL_ROLE *role, void *context),
5790        int (*on_edge) (ACL_ROLE *current, ACL_ROLE *neighbour, void *context))
5791 {
5792   return traverse_role_graph_impl(role, context,
5793                     my_offsetof(ACL_ROLE, parent_grantee),
5794                     (int (*)(ACL_USER_BASE *, void *))on_node,
5795                     (int (*)(ACL_USER_BASE *, ACL_ROLE *, void *))on_edge);
5796 }
5797 
5798 /**
5799   Traverse the role grant graph, going from a user or a role to granted roles.
5800 
5801   This is used, for example, to print all grants available to a user or a role
5802   (as in SHOW GRANTS).
5803 */
5804 
traverse_role_graph_down(ACL_USER_BASE * user,void * context,int (* on_node)(ACL_USER_BASE * role,void * context),int (* on_edge)(ACL_USER_BASE * current,ACL_ROLE * neighbour,void * context))5805 static int traverse_role_graph_down(ACL_USER_BASE *user, void *context,
5806        int (*on_node) (ACL_USER_BASE *role, void *context),
5807        int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
5808 {
5809   return traverse_role_graph_impl(user, context,
5810                              my_offsetof(ACL_USER_BASE, role_grants),
5811                              on_node, on_edge);
5812 }
5813 
5814 /*
5815   To find all db/table/routine privilege for a specific role
5816   we need to scan the array of privileges. It can be big.
5817   But the set of privileges granted to a role in question (or
5818   to roles directly granted to the role in question) is supposedly
5819   much smaller.
5820 
5821   We put a role and all roles directly granted to it in a hash, and iterate
5822   the (suposedly long) array of privileges, filtering out "interesting"
5823   entries using the role hash. We put all these "interesting"
5824   entries in a (suposedly small) dynamic array and them use it for merging.
5825 */
role_key(const ACL_ROLE * role,size_t * klen,my_bool)5826 static uchar* role_key(const ACL_ROLE *role, size_t *klen, my_bool)
5827 {
5828   *klen= role->user.length;
5829   return (uchar*) role->user.str;
5830 }
5831 typedef Hash_set<ACL_ROLE> role_hash_t;
5832 
merge_role_global_privileges(ACL_ROLE * grantee)5833 static bool merge_role_global_privileges(ACL_ROLE *grantee)
5834 {
5835   ulong old= grantee->access;
5836   grantee->access= grantee->initial_role_access;
5837 
5838   DBUG_EXECUTE_IF("role_merge_stats", role_global_merges++;);
5839 
5840   for (uint i= 0; i < grantee->role_grants.elements; i++)
5841   {
5842     ACL_ROLE *r= *dynamic_element(&grantee->role_grants, i, ACL_ROLE**);
5843     grantee->access|= r->access;
5844   }
5845   return old != grantee->access;
5846 }
5847 
db_name_sort(const int * db1,const int * db2)5848 static int db_name_sort(const int *db1, const int *db2)
5849 {
5850   return strcmp(acl_dbs.at(*db1).db, acl_dbs.at(*db2).db);
5851 }
5852 
5853 /**
5854   update ACL_DB for given database and a given role with merged privileges
5855 
5856   @param merged ACL_DB of the role in question (or -1 if it wasn't found)
5857   @param first  first ACL_DB in an array for the database in question
5858   @param access new privileges for the given role on the gived database
5859   @param role   the name of the given role
5860 
5861   @return a bitmap of
5862           1 - privileges were changed
5863           2 - ACL_DB was added
5864           4 - ACL_DB was deleted
5865 */
update_role_db(int merged,int first,ulong access,const char * role)5866 static int update_role_db(int merged, int first, ulong access,
5867                           const char *role)
5868 {
5869   if (first < 0)
5870     return 0;
5871 
5872   DBUG_EXECUTE_IF("role_merge_stats", role_db_merges++;);
5873 
5874   if (merged < 0)
5875   {
5876     /*
5877       there's no ACL_DB for this role (all db grants come from granted roles)
5878       we need to create it
5879 
5880       Note that we cannot use acl_insert_db() now:
5881       1. it'll sort elements in the acl_dbs, so the pointers will become invalid
5882       2. we may need many of them, no need to sort every time
5883     */
5884     DBUG_ASSERT(access);
5885     ACL_DB acl_db;
5886     acl_db.user= role;
5887     acl_db.host.hostname= const_cast<char*>("");
5888     acl_db.host.ip= acl_db.host.ip_mask= 0;
5889     acl_db.db= acl_dbs.at(first).db;
5890     acl_db.access= access;
5891     acl_db.initial_access= 0;
5892     acl_db.sort=get_sort(3, "", acl_db.db, role);
5893     acl_dbs.push(acl_db);
5894     return 2;
5895   }
5896   else if (access == 0)
5897   {
5898     /*
5899       there is ACL_DB but the role has no db privileges granted
5900       (all privileges were coming from granted roles, and now those roles
5901       were dropped or had their privileges revoked).
5902       we need to remove this ACL_DB entry
5903 
5904       Note, that we cannot delete now:
5905       1. it'll shift elements in the acl_dbs, so the pointers will become invalid
5906       2. it's O(N) operation, and we may need many of them
5907       so we only mark elements deleted and will delete later.
5908     */
5909     acl_dbs.at(merged).sort= 0; // lower than any valid ACL_DB sort value, will be sorted last
5910     return 4;
5911   }
5912   else if (acl_dbs.at(merged).access != access)
5913   {
5914     /* this is easy */
5915     acl_dbs.at(merged).access= access;
5916     return 1;
5917   }
5918   return 0;
5919 }
5920 
5921 /**
5922   merges db privileges from roles granted to the role 'grantee'.
5923 
5924   @return true if database privileges of the 'grantee' were changed
5925 
5926 */
merge_role_db_privileges(ACL_ROLE * grantee,const char * dbname,role_hash_t * rhash)5927 static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname,
5928                                      role_hash_t *rhash)
5929 {
5930   Dynamic_array<int> dbs;
5931 
5932   /*
5933     Supposedly acl_dbs can be huge, but only a handful of db grants
5934     apply to grantee or roles directly granted to grantee.
5935 
5936     Collect these applicable db grants.
5937   */
5938   for (uint i=0 ; i < acl_dbs.elements() ; i++)
5939   {
5940     ACL_DB *db= &acl_dbs.at(i);
5941     if (db->host.hostname[0])
5942       continue;
5943     if (dbname && strcmp(db->db, dbname))
5944       continue;
5945     ACL_ROLE *r= rhash->find(db->user, strlen(db->user));
5946     if (!r)
5947       continue;
5948     dbs.append(i);
5949   }
5950   dbs.sort(db_name_sort);
5951 
5952   /*
5953     Because dbs array is sorted by the db name, all grants for the same db
5954     (that should be merged) are sorted together. The grantee's ACL_DB element
5955     is not necessarily the first and may be not present at all.
5956   */
5957   int first= -1, merged= -1;
5958   ulong access= 0, update_flags= 0;
5959   for (int *p= dbs.front(); p <= dbs.back(); p++)
5960   {
5961     if (first<0 || (!dbname && strcmp(acl_dbs.at(p[0]).db, acl_dbs.at(p[-1]).db)))
5962     { // new db name series
5963       update_flags|= update_role_db(merged, first, access, grantee->user.str);
5964       merged= -1;
5965       access= 0;
5966       first= *p;
5967     }
5968     if (strcmp(acl_dbs.at(*p).user, grantee->user.str) == 0)
5969       access|= acl_dbs.at(merged= *p).initial_access;
5970     else
5971       access|= acl_dbs.at(*p).access;
5972   }
5973   update_flags|= update_role_db(merged, first, access, grantee->user.str);
5974 
5975   /*
5976     to make this code a bit simpler, we sort on deletes, to move
5977     deleted elements to the end of the array. strictly speaking it's
5978     unnecessary, it'd be faster to remove them in one O(N) array scan.
5979 
5980     on the other hand, qsort on almost sorted array is pretty fast anyway...
5981   */
5982   if (update_flags & (2|4))
5983   { // inserted or deleted, need to sort
5984     acl_dbs.sort((acl_dbs_cmp)acl_compare);
5985   }
5986   if (update_flags & 4)
5987   { // deleted, trim the end
5988     while (acl_dbs.elements() && acl_dbs.back()->sort == 0)
5989       acl_dbs.pop();
5990   }
5991   return update_flags;
5992 }
5993 
table_name_sort(GRANT_TABLE * const * tbl1,GRANT_TABLE * const * tbl2)5994 static int table_name_sort(GRANT_TABLE * const *tbl1, GRANT_TABLE * const *tbl2)
5995 {
5996   int res = strcmp((*tbl1)->db, (*tbl2)->db);
5997   if (res) return res;
5998   return strcmp((*tbl1)->tname, (*tbl2)->tname);
5999 }
6000 
6001 /**
6002   merges column privileges for the entry 'merged'
6003 
6004   @param merged GRANT_TABLE to merge the privileges into
6005   @param cur    first entry in the array of GRANT_TABLE's for a given table
6006   @param last   last entry in the array of GRANT_TABLE's for a given table,
6007                 all entries between cur and last correspond to the *same* table
6008 
6009   @return 1 if the _set of columns_ in 'merged' was changed
6010           (not if the _set of privileges_ was changed).
6011 */
update_role_columns(GRANT_TABLE * merged,GRANT_TABLE ** cur,GRANT_TABLE ** last)6012 static int update_role_columns(GRANT_TABLE *merged,
6013                                GRANT_TABLE **cur, GRANT_TABLE **last)
6014 
6015 {
6016   ulong rights __attribute__((unused))= 0;
6017   int changed= 0;
6018   if (!merged->cols)
6019   {
6020     changed= merged->hash_columns.records > 0;
6021     my_hash_reset(&merged->hash_columns);
6022     return changed;
6023   }
6024 
6025   DBUG_EXECUTE_IF("role_merge_stats", role_column_merges++;);
6026 
6027   HASH *mh= &merged->hash_columns;
6028   for (uint i=0 ; i < mh->records ; i++)
6029   {
6030     GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
6031     col->rights= col->init_rights;
6032   }
6033 
6034   for (; cur < last; cur++)
6035   {
6036     if (*cur == merged)
6037       continue;
6038     HASH *ch= &cur[0]->hash_columns;
6039     for (uint i=0 ; i < ch->records ; i++)
6040     {
6041       GRANT_COLUMN *ccol = (GRANT_COLUMN *)my_hash_element(ch, i);
6042       GRANT_COLUMN *mcol = (GRANT_COLUMN *)my_hash_search(mh,
6043                                   (uchar *)ccol->column, ccol->key_length);
6044       if (mcol)
6045         mcol->rights|= ccol->rights;
6046       else
6047       {
6048         changed= 1;
6049         my_hash_insert(mh, (uchar*)new (&grant_memroot) GRANT_COLUMN(ccol));
6050       }
6051     }
6052   }
6053 
6054   for (uint i=0 ; i < mh->records ; i++)
6055   {
6056     GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
6057     rights|= col->rights;
6058     if (!col->rights)
6059     {
6060       changed= 1;
6061       my_hash_delete(mh, (uchar*)col);
6062     }
6063   }
6064   DBUG_ASSERT(rights == merged->cols);
6065   return changed;
6066 }
6067 
6068 /**
6069   update GRANT_TABLE for a given table and a given role with merged privileges
6070 
6071   @param merged GRANT_TABLE of the role in question (or NULL if it wasn't found)
6072   @param first  first GRANT_TABLE in an array for the table in question
6073   @param last   last entry in the array of GRANT_TABLE's for a given table,
6074                 all entries between first and last correspond to the *same* table
6075   @param privs  new table-level privileges for 'merged'
6076   @param cols   new OR-ed column-level privileges for 'merged'
6077   @param role   the name of the given role
6078 
6079   @return a bitmap of
6080           1 - privileges were changed
6081           2 - GRANT_TABLE was added
6082           4 - GRANT_TABLE was deleted
6083 */
update_role_table_columns(GRANT_TABLE * merged,GRANT_TABLE ** first,GRANT_TABLE ** last,ulong privs,ulong cols,const char * role)6084 static int update_role_table_columns(GRANT_TABLE *merged,
6085                                      GRANT_TABLE **first, GRANT_TABLE **last,
6086                                      ulong privs, ulong cols, const char *role)
6087 {
6088   if (!first)
6089     return 0;
6090 
6091   DBUG_EXECUTE_IF("role_merge_stats", role_table_merges++;);
6092 
6093   if (merged == NULL)
6094   {
6095     /*
6096       there's no GRANT_TABLE for this role (all table grants come from granted
6097       roles) we need to create it
6098     */
6099     DBUG_ASSERT(privs | cols);
6100     merged= new (&grant_memroot) GRANT_TABLE("", first[0]->db, role, first[0]->tname,
6101                                      privs, cols);
6102     merged->init_privs= merged->init_cols= 0;
6103     update_role_columns(merged, first, last);
6104     my_hash_insert(&column_priv_hash,(uchar*) merged);
6105     return 2;
6106   }
6107   else if ((privs | cols) == 0)
6108   {
6109     /*
6110       there is GRANT_TABLE object but the role has no table or column
6111       privileges granted (all privileges were coming from granted roles, and
6112       now those roles were dropped or had their privileges revoked).
6113       we need to remove this GRANT_TABLE
6114     */
6115     DBUG_EXECUTE_IF("role_merge_stats",
6116                     role_column_merges+= MY_TEST(merged->cols););
6117     my_hash_delete(&column_priv_hash,(uchar*) merged);
6118     return 4;
6119   }
6120   else
6121   {
6122     bool changed= merged->cols != cols || merged->privs != privs;
6123     merged->cols= cols;
6124     merged->privs= privs;
6125     if (update_role_columns(merged, first, last))
6126       changed= true;
6127     return changed;
6128   }
6129 }
6130 
6131 /**
6132   merges table privileges from roles granted to the role 'grantee'.
6133 
6134   @return true if table privileges of the 'grantee' were changed
6135 
6136 */
merge_role_table_and_column_privileges(ACL_ROLE * grantee,const char * db,const char * tname,role_hash_t * rhash)6137 static bool merge_role_table_and_column_privileges(ACL_ROLE *grantee,
6138                         const char *db, const char *tname, role_hash_t *rhash)
6139 {
6140   Dynamic_array<GRANT_TABLE *> grants;
6141   DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
6142 
6143   /*
6144     first, collect table/column privileges granted to
6145     roles in question.
6146   */
6147   for (uint i=0 ; i < column_priv_hash.records ; i++)
6148   {
6149     GRANT_TABLE *grant= (GRANT_TABLE *) my_hash_element(&column_priv_hash, i);
6150     if (grant->host.hostname[0])
6151       continue;
6152     if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
6153       continue;
6154     ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
6155     if (!r)
6156       continue;
6157     grants.append(grant);
6158   }
6159   grants.sort(table_name_sort);
6160 
6161   GRANT_TABLE **first= NULL, *merged= NULL, **cur;
6162   ulong privs= 0, cols= 0, update_flags= 0;
6163   for (cur= grants.front(); cur <= grants.back(); cur++)
6164   {
6165     if (!first ||
6166         (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
6167                    strcmp(cur[0]->tname, cur[-1]->tname))))
6168     { // new db.tname series
6169       update_flags|= update_role_table_columns(merged, first, cur,
6170                                                privs, cols, grantee->user.str);
6171       merged= NULL;
6172       privs= cols= 0;
6173       first= cur;
6174     }
6175     if (strcmp(cur[0]->user, grantee->user.str) == 0)
6176     {
6177       merged= cur[0];
6178       cols|= cur[0]->init_cols;
6179       privs|= cur[0]->init_privs;
6180     }
6181     else
6182     {
6183       cols|= cur[0]->cols;
6184       privs|= cur[0]->privs;
6185     }
6186   }
6187   update_flags|= update_role_table_columns(merged, first, cur,
6188                                            privs, cols, grantee->user.str);
6189 
6190   return update_flags;
6191 }
6192 
routine_name_sort(GRANT_NAME * const * r1,GRANT_NAME * const * r2)6193 static int routine_name_sort(GRANT_NAME * const *r1, GRANT_NAME * const *r2)
6194 {
6195   int res= strcmp((*r1)->db, (*r2)->db);
6196   if (res) return res;
6197   return strcmp((*r1)->tname, (*r2)->tname);
6198 }
6199 
6200 /**
6201   update GRANT_NAME for a given routine and a given role with merged privileges
6202 
6203   @param merged GRANT_NAME of the role in question (or NULL if it wasn't found)
6204   @param first  first GRANT_NAME in an array for the routine in question
6205   @param privs  new routine-level privileges for 'merged'
6206   @param role   the name of the given role
6207   @param hash   proc_priv_hash or func_priv_hash
6208 
6209   @return a bitmap of
6210           1 - privileges were changed
6211           2 - GRANT_NAME was added
6212           4 - GRANT_NAME was deleted
6213 */
update_role_routines(GRANT_NAME * merged,GRANT_NAME ** first,ulong privs,const char * role,HASH * hash)6214 static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first,
6215                                 ulong privs, const char *role, HASH *hash)
6216 {
6217   if (!first)
6218     return 0;
6219 
6220   DBUG_EXECUTE_IF("role_merge_stats", role_routine_merges++;);
6221 
6222   if (merged == NULL)
6223   {
6224     /*
6225       there's no GRANT_NAME for this role (all routine grants come from granted
6226       roles) we need to create it
6227     */
6228     DBUG_ASSERT(privs);
6229     merged= new (&grant_memroot) GRANT_NAME("", first[0]->db, role, first[0]->tname,
6230                                     privs, true);
6231     merged->init_privs= 0; // all privs are inherited
6232     my_hash_insert(hash, (uchar *)merged);
6233     return 2;
6234   }
6235   else if (privs == 0)
6236   {
6237     /*
6238       there is GRANT_NAME but the role has no privileges granted
6239       (all privileges were coming from granted roles, and now those roles
6240       were dropped or had their privileges revoked).
6241       we need to remove this entry
6242     */
6243     my_hash_delete(hash, (uchar*)merged);
6244     return 4;
6245   }
6246   else if (merged->privs != privs)
6247   {
6248     /* this is easy */
6249     merged->privs= privs;
6250     return 1;
6251   }
6252   return 0;
6253 }
6254 
6255 /**
6256   merges routine privileges from roles granted to the role 'grantee'.
6257 
6258   @return true if routine privileges of the 'grantee' were changed
6259 
6260 */
merge_role_routine_grant_privileges(ACL_ROLE * grantee,const char * db,const char * tname,role_hash_t * rhash,HASH * hash)6261 static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee,
6262             const char *db, const char *tname, role_hash_t *rhash, HASH *hash)
6263 {
6264   ulong update_flags= 0;
6265 
6266   DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
6267 
6268   Dynamic_array<GRANT_NAME *> grants;
6269 
6270   /* first, collect routine privileges granted to roles in question */
6271   for (uint i=0 ; i < hash->records ; i++)
6272   {
6273     GRANT_NAME *grant= (GRANT_NAME *) my_hash_element(hash, i);
6274     if (grant->host.hostname[0])
6275       continue;
6276     if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
6277       continue;
6278     ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
6279     if (!r)
6280       continue;
6281     grants.append(grant);
6282   }
6283   grants.sort(routine_name_sort);
6284 
6285   GRANT_NAME **first= NULL, *merged= NULL;
6286   ulong privs= 0 ;
6287   for (GRANT_NAME **cur= grants.front(); cur <= grants.back(); cur++)
6288   {
6289     if (!first ||
6290         (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
6291                     strcmp(cur[0]->tname, cur[-1]->tname))))
6292     { // new db.tname series
6293       update_flags|= update_role_routines(merged, first, privs,
6294                                           grantee->user.str, hash);
6295       merged= NULL;
6296       privs= 0;
6297       first= cur;
6298     }
6299     if (strcmp(cur[0]->user, grantee->user.str) == 0)
6300     {
6301       merged= cur[0];
6302       privs|= cur[0]->init_privs;
6303     }
6304     else
6305     {
6306       privs|= cur[0]->privs;
6307     }
6308   }
6309   update_flags|= update_role_routines(merged, first, privs,
6310                                       grantee->user.str, hash);
6311   return update_flags;
6312 }
6313 
6314 /**
6315   update privileges of the 'grantee' from all roles, granted to it
6316 */
merge_role_privileges(ACL_ROLE * role,ACL_ROLE * grantee,void * context)6317 static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
6318                                  ACL_ROLE *grantee, void *context)
6319 {
6320   PRIVS_TO_MERGE *data= (PRIVS_TO_MERGE *)context;
6321 
6322   DBUG_ASSERT(grantee->counter > 0);
6323   if (--grantee->counter)
6324     return 1; // don't recurse into grantee just yet
6325 
6326   grantee->counter= 1; // Mark the grantee as merged.
6327 
6328   /* if we'll do db/table/routine privileges, create a hash of role names */
6329   role_hash_t role_hash(role_key);
6330   if (data->what != PRIVS_TO_MERGE::GLOBAL)
6331   {
6332     role_hash.insert(grantee);
6333     for (uint i= 0; i < grantee->role_grants.elements; i++)
6334       role_hash.insert(*dynamic_element(&grantee->role_grants, i, ACL_ROLE**));
6335   }
6336 
6337   bool all= data->what == PRIVS_TO_MERGE::ALL;
6338   bool changed= false;
6339   if (all || data->what == PRIVS_TO_MERGE::GLOBAL)
6340     changed|= merge_role_global_privileges(grantee);
6341   if (all || data->what == PRIVS_TO_MERGE::DB)
6342     changed|= merge_role_db_privileges(grantee, data->db, &role_hash);
6343   if (all || data->what == PRIVS_TO_MERGE::TABLE_COLUMN)
6344     changed|= merge_role_table_and_column_privileges(grantee,
6345                                              data->db, data->name, &role_hash);
6346   if (all || data->what == PRIVS_TO_MERGE::PROC)
6347     changed|= merge_role_routine_grant_privileges(grantee,
6348                             data->db, data->name, &role_hash, &proc_priv_hash);
6349   if (all || data->what == PRIVS_TO_MERGE::FUNC)
6350     changed|= merge_role_routine_grant_privileges(grantee,
6351                             data->db, data->name, &role_hash, &func_priv_hash);
6352   if (all || data->what == PRIVS_TO_MERGE::PACKAGE_SPEC)
6353     changed|= merge_role_routine_grant_privileges(grantee,
6354                             data->db, data->name, &role_hash,
6355                             &package_spec_priv_hash);
6356   if (all || data->what == PRIVS_TO_MERGE::PACKAGE_BODY)
6357     changed|= merge_role_routine_grant_privileges(grantee,
6358                             data->db, data->name, &role_hash,
6359                             &package_body_priv_hash);
6360   return !changed; // don't recurse into the subgraph if privs didn't change
6361 }
6362 
merge_one_role_privileges(ACL_ROLE * grantee)6363 static bool merge_one_role_privileges(ACL_ROLE *grantee)
6364 {
6365   PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
6366   grantee->counter= 1;
6367   return merge_role_privileges(0, grantee, &data);
6368 }
6369 
6370 /*****************************************************************
6371   End of the role privilege propagation and graph traversal code
6372 ******************************************************************/
6373 
has_auth(LEX_USER * user,LEX * lex)6374 static bool has_auth(LEX_USER *user, LEX *lex)
6375 {
6376   return user->pwtext.length || user->pwhash.length || user->plugin.length || user->auth.length ||
6377          lex->ssl_type != SSL_TYPE_NOT_SPECIFIED || lex->ssl_cipher ||
6378          lex->x509_issuer || lex->x509_subject ||
6379          lex->mqh.specified_limits;
6380 }
6381 
fix_and_copy_user(LEX_USER * to,LEX_USER * from,THD * thd)6382 static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd)
6383 {
6384   if (to != from)
6385   {
6386     /* preserve authentication information, if LEX_USER was  reallocated */
6387     to->pwtext= from->pwtext;
6388     to->pwhash= from->pwhash;
6389     to->plugin= from->plugin;
6390     to->auth= from->auth;
6391   }
6392 
6393   if (fix_lex_user(thd, to))
6394     return true;
6395 
6396   return false;
6397 }
6398 
copy_and_check_auth(LEX_USER * to,LEX_USER * from,THD * thd)6399 static bool copy_and_check_auth(LEX_USER *to, LEX_USER *from, THD *thd)
6400 {
6401   if (fix_and_copy_user(to, from, thd))
6402     return true;
6403 
6404   // if changing auth for an existing user
6405   if (has_auth(to, thd->lex) && find_user_exact(to->host.str, to->user.str))
6406   {
6407     mysql_mutex_unlock(&acl_cache->lock);
6408     bool res= check_alter_user(thd, to->host.str, to->user.str);
6409     mysql_mutex_lock(&acl_cache->lock);
6410     return res;
6411   }
6412 
6413   return false;
6414 }
6415 
6416 
6417 /*
6418   Store table level and column level grants in the privilege tables
6419 
6420   SYNOPSIS
6421     mysql_table_grant()
6422     thd			Thread handle
6423     table_list		List of tables to give grant
6424     user_list		List of users to give grant
6425     columns		List of columns to give grant
6426     rights		Table level grant
6427     revoke_grant	Set to 1 if this is a REVOKE command
6428 
6429   RETURN
6430     FALSE ok
6431     TRUE  error
6432 */
6433 
mysql_table_grant(THD * thd,TABLE_LIST * table_list,List<LEX_USER> & user_list,List<LEX_COLUMN> & columns,ulong rights,bool revoke_grant)6434 int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
6435 		      List <LEX_USER> &user_list,
6436 		      List <LEX_COLUMN> &columns, ulong rights,
6437 		      bool revoke_grant)
6438 {
6439   ulong column_priv= 0;
6440   int result;
6441   List_iterator <LEX_USER> str_list (user_list);
6442   LEX_USER *Str, *tmp_Str;
6443   bool create_new_users=0;
6444   const char *db_name, *table_name;
6445   DBUG_ENTER("mysql_table_grant");
6446 
6447   if (rights & ~TABLE_ACLS)
6448   {
6449     my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
6450                ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
6451                MYF(0));
6452     DBUG_RETURN(TRUE);
6453   }
6454 
6455   if (!revoke_grant)
6456   {
6457     if (columns.elements)
6458     {
6459       class LEX_COLUMN *column;
6460       List_iterator <LEX_COLUMN> column_iter(columns);
6461 
6462       if (open_normal_and_derived_tables(thd, table_list, 0, DT_PREPARE))
6463         DBUG_RETURN(TRUE);
6464 
6465       while ((column = column_iter++))
6466       {
6467         uint unused_field_idx= NO_CACHED_FIELD_INDEX;
6468         TABLE_LIST *dummy;
6469         Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
6470                                          column->column.length(),
6471                                          column->column.ptr(), NULL, NULL,
6472                                          NULL, TRUE, FALSE,
6473                                          &unused_field_idx, FALSE, &dummy);
6474         if (unlikely(f == (Field*)0))
6475         {
6476           my_error(ER_BAD_FIELD_ERROR, MYF(0),
6477                    column->column.c_ptr(), table_list->alias.str);
6478           DBUG_RETURN(TRUE);
6479         }
6480         if (unlikely(f == (Field *)-1))
6481           DBUG_RETURN(TRUE);
6482         column_priv|= column->rights;
6483       }
6484       close_mysql_tables(thd);
6485     }
6486     else
6487     {
6488       if (!(rights & CREATE_ACL))
6489       {
6490         if (!ha_table_exists(thd, &table_list->db, &table_list->table_name))
6491         {
6492           my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db.str,
6493                    table_list->alias.str);
6494           DBUG_RETURN(TRUE);
6495         }
6496       }
6497       if (table_list->grant.want_privilege)
6498       {
6499         char command[128];
6500         get_privilege_desc(command, sizeof(command),
6501                            table_list->grant.want_privilege);
6502         my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
6503                  command, thd->security_ctx->priv_user,
6504                  thd->security_ctx->host_or_ip, table_list->alias.str);
6505         DBUG_RETURN(-1);
6506       }
6507     }
6508   }
6509 
6510   /*
6511     Open the mysql.user and mysql.tables_priv tables.
6512     Don't open column table if we don't need it !
6513   */
6514   int maybe_columns_priv= 0;
6515   if (column_priv ||
6516       (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
6517     maybe_columns_priv= Table_columns_priv;
6518 
6519   /*
6520     The lock api is depending on the thd->lex variable which needs to be
6521     re-initialized.
6522   */
6523   Query_tables_list backup;
6524   thd->lex->reset_n_backup_query_tables_list(&backup);
6525   /*
6526     Restore Query_tables_list::sql_command value, which was reset
6527     above, as the code writing query to the binary log assumes that
6528     this value corresponds to the statement being executed.
6529   */
6530   thd->lex->sql_command= backup.sql_command;
6531 
6532   Grant_tables tables(Table_user | Table_tables_priv | maybe_columns_priv,
6533                       TL_WRITE);
6534   if ((result= tables.open_and_lock(thd)))
6535   {
6536     thd->lex->restore_backup_query_tables_list(&backup);
6537     DBUG_RETURN(result != 1);
6538   }
6539 
6540   if (!revoke_grant)
6541     create_new_users= test_if_create_new_users(thd);
6542   mysql_rwlock_wrlock(&LOCK_grant);
6543   mysql_mutex_lock(&acl_cache->lock);
6544   MEM_ROOT *old_root= thd->mem_root;
6545   thd->mem_root= &grant_memroot;
6546   grant_version++;
6547 
6548   while ((tmp_Str = str_list++))
6549   {
6550     int error;
6551     GRANT_TABLE *grant_table;
6552     if (!(Str= get_current_user(thd, tmp_Str, false)))
6553     {
6554       result= TRUE;
6555       continue;
6556     }
6557     /* Create user if needed */
6558     error= copy_and_check_auth(Str, tmp_Str, thd) ||
6559            replace_user_table(thd, tables.user_table(), *Str,
6560                                0, revoke_grant, create_new_users,
6561                                MY_TEST(thd->variables.sql_mode &
6562                                        MODE_NO_AUTO_CREATE_USER));
6563     if (unlikely(error))
6564     {
6565       result= TRUE;				// Remember error
6566       continue;					// Add next user
6567     }
6568 
6569     db_name= table_list->get_db_name();
6570     table_name= table_list->get_table_name();
6571 
6572     /* Find/create cached table grant */
6573     grant_table= table_hash_search(Str->host.str, NullS, db_name,
6574 				   Str->user.str, table_name, 1);
6575     if (!grant_table)
6576     {
6577       if (revoke_grant)
6578       {
6579 	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
6580                  Str->user.str, Str->host.str, table_list->table_name.str);
6581 	result= TRUE;
6582 	continue;
6583       }
6584       grant_table = new GRANT_TABLE (Str->host.str, db_name,
6585 				     Str->user.str, table_name,
6586 				     rights,
6587 				     column_priv);
6588       if (!grant_table ||
6589         my_hash_insert(&column_priv_hash,(uchar*) grant_table))
6590       {
6591 	result= TRUE;				/* purecov: deadcode */
6592 	continue;				/* purecov: deadcode */
6593       }
6594     }
6595 
6596     /* If revoke_grant, calculate the new column privilege for tables_priv */
6597     if (revoke_grant)
6598     {
6599       class LEX_COLUMN *column;
6600       List_iterator <LEX_COLUMN> column_iter(columns);
6601       GRANT_COLUMN *grant_column;
6602 
6603       /* Fix old grants */
6604       while ((column = column_iter++))
6605       {
6606 	grant_column = column_hash_search(grant_table,
6607 					  column->column.ptr(),
6608 					  column->column.length());
6609 	if (grant_column)
6610 	  grant_column->rights&= ~(column->rights | rights);
6611       }
6612       /* scan trough all columns to get new column grant */
6613       column_priv= 0;
6614       for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
6615       {
6616         grant_column= (GRANT_COLUMN*)
6617           my_hash_element(&grant_table->hash_columns, idx);
6618 	grant_column->rights&= ~rights;		// Fix other columns
6619 	column_priv|= grant_column->rights;
6620       }
6621     }
6622     else
6623     {
6624       column_priv|= grant_table->cols;
6625     }
6626 
6627 
6628     /* update table and columns */
6629 
6630     /* TODO(cvicentiu) refactor replace_table_table to use Tables_priv_table
6631        instead of TABLE directly. */
6632     if (replace_table_table(thd, grant_table, tables.tables_priv_table().table(),
6633                             *Str, db_name, table_name,
6634 			    rights, column_priv, revoke_grant))
6635     {
6636       /* Should only happen if table is crashed */
6637       result= TRUE;			       /* purecov: deadcode */
6638     }
6639     else if (tables.columns_priv_table().table_exists())
6640     {
6641       /* TODO(cvicentiu) refactor replace_column_table to use Columns_priv_table
6642          instead of TABLE directly. */
6643       if (replace_column_table(grant_table, tables.columns_priv_table().table(),
6644                                *Str, columns, db_name, table_name, rights,
6645                                revoke_grant))
6646       {
6647 	result= TRUE;
6648       }
6649     }
6650     if (Str->is_role())
6651       propagate_role_grants(find_acl_role(Str->user.str),
6652                             PRIVS_TO_MERGE::TABLE_COLUMN, db_name, table_name);
6653   }
6654 
6655   thd->mem_root= old_root;
6656   mysql_mutex_unlock(&acl_cache->lock);
6657 
6658   if (!result) /* success */
6659   {
6660     result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
6661   }
6662 
6663   mysql_rwlock_unlock(&LOCK_grant);
6664 
6665   if (!result) /* success */
6666     my_ok(thd);
6667 
6668   thd->lex->restore_backup_query_tables_list(&backup);
6669   DBUG_RETURN(result);
6670 }
6671 
6672 
6673 /**
6674   Store routine level grants in the privilege tables
6675 
6676   @param thd Thread handle
6677   @param table_list List of routines to give grant
6678   @param sph SP handler
6679   @param user_list List of users to give grant
6680   @param rights Table level grant
6681   @param revoke_grant Is this is a REVOKE command?
6682 
6683   @return
6684     @retval FALSE Success.
6685     @retval TRUE An error occurred.
6686 */
6687 
mysql_routine_grant(THD * thd,TABLE_LIST * table_list,const Sp_handler * sph,List<LEX_USER> & user_list,ulong rights,bool revoke_grant,bool write_to_binlog)6688 bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list,
6689                          const Sp_handler *sph,
6690 			 List <LEX_USER> &user_list, ulong rights,
6691 			 bool revoke_grant, bool write_to_binlog)
6692 {
6693   List_iterator <LEX_USER> str_list (user_list);
6694   LEX_USER *Str, *tmp_Str;
6695   bool create_new_users= 0;
6696   int result;
6697   const char *db_name, *table_name;
6698   DBUG_ENTER("mysql_routine_grant");
6699 
6700   if (rights & ~PROC_ACLS)
6701   {
6702     my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
6703                ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
6704                MYF(0));
6705     DBUG_RETURN(TRUE);
6706   }
6707 
6708   if (!revoke_grant)
6709   {
6710     if (sph->sp_exist_routines(thd, table_list))
6711       DBUG_RETURN(TRUE);
6712   }
6713 
6714   Grant_tables tables(Table_user | Table_procs_priv, TL_WRITE);
6715   if ((result= tables.open_and_lock(thd)))
6716     DBUG_RETURN(result != 1);
6717 
6718   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6719 
6720   if (!revoke_grant)
6721     create_new_users= test_if_create_new_users(thd);
6722   mysql_rwlock_wrlock(&LOCK_grant);
6723   mysql_mutex_lock(&acl_cache->lock);
6724   MEM_ROOT *old_root= thd->mem_root;
6725   thd->mem_root= &grant_memroot;
6726 
6727   DBUG_PRINT("info",("now time to iterate and add users"));
6728 
6729   while ((tmp_Str= str_list++))
6730   {
6731     GRANT_NAME *grant_name;
6732     if (!(Str= get_current_user(thd, tmp_Str, false)))
6733     {
6734       result= TRUE;
6735       continue;
6736     }
6737     /* Create user if needed */
6738     if (copy_and_check_auth(Str, tmp_Str, thd) ||
6739         replace_user_table(thd, tables.user_table(), *Str,
6740 			   0, revoke_grant, create_new_users,
6741                            MY_TEST(thd->variables.sql_mode &
6742                                      MODE_NO_AUTO_CREATE_USER)))
6743     {
6744       result= TRUE;
6745       continue;
6746     }
6747 
6748     db_name= table_list->db.str;
6749     table_name= table_list->table_name.str;
6750     grant_name= routine_hash_search(Str->host.str, NullS, db_name,
6751                                     Str->user.str, table_name, sph, 1);
6752     if (!grant_name || !grant_name->init_privs)
6753     {
6754       if (revoke_grant)
6755       {
6756         my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
6757 	         Str->user.str, Str->host.str, table_name);
6758 	result= TRUE;
6759 	continue;
6760       }
6761       grant_name= new GRANT_NAME(Str->host.str, db_name,
6762 				 Str->user.str, table_name,
6763 				 rights, TRUE);
6764       if (!grant_name ||
6765         my_hash_insert(sph->get_priv_hash(), (uchar*) grant_name))
6766       {
6767         result= TRUE;
6768 	continue;
6769       }
6770     }
6771 
6772     /* TODO(cvicentiu) refactor replace_routine_table to use Tables_procs_priv
6773        instead of TABLE directly. */
6774     if (tables.procs_priv_table().no_such_table() ||
6775         replace_routine_table(thd, grant_name, tables.procs_priv_table().table(),
6776                               *Str, db_name, table_name, sph, rights,
6777                               revoke_grant) != 0)
6778     {
6779       result= TRUE;
6780       continue;
6781     }
6782     if (Str->is_role())
6783       propagate_role_grants(find_acl_role(Str->user.str),
6784                             sp_privs_to_merge(sph->type()),
6785                             db_name, table_name);
6786   }
6787   thd->mem_root= old_root;
6788   mysql_mutex_unlock(&acl_cache->lock);
6789 
6790   if (write_to_binlog)
6791   {
6792     if (write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
6793       result= TRUE;
6794   }
6795 
6796   mysql_rwlock_unlock(&LOCK_grant);
6797 
6798   /* Tables are automatically closed */
6799   DBUG_RETURN(result);
6800 }
6801 
6802 /**
6803   append a user or role name to a buffer that will be later used as an error message
6804 */
append_user(THD * thd,String * str,const LEX_CSTRING * u,const LEX_CSTRING * h)6805 static void append_user(THD *thd, String *str,
6806                         const LEX_CSTRING *u, const LEX_CSTRING *h)
6807 {
6808   if (str->length())
6809     str->append(',');
6810   append_query_string(system_charset_info, str, u->str, u->length,
6811                       thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
6812   /* hostname part is not relevant for roles, it is always empty */
6813   if (u->length == 0 || h->length != 0)
6814   {
6815     str->append('@');
6816     append_query_string(system_charset_info, str, h->str, h->length,
6817                         thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
6818   }
6819 }
6820 
append_user(THD * thd,String * str,LEX_USER * user)6821 static void append_user(THD *thd, String *str, LEX_USER *user)
6822 {
6823   append_user(thd, str, & user->user, & user->host);
6824 }
6825 
6826 /**
6827   append a string to a buffer that will be later used as an error message
6828 
6829   @note
6830   a string can be either CURRENT_USER or CURRENT_ROLE or NONE, it should be
6831   neither quoted nor escaped.
6832 */
append_str(String * str,const char * s,size_t l)6833 static void append_str(String *str, const char *s, size_t l)
6834 {
6835   if (str->length())
6836     str->append(',');
6837   str->append(s, l);
6838 }
6839 
can_grant_role_callback(ACL_USER_BASE * grantee,ACL_ROLE * role,void * data)6840 static int can_grant_role_callback(ACL_USER_BASE *grantee,
6841                                    ACL_ROLE *role, void *data)
6842 {
6843   ROLE_GRANT_PAIR *pair;
6844 
6845   if (role != (ACL_ROLE*)data)
6846     return 0; // keep searching
6847 
6848   if (grantee->flags & IS_ROLE)
6849     pair= find_role_grant_pair(&grantee->user, &empty_clex_str, &role->user);
6850   else
6851   {
6852     ACL_USER *user= (ACL_USER *)grantee;
6853     LEX_CSTRING host= { user->host.hostname, user->hostname_length };
6854     pair= find_role_grant_pair(&user->user, &host, &role->user);
6855   }
6856   if (!pair->with_admin)
6857     return 0; // keep searching
6858 
6859   return -1; // abort the traversal
6860 }
6861 
6862 
6863 /*
6864   One can only grant a role if SELECT * FROM I_S.APPLICABLE_ROLES shows this
6865   role as grantable.
6866 
6867   What this really means - we need to traverse role graph for the current user
6868   looking for our role being granted with the admin option.
6869 */
can_grant_role(THD * thd,ACL_ROLE * role)6870 static bool can_grant_role(THD *thd, ACL_ROLE *role)
6871 {
6872   Security_context *sctx= thd->security_ctx;
6873 
6874   if (!sctx->user) // replication
6875     return true;
6876 
6877   ACL_USER *grantee= find_user_exact(sctx->priv_host, sctx->priv_user);
6878   if (!grantee)
6879     return false;
6880 
6881   return traverse_role_graph_down(grantee, role, NULL,
6882                                   can_grant_role_callback) == -1;
6883 }
6884 
6885 
mysql_grant_role(THD * thd,List<LEX_USER> & list,bool revoke)6886 bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
6887 {
6888   DBUG_ENTER("mysql_grant_role");
6889   /*
6890      The first entry in the list is the granted role. Need at least two
6891      entries for the command to be valid
6892    */
6893   DBUG_ASSERT(list.elements >= 2);
6894   int result;
6895   bool create_new_user, no_auto_create_user;
6896   String wrong_users;
6897   LEX_USER *user, *granted_role;
6898   LEX_CSTRING rolename;
6899   LEX_CSTRING username;
6900   LEX_CSTRING hostname;
6901   ACL_ROLE *role, *role_as_user;
6902 
6903   List_iterator <LEX_USER> user_list(list);
6904   granted_role= user_list++;
6905   if (!(granted_role= get_current_user(thd, granted_role)))
6906     DBUG_RETURN(TRUE);
6907 
6908   DBUG_ASSERT(granted_role->is_role());
6909   rolename= granted_role->user;
6910 
6911   create_new_user= test_if_create_new_users(thd);
6912   no_auto_create_user= MY_TEST(thd->variables.sql_mode &
6913                                MODE_NO_AUTO_CREATE_USER);
6914 
6915   Grant_tables tables(Table_user | Table_roles_mapping, TL_WRITE);
6916   if ((result= tables.open_and_lock(thd)))
6917     DBUG_RETURN(result != 1);
6918 
6919   mysql_rwlock_wrlock(&LOCK_grant);
6920   mysql_mutex_lock(&acl_cache->lock);
6921   if (!(role= find_acl_role(rolename.str)))
6922   {
6923     mysql_mutex_unlock(&acl_cache->lock);
6924     mysql_rwlock_unlock(&LOCK_grant);
6925     my_error(ER_INVALID_ROLE, MYF(0), rolename.str);
6926     DBUG_RETURN(TRUE);
6927   }
6928 
6929   if (!can_grant_role(thd, role))
6930   {
6931     mysql_mutex_unlock(&acl_cache->lock);
6932     mysql_rwlock_unlock(&LOCK_grant);
6933     my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
6934              thd->security_ctx->priv_user, thd->security_ctx->priv_host);
6935     DBUG_RETURN(TRUE);
6936   }
6937 
6938   while ((user= user_list++))
6939   {
6940     role_as_user= NULL;
6941     /* current_role is treated slightly different */
6942     if (user->user.str == current_role.str)
6943     {
6944       /* current_role is NONE */
6945       if (!thd->security_ctx->priv_role[0])
6946       {
6947         my_error(ER_INVALID_ROLE, MYF(0), "NONE");
6948         append_str(&wrong_users, STRING_WITH_LEN("NONE"));
6949         result= 1;
6950         continue;
6951       }
6952       if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role)))
6953       {
6954         LEX_CSTRING ls= { thd->security_ctx->priv_role,
6955                           strlen(thd->security_ctx->priv_role) };
6956         append_user(thd, &wrong_users, &ls, &empty_clex_str);
6957         result= 1;
6958         continue;
6959       }
6960 
6961       /* can not grant current_role to current_role */
6962       if (granted_role->user.str == current_role.str)
6963       {
6964         append_user(thd, &wrong_users, &role_as_user->user, &empty_clex_str);
6965         result= 1;
6966         continue;
6967       }
6968       username.str= thd->security_ctx->priv_role;
6969       username.length= strlen(username.str);
6970       hostname= empty_clex_str;
6971     }
6972     else if (user->user.str == current_user.str)
6973     {
6974       username.str= thd->security_ctx->priv_user;
6975       username.length= strlen(username.str);
6976       hostname.str= thd->security_ctx->priv_host;
6977       hostname.length= strlen(hostname.str);
6978     }
6979     else
6980     {
6981       username= user->user;
6982       if (user->host.str)
6983         hostname= user->host;
6984       else
6985       if ((role_as_user= find_acl_role(user->user.str)))
6986         hostname= empty_clex_str;
6987       else
6988       {
6989         if (is_invalid_role_name(username.str))
6990         {
6991           append_user(thd, &wrong_users, &username, &empty_clex_str);
6992           result= 1;
6993           continue;
6994         }
6995         hostname= host_not_specified;
6996       }
6997     }
6998 
6999     ROLE_GRANT_PAIR *hash_entry= find_role_grant_pair(&username, &hostname,
7000                                                       &rolename);
7001     ACL_USER_BASE *grantee= role_as_user;
7002 
7003     if (has_auth(user, thd->lex))
7004       DBUG_ASSERT(!grantee);
7005     else if (!grantee)
7006       grantee= find_user_exact(hostname.str, username.str);
7007 
7008     if (!grantee && !revoke)
7009     {
7010       LEX_USER user_combo = *user;
7011       user_combo.host = hostname;
7012       user_combo.user = username;
7013 
7014       if (copy_and_check_auth(&user_combo, &user_combo, thd) ||
7015           replace_user_table(thd, tables.user_table(), user_combo, 0,
7016                              false, create_new_user,
7017                              no_auto_create_user))
7018       {
7019         append_user(thd, &wrong_users, &username, &hostname);
7020         result= 1;
7021         continue;
7022       }
7023       grantee= find_user_exact(hostname.str, username.str);
7024 
7025       /* either replace_user_table failed, or we've added the user */
7026       DBUG_ASSERT(grantee);
7027     }
7028 
7029     if (!grantee)
7030     {
7031       append_user(thd, &wrong_users, &username, &hostname);
7032       result= 1;
7033       continue;
7034     }
7035 
7036     if (!revoke)
7037     {
7038       if (hash_entry)
7039       {
7040         // perhaps, updating an existing grant, adding WITH ADMIN OPTION
7041       }
7042       else
7043       {
7044         add_role_user_mapping(grantee, role);
7045 
7046         /*
7047           Check if this grant would cause a cycle. It only needs to be run
7048           if we're granting a role to a role
7049         */
7050         if (role_as_user &&
7051             traverse_role_graph_down(role, 0, 0, 0) == ROLE_CYCLE_FOUND)
7052         {
7053           append_user(thd, &wrong_users, &username, &empty_clex_str);
7054           result= 1;
7055           undo_add_role_user_mapping(grantee, role);
7056           continue;
7057         }
7058       }
7059     }
7060     else
7061     {
7062       /* grant was already removed or never existed */
7063       if (!hash_entry)
7064       {
7065         append_user(thd, &wrong_users, &username, &hostname);
7066         result= 1;
7067         continue;
7068       }
7069       if (thd->lex->with_admin_option)
7070       {
7071         // only revoking an admin option, not the complete grant
7072       }
7073       else
7074       {
7075         /* revoke a role grant */
7076         remove_role_user_mapping(grantee, role);
7077       }
7078     }
7079 
7080     /* write into the roles_mapping table */
7081     /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
7082        Roles_mapping_table instead of TABLE directly. */
7083     if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
7084                                     &username, &hostname, &rolename,
7085                                     thd->lex->with_admin_option,
7086                                     hash_entry, revoke))
7087     {
7088       append_user(thd, &wrong_users, &username, &empty_clex_str);
7089       result= 1;
7090       if (!revoke)
7091       {
7092         /* need to remove the mapping added previously */
7093         undo_add_role_user_mapping(grantee, role);
7094       }
7095       else
7096       {
7097         /* need to restore the mapping deleted previously */
7098         add_role_user_mapping(grantee, role);
7099       }
7100       continue;
7101     }
7102     update_role_mapping(&username, &hostname, &rolename,
7103                         thd->lex->with_admin_option, hash_entry, revoke);
7104 
7105     /*
7106        Only need to propagate grants when granting/revoking a role to/from
7107        a role
7108     */
7109     if (role_as_user && merge_one_role_privileges(role_as_user) == 0)
7110       propagate_role_grants(role_as_user, PRIVS_TO_MERGE::ALL);
7111   }
7112 
7113   mysql_mutex_unlock(&acl_cache->lock);
7114 
7115   if (result)
7116     my_error(revoke ? ER_CANNOT_REVOKE_ROLE : ER_CANNOT_GRANT_ROLE, MYF(0),
7117              rolename.str, wrong_users.c_ptr_safe());
7118   else
7119     result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7120 
7121   mysql_rwlock_unlock(&LOCK_grant);
7122 
7123   DBUG_RETURN(result);
7124 }
7125 
7126 
mysql_grant(THD * thd,const char * db,List<LEX_USER> & list,ulong rights,bool revoke_grant,bool is_proxy)7127 bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
7128                  ulong rights, bool revoke_grant, bool is_proxy)
7129 {
7130   List_iterator <LEX_USER> str_list (list);
7131   LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
7132   char tmp_db[SAFE_NAME_LEN+1];
7133   bool create_new_users=0;
7134   int result;
7135   DBUG_ENTER("mysql_grant");
7136 
7137   if (lower_case_table_names && db)
7138   {
7139     char *end= strnmov(tmp_db,db, sizeof(tmp_db));
7140     if (end >= tmp_db + sizeof(tmp_db))
7141     {
7142       my_error(ER_WRONG_DB_NAME ,MYF(0), db);
7143       DBUG_RETURN(TRUE);
7144     }
7145     my_casedn_str(files_charset_info, tmp_db);
7146     db=tmp_db;
7147   }
7148 
7149   if (is_proxy)
7150   {
7151     DBUG_ASSERT(!db);
7152     proxied_user= str_list++;
7153   }
7154 
7155   Grant_tables tables(Table_user | (is_proxy ? Table_proxies_priv : Table_db),
7156                       TL_WRITE);
7157   if ((result= tables.open_and_lock(thd)))
7158     DBUG_RETURN(result != 1);
7159 
7160   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7161 
7162   if (!revoke_grant)
7163     create_new_users= test_if_create_new_users(thd);
7164 
7165   /* go through users in user_list */
7166   mysql_rwlock_wrlock(&LOCK_grant);
7167   mysql_mutex_lock(&acl_cache->lock);
7168   grant_version++;
7169 
7170   if (proxied_user)
7171   {
7172     if (!(proxied_user= get_current_user(thd, proxied_user, false)))
7173       DBUG_RETURN(TRUE);
7174     DBUG_ASSERT(proxied_user->host.length); // not a Role
7175   }
7176 
7177   while ((tmp_Str = str_list++))
7178   {
7179     if (!(Str= get_current_user(thd, tmp_Str, false)))
7180     {
7181       result= true;
7182       continue;
7183     }
7184 
7185     if (copy_and_check_auth(Str, tmp_Str, thd) ||
7186         replace_user_table(thd, tables.user_table(), *Str,
7187                            (!db ? rights : 0), revoke_grant, create_new_users,
7188                            MY_TEST(thd->variables.sql_mode &
7189                                    MODE_NO_AUTO_CREATE_USER)))
7190       result= true;
7191     else if (db)
7192     {
7193       ulong db_rights= rights & DB_ACLS;
7194       if (db_rights  == rights)
7195       {
7196 	if (replace_db_table(tables.db_table().table(), db, *Str, db_rights,
7197 			     revoke_grant))
7198 	  result= true;
7199       }
7200       else
7201       {
7202 	my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
7203 	result= true;
7204       }
7205     }
7206     else if (is_proxy)
7207     {
7208     /* TODO(cvicentiu) refactor replace_proxies_priv_table to use
7209        Proxies_priv_table  instead of TABLE directly. */
7210       if (tables.proxies_priv_table().no_such_table() ||
7211           replace_proxies_priv_table (thd, tables.proxies_priv_table().table(),
7212                                       Str, proxied_user,
7213                                       rights & GRANT_ACL ? TRUE : FALSE,
7214                                       revoke_grant))
7215         result= true;
7216     }
7217     if (Str->is_role())
7218       propagate_role_grants(find_acl_role(Str->user.str),
7219                             db ? PRIVS_TO_MERGE::DB : PRIVS_TO_MERGE::GLOBAL,
7220                             db);
7221   }
7222   mysql_mutex_unlock(&acl_cache->lock);
7223 
7224   if (!result)
7225   {
7226     result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7227   }
7228 
7229   mysql_rwlock_unlock(&LOCK_grant);
7230 
7231   if (!result)
7232     my_ok(thd);
7233 
7234   DBUG_RETURN(result);
7235 }
7236 
7237 
7238 /* Free grant array if possible */
7239 
grant_free(void)7240 void  grant_free(void)
7241 {
7242   DBUG_ENTER("grant_free");
7243   my_hash_free(&column_priv_hash);
7244   my_hash_free(&proc_priv_hash);
7245   my_hash_free(&func_priv_hash);
7246   my_hash_free(&package_spec_priv_hash);
7247   my_hash_free(&package_body_priv_hash);
7248   free_root(&grant_memroot,MYF(0));
7249   DBUG_VOID_RETURN;
7250 }
7251 
7252 
7253 /**
7254   @brief Initialize structures responsible for table/column-level privilege
7255    checking and load information for them from tables in the 'mysql' database.
7256 
7257   @return Error status
7258     @retval 0 OK
7259     @retval 1 Could not initialize grant subsystem.
7260 */
7261 
grant_init()7262 bool grant_init()
7263 {
7264   THD  *thd;
7265   bool return_val;
7266   DBUG_ENTER("grant_init");
7267 
7268   if (!(thd= new THD(0)))
7269     DBUG_RETURN(1);				/* purecov: deadcode */
7270   thd->thread_stack= (char*) &thd;
7271   thd->store_globals();
7272   return_val=  grant_reload(thd);
7273   delete thd;
7274   DBUG_RETURN(return_val);
7275 }
7276 
7277 
7278 /**
7279   @brief Initialize structures responsible for table/column-level privilege
7280     checking and load information about grants from open privilege tables.
7281 
7282   @param thd Current thread
7283   @param tables List containing open "mysql.tables_priv" and
7284     "mysql.columns_priv" tables.
7285 
7286   @see grant_reload
7287 
7288   @return Error state
7289     @retval FALSE Success
7290     @retval TRUE Error
7291 */
7292 
grant_load(THD * thd,const Tables_priv_table & tables_priv,const Columns_priv_table & columns_priv,const Procs_priv_table & procs_priv)7293 static bool grant_load(THD *thd,
7294                        const Tables_priv_table& tables_priv,
7295                        const Columns_priv_table& columns_priv,
7296                        const Procs_priv_table& procs_priv)
7297 {
7298   bool return_val= 1;
7299   TABLE *t_table, *c_table, *p_table;
7300   bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
7301   MEM_ROOT *save_mem_root= thd->mem_root;
7302   sql_mode_t old_sql_mode= thd->variables.sql_mode;
7303   DBUG_ENTER("grant_load");
7304 
7305   thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
7306 
7307   (void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
7308                       0,0,0, (my_hash_get_key) get_grant_table,
7309                       (my_hash_free_key) free_grant_table,0);
7310   (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
7311                       0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7312   (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
7313                       0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7314   (void) my_hash_init(&package_spec_priv_hash, &my_charset_utf8_bin,
7315                       0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7316   (void) my_hash_init(&package_body_priv_hash, &my_charset_utf8_bin,
7317                       0,0,0, (my_hash_get_key) get_grant_table, 0,0);
7318   init_sql_alloc(&grant_memroot, "GRANT", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
7319 
7320   t_table= tables_priv.table();
7321   c_table= columns_priv.table();
7322   p_table= procs_priv.table(); // this can be NULL
7323 
7324   if (t_table->file->ha_index_init(0, 1))
7325     goto end_index_init;
7326 
7327   t_table->use_all_columns();
7328   c_table->use_all_columns();
7329 
7330   thd->mem_root= &grant_memroot;
7331 
7332   if (!t_table->file->ha_index_first(t_table->record[0]))
7333   {
7334     do
7335     {
7336       GRANT_TABLE *mem_check;
7337       /* TODO(cvicentiu) convert this to use tables_priv and columns_priv. */
7338       if (!(mem_check= new (&grant_memroot) GRANT_TABLE(t_table, c_table)))
7339       {
7340 	/* This could only happen if we are out memory */
7341 	goto end_unlock;
7342       }
7343 
7344       if (check_no_resolve)
7345       {
7346 	if (hostname_requires_resolving(mem_check->host.hostname))
7347 	{
7348           sql_print_warning("'tables_priv' entry '%s %s@%s' "
7349                             "ignored in --skip-name-resolve mode.",
7350                             mem_check->tname,
7351                             safe_str(mem_check->user),
7352                             safe_str(mem_check->host.hostname));
7353 	  continue;
7354 	}
7355       }
7356 
7357       if (! mem_check->ok())
7358 	delete mem_check;
7359       else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
7360       {
7361 	delete mem_check;
7362 	goto end_unlock;
7363       }
7364     }
7365     while (!t_table->file->ha_index_next(t_table->record[0]));
7366   }
7367 
7368   return_val= 0;
7369 
7370   if (p_table)
7371   {
7372     if (p_table->file->ha_index_init(0, 1))
7373       goto end_unlock;
7374 
7375     p_table->use_all_columns();
7376 
7377     if (!p_table->file->ha_index_first(p_table->record[0]))
7378     {
7379       do
7380       {
7381         GRANT_NAME *mem_check;
7382         HASH *hash;
7383         if (!(mem_check= new (&grant_memroot) GRANT_NAME(p_table, TRUE)))
7384         {
7385           /* This could only happen if we are out memory */
7386           goto end_unlock_p;
7387         }
7388 
7389         if (check_no_resolve)
7390         {
7391           if (hostname_requires_resolving(mem_check->host.hostname))
7392           {
7393             sql_print_warning("'procs_priv' entry '%s %s@%s' "
7394                               "ignored in --skip-name-resolve mode.",
7395                               mem_check->tname, mem_check->user,
7396                               safe_str(mem_check->host.hostname));
7397             continue;
7398           }
7399         }
7400         stored_procedure_type type= (stored_procedure_type)procs_priv.routine_type()->val_int();
7401         const Sp_handler *sph= Sp_handler::handler(type);
7402         if (!sph || !(hash= sph->get_priv_hash()))
7403         {
7404           sql_print_warning("'procs_priv' entry '%s' "
7405                             "ignored, bad routine type",
7406                             mem_check->tname);
7407           continue;
7408         }
7409 
7410         mem_check->privs= fix_rights_for_procedure(mem_check->privs);
7411         mem_check->init_privs= mem_check->privs;
7412         if (! mem_check->ok())
7413           delete mem_check;
7414         else if (my_hash_insert(hash, (uchar*) mem_check))
7415         {
7416           delete mem_check;
7417           goto end_unlock_p;
7418         }
7419       }
7420       while (!p_table->file->ha_index_next(p_table->record[0]));
7421     }
7422   }
7423 
7424 end_unlock_p:
7425   if (p_table)
7426     p_table->file->ha_index_end();
7427 end_unlock:
7428   t_table->file->ha_index_end();
7429   thd->mem_root= save_mem_root;
7430 end_index_init:
7431   thd->variables.sql_mode= old_sql_mode;
7432   DBUG_RETURN(return_val);
7433 }
7434 
propagate_role_grants_action(void * role_ptr,void * ptr)7435 static my_bool propagate_role_grants_action(void *role_ptr,
7436                                             void *ptr __attribute__((unused)))
7437 {
7438   ACL_ROLE *role= static_cast<ACL_ROLE *>(role_ptr);
7439   if (role->counter)
7440     return 0;
7441 
7442   mysql_mutex_assert_owner(&acl_cache->lock);
7443   PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
7444   traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
7445   return 0;
7446 }
7447 
7448 
7449 /**
7450   @brief Reload information about table and column level privileges if possible
7451 
7452   @param thd Current thread
7453 
7454   Locked tables are checked by acl_reload() and doesn't have to be checked
7455   in this call.
7456   This function is also used for initialization of structures responsible
7457   for table/column-level privilege checking.
7458 
7459   @return Error state
7460     @retval FALSE Success
7461     @retval TRUE  Error
7462 */
7463 
grant_reload(THD * thd)7464 bool grant_reload(THD *thd)
7465 {
7466   HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
7467   HASH old_package_spec_priv_hash, old_package_body_priv_hash;
7468   MEM_ROOT old_mem;
7469   int result;
7470   DBUG_ENTER("grant_reload");
7471 
7472   /*
7473     To avoid deadlocks we should obtain table locks before
7474     obtaining LOCK_grant rwlock.
7475   */
7476 
7477   Grant_tables tables(Table_tables_priv | Table_columns_priv| Table_procs_priv,
7478                       TL_READ);
7479   if ((result= tables.open_and_lock(thd)))
7480     DBUG_RETURN(result != 1);
7481 
7482   mysql_rwlock_wrlock(&LOCK_grant);
7483   grant_version++;
7484   old_column_priv_hash= column_priv_hash;
7485   old_proc_priv_hash= proc_priv_hash;
7486   old_func_priv_hash= func_priv_hash;
7487   old_package_spec_priv_hash= package_spec_priv_hash;
7488   old_package_body_priv_hash= package_body_priv_hash;
7489 
7490   /*
7491     Create a new memory pool but save the current memory pool to make an undo
7492     opertion possible in case of failure.
7493   */
7494   old_mem= grant_memroot;
7495 
7496   if ((result= grant_load(thd,
7497                           tables.tables_priv_table(),
7498                           tables.columns_priv_table(),
7499                           tables.procs_priv_table())))
7500   {						// Error. Revert to old hash
7501     DBUG_PRINT("error",("Reverting to old privileges"));
7502     grant_free();				/* purecov: deadcode */
7503     column_priv_hash= old_column_priv_hash;	/* purecov: deadcode */
7504     proc_priv_hash= old_proc_priv_hash;
7505     func_priv_hash= old_func_priv_hash;
7506     package_spec_priv_hash= old_package_spec_priv_hash;
7507     package_body_priv_hash= old_package_body_priv_hash;
7508     grant_memroot= old_mem;                     /* purecov: deadcode */
7509   }
7510   else
7511   {
7512     my_hash_free(&old_column_priv_hash);
7513     my_hash_free(&old_proc_priv_hash);
7514     my_hash_free(&old_func_priv_hash);
7515     my_hash_free(&old_package_spec_priv_hash);
7516     my_hash_free(&old_package_body_priv_hash);
7517     free_root(&old_mem,MYF(0));
7518   }
7519 
7520   mysql_mutex_lock(&acl_cache->lock);
7521   my_hash_iterate(&acl_roles, propagate_role_grants_action, NULL);
7522   mysql_mutex_unlock(&acl_cache->lock);
7523 
7524   mysql_rwlock_unlock(&LOCK_grant);
7525 
7526   close_mysql_tables(thd);
7527 
7528   DBUG_RETURN(result);
7529 }
7530 
7531 
7532 /**
7533   @brief Check table level grants
7534 
7535   @param thd          Thread handler
7536   @param want_access  Bits of privileges user needs to have.
7537   @param tables       List of tables to check. The user should have
7538                       'want_access' to all tables in list.
7539   @param any_combination_will_do TRUE if it's enough to have any privilege for
7540     any combination of the table columns.
7541   @param number       Check at most this number of tables.
7542   @param no_errors    TRUE if no error should be sent directly to the client.
7543 
7544   If table->grant.want_privilege != 0 then the requested privileges where
7545   in the set of COL_ACLS but access was not granted on the table level. As
7546   a consequence an extra check of column privileges is required.
7547 
7548   Specifically if this function returns FALSE the user has some kind of
7549   privilege on a combination of columns in each table.
7550 
7551   This function is usually preceeded by check_access which establish the
7552   User-, Db- and Host access rights.
7553 
7554   @see check_access
7555   @see check_table_access
7556 
7557   @note
7558      This functions assumes that either number of tables to be inspected
7559      by it is limited explicitly (i.e. is is not UINT_MAX) or table list
7560      used and thd->lex->query_tables_own_last value correspond to each
7561      other (the latter should be either 0 or point to next_global member
7562      of one of elements of this table list).
7563 
7564      We delay locking of LOCK_grant until we really need it as we assume that
7565      most privileges be resolved with user or db level accesses.
7566 
7567    @return Access status
7568      @retval FALSE Access granted; But column privileges might need to be
7569       checked.
7570      @retval TRUE The user did not have the requested privileges on any of the
7571       tables.
7572 
7573 */
7574 
check_grant(THD * thd,ulong want_access,TABLE_LIST * tables,bool any_combination_will_do,uint number,bool no_errors)7575 bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
7576                  bool any_combination_will_do, uint number, bool no_errors)
7577 {
7578   TABLE_LIST *tl;
7579   TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
7580   Security_context *sctx= thd->security_ctx;
7581   uint i;
7582   ulong original_want_access= want_access;
7583   bool locked= 0;
7584   GRANT_TABLE *grant_table;
7585   GRANT_TABLE *grant_table_role= NULL;
7586   DBUG_ENTER("check_grant");
7587   DBUG_ASSERT(number > 0);
7588 
7589   /*
7590     Walk through the list of tables that belong to the query and save the
7591     requested access (orig_want_privilege) to be able to use it when
7592     checking access rights to the underlying tables of a view. Our grant
7593     system gradually eliminates checked bits from want_privilege and thus
7594     after all checks are done we can no longer use it.
7595     The check that first_not_own_table is not reached is for the case when
7596     the given table list refers to the list for prelocking (contains tables
7597     of other queries). For simple queries first_not_own_table is 0.
7598   */
7599   for (i= 0, tl= tables;
7600        i < number  && tl != first_not_own_table;
7601        tl= tl->next_global, i++)
7602   {
7603     /*
7604       Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
7605       It will be checked during making view.
7606     */
7607     tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
7608   }
7609   number= i;
7610 
7611   for (tl= tables; number-- ; tl= tl->next_global)
7612   {
7613     TABLE_LIST *const t_ref=
7614       tl->correspondent_table ? tl->correspondent_table : tl;
7615     sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx;
7616     ulong orig_want_access= original_want_access;
7617 
7618     /*
7619       If sequence is used as part of NEXT VALUE, PREVIOUS VALUE or SELECT,
7620       we need to modify the requested access rights depending on how the
7621       sequence is used.
7622     */
7623     if (t_ref->sequence &&
7624         !(want_access & ~(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL)))
7625     {
7626       /*
7627         We want to have either SELECT or INSERT rights to sequences depending
7628         on how they are accessed
7629       */
7630       orig_want_access= ((t_ref->lock_type == TL_WRITE_ALLOW_WRITE) ?
7631                          INSERT_ACL : SELECT_ACL);
7632     }
7633 
7634     if (tl->with || !tl->db.str ||
7635         (tl->select_lex &&
7636          (tl->with= tl->select_lex->find_table_def_in_with_clauses(tl))))
7637       continue;
7638 
7639     const ACL_internal_table_access *access=
7640       get_cached_table_access(&t_ref->grant.m_internal,
7641                               t_ref->get_db_name(),
7642                               t_ref->get_table_name());
7643 
7644     if (access)
7645     {
7646       switch(access->check(orig_want_access, &t_ref->grant.privilege))
7647       {
7648       case ACL_INTERNAL_ACCESS_GRANTED:
7649         /*
7650           Currently,
7651           -  the information_schema does not subclass ACL_internal_table_access,
7652           there are no per table privilege checks for I_S,
7653           - the performance schema does use per tables checks, but at most
7654           returns 'CHECK_GRANT', and never 'ACCESS_GRANTED'.
7655           so this branch is not used.
7656         */
7657         DBUG_ASSERT(0);
7658       case ACL_INTERNAL_ACCESS_DENIED:
7659         goto err;
7660       case ACL_INTERNAL_ACCESS_CHECK_GRANT:
7661         break;
7662       }
7663     }
7664 
7665     want_access= orig_want_access;
7666     want_access&= ~sctx->master_access;
7667     if (!want_access)
7668       continue;                                 // ok
7669 
7670     if (!(~t_ref->grant.privilege & want_access) ||
7671         t_ref->is_anonymous_derived_table() || t_ref->schema_table)
7672     {
7673       /*
7674         It is subquery in the FROM clause. VIEW set t_ref->derived after
7675         table opening, but this function always called before table opening.
7676 
7677         NOTE: is_derived() can't be used here because subquery in this case
7678         the FROM clase (derived tables) can be not be marked yet.
7679       */
7680       if (t_ref->is_anonymous_derived_table() || t_ref->schema_table)
7681       {
7682         /*
7683           If it's a temporary table created for a subquery in the FROM
7684           clause, or an INFORMATION_SCHEMA table, drop the request for
7685           a privilege.
7686         */
7687         t_ref->grant.want_privilege= 0;
7688       }
7689       continue;
7690     }
7691 
7692     if (is_temporary_table(t_ref))
7693     {
7694       /*
7695         If this table list element corresponds to a pre-opened temporary
7696         table skip checking of all relevant table-level privileges for it.
7697         Note that during creation of temporary table we still need to check
7698         if user has CREATE_TMP_ACL.
7699       */
7700       t_ref->grant.privilege|= TMP_TABLE_ACLS;
7701       t_ref->grant.want_privilege= 0;
7702       continue;
7703     }
7704 
7705     if (!locked)
7706     {
7707       locked= 1;
7708       mysql_rwlock_rdlock(&LOCK_grant);
7709     }
7710 
7711     grant_table= table_hash_search(sctx->host, sctx->ip,
7712                                    t_ref->get_db_name(),
7713                                    sctx->priv_user,
7714                                    t_ref->get_table_name(),
7715                                    FALSE);
7716     if (sctx->priv_role[0])
7717       grant_table_role= table_hash_search("", NULL, t_ref->get_db_name(),
7718                                           sctx->priv_role,
7719                                           t_ref->get_table_name(),
7720                                           TRUE);
7721 
7722     if (!grant_table && !grant_table_role)
7723     {
7724       want_access&= ~t_ref->grant.privilege;
7725       goto err;					// No grants
7726     }
7727 
7728     /*
7729       For SHOW COLUMNS, SHOW INDEX it is enough to have some
7730       privileges on any column combination on the table.
7731     */
7732     if (any_combination_will_do)
7733       continue;
7734 
7735     t_ref->grant.grant_table_user= grant_table; // Remember for column test
7736     t_ref->grant.grant_table_role= grant_table_role;
7737     t_ref->grant.version= grant_version;
7738     t_ref->grant.privilege|= grant_table ? grant_table->privs : 0;
7739     t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : 0;
7740     t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege);
7741 
7742     if (!(~t_ref->grant.privilege & want_access))
7743       continue;
7744 
7745     if ((want_access&= ~((grant_table ? grant_table->cols : 0) |
7746                         (grant_table_role ? grant_table_role->cols : 0) |
7747                         t_ref->grant.privilege)))
7748     {
7749       goto err;                                 // impossible
7750     }
7751   }
7752   if (locked)
7753     mysql_rwlock_unlock(&LOCK_grant);
7754   DBUG_RETURN(FALSE);
7755 
7756 err:
7757   if (locked)
7758     mysql_rwlock_unlock(&LOCK_grant);
7759   if (!no_errors)				// Not a silent skip of table
7760   {
7761     char command[128];
7762     get_privilege_desc(command, sizeof(command), want_access);
7763     status_var_increment(thd->status_var.access_denied_errors);
7764 
7765     my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
7766              command,
7767              sctx->priv_user,
7768              sctx->host_or_ip,
7769              tl ? tl->get_table_name() : "unknown");
7770   }
7771   DBUG_RETURN(TRUE);
7772 }
7773 
7774 
check_grant_column_int(GRANT_TABLE * grant_table,const char * name,uint length,ulong * want_access)7775 static void check_grant_column_int(GRANT_TABLE *grant_table, const char *name,
7776                                    uint length, ulong *want_access)
7777 {
7778   if (grant_table)
7779   {
7780     *want_access&= ~grant_table->privs;
7781     if (*want_access & grant_table->cols)
7782     {
7783       GRANT_COLUMN *grant_column= column_hash_search(grant_table, name, length);
7784       if (grant_column)
7785         *want_access&= ~grant_column->rights;
7786     }
7787   }
7788 }
7789 
7790 /*
7791   Check column rights in given security context
7792 
7793   SYNOPSIS
7794     check_grant_column()
7795     thd                  thread handler
7796     grant                grant information structure
7797     db_name              db name
7798     table_name           table  name
7799     name                 column name
7800     length               column name length
7801     sctx                 security context
7802 
7803   RETURN
7804     FALSE OK
7805     TRUE  access denied
7806 */
7807 
check_grant_column(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * name,size_t length,Security_context * sctx)7808 bool check_grant_column(THD *thd, GRANT_INFO *grant,
7809 			const char *db_name, const char *table_name,
7810 			const char *name, size_t length,  Security_context *sctx)
7811 {
7812   ulong want_access= grant->want_privilege & ~grant->privilege;
7813   DBUG_ENTER("check_grant_column");
7814   DBUG_PRINT("enter", ("table: %s  want_access: %lu", table_name, want_access));
7815 
7816   if (!want_access)
7817     DBUG_RETURN(0);				// Already checked
7818 
7819   mysql_rwlock_rdlock(&LOCK_grant);
7820 
7821   /* reload table if someone has modified any grants */
7822 
7823   if (grant->version != grant_version)
7824   {
7825     grant->grant_table_user=
7826       table_hash_search(sctx->host, sctx->ip, db_name,
7827 			sctx->priv_user,
7828 			table_name, 0);         /* purecov: inspected */
7829     grant->grant_table_role=
7830       sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
7831                                              sctx->priv_role,
7832                                              table_name, TRUE) : NULL;
7833     grant->version= grant_version;		/* purecov: inspected */
7834   }
7835 
7836   check_grant_column_int(grant->grant_table_user, name, (uint)length,
7837                          &want_access);
7838   check_grant_column_int(grant->grant_table_role, name, (uint)length,
7839                          &want_access);
7840 
7841   mysql_rwlock_unlock(&LOCK_grant);
7842   if (!want_access)
7843     DBUG_RETURN(0);
7844 
7845   char command[128];
7846   get_privilege_desc(command, sizeof(command), want_access);
7847   /* TODO perhaps error should print current rolename aswell */
7848   my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), command, sctx->priv_user,
7849            sctx->host_or_ip, name, table_name);
7850   DBUG_RETURN(1);
7851 }
7852 
7853 
7854 /*
7855   Check the access right to a column depending on the type of table.
7856 
7857   SYNOPSIS
7858     check_column_grant_in_table_ref()
7859     thd              thread handler
7860     table_ref        table reference where to check the field
7861     name             name of field to check
7862     length           length of name
7863     fld              use fld object to check invisibility when it is
7864                      not 0, not_found_field, view_ref_found
7865 
7866   DESCRIPTION
7867     Check the access rights to a column depending on the type of table
7868     reference where the column is checked. The function provides a
7869     generic interface to check column access rights that hides the
7870     heterogeneity of the column representation - whether it is a view
7871     or a stored table colum.
7872 
7873   RETURN
7874     FALSE OK
7875     TRUE  access denied
7876 */
7877 
check_column_grant_in_table_ref(THD * thd,TABLE_LIST * table_ref,const char * name,size_t length,Field * fld)7878 bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
7879                                      const char *name, size_t length,
7880                                      Field *fld)
7881 {
7882   GRANT_INFO *grant;
7883   const char *db_name;
7884   const char *table_name;
7885   Security_context *sctx= table_ref->security_ctx ?
7886                           table_ref->security_ctx : thd->security_ctx;
7887   if (fld && fld != not_found_field && fld != view_ref_found
7888           && fld->invisible >= INVISIBLE_SYSTEM)
7889       return false;
7890 
7891   if (table_ref->view || table_ref->field_translation)
7892   {
7893     /* View or derived information schema table. */
7894     ulong view_privs;
7895     grant= &(table_ref->grant);
7896     db_name= table_ref->view_db.str;
7897     table_name= table_ref->view_name.str;
7898     if (table_ref->belong_to_view &&
7899         thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
7900     {
7901       view_privs= get_column_grant(thd, grant, db_name, table_name, name);
7902       if (view_privs & VIEW_ANY_ACL)
7903       {
7904         table_ref->belong_to_view->allowed_show= TRUE;
7905         return FALSE;
7906       }
7907       table_ref->belong_to_view->allowed_show= FALSE;
7908       my_message(ER_VIEW_NO_EXPLAIN, ER_THD(thd, ER_VIEW_NO_EXPLAIN), MYF(0));
7909       return TRUE;
7910     }
7911   }
7912   else
7913   {
7914     /* Normal or temporary table. */
7915     TABLE *table= table_ref->table;
7916     grant= &(table->grant);
7917     db_name= table->s->db.str;
7918     table_name= table->s->table_name.str;
7919   }
7920 
7921   if (grant->want_privilege)
7922     return check_grant_column(thd, grant, db_name, table_name, name,
7923                               length, sctx);
7924   else
7925     return FALSE;
7926 
7927 }
7928 
7929 
7930 /**
7931   @brief check if a query can access a set of columns
7932 
7933   @param  thd  the current thread
7934   @param  want_access_arg  the privileges requested
7935   @param  fields an iterator over the fields of a table reference.
7936   @return Operation status
7937     @retval 0 Success
7938     @retval 1 Falure
7939   @details This function walks over the columns of a table reference
7940    The columns may originate from different tables, depending on the kind of
7941    table reference, e.g. join, view.
7942    For each table it will retrieve the grant information and will use it
7943    to check the required access privileges for the fields requested from it.
7944 */
check_grant_all_columns(THD * thd,ulong want_access_arg,Field_iterator_table_ref * fields)7945 bool check_grant_all_columns(THD *thd, ulong want_access_arg,
7946                              Field_iterator_table_ref *fields)
7947 {
7948   Security_context *sctx= thd->security_ctx;
7949   ulong UNINIT_VAR(want_access);
7950   const char *table_name= NULL;
7951   const char* db_name;
7952   GRANT_INFO *grant;
7953   GRANT_TABLE *UNINIT_VAR(grant_table);
7954   GRANT_TABLE *UNINIT_VAR(grant_table_role);
7955   /*
7956      Flag that gets set if privilege checking has to be performed on column
7957      level.
7958   */
7959   bool using_column_privileges= FALSE;
7960 
7961   mysql_rwlock_rdlock(&LOCK_grant);
7962 
7963   for (; !fields->end_of_fields(); fields->next())
7964   {
7965     if (fields->field() &&
7966         fields->field()->invisible >= INVISIBLE_SYSTEM)
7967       continue;
7968     LEX_CSTRING *field_name= fields->name();
7969 
7970     if (table_name != fields->get_table_name())
7971     {
7972       table_name= fields->get_table_name();
7973       db_name= fields->get_db_name();
7974       grant= fields->grant();
7975       /* get a fresh one for each table */
7976       want_access= want_access_arg & ~grant->privilege;
7977       if (want_access)
7978       {
7979         /* reload table if someone has modified any grants */
7980         if (grant->version != grant_version)
7981         {
7982           grant->grant_table_user=
7983             table_hash_search(sctx->host, sctx->ip, db_name,
7984                               sctx->priv_user,
7985                               table_name, 0);	/* purecov: inspected */
7986           grant->grant_table_role=
7987             sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
7988                                                    sctx->priv_role,
7989                                                    table_name, TRUE) : NULL;
7990           grant->version= grant_version;	/* purecov: inspected */
7991         }
7992 
7993         grant_table= grant->grant_table_user;
7994         grant_table_role= grant->grant_table_role;
7995         DBUG_ASSERT (grant_table || grant_table_role);
7996       }
7997     }
7998 
7999     if (want_access)
8000     {
8001       ulong have_access= 0;
8002       if (grant_table)
8003       {
8004         GRANT_COLUMN *grant_column=
8005           column_hash_search(grant_table, field_name->str, field_name->length);
8006         if (grant_column)
8007           have_access= grant_column->rights;
8008       }
8009       if (grant_table_role)
8010       {
8011         GRANT_COLUMN *grant_column=
8012           column_hash_search(grant_table_role, field_name->str,
8013                              field_name->length);
8014         if (grant_column)
8015           have_access|= grant_column->rights;
8016       }
8017 
8018       if (have_access)
8019         using_column_privileges= TRUE;
8020       if (want_access & ~have_access)
8021         goto err;
8022     }
8023   }
8024   mysql_rwlock_unlock(&LOCK_grant);
8025   return 0;
8026 
8027 err:
8028   mysql_rwlock_unlock(&LOCK_grant);
8029 
8030   char command[128];
8031   get_privilege_desc(command, sizeof(command), want_access);
8032   /*
8033     Do not give an error message listing a column name unless the user has
8034     privilege to see all columns.
8035   */
8036   if (using_column_privileges)
8037     my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
8038              command, sctx->priv_user,
8039              sctx->host_or_ip, table_name);
8040   else
8041     my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
8042              command,
8043              sctx->priv_user,
8044              sctx->host_or_ip,
8045              fields->name()->str,
8046              table_name);
8047   return 1;
8048 }
8049 
8050 
check_grant_db_routine(THD * thd,const char * db,HASH * hash)8051 static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
8052 {
8053   Security_context *sctx= thd->security_ctx;
8054 
8055   for (uint idx= 0; idx < hash->records; ++idx)
8056   {
8057     GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
8058 
8059     if (strcmp(item->user, sctx->priv_user) == 0 &&
8060         strcmp(item->db, db) == 0 &&
8061         compare_hostname(&item->host, sctx->host, sctx->ip))
8062     {
8063       return FALSE;
8064     }
8065     if (sctx->priv_role[0] && strcmp(item->user, sctx->priv_role) == 0 &&
8066         strcmp(item->db, db) == 0 &&
8067         (!item->host.hostname || !item->host.hostname[0]))
8068     {
8069       return FALSE; /* Found current role match */
8070     }
8071   }
8072 
8073   return TRUE;
8074 }
8075 
8076 
8077 /*
8078   Check if a user has the right to access a database
8079   Access is accepted if he has a grant for any table/routine in the database
8080   Return 1 if access is denied
8081 */
8082 
check_grant_db(THD * thd,const char * db)8083 bool check_grant_db(THD *thd, const char *db)
8084 {
8085   Security_context *sctx= thd->security_ctx;
8086   char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end;
8087   char helping2 [SAFE_NAME_LEN + USERNAME_LENGTH+2], *tmp_db;
8088   uint len, UNINIT_VAR(len2);
8089   bool error= TRUE;
8090 
8091   tmp_db= strmov(helping, sctx->priv_user) + 1;
8092   end= strnmov(tmp_db, db, helping + sizeof(helping) - tmp_db);
8093 
8094   if (end >= helping + sizeof(helping)) // db name was truncated
8095     return 1;                           // no privileges for an invalid db name
8096 
8097   if (lower_case_table_names)
8098   {
8099     end = tmp_db + my_casedn_str(files_charset_info, tmp_db);
8100     db=tmp_db;
8101   }
8102 
8103   len= (uint) (end - helping) + 1;
8104 
8105   /*
8106      If a role is set, we need to check for privileges here as well.
8107   */
8108   if (sctx->priv_role[0])
8109   {
8110     end= strmov(helping2, sctx->priv_role) + 1;
8111     end= strnmov(end, db, helping2 + sizeof(helping2) - end);
8112     len2= (uint) (end - helping2) + 1;
8113   }
8114 
8115 
8116   mysql_rwlock_rdlock(&LOCK_grant);
8117 
8118   for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
8119   {
8120     GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
8121                                                              idx);
8122     if (len < grant_table->key_length &&
8123         !memcmp(grant_table->hash_key, helping, len) &&
8124         compare_hostname(&grant_table->host, sctx->host, sctx->ip))
8125     {
8126       error= FALSE; /* Found match. */
8127       break;
8128     }
8129     if (sctx->priv_role[0] &&
8130         len2 < grant_table->key_length &&
8131         !memcmp(grant_table->hash_key, helping2, len2) &&
8132         (!grant_table->host.hostname || !grant_table->host.hostname[0]))
8133     {
8134       error= FALSE; /* Found role match */
8135       break;
8136     }
8137   }
8138 
8139   if (error)
8140     error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
8141            check_grant_db_routine(thd, db, &func_priv_hash) &&
8142            check_grant_db_routine(thd, db, &package_spec_priv_hash) &&
8143            check_grant_db_routine(thd, db, &package_body_priv_hash);
8144 
8145   mysql_rwlock_unlock(&LOCK_grant);
8146 
8147   return error;
8148 }
8149 
8150 
8151 /****************************************************************************
8152   Check routine level grants
8153 
8154   SYNPOSIS
8155    bool check_grant_routine()
8156    thd		Thread handler
8157    want_access  Bits of privileges user needs to have
8158    procs	List of routines to check. The user should have 'want_access'
8159    sph          SP handler
8160    no_errors	If 0 then we write an error. The error is sent directly to
8161 		the client
8162 
8163    RETURN
8164      0  ok
8165      1  Error: User did not have the requested privielges
8166 ****************************************************************************/
8167 
check_grant_routine(THD * thd,ulong want_access,TABLE_LIST * procs,const Sp_handler * sph,bool no_errors)8168 bool check_grant_routine(THD *thd, ulong want_access,
8169 			 TABLE_LIST *procs, const Sp_handler *sph,
8170 			 bool no_errors)
8171 {
8172   TABLE_LIST *table;
8173   Security_context *sctx= thd->security_ctx;
8174   char *user= sctx->priv_user;
8175   char *host= sctx->priv_host;
8176   char *role= sctx->priv_role;
8177   DBUG_ENTER("check_grant_routine");
8178 
8179   want_access&= ~sctx->master_access;
8180   if (!want_access)
8181     DBUG_RETURN(0);                             // ok
8182 
8183   mysql_rwlock_rdlock(&LOCK_grant);
8184   for (table= procs; table; table= table->next_global)
8185   {
8186     GRANT_NAME *grant_proc;
8187     if ((grant_proc= routine_hash_search(host, sctx->ip, table->db.str, user,
8188                                          table->table_name.str, sph, 0)))
8189       table->grant.privilege|= grant_proc->privs;
8190     if (role[0]) /* current role set check */
8191     {
8192       if ((grant_proc= routine_hash_search("", NULL, table->db.str, role,
8193                                            table->table_name.str, sph, 0)))
8194       table->grant.privilege|= grant_proc->privs;
8195     }
8196 
8197     if (want_access & ~table->grant.privilege)
8198     {
8199       want_access &= ~table->grant.privilege;
8200       goto err;
8201     }
8202   }
8203   mysql_rwlock_unlock(&LOCK_grant);
8204   DBUG_RETURN(0);
8205 err:
8206   mysql_rwlock_unlock(&LOCK_grant);
8207   if (!no_errors)
8208   {
8209     char buff[1024];
8210     const char *command="";
8211     if (table)
8212       strxmov(buff, table->db.str, ".", table->table_name.str, NullS);
8213     if (want_access & EXECUTE_ACL)
8214       command= "execute";
8215     else if (want_access & ALTER_PROC_ACL)
8216       command= "alter routine";
8217     else if (want_access & GRANT_ACL)
8218       command= "grant";
8219     my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
8220              command, user, host, table ? buff : "unknown");
8221   }
8222   DBUG_RETURN(1);
8223 }
8224 
8225 
8226 /*
8227   Check if routine has any of the
8228   routine level grants
8229 
8230   SYNPOSIS
8231    bool    check_routine_level_acl()
8232    thd	        Thread handler
8233    db           Database name
8234    name         Routine name
8235 
8236   RETURN
8237    0            Ok
8238    1            error
8239 */
8240 
check_routine_level_acl(THD * thd,const char * db,const char * name,const Sp_handler * sph)8241 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
8242                              const Sp_handler *sph)
8243 {
8244   bool no_routine_acl= 1;
8245   GRANT_NAME *grant_proc;
8246   Security_context *sctx= thd->security_ctx;
8247   mysql_rwlock_rdlock(&LOCK_grant);
8248   if ((grant_proc= routine_hash_search(sctx->priv_host,
8249                                        sctx->ip, db,
8250                                        sctx->priv_user,
8251                                        name, sph, 0)))
8252     no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
8253 
8254   if (no_routine_acl && sctx->priv_role[0]) /* current set role check */
8255   {
8256     if ((grant_proc= routine_hash_search("",
8257                                          NULL, db,
8258                                          sctx->priv_role,
8259                                          name, sph, 0)))
8260       no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
8261   }
8262   mysql_rwlock_unlock(&LOCK_grant);
8263   return no_routine_acl;
8264 }
8265 
8266 
8267 /*****************************************************************************
8268   Functions to retrieve the grant for a table/column  (for SHOW functions)
8269 *****************************************************************************/
8270 
get_table_grant(THD * thd,TABLE_LIST * table)8271 ulong get_table_grant(THD *thd, TABLE_LIST *table)
8272 {
8273   ulong privilege;
8274   Security_context *sctx= thd->security_ctx;
8275   const char *db = table->db.str ? table->db.str : thd->db.str;
8276   GRANT_TABLE *grant_table;
8277   GRANT_TABLE *grant_table_role= NULL;
8278 
8279   mysql_rwlock_rdlock(&LOCK_grant);
8280 #ifdef EMBEDDED_LIBRARY
8281   grant_table= NULL;
8282   grant_table_role= NULL;
8283 #else
8284   grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
8285 				 table->table_name.str, 0);
8286   if (sctx->priv_role[0])
8287     grant_table_role= table_hash_search("", "", db, sctx->priv_role,
8288                                         table->table_name.str, 0);
8289 #endif
8290   table->grant.grant_table_user= grant_table; // Remember for column test
8291   table->grant.grant_table_role= grant_table_role;
8292   table->grant.version=grant_version;
8293   if (grant_table)
8294     table->grant.privilege|= grant_table->privs;
8295   if (grant_table_role)
8296     table->grant.privilege|= grant_table_role->privs;
8297   privilege= table->grant.privilege;
8298   mysql_rwlock_unlock(&LOCK_grant);
8299   return privilege;
8300 }
8301 
8302 
8303 /*
8304   Determine the access priviliges for a field.
8305 
8306   SYNOPSIS
8307     get_column_grant()
8308     thd         thread handler
8309     grant       grants table descriptor
8310     db_name     name of database that the field belongs to
8311     table_name  name of table that the field belongs to
8312     field_name  name of field
8313 
8314   DESCRIPTION
8315     The procedure may also modify: grant->grant_table and grant->version.
8316 
8317   RETURN
8318     The access priviliges for the field db_name.table_name.field_name
8319 */
8320 
get_column_grant(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * field_name)8321 ulong get_column_grant(THD *thd, GRANT_INFO *grant,
8322                        const char *db_name, const char *table_name,
8323                        const char *field_name)
8324 {
8325   GRANT_TABLE *grant_table;
8326   GRANT_TABLE *grant_table_role;
8327   GRANT_COLUMN *grant_column;
8328   ulong priv= 0;
8329 
8330   mysql_rwlock_rdlock(&LOCK_grant);
8331   /* reload table if someone has modified any grants */
8332   if (grant->version != grant_version)
8333   {
8334     Security_context *sctx= thd->security_ctx;
8335     grant->grant_table_user=
8336       table_hash_search(sctx->host, sctx->ip,
8337                         db_name, sctx->priv_user,
8338                         table_name, 0);         /* purecov: inspected */
8339     grant->grant_table_role=
8340       sctx->priv_role[0] ? table_hash_search("", "", db_name,
8341                                              sctx->priv_role,
8342                                              table_name, TRUE) : NULL;
8343     grant->version= grant_version;              /* purecov: inspected */
8344   }
8345 
8346   grant_table= grant->grant_table_user;
8347   grant_table_role= grant->grant_table_role;
8348 
8349   if (!grant_table && !grant_table_role)
8350     priv= grant->privilege;
8351   else
8352   {
8353     if (grant_table)
8354     {
8355       grant_column= column_hash_search(grant_table, field_name,
8356                                        (uint) strlen(field_name));
8357       if (!grant_column)
8358         priv= (grant->privilege | grant_table->privs);
8359       else
8360         priv= (grant->privilege | grant_table->privs | grant_column->rights);
8361     }
8362 
8363     if (grant_table_role)
8364     {
8365       grant_column= column_hash_search(grant_table_role, field_name,
8366                                        (uint) strlen(field_name));
8367       if (!grant_column)
8368         priv|= (grant->privilege | grant_table_role->privs);
8369       else
8370         priv|= (grant->privilege | grant_table_role->privs |
8371                 grant_column->rights);
8372     }
8373   }
8374   mysql_rwlock_unlock(&LOCK_grant);
8375   return priv;
8376 }
8377 
8378 
8379 /* Help function for mysql_show_grants */
8380 
add_user_option(String * grant,long value,const char * name,bool is_signed)8381 static void add_user_option(String *grant, long value, const char *name,
8382                             bool is_signed)
8383 {
8384   if (value)
8385   {
8386     char buff[22], *p; // just as in int2str
8387     grant->append(' ');
8388     grant->append(name, strlen(name));
8389     grant->append(' ');
8390     p=int10_to_str(value, buff, is_signed ? -10 : 10);
8391     grant->append(buff,p-buff);
8392   }
8393 }
8394 
8395 
add_user_option(String * grant,double value,const char * name)8396 static void add_user_option(String *grant, double value, const char *name)
8397 {
8398   if (value != 0.0 )
8399   {
8400     char buff[FLOATING_POINT_BUFFER];
8401     size_t len;
8402     grant->append(' ');
8403     grant->append(name, strlen(name));
8404     grant->append(' ');
8405     len= my_fcvt(value, 6, buff, NULL);
8406     grant->append(buff, len);
8407   }
8408 }
8409 
add_user_parameters(THD * thd,String * result,ACL_USER * acl_user,bool with_grant)8410 static void add_user_parameters(THD *thd, String *result, ACL_USER* acl_user,
8411                                 bool with_grant)
8412 {
8413   result->append('@');
8414   append_identifier(thd, result, acl_user->host.hostname,
8415                     acl_user->hostname_length);
8416 
8417   if (acl_user->plugin.str == native_password_plugin_name.str ||
8418       acl_user->plugin.str == old_password_plugin_name.str)
8419   {
8420     if (acl_user->auth_string.length)
8421     {
8422       DBUG_ASSERT(acl_user->salt_len);
8423       result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
8424       result->append(&acl_user->auth_string);
8425       result->append('\'');
8426     }
8427   }
8428   else
8429   {
8430     result->append(STRING_WITH_LEN(" IDENTIFIED VIA "));
8431     result->append(&acl_user->plugin);
8432     if (acl_user->auth_string.length)
8433     {
8434       result->append(STRING_WITH_LEN(" USING '"));
8435       result->append(&acl_user->auth_string);
8436       result->append('\'');
8437     }
8438   }
8439   /* "show grants" SSL related stuff */
8440   if (acl_user->ssl_type == SSL_TYPE_ANY)
8441     result->append(STRING_WITH_LEN(" REQUIRE SSL"));
8442   else if (acl_user->ssl_type == SSL_TYPE_X509)
8443     result->append(STRING_WITH_LEN(" REQUIRE X509"));
8444   else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
8445   {
8446     int ssl_options = 0;
8447     result->append(STRING_WITH_LEN(" REQUIRE "));
8448     if (acl_user->x509_issuer)
8449     {
8450       ssl_options++;
8451       result->append(STRING_WITH_LEN("ISSUER \'"));
8452       result->append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
8453       result->append('\'');
8454     }
8455     if (acl_user->x509_subject)
8456     {
8457       if (ssl_options++)
8458         result->append(' ');
8459       result->append(STRING_WITH_LEN("SUBJECT \'"));
8460       result->append(acl_user->x509_subject,strlen(acl_user->x509_subject),
8461                     system_charset_info);
8462       result->append('\'');
8463     }
8464     if (acl_user->ssl_cipher)
8465     {
8466       if (ssl_options++)
8467         result->append(' ');
8468       result->append(STRING_WITH_LEN("CIPHER '"));
8469       result->append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
8470                     system_charset_info);
8471       result->append('\'');
8472     }
8473   }
8474   if (with_grant ||
8475       (acl_user->user_resource.questions ||
8476        acl_user->user_resource.updates ||
8477        acl_user->user_resource.conn_per_hour ||
8478        acl_user->user_resource.user_conn ||
8479        acl_user->user_resource.max_statement_time != 0.0))
8480   {
8481     result->append(STRING_WITH_LEN(" WITH"));
8482     if (with_grant)
8483       result->append(STRING_WITH_LEN(" GRANT OPTION"));
8484     add_user_option(result, acl_user->user_resource.questions,
8485                     "MAX_QUERIES_PER_HOUR", false);
8486     add_user_option(result, acl_user->user_resource.updates,
8487                     "MAX_UPDATES_PER_HOUR", false);
8488     add_user_option(result, acl_user->user_resource.conn_per_hour,
8489                     "MAX_CONNECTIONS_PER_HOUR", false);
8490     add_user_option(result, acl_user->user_resource.user_conn,
8491                     "MAX_USER_CONNECTIONS", true);
8492     add_user_option(result, acl_user->user_resource.max_statement_time,
8493                     "MAX_STATEMENT_TIME");
8494   }
8495 }
8496 
8497 static const char *command_array[]=
8498 {
8499   "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
8500   "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
8501   "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
8502   "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
8503   "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
8504   "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE",
8505   "DELETE HISTORY"
8506 };
8507 
8508 static uint command_lengths[]=
8509 {
8510   6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
8511   14, 13, 11, 5, 7, 17, 14,
8512 };
8513 
8514 
print_grants_for_role(THD * thd,ACL_ROLE * role)8515 static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
8516 {
8517   char buff[1024];
8518 
8519   if (show_role_grants(thd, "", role, buff, sizeof(buff)))
8520     return TRUE;
8521 
8522   if (show_global_privileges(thd, role, TRUE, buff, sizeof(buff)))
8523     return TRUE;
8524 
8525   if (show_database_privileges(thd, role->user.str, "", buff, sizeof(buff)))
8526     return TRUE;
8527 
8528   if (show_table_and_column_privileges(thd, role->user.str, "", buff, sizeof(buff)))
8529     return TRUE;
8530 
8531   if (show_routine_grants(thd, role->user.str, "", &sp_handler_procedure,
8532                           buff, sizeof(buff)))
8533     return TRUE;
8534 
8535   if (show_routine_grants(thd, role->user.str, "", &sp_handler_function,
8536                           buff, sizeof(buff)))
8537     return TRUE;
8538 
8539   if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_spec,
8540                           buff, sizeof(buff)))
8541     return TRUE;
8542 
8543   if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_body,
8544                           buff, sizeof(buff)))
8545     return TRUE;
8546 
8547   return FALSE;
8548 
8549 }
8550 
8551 
mysql_show_create_user(THD * thd,LEX_USER * lex_user)8552 bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
8553 {
8554   const char *username= NULL, *hostname= NULL;
8555   char buff[1024]; //Show create user should not take more than 1024 bytes.
8556   Protocol *protocol= thd->protocol;
8557   bool error= false;
8558   ACL_USER *acl_user;
8559   uint head_length;
8560   DBUG_ENTER("mysql_show_create_user");
8561 
8562   if (get_show_user(thd, lex_user, &username, &hostname, NULL))
8563     DBUG_RETURN(TRUE);
8564 
8565   List<Item> field_list;
8566   head_length= (uint) (strxmov(buff, "CREATE USER for ", username, "@",
8567                                hostname, NullS) - buff);
8568   Item_string *field = new (thd->mem_root) Item_string_ascii(thd, "", 0);
8569   if (!field)
8570     DBUG_RETURN(true);                          // Error given my my_alloc()
8571 
8572   field->name.str= buff;
8573   field->name.length= head_length;
8574   field->max_length= sizeof(buff);
8575   field_list.push_back(field, thd->mem_root);
8576   if (protocol->send_result_set_metadata(&field_list,
8577                                          Protocol::SEND_NUM_ROWS |
8578                                          Protocol::SEND_EOF))
8579     DBUG_RETURN(true);
8580 
8581   String result(buff, sizeof(buff), system_charset_info);
8582   result.length(0);
8583   mysql_rwlock_rdlock(&LOCK_grant);
8584   mysql_mutex_lock(&acl_cache->lock);
8585 
8586   acl_user= find_user_exact(hostname, username);
8587 
8588   // User not found in the internal data structures.
8589   if (!acl_user)
8590   {
8591     my_error(ER_PASSWORD_NO_MATCH, MYF(0));
8592     error= true;
8593     goto end;
8594   }
8595 
8596   result.append("CREATE USER ");
8597   append_identifier(thd, &result, username, strlen(username));
8598   add_user_parameters(thd, &result, acl_user, false);
8599 
8600   protocol->prepare_for_resend();
8601   protocol->store(result.ptr(), result.length(), result.charset());
8602   if (protocol->write())
8603   {
8604     error= true;
8605   }
8606   my_eof(thd);
8607 
8608 end:
8609   mysql_rwlock_unlock(&LOCK_grant);
8610   mysql_mutex_unlock(&acl_cache->lock);
8611 
8612   DBUG_RETURN(error);
8613 }
8614 
8615 
show_grants_callback(ACL_USER_BASE * role,void * data)8616 static int show_grants_callback(ACL_USER_BASE *role, void *data)
8617 {
8618   THD *thd= (THD *)data;
8619   DBUG_ASSERT(role->flags & IS_ROLE);
8620   if (print_grants_for_role(thd, (ACL_ROLE *)role))
8621     return -1;
8622   return 0;
8623 }
8624 
mysql_show_grants_get_fields(THD * thd,List<Item> * fields,const char * name,size_t length)8625 void mysql_show_grants_get_fields(THD *thd, List<Item> *fields,
8626                                   const char *name, size_t length)
8627 {
8628   Item_string *field=new (thd->mem_root) Item_string_ascii(thd, "", 0);
8629   /* Set name explicit to avoid character set conversions */
8630   field->name.str= name;
8631   field->name.length= length;
8632   field->max_length=1024;
8633   fields->push_back(field, thd->mem_root);
8634 }
8635 
8636 /** checks privileges for SHOW GRANTS and SHOW CREATE USER
8637 
8638   @note that in case of SHOW CREATE USER the parser guarantees
8639   that a role can never happen here, so *rolename will never
8640   be assigned to
8641 */
get_show_user(THD * thd,LEX_USER * lex_user,const char ** username,const char ** hostname,const char ** rolename)8642 bool get_show_user(THD *thd, LEX_USER *lex_user, const char **username,
8643                    const char **hostname, const char **rolename)
8644 {
8645   if (lex_user->user.str == current_user.str)
8646   {
8647     *username= thd->security_ctx->priv_user;
8648     *hostname= thd->security_ctx->priv_host;
8649     return 0;
8650   }
8651   if (lex_user->user.str == current_role.str)
8652   {
8653     *rolename= thd->security_ctx->priv_role;
8654     return 0;
8655   }
8656   if (lex_user->user.str == current_user_and_current_role.str)
8657   {
8658     *username= thd->security_ctx->priv_user;
8659     *hostname= thd->security_ctx->priv_host;
8660     *rolename= thd->security_ctx->priv_role;
8661     return 0;
8662   }
8663 
8664   Security_context *sctx= thd->security_ctx;
8665   bool do_check_access;
8666 
8667   if (!(lex_user= get_current_user(thd, lex_user)))
8668     return 1;
8669 
8670   if (lex_user->is_role())
8671   {
8672     *rolename= lex_user->user.str;
8673     do_check_access= strcmp(*rolename, sctx->priv_role);
8674   }
8675   else
8676   {
8677     *username= lex_user->user.str;
8678     *hostname= lex_user->host.str;
8679     do_check_access= strcmp(*username, sctx->priv_user) ||
8680                      strcmp(*hostname, sctx->priv_host);
8681   }
8682 
8683   if (do_check_access && check_access(thd, SELECT_ACL, "mysql", 0, 0, 1, 0))
8684     return 1;
8685   return 0;
8686 }
8687 
8688 /*
8689   SHOW GRANTS;  Send grants for a user to the client
8690 
8691   IMPLEMENTATION
8692    Send to client grant-like strings depicting user@host privileges
8693 */
8694 
mysql_show_grants(THD * thd,LEX_USER * lex_user)8695 bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
8696 {
8697   int  error = -1;
8698   ACL_USER *UNINIT_VAR(acl_user);
8699   ACL_ROLE *acl_role= NULL;
8700   char buff[1024];
8701   Protocol *protocol= thd->protocol;
8702   const char *username= NULL, *hostname= NULL, *rolename= NULL, *end;
8703   DBUG_ENTER("mysql_show_grants");
8704 
8705   if (!initialized)
8706   {
8707     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
8708     DBUG_RETURN(TRUE);
8709   }
8710 
8711   if (get_show_user(thd, lex_user, &username, &hostname, &rolename))
8712     DBUG_RETURN(TRUE);
8713 
8714   DBUG_ASSERT(rolename || username);
8715 
8716   List<Item> field_list;
8717   if (username)
8718     end= strxmov(buff,"Grants for ",username,"@",hostname, NullS);
8719   else
8720     end= strxmov(buff,"Grants for ",rolename, NullS);
8721 
8722   mysql_show_grants_get_fields(thd, &field_list, buff, (uint) (end-buff));
8723 
8724   if (protocol->send_result_set_metadata(&field_list,
8725                                Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
8726     DBUG_RETURN(TRUE);
8727 
8728   mysql_rwlock_rdlock(&LOCK_grant);
8729   mysql_mutex_lock(&acl_cache->lock);
8730 
8731   if (username)
8732   {
8733     acl_user= find_user_exact(hostname, username);
8734     if (!acl_user)
8735     {
8736       mysql_mutex_unlock(&acl_cache->lock);
8737       mysql_rwlock_unlock(&LOCK_grant);
8738 
8739       my_error(ER_NONEXISTING_GRANT, MYF(0),
8740                username, hostname);
8741       DBUG_RETURN(TRUE);
8742     }
8743 
8744     /* Show granted roles to acl_user */
8745     if (show_role_grants(thd, hostname, acl_user, buff, sizeof(buff)))
8746       goto end;
8747 
8748     /* Add first global access grants */
8749     if (show_global_privileges(thd, acl_user, FALSE, buff, sizeof(buff)))
8750       goto end;
8751 
8752     /* Add database access */
8753     if (show_database_privileges(thd, username, hostname, buff, sizeof(buff)))
8754       goto end;
8755 
8756     /* Add table & column access */
8757     if (show_table_and_column_privileges(thd, username, hostname, buff, sizeof(buff)))
8758       goto end;
8759 
8760     if (show_routine_grants(thd, username, hostname, &sp_handler_procedure,
8761                             buff, sizeof(buff)))
8762       goto end;
8763 
8764     if (show_routine_grants(thd, username, hostname, &sp_handler_function,
8765                             buff, sizeof(buff)))
8766       goto end;
8767 
8768     if (show_routine_grants(thd, username, hostname, &sp_handler_package_spec,
8769                             buff, sizeof(buff)))
8770       goto end;
8771 
8772     if (show_routine_grants(thd, username, hostname, &sp_handler_package_body,
8773                             buff, sizeof(buff)))
8774       goto end;
8775 
8776     if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff)))
8777       goto end;
8778   }
8779 
8780   if (rolename)
8781   {
8782     acl_role= find_acl_role(rolename);
8783     if (acl_role)
8784     {
8785       /* get a list of all inherited roles */
8786       traverse_role_graph_down(acl_role, thd, show_grants_callback, NULL);
8787     }
8788     else
8789     {
8790       if (lex_user->user.str == current_role.str)
8791       {
8792         mysql_mutex_unlock(&acl_cache->lock);
8793         mysql_rwlock_unlock(&LOCK_grant);
8794         my_error(ER_NONEXISTING_GRANT, MYF(0),
8795                  thd->security_ctx->priv_user,
8796                  thd->security_ctx->priv_host);
8797         DBUG_RETURN(TRUE);
8798       }
8799     }
8800   }
8801 
8802   if (username)
8803   {
8804     /* Show default role to acl_user */
8805     if (show_default_role(thd, acl_user, buff, sizeof(buff)))
8806       goto end;
8807   }
8808 
8809 
8810   error= 0;
8811 end:
8812   mysql_mutex_unlock(&acl_cache->lock);
8813   mysql_rwlock_unlock(&LOCK_grant);
8814 
8815   my_eof(thd);
8816   DBUG_RETURN(error);
8817 }
8818 
find_role_grant_pair(const LEX_CSTRING * u,const LEX_CSTRING * h,const LEX_CSTRING * r)8819 static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u,
8820                                              const LEX_CSTRING *h,
8821                                              const LEX_CSTRING *r)
8822 {
8823   char buf[1024];
8824   String pair_key(buf, sizeof(buf), &my_charset_bin);
8825 
8826   size_t key_length= u->length + h->length + r->length + 3;
8827   pair_key.alloc(key_length);
8828 
8829   strmov(strmov(strmov(const_cast<char*>(pair_key.ptr()),
8830                        safe_str(u->str)) + 1, h->str) + 1, r->str);
8831 
8832   return (ROLE_GRANT_PAIR *)
8833     my_hash_search(&acl_roles_mappings, (uchar*)pair_key.ptr(), key_length);
8834 }
8835 
show_default_role(THD * thd,ACL_USER * acl_entry,char * buff,size_t buffsize)8836 static bool show_default_role(THD *thd, ACL_USER *acl_entry,
8837                               char *buff, size_t buffsize)
8838 {
8839   Protocol *protocol= thd->protocol;
8840   LEX_CSTRING def_rolename= acl_entry->default_rolename;
8841 
8842   if (def_rolename.length)
8843   {
8844     String def_str(buff, buffsize, system_charset_info);
8845     def_str.length(0);
8846     def_str.append(STRING_WITH_LEN("SET DEFAULT ROLE "));
8847     append_identifier(thd, &def_str, def_rolename.str, def_rolename.length);
8848     def_str.append(" FOR ");
8849     append_identifier(thd, &def_str, acl_entry->user.str, acl_entry->user.length);
8850     DBUG_ASSERT(!(acl_entry->flags & IS_ROLE));
8851     def_str.append('@');
8852     append_identifier(thd, &def_str, acl_entry->host.hostname,
8853                       acl_entry->hostname_length);
8854     protocol->prepare_for_resend();
8855     protocol->store(def_str.ptr(),def_str.length(),def_str.charset());
8856     if (protocol->write())
8857     {
8858       return TRUE;
8859     }
8860   }
8861   return FALSE;
8862 }
8863 
show_role_grants(THD * thd,const char * hostname,ACL_USER_BASE * acl_entry,char * buff,size_t buffsize)8864 static bool show_role_grants(THD *thd, const char *hostname,
8865                              ACL_USER_BASE *acl_entry,
8866                              char *buff, size_t buffsize)
8867 {
8868   uint counter;
8869   Protocol *protocol= thd->protocol;
8870   LEX_CSTRING host= {const_cast<char*>(hostname), strlen(hostname)};
8871 
8872   String grant(buff, buffsize, system_charset_info);
8873   for (counter= 0; counter < acl_entry->role_grants.elements; counter++)
8874   {
8875     grant.length(0);
8876     grant.append(STRING_WITH_LEN("GRANT "));
8877     ACL_ROLE *acl_role= *(dynamic_element(&acl_entry->role_grants, counter,
8878                                           ACL_ROLE**));
8879     append_identifier(thd, &grant, acl_role->user.str, acl_role->user.length);
8880     grant.append(STRING_WITH_LEN(" TO "));
8881     append_identifier(thd, &grant, acl_entry->user.str, acl_entry->user.length);
8882     if (!(acl_entry->flags & IS_ROLE))
8883     {
8884       grant.append('@');
8885       append_identifier(thd, &grant, host.str, host.length);
8886     }
8887 
8888     ROLE_GRANT_PAIR *pair=
8889       find_role_grant_pair(&acl_entry->user, &host, &acl_role->user);
8890     DBUG_ASSERT(pair);
8891 
8892     if (pair->with_admin)
8893       grant.append(STRING_WITH_LEN(" WITH ADMIN OPTION"));
8894 
8895     protocol->prepare_for_resend();
8896     protocol->store(grant.ptr(),grant.length(),grant.charset());
8897     if (protocol->write())
8898     {
8899       return TRUE;
8900     }
8901   }
8902   return FALSE;
8903 }
8904 
show_global_privileges(THD * thd,ACL_USER_BASE * acl_entry,bool handle_as_role,char * buff,size_t buffsize)8905 static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
8906                                    bool handle_as_role,
8907                                    char *buff, size_t buffsize)
8908 {
8909   uint counter;
8910   ulong want_access;
8911   Protocol *protocol= thd->protocol;
8912 
8913   String global(buff, buffsize, system_charset_info);
8914   global.length(0);
8915   global.append(STRING_WITH_LEN("GRANT "));
8916 
8917   if (handle_as_role)
8918     want_access= ((ACL_ROLE *)acl_entry)->initial_role_access;
8919   else
8920     want_access= acl_entry->access;
8921   if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
8922     global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
8923   else if (!(want_access & ~GRANT_ACL))
8924     global.append(STRING_WITH_LEN("USAGE"));
8925   else
8926   {
8927     bool found=0;
8928     ulong j,test_access= want_access & ~GRANT_ACL;
8929     for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
8930     {
8931       if (test_access & j)
8932       {
8933         if (found)
8934           global.append(STRING_WITH_LEN(", "));
8935         found=1;
8936         global.append(command_array[counter],command_lengths[counter]);
8937       }
8938     }
8939   }
8940   global.append (STRING_WITH_LEN(" ON *.* TO "));
8941   append_identifier(thd, &global, acl_entry->user.str, acl_entry->user.length);
8942 
8943   if (!handle_as_role)
8944     add_user_parameters(thd, &global, (ACL_USER *)acl_entry,
8945                         (want_access & GRANT_ACL));
8946 
8947   else if (want_access & GRANT_ACL)
8948     global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
8949   protocol->prepare_for_resend();
8950   protocol->store(global.ptr(),global.length(),global.charset());
8951   if (protocol->write())
8952     return TRUE;
8953 
8954   return FALSE;
8955 
8956 }
8957 
8958 
add_to_user(THD * thd,String * result,const char * user,bool is_user,const char * host)8959 static void add_to_user(THD *thd, String *result, const char *user,
8960                         bool is_user, const char *host)
8961 {
8962   result->append(STRING_WITH_LEN(" TO "));
8963   append_identifier(thd, result, user, strlen(user));
8964   if (is_user)
8965   {
8966     result->append('@');
8967     // host and lex_user->host are equal except for case
8968     append_identifier(thd, result, host, strlen(host));
8969   }
8970 }
8971 
8972 
show_database_privileges(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)8973 static bool show_database_privileges(THD *thd, const char *username,
8974                                      const char *hostname,
8975                                      char *buff, size_t buffsize)
8976 {
8977   ulong want_access;
8978   Protocol *protocol= thd->protocol;
8979 
8980   for (uint i=0 ; i < acl_dbs.elements() ; i++)
8981   {
8982     const char *user, *host;
8983 
8984     ACL_DB *acl_db= &acl_dbs.at(i);
8985     user= safe_str(acl_db->user);
8986     host=acl_db->host.hostname;
8987 
8988     /*
8989       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
8990       but make it case-insensitive because that's the way they are
8991       actually applied, and showing fewer privileges than are applied
8992       would be wrong from a security point of view.
8993     */
8994 
8995     if (!strcmp(username, user) &&
8996         !my_strcasecmp(system_charset_info, hostname, host))
8997     {
8998       /*
8999         do not print inherited access bits for roles,
9000         the role bits present in the table are what matters
9001       */
9002       if (*hostname) // User
9003         want_access=acl_db->access;
9004       else // Role
9005         want_access=acl_db->initial_access;
9006       if (want_access)
9007       {
9008         String db(buff, buffsize, system_charset_info);
9009         db.length(0);
9010         db.append(STRING_WITH_LEN("GRANT "));
9011 
9012         if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
9013           db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
9014         else if (!(want_access & ~GRANT_ACL))
9015           db.append(STRING_WITH_LEN("USAGE"));
9016         else
9017         {
9018           int found=0, cnt;
9019           ulong j,test_access= want_access & ~GRANT_ACL;
9020           for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
9021           {
9022             if (test_access & j)
9023             {
9024               if (found)
9025                 db.append(STRING_WITH_LEN(", "));
9026               found = 1;
9027               db.append(command_array[cnt],command_lengths[cnt]);
9028             }
9029           }
9030         }
9031         db.append (STRING_WITH_LEN(" ON "));
9032         append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
9033         db.append (STRING_WITH_LEN(".*"));
9034         add_to_user(thd, &db, username, (*hostname), host);
9035         if (want_access & GRANT_ACL)
9036           db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9037         protocol->prepare_for_resend();
9038         protocol->store(db.ptr(),db.length(),db.charset());
9039         if (protocol->write())
9040         {
9041           return TRUE;
9042         }
9043       }
9044     }
9045   }
9046   return FALSE;
9047 
9048 }
9049 
show_table_and_column_privileges(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)9050 static bool show_table_and_column_privileges(THD *thd, const char *username,
9051                                              const char *hostname,
9052                                              char *buff, size_t buffsize)
9053 {
9054   uint counter, index;
9055   Protocol *protocol= thd->protocol;
9056 
9057   for (index=0 ; index < column_priv_hash.records ; index++)
9058   {
9059     const char *user, *host;
9060     GRANT_TABLE *grant_table= (GRANT_TABLE*)
9061       my_hash_element(&column_priv_hash, index);
9062 
9063     user= safe_str(grant_table->user);
9064     host= grant_table->host.hostname;
9065 
9066     /*
9067       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
9068       but make it case-insensitive because that's the way they are
9069       actually applied, and showing fewer privileges than are applied
9070       would be wrong from a security point of view.
9071     */
9072 
9073     if (!strcmp(username,user) &&
9074         !my_strcasecmp(system_charset_info, hostname, host))
9075     {
9076       ulong table_access;
9077       ulong cols_access;
9078       if (*hostname) // User
9079       {
9080         table_access= grant_table->privs;
9081         cols_access= grant_table->cols;
9082       }
9083       else // Role
9084       {
9085         table_access= grant_table->init_privs;
9086         cols_access= grant_table->init_cols;
9087       }
9088 
9089       if ((table_access | cols_access) != 0)
9090       {
9091         String global(buff, sizeof(buff), system_charset_info);
9092         ulong test_access= (table_access | cols_access) & ~GRANT_ACL;
9093 
9094         global.length(0);
9095         global.append(STRING_WITH_LEN("GRANT "));
9096 
9097         if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
9098           global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
9099         else if (!test_access)
9100           global.append(STRING_WITH_LEN("USAGE"));
9101         else
9102         {
9103           /* Add specific column access */
9104           int found= 0;
9105           ulong j;
9106 
9107           for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
9108           {
9109             if (test_access & j)
9110             {
9111               if (found)
9112                 global.append(STRING_WITH_LEN(", "));
9113               found= 1;
9114               global.append(command_array[counter],command_lengths[counter]);
9115 
9116               if (grant_table->cols)
9117               {
9118                 uint found_col= 0;
9119                 HASH *hash_columns;
9120                 hash_columns= &grant_table->hash_columns;
9121 
9122                 for (uint col_index=0 ;
9123                      col_index < hash_columns->records ;
9124                      col_index++)
9125                 {
9126                   GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
9127                     my_hash_element(hash_columns,col_index);
9128                   if (j & (*hostname ? grant_column->rights         // User
9129                                      : grant_column->init_rights))  // Role
9130                   {
9131                     if (!found_col)
9132                     {
9133                       found_col= 1;
9134                       /*
9135                         If we have a duplicated table level privilege, we
9136                         must write the access privilege name again.
9137                       */
9138                       if (table_access & j)
9139                       {
9140                         global.append(STRING_WITH_LEN(", "));
9141                         global.append(command_array[counter],
9142                                       command_lengths[counter]);
9143                       }
9144                       global.append(STRING_WITH_LEN(" ("));
9145                     }
9146                     else
9147                       global.append(STRING_WITH_LEN(", "));
9148                     global.append(grant_column->column,
9149                                   grant_column->key_length,
9150                                   system_charset_info);
9151                   }
9152                 }
9153                 if (found_col)
9154                   global.append(')');
9155               }
9156             }
9157           }
9158         }
9159         global.append(STRING_WITH_LEN(" ON "));
9160         append_identifier(thd, &global, grant_table->db,
9161                           strlen(grant_table->db));
9162         global.append('.');
9163         append_identifier(thd, &global, grant_table->tname,
9164                           strlen(grant_table->tname));
9165         add_to_user(thd, &global, username, (*hostname), host);
9166         if (table_access & GRANT_ACL)
9167           global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9168         protocol->prepare_for_resend();
9169         protocol->store(global.ptr(),global.length(),global.charset());
9170         if (protocol->write())
9171         {
9172           return TRUE;
9173         }
9174       }
9175     }
9176   }
9177   return FALSE;
9178 
9179 }
9180 
show_routine_grants(THD * thd,const char * username,const char * hostname,const Sp_handler * sph,char * buff,int buffsize)9181 static int show_routine_grants(THD* thd,
9182                                const char *username, const char *hostname,
9183                                const Sp_handler *sph,
9184                                char *buff, int buffsize)
9185 {
9186   uint counter, index;
9187   int error= 0;
9188   Protocol *protocol= thd->protocol;
9189   HASH *hash= sph->get_priv_hash();
9190   /* Add routine access */
9191   for (index=0 ; index < hash->records ; index++)
9192   {
9193     const char *user, *host;
9194     GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
9195 
9196     user= safe_str(grant_proc->user);
9197     host= grant_proc->host.hostname;
9198 
9199     /*
9200       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
9201       but make it case-insensitive because that's the way they are
9202       actually applied, and showing fewer privileges than are applied
9203       would be wrong from a security point of view.
9204     */
9205 
9206     if (!strcmp(username, user) &&
9207         !my_strcasecmp(system_charset_info, hostname, host))
9208     {
9209       ulong proc_access;
9210       if (*hostname) // User
9211         proc_access= grant_proc->privs;
9212       else // Role
9213         proc_access= grant_proc->init_privs;
9214 
9215       if (proc_access != 0)
9216       {
9217 	String global(buff, buffsize, system_charset_info);
9218 	ulong test_access= proc_access & ~GRANT_ACL;
9219 
9220 	global.length(0);
9221 	global.append(STRING_WITH_LEN("GRANT "));
9222 
9223 	if (!test_access)
9224  	  global.append(STRING_WITH_LEN("USAGE"));
9225 	else
9226 	{
9227           /* Add specific procedure access */
9228 	  int found= 0;
9229 	  ulong j;
9230 
9231 	  for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
9232 	  {
9233 	    if (test_access & j)
9234 	    {
9235 	      if (found)
9236 		global.append(STRING_WITH_LEN(", "));
9237 	      found= 1;
9238 	      global.append(command_array[counter],command_lengths[counter]);
9239 	    }
9240 	  }
9241 	}
9242 	global.append(STRING_WITH_LEN(" ON "));
9243         LEX_CSTRING tmp= sph->type_lex_cstring();
9244         global.append(&tmp);
9245         global.append(' ');
9246 	append_identifier(thd, &global, grant_proc->db,
9247 			  strlen(grant_proc->db));
9248 	global.append('.');
9249 	append_identifier(thd, &global, grant_proc->tname,
9250 			  strlen(grant_proc->tname));
9251         add_to_user(thd, &global, username, (*hostname), host);
9252 	if (proc_access & GRANT_ACL)
9253 	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9254 	protocol->prepare_for_resend();
9255 	protocol->store(global.ptr(),global.length(),global.charset());
9256 	if (protocol->write())
9257 	{
9258 	  error= -1;
9259 	  break;
9260 	}
9261       }
9262     }
9263   }
9264   return error;
9265 }
9266 
9267 
9268 /*
9269   Make a clear-text version of the requested privilege.
9270 */
9271 
get_privilege_desc(char * to,uint max_length,ulong access)9272 void get_privilege_desc(char *to, uint max_length, ulong access)
9273 {
9274   uint pos;
9275   char *start=to;
9276   DBUG_ASSERT(max_length >= 30);                // For end ', ' removal
9277 
9278   if (access)
9279   {
9280     max_length--;				// Reserve place for end-zero
9281     for (pos=0 ; access ; pos++, access>>=1)
9282     {
9283       if ((access & 1) &&
9284 	  command_lengths[pos] + (uint) (to-start) < max_length)
9285       {
9286 	to= strmov(to, command_array[pos]);
9287         *to++= ',';
9288         *to++= ' ';
9289       }
9290     }
9291     to--;                                       // Remove end ' '
9292     to--;					// Remove end ','
9293   }
9294   *to=0;
9295 }
9296 
9297 
get_mqh(const char * user,const char * host,USER_CONN * uc)9298 void get_mqh(const char *user, const char *host, USER_CONN *uc)
9299 {
9300   ACL_USER *acl_user;
9301 
9302   mysql_mutex_lock(&acl_cache->lock);
9303 
9304   if (initialized && (acl_user= find_user_wild(host,user)))
9305     uc->user_resources= acl_user->user_resource;
9306   else
9307     bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
9308 
9309   mysql_mutex_unlock(&acl_cache->lock);
9310 }
9311 
9312 /*
9313   Modify a privilege table.
9314 
9315   SYNOPSIS
9316     modify_grant_table()
9317     table                       The table to modify.
9318     host_field                  The host name field.
9319     user_field                  The user name field.
9320     user_to                     The new name for the user if to be renamed,
9321                                 NULL otherwise.
9322 
9323   DESCRIPTION
9324   Update user/host in the current record if user_to is not NULL.
9325   Delete the current record if user_to is NULL.
9326 
9327   RETURN
9328     0           OK.
9329     != 0        Error.
9330 */
9331 
modify_grant_table(TABLE * table,Field * host_field,Field * user_field,LEX_USER * user_to)9332 static int modify_grant_table(TABLE *table, Field *host_field,
9333                               Field *user_field, LEX_USER *user_to)
9334 {
9335   int error;
9336   DBUG_ENTER("modify_grant_table");
9337 
9338   if (user_to)
9339   {
9340     /* rename */
9341     store_record(table, record[1]);
9342     host_field->store(user_to->host.str, user_to->host.length,
9343                       system_charset_info);
9344     user_field->store(user_to->user.str, user_to->user.length,
9345                       system_charset_info);
9346     if (unlikely(error= table->file->ha_update_row(table->record[1],
9347                                                    table->record[0])) &&
9348         error != HA_ERR_RECORD_IS_THE_SAME)
9349       table->file->print_error(error, MYF(0));
9350     else
9351       error= 0;
9352   }
9353   else
9354   {
9355     /* delete */
9356     if (unlikely((error=table->file->ha_delete_row(table->record[0]))))
9357       table->file->print_error(error, MYF(0));
9358   }
9359 
9360   DBUG_RETURN(error);
9361 }
9362 
9363 /*
9364   Handle the roles_mapping privilege table
9365 */
handle_roles_mappings_table(TABLE * table,bool drop,LEX_USER * user_from,LEX_USER * user_to)9366 static int handle_roles_mappings_table(TABLE *table, bool drop,
9367                                        LEX_USER *user_from, LEX_USER *user_to)
9368 {
9369   /*
9370     All entries (Host, User) that match user_from will be renamed,
9371     as well as all Role entries that match if user_from.host.str == ""
9372 
9373     Otherwise, only matching (Host, User) will be renamed.
9374   */
9375   DBUG_ENTER("handle_roles_mappings_table");
9376 
9377   int error;
9378   int result= 0;
9379   THD *thd= table->in_use;
9380   const char *host, *user, *role;
9381   Field *host_field= table->field[0];
9382   Field *user_field= table->field[1];
9383   Field *role_field= table->field[2];
9384 
9385   DBUG_PRINT("info", ("Rewriting entry in roles_mapping table: %s@%s",
9386                       user_from->user.str, user_from->host.str));
9387   table->use_all_columns();
9388 
9389   if (unlikely(table->file->ha_rnd_init_with_error(1)))
9390     result= -1;
9391   else
9392   {
9393     while((error= table->file->ha_rnd_next(table->record[0])) !=
9394           HA_ERR_END_OF_FILE)
9395     {
9396       if (error)
9397       {
9398         DBUG_PRINT("info", ("scan error: %d", error));
9399         continue;
9400       }
9401 
9402       host= safe_str(get_field(thd->mem_root, host_field));
9403       user= safe_str(get_field(thd->mem_root, user_field));
9404 
9405       if (!(strcmp(user_from->user.str, user) ||
9406             my_strcasecmp(system_charset_info, user_from->host.str, host)))
9407         result= ((drop || user_to) &&
9408                  modify_grant_table(table, host_field, user_field, user_to)) ?
9409           -1 : result ? result : 1; /* Error or keep result or found. */
9410       else
9411       {
9412         role= safe_str(get_field(thd->mem_root, role_field));
9413 
9414         if (!user_from->is_role() || strcmp(user_from->user.str, role))
9415           continue;
9416 
9417         error= 0;
9418 
9419         if (drop) /* drop if requested */
9420         {
9421           if (unlikely((error= table->file->ha_delete_row(table->record[0]))))
9422             table->file->print_error(error, MYF(0));
9423         }
9424         else if (user_to)
9425         {
9426           store_record(table, record[1]);
9427           role_field->store(user_to->user.str, user_to->user.length,
9428                             system_charset_info);
9429           if (unlikely(error= table->file->ha_update_row(table->record[1],
9430                                                          table->record[0])) &&
9431               error != HA_ERR_RECORD_IS_THE_SAME)
9432             table->file->print_error(error, MYF(0));
9433         }
9434 
9435         /* Error or keep result or found. */
9436         result= error ? -1 : result ? result : 1;
9437       }
9438     }
9439     table->file->ha_rnd_end();
9440   }
9441   DBUG_RETURN(result);
9442 }
9443 
9444 /*
9445   Handle a privilege table.
9446 
9447   SYNOPSIS
9448     handle_grant_table()
9449     grant_table                 An open grant table handle.
9450     which_table                 Which grant table to handle.
9451     drop                        If user_from is to be dropped.
9452     user_from                   The the user to be searched/dropped/renamed.
9453     user_to                     The new name for the user if to be renamed,
9454                                 NULL otherwise.
9455 
9456   DESCRIPTION
9457     Scan through all records in a grant table and apply the requested
9458     operation. For the "user" table, a single index access is sufficient,
9459     since there is an unique index on (host, user).
9460     Delete from grant table if drop is true.
9461     Update in grant table if drop is false and user_to is not NULL.
9462     Search in grant table if drop is false and user_to is NULL.
9463 
9464   RETURN
9465     > 0         At least one record matched.
9466     0           OK, but no record matched.
9467     < 0         Error.
9468 
9469    TODO(cvicentiu) refactor handle_grant_table to use
9470    Grant_table_base instead of TABLE directly.
9471 */
9472 
handle_grant_table(THD * thd,const Grant_table_base & grant_table,enum enum_acl_tables which_table,bool drop,LEX_USER * user_from,LEX_USER * user_to)9473 static int handle_grant_table(THD *thd, const Grant_table_base& grant_table,
9474                               enum enum_acl_tables which_table, bool drop,
9475                               LEX_USER *user_from, LEX_USER *user_to)
9476 {
9477   int result= 0;
9478   int error;
9479   TABLE *table= grant_table.table();
9480   Field *host_field= table->field[0];
9481   Field *user_field= table->field[which_table == USER_TABLE ||
9482                                   which_table == PROXIES_PRIV_TABLE ? 1 : 2];
9483   const char *host_str= user_from->host.str;
9484   const char *user_str= user_from->user.str;
9485   const char *host;
9486   const char *user;
9487   uchar user_key[MAX_KEY_LENGTH];
9488   uint key_prefix_length;
9489   DBUG_ENTER("handle_grant_table");
9490 
9491   if (which_table == ROLES_MAPPING_TABLE)
9492   {
9493     result= handle_roles_mappings_table(table, drop, user_from, user_to);
9494     DBUG_RETURN(result);
9495   }
9496 
9497   table->use_all_columns();
9498   if (which_table == USER_TABLE) // mysql.user table
9499   {
9500     /*
9501       The 'user' table has an unique index on (host, user).
9502       Thus, we can handle everything with a single index access.
9503       The host- and user fields are consecutive in the user table records.
9504       So we set host- and user fields of table->record[0] and use the
9505       pointer to the host field as key.
9506       index_read_idx() will replace table->record[0] (its first argument)
9507       by the searched record, if it exists.
9508     */
9509     DBUG_PRINT("info",("read table: '%s'  search: '%s'@'%s'",
9510                        table->s->table_name.str, user_str, host_str));
9511     host_field->store(host_str, user_from->host.length, system_charset_info);
9512     user_field->store(user_str, user_from->user.length, system_charset_info);
9513 
9514     key_prefix_length= (table->key_info->key_part[0].store_length +
9515                         table->key_info->key_part[1].store_length);
9516     key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
9517 
9518     error= table->file->ha_index_read_idx_map(table->record[0], 0,
9519                                               user_key, (key_part_map)3,
9520                                               HA_READ_KEY_EXACT);
9521     if (!unlikely(error) && !*host_str)
9522     {
9523       // verify that we got a role or a user, as needed
9524       if (static_cast<const User_table&>(grant_table).check_is_role() !=
9525           user_from->is_role())
9526         error= HA_ERR_KEY_NOT_FOUND;
9527     }
9528     if (unlikely(error))
9529     {
9530       if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
9531       {
9532         table->file->print_error(error, MYF(0));
9533         result= -1;
9534       }
9535     }
9536     else
9537     {
9538       /* If requested, delete or update the record. */
9539       result= ((drop || user_to) &&
9540                modify_grant_table(table, host_field, user_field, user_to)) ?
9541         -1 : 1; /* Error or found. */
9542     }
9543     DBUG_PRINT("info",("read result: %d", result));
9544   }
9545   else
9546   {
9547     /*
9548       The non-'user' table do not have indexes on (host, user).
9549       And their host- and user fields are not consecutive.
9550       Thus, we need to do a table scan to find all matching records.
9551     */
9552     if (unlikely(table->file->ha_rnd_init_with_error(1)))
9553       result= -1;
9554     else
9555     {
9556 #ifdef EXTRA_DEBUG
9557       DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
9558                          table->s->table_name.str, user_str, host_str));
9559 #endif
9560       while ((error= table->file->ha_rnd_next(table->record[0])) !=
9561              HA_ERR_END_OF_FILE)
9562       {
9563         if (error)
9564         {
9565           /* Most probable 'deleted record'. */
9566           DBUG_PRINT("info",("scan error: %d", error));
9567           continue;
9568         }
9569         host= safe_str(get_field(thd->mem_root, host_field));
9570         user= safe_str(get_field(thd->mem_root, user_field));
9571 
9572 #ifdef EXTRA_DEBUG
9573         if (which_table != PROXIES_PRIV_TABLE)
9574         {
9575           DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
9576                              user, host,
9577                              get_field(thd->mem_root, table->field[1]) /*db*/,
9578                              get_field(thd->mem_root, table->field[3]) /*table*/,
9579                              get_field(thd->mem_root,
9580                                        table->field[4]) /*column*/));
9581         }
9582 #endif
9583         if (strcmp(user_str, user) ||
9584             my_strcasecmp(system_charset_info, host_str, host))
9585           continue;
9586 
9587         /* If requested, delete or update the record. */
9588         result= ((drop || user_to) &&
9589                  modify_grant_table(table, host_field, user_field, user_to)) ?
9590           -1 : result ? result : 1; /* Error or keep result or found. */
9591         /* If search is requested, we do not need to search further. */
9592         if (! drop && ! user_to)
9593           break ;
9594       }
9595       (void) table->file->ha_rnd_end();
9596       DBUG_PRINT("info",("scan result: %d", result));
9597     }
9598   }
9599 
9600   DBUG_RETURN(result);
9601 }
9602 
9603 
9604 /**
9605   Handle an in-memory privilege structure.
9606 
9607   @param struct_no  The number of the structure to handle (0..6).
9608   @param drop       If user_from is to be dropped.
9609   @param user_from  The the user to be searched/dropped/renamed.
9610   @param user_to    The new name for the user if to be renamed, NULL otherwise.
9611 
9612   @note
9613     Scan through all elements in an in-memory grant structure and apply
9614     the requested operation.
9615     Delete from grant structure if drop is true.
9616     Update in grant structure if drop is false and user_to is not NULL.
9617     Search in grant structure if drop is false and user_to is NULL.
9618 
9619   @retval > 0  At least one element matched.
9620   @retval 0    OK, but no element matched.
9621 */
9622 
handle_grant_struct(enum enum_acl_lists struct_no,bool drop,LEX_USER * user_from,LEX_USER * user_to)9623 static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
9624                                LEX_USER *user_from, LEX_USER *user_to)
9625 {
9626   int result= 0;
9627   int elements;
9628   bool restart;
9629   const char *UNINIT_VAR(user);
9630   const char *UNINIT_VAR(host);
9631   ACL_USER *acl_user= NULL;
9632   ACL_ROLE *acl_role= NULL;
9633   ACL_DB *acl_db= NULL;
9634   ACL_PROXY_USER *acl_proxy_user= NULL;
9635   GRANT_NAME *grant_name= NULL;
9636   ROLE_GRANT_PAIR *UNINIT_VAR(role_grant_pair);
9637   HASH *grant_name_hash= NULL;
9638   HASH *roles_mappings_hash= NULL;
9639   DBUG_ENTER("handle_grant_struct");
9640   DBUG_PRINT("info",("scan struct: %u  search: '%s'@'%s'",
9641                      struct_no, user_from->user.str, user_from->host.str));
9642 
9643   mysql_mutex_assert_owner(&acl_cache->lock);
9644 
9645   /* No point in querying ROLE ACL if user_from is not a role */
9646   if (struct_no == ROLE_ACL && user_from->host.length)
9647     DBUG_RETURN(0);
9648 
9649   /* same. no roles in PROXY_USERS_ACL */
9650   if (struct_no == PROXY_USERS_ACL && user_from->is_role())
9651     DBUG_RETURN(0);
9652 
9653   if (struct_no == ROLE_ACL) //no need to scan the structures in this case
9654   {
9655     acl_role= find_acl_role(user_from->user.str);
9656     if (!acl_role)
9657       DBUG_RETURN(0);
9658 
9659     if (!drop && !user_to) //role was found
9660       DBUG_RETURN(1);
9661 
9662     /* this calls for a role update */
9663     const char *old_key= acl_role->user.str;
9664     size_t old_key_length= acl_role->user.length;
9665     if (drop)
9666     {
9667       /* all grants must be revoked from this role by now. propagate this */
9668       propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL);
9669 
9670       // delete the role from cross-reference arrays
9671       for (uint i=0; i < acl_role->role_grants.elements; i++)
9672       {
9673         ACL_ROLE *grant= *dynamic_element(&acl_role->role_grants,
9674                                           i, ACL_ROLE**);
9675         remove_ptr_from_dynarray(&grant->parent_grantee, acl_role);
9676       }
9677 
9678       for (uint i=0; i < acl_role->parent_grantee.elements; i++)
9679       {
9680         ACL_USER_BASE *grantee= *dynamic_element(&acl_role->parent_grantee,
9681                                                  i, ACL_USER_BASE**);
9682         remove_ptr_from_dynarray(&grantee->role_grants, acl_role);
9683       }
9684 
9685       my_hash_delete(&acl_roles, (uchar*) acl_role);
9686       DBUG_RETURN(1);
9687     }
9688     acl_role->user.str= strdup_root(&acl_memroot, user_to->user.str);
9689     acl_role->user.length= user_to->user.length;
9690 
9691     my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key,
9692                    old_key_length);
9693     DBUG_RETURN(1);
9694 
9695   }
9696 
9697   /* Get the number of elements in the in-memory structure. */
9698   switch (struct_no) {
9699   case USER_ACL:
9700     elements= acl_users.elements;
9701     break;
9702   case DB_ACL:
9703     elements= int(acl_dbs.elements());
9704     break;
9705   case COLUMN_PRIVILEGES_HASH:
9706     grant_name_hash= &column_priv_hash;
9707     elements= grant_name_hash->records;
9708     break;
9709   case PROC_PRIVILEGES_HASH:
9710     grant_name_hash= &proc_priv_hash;
9711     elements= grant_name_hash->records;
9712     break;
9713   case FUNC_PRIVILEGES_HASH:
9714     grant_name_hash= &func_priv_hash;
9715     elements= grant_name_hash->records;
9716     break;
9717   case PACKAGE_SPEC_PRIVILEGES_HASH:
9718     grant_name_hash= &package_spec_priv_hash;
9719     elements= grant_name_hash->records;
9720     break;
9721   case PACKAGE_BODY_PRIVILEGES_HASH:
9722     grant_name_hash= &package_body_priv_hash;
9723     elements= grant_name_hash->records;
9724     break;
9725   case PROXY_USERS_ACL:
9726     elements= acl_proxy_users.elements;
9727     break;
9728   case ROLES_MAPPINGS_HASH:
9729     roles_mappings_hash= &acl_roles_mappings;
9730     elements= roles_mappings_hash->records;
9731     break;
9732   default:
9733     DBUG_ASSERT(0);
9734     DBUG_RETURN(-1);
9735   }
9736 
9737 
9738 #ifdef EXTRA_DEBUG
9739     DBUG_PRINT("loop",("scan struct: %u  search    user: '%s'  host: '%s'",
9740                        struct_no, user_from->user.str, user_from->host.str));
9741 #endif
9742   /* Loop over elements backwards as it may reduce the number of mem-moves
9743      for dynamic arrays.
9744 
9745      We restart the loop, if we deleted or updated anything in a hash table
9746      because calling my_hash_delete or my_hash_update shuffles elements indices
9747      and we can miss some if we do only one scan.
9748   */
9749   do {
9750     restart= false;
9751     for (int idx= elements - 1; idx >= 0; idx--)
9752     {
9753       /*
9754         Get a pointer to the element.
9755       */
9756       switch (struct_no) {
9757       case USER_ACL:
9758         acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
9759         user= acl_user->user.str;
9760         host= acl_user->host.hostname;
9761       break;
9762 
9763       case DB_ACL:
9764         acl_db= &acl_dbs.at(idx);
9765         user= acl_db->user;
9766         host= acl_db->host.hostname;
9767         break;
9768 
9769       case COLUMN_PRIVILEGES_HASH:
9770       case PROC_PRIVILEGES_HASH:
9771       case FUNC_PRIVILEGES_HASH:
9772       case PACKAGE_SPEC_PRIVILEGES_HASH:
9773       case PACKAGE_BODY_PRIVILEGES_HASH:
9774         grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
9775         user= grant_name->user;
9776         host= grant_name->host.hostname;
9777         break;
9778 
9779       case PROXY_USERS_ACL:
9780         acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
9781         user= acl_proxy_user->get_user();
9782         host= acl_proxy_user->get_host();
9783         break;
9784 
9785       case ROLES_MAPPINGS_HASH:
9786         role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx);
9787         user= role_grant_pair->u_uname;
9788         host= role_grant_pair->u_hname;
9789         break;
9790 
9791       default:
9792         DBUG_ASSERT(0);
9793       }
9794       if (! user)
9795         user= "";
9796       if (! host)
9797         host= "";
9798 
9799 #ifdef EXTRA_DEBUG
9800       DBUG_PRINT("loop",("scan struct: %u  index: %u  user: '%s'  host: '%s'",
9801                          struct_no, idx, user, host));
9802 #endif
9803 
9804       if (struct_no == ROLES_MAPPINGS_HASH)
9805       {
9806         const char* role= role_grant_pair->r_uname? role_grant_pair->r_uname: "";
9807         if (user_from->is_role())
9808         {
9809           /* When searching for roles within the ROLES_MAPPINGS_HASH, we have
9810              to check both the user field as well as the role field for a match.
9811 
9812              It is possible to have a role granted to a role. If we are going
9813              to modify the mapping entry, it needs to be done on either on the
9814              "user" end (here represented by a role) or the "role" end. At least
9815              one part must match.
9816 
9817              If the "user" end has a not-empty host string, it can never match
9818              as we are searching for a role here. A role always has an empty host
9819              string.
9820           */
9821           if ((*host || strcmp(user_from->user.str, user)) &&
9822               strcmp(user_from->user.str, role))
9823             continue;
9824         }
9825         else
9826         {
9827           if (strcmp(user_from->user.str, user) ||
9828               my_strcasecmp(system_charset_info, user_from->host.str, host))
9829             continue;
9830         }
9831       }
9832       else
9833       {
9834         if (strcmp(user_from->user.str, user) ||
9835             my_strcasecmp(system_charset_info, user_from->host.str, host))
9836           continue;
9837       }
9838 
9839       result= 1; /* At least one element found. */
9840       if ( drop )
9841       {
9842         elements--;
9843         switch ( struct_no ) {
9844         case USER_ACL:
9845           free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*));
9846           delete_dynamic_element(&acl_users, idx);
9847           break;
9848 
9849         case DB_ACL:
9850           acl_dbs.del(idx);
9851           break;
9852 
9853         case COLUMN_PRIVILEGES_HASH:
9854         case PROC_PRIVILEGES_HASH:
9855         case FUNC_PRIVILEGES_HASH:
9856         case PACKAGE_SPEC_PRIVILEGES_HASH:
9857         case PACKAGE_BODY_PRIVILEGES_HASH:
9858           my_hash_delete(grant_name_hash, (uchar*) grant_name);
9859           restart= true;
9860           break;
9861 
9862         case PROXY_USERS_ACL:
9863           delete_dynamic_element(&acl_proxy_users, idx);
9864           break;
9865 
9866         case ROLES_MAPPINGS_HASH:
9867           my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair);
9868           restart= true;
9869           break;
9870 
9871         default:
9872           DBUG_ASSERT(0);
9873           break;
9874         }
9875       }
9876       else if ( user_to )
9877       {
9878         switch ( struct_no ) {
9879         case USER_ACL:
9880           acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str);
9881           acl_user->user.length= user_to->user.length;
9882           update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str));
9883           acl_user->hostname_length= strlen(acl_user->host.hostname);
9884           break;
9885 
9886         case DB_ACL:
9887           acl_db->user= strdup_root(&acl_memroot, user_to->user.str);
9888           update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str));
9889           break;
9890 
9891         case COLUMN_PRIVILEGES_HASH:
9892         case PROC_PRIVILEGES_HASH:
9893         case FUNC_PRIVILEGES_HASH:
9894         case PACKAGE_SPEC_PRIVILEGES_HASH:
9895         case PACKAGE_BODY_PRIVILEGES_HASH:
9896           {
9897             /*
9898               Save old hash key and its length to be able to properly update
9899               element position in hash.
9900             */
9901             char *old_key= grant_name->hash_key;
9902             size_t old_key_length= grant_name->key_length;
9903 
9904             /*
9905               Update the grant structure with the new user name and host name.
9906             */
9907             grant_name->set_user_details(user_to->host.str, grant_name->db,
9908                                          user_to->user.str, grant_name->tname,
9909                                          TRUE);
9910 
9911             /*
9912               Since username is part of the hash key, when the user name
9913               is renamed, the hash key is changed. Update the hash to
9914               ensure that the position matches the new hash key value
9915             */
9916             my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
9917                            old_key_length);
9918             restart= true;
9919             break;
9920           }
9921 
9922         case PROXY_USERS_ACL:
9923           acl_proxy_user->set_user (&acl_memroot, user_to->user.str);
9924           acl_proxy_user->set_host (&acl_memroot, user_to->host.str);
9925           break;
9926 
9927         case ROLES_MAPPINGS_HASH:
9928           {
9929             /*
9930               Save old hash key and its length to be able to properly update
9931               element position in hash.
9932             */
9933             char *old_key= role_grant_pair->hashkey.str;
9934             size_t old_key_length= role_grant_pair->hashkey.length;
9935             bool oom;
9936 
9937             if (user_to->is_role())
9938               oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname,
9939                                          role_grant_pair->u_hname,
9940                                          user_to->user.str, false);
9941             else
9942               oom= role_grant_pair->init(&acl_memroot, user_to->user.str,
9943                                          user_to->host.str,
9944                                          role_grant_pair->r_uname, false);
9945             if (oom)
9946               DBUG_RETURN(-1);
9947 
9948             my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair,
9949                            (uchar*) old_key, old_key_length);
9950             restart= true;
9951             break;
9952           }
9953 
9954         default:
9955           DBUG_ASSERT(0);
9956           break;
9957         }
9958 
9959       }
9960       else
9961       {
9962         /* If search is requested, we do not need to search further. */
9963         break;
9964       }
9965     }
9966   } while (restart);
9967 #ifdef EXTRA_DEBUG
9968   DBUG_PRINT("loop",("scan struct: %u  result %d", struct_no, result));
9969 #endif
9970 
9971   DBUG_RETURN(result);
9972 }
9973 
9974 
9975 /*
9976   Handle all privilege tables and in-memory privilege structures.
9977 
9978   SYNOPSIS
9979     handle_grant_data()
9980     tables                      The array with the four open tables.
9981     drop                        If user_from is to be dropped.
9982     user_from                   The the user to be searched/dropped/renamed.
9983     user_to                     The new name for the user if to be renamed,
9984                                 NULL otherwise.
9985 
9986   DESCRIPTION
9987     Go through all grant tables and in-memory grant structures and apply
9988     the requested operation.
9989     Delete from grant data if drop is true.
9990     Update in grant data if drop is false and user_to is not NULL.
9991     Search in grant data if drop is false and user_to is NULL.
9992 
9993   RETURN
9994     > 0         At least one element matched.
9995     0           OK, but no element matched.
9996     < 0         Error.
9997 */
9998 
handle_grant_data(THD * thd,Grant_tables & tables,bool drop,LEX_USER * user_from,LEX_USER * user_to)9999 static int handle_grant_data(THD *thd, Grant_tables& tables, bool drop,
10000                              LEX_USER *user_from, LEX_USER *user_to)
10001 {
10002   int result= 0;
10003   int found;
10004   bool handle_as_role= user_from->is_role();
10005   bool search_only= !drop && !user_to;
10006   DBUG_ENTER("handle_grant_data");
10007 
10008   if (user_to)
10009     DBUG_ASSERT(handle_as_role == user_to->is_role());
10010 
10011   if (search_only)
10012   {
10013     /* quickly search in-memory structures first */
10014     if (handle_as_role && find_acl_role(user_from->user.str))
10015       DBUG_RETURN(1); // found
10016 
10017     if (!handle_as_role && find_user_exact(user_from->host.str, user_from->user.str))
10018       DBUG_RETURN(1); // found
10019   }
10020 
10021   /* Handle db table. */
10022   if ((found= handle_grant_table(thd, tables.db_table(),
10023                                  DB_TABLE, drop, user_from,
10024                                  user_to)) < 0)
10025   {
10026     /* Handle of table failed, don't touch the in-memory array. */
10027     result= -1;
10028   }
10029   else
10030   {
10031     /* Handle db array. */
10032     if ((handle_grant_struct(DB_ACL, drop, user_from, user_to) || found)
10033         && ! result)
10034     {
10035       result= 1; /* At least one record/element found. */
10036       /* If search is requested, we do not need to search further. */
10037       if (search_only)
10038         goto end;
10039       acl_cache->clear(1);
10040     }
10041   }
10042 
10043   /* Handle stored routines table. */
10044   if ((found= handle_grant_table(thd, tables.procs_priv_table(),
10045                                  PROCS_PRIV_TABLE, drop,
10046                                  user_from, user_to)) < 0)
10047   {
10048     /* Handle of table failed, don't touch in-memory array. */
10049     result= -1;
10050   }
10051   else
10052   {
10053     /* Handle procs array. */
10054     if ((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10055         && ! result)
10056     {
10057       result= 1; /* At least one record/element found. */
10058       /* If search is requested, we do not need to search further. */
10059       if (search_only)
10060         goto end;
10061     }
10062     /* Handle funcs array. */
10063     if ((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10064         && ! result)
10065     {
10066       result= 1; /* At least one record/element found. */
10067       /* If search is requested, we do not need to search further. */
10068       if (search_only)
10069         goto end;
10070     }
10071     /* Handle package spec array. */
10072     if ((handle_grant_struct(PACKAGE_SPEC_PRIVILEGES_HASH,
10073                              drop, user_from, user_to) || found)
10074         && ! result)
10075     {
10076       result= 1; /* At least one record/element found. */
10077       /* If search is requested, we do not need to search further. */
10078       if (search_only)
10079         goto end;
10080     }
10081     /* Handle package body array. */
10082     if ((handle_grant_struct(PACKAGE_BODY_PRIVILEGES_HASH,
10083                              drop, user_from, user_to) || found)
10084         && ! result)
10085     {
10086       result= 1; /* At least one record/element found. */
10087       /* If search is requested, we do not need to search further. */
10088       if (search_only)
10089         goto end;
10090     }
10091   }
10092 
10093   /* Handle tables table. */
10094   if ((found= handle_grant_table(thd, tables.tables_priv_table(),
10095                                  TABLES_PRIV_TABLE, drop,
10096                                  user_from, user_to)) < 0)
10097   {
10098     /* Handle of table failed, don't touch columns and in-memory array. */
10099     result= -1;
10100   }
10101   else
10102   {
10103     if (found && ! result)
10104     {
10105       result= 1; /* At least one record found. */
10106       /* If search is requested, we do not need to search further. */
10107       if (search_only)
10108         goto end;
10109     }
10110 
10111     /* Handle columns table. */
10112     if ((found= handle_grant_table(thd, tables.columns_priv_table(),
10113                                    COLUMNS_PRIV_TABLE, drop,
10114                                    user_from, user_to)) < 0)
10115     {
10116       /* Handle of table failed, don't touch the in-memory array. */
10117       result= -1;
10118     }
10119     else
10120     {
10121       /* Handle columns hash. */
10122       if ((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10123           && ! result)
10124         result= 1; /* At least one record/element found. */
10125       if (search_only)
10126         goto end;
10127     }
10128   }
10129 
10130   /* Handle proxies_priv table. */
10131   if (tables.proxies_priv_table().table_exists())
10132   {
10133     if ((found= handle_grant_table(thd, tables.proxies_priv_table(),
10134                                    PROXIES_PRIV_TABLE, drop,
10135                                    user_from, user_to)) < 0)
10136     {
10137       /* Handle of table failed, don't touch the in-memory array. */
10138       result= -1;
10139     }
10140     else
10141     {
10142       /* Handle proxies_priv array. */
10143       if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) || found)
10144           && ! result)
10145         result= 1; /* At least one record/element found. */
10146       if (search_only)
10147         goto end;
10148     }
10149   }
10150 
10151   /* Handle roles_mapping table. */
10152   if (tables.roles_mapping_table().table_exists())
10153   {
10154     if ((found= handle_grant_table(thd, tables.roles_mapping_table(),
10155                                    ROLES_MAPPING_TABLE, drop,
10156                                    user_from, user_to)) < 0)
10157     {
10158       /* Handle of table failed, don't touch the in-memory array. */
10159       result= -1;
10160     }
10161     else
10162     {
10163       /* Handle acl_roles_mappings array */
10164       if ((handle_grant_struct(ROLES_MAPPINGS_HASH, drop, user_from, user_to) || found)
10165           && ! result)
10166         result= 1; /* At least one record/element found */
10167       if (search_only)
10168         goto end;
10169     }
10170   }
10171 
10172   /* Handle user table. */
10173   if ((found= handle_grant_table(thd, tables.user_table(), USER_TABLE,
10174                                  drop, user_from, user_to)) < 0)
10175   {
10176     /* Handle of table failed, don't touch the in-memory array. */
10177     result= -1;
10178   }
10179   else
10180   {
10181     enum enum_acl_lists what= handle_as_role ? ROLE_ACL : USER_ACL;
10182     if (((handle_grant_struct(what, drop, user_from, user_to)) || found) && !result)
10183     {
10184       result= 1; /* At least one record/element found. */
10185       DBUG_ASSERT(! search_only);
10186     }
10187   }
10188 
10189 end:
10190   DBUG_RETURN(result);
10191 }
10192 
10193 /*
10194   Create a list of users.
10195 
10196   SYNOPSIS
10197     mysql_create_user()
10198     thd                         The current thread.
10199     list                        The users to create.
10200     handle_as_role              Handle the user list as roles if true
10201 
10202   RETURN
10203     FALSE       OK.
10204     TRUE        Error.
10205 */
10206 
mysql_create_user(THD * thd,List<LEX_USER> & list,bool handle_as_role)10207 bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
10208 {
10209   int result;
10210   String wrong_users;
10211   LEX_USER *user_name;
10212   List_iterator <LEX_USER> user_list(list);
10213   bool binlog= false;
10214   bool some_users_dropped= false;
10215   DBUG_ENTER("mysql_create_user");
10216   DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
10217 
10218   if (handle_as_role && sp_process_definer(thd))
10219     DBUG_RETURN(TRUE);
10220 
10221   /* CREATE USER may be skipped on replication client. */
10222   Grant_tables tables(Table_user | Table_db |
10223                       Table_tables_priv | Table_columns_priv |
10224                       Table_procs_priv | Table_proxies_priv |
10225                       Table_roles_mapping, TL_WRITE);
10226   if ((result= tables.open_and_lock(thd)))
10227     DBUG_RETURN(result != 1);
10228 
10229   mysql_rwlock_wrlock(&LOCK_grant);
10230   mysql_mutex_lock(&acl_cache->lock);
10231 
10232   while ((user_name= user_list++))
10233   {
10234     if (user_name->user.str == current_user.str)
10235     {
10236       append_str(&wrong_users, STRING_WITH_LEN("CURRENT_USER"));
10237       result= TRUE;
10238       continue;
10239     }
10240 
10241     if (user_name->user.str == current_role.str)
10242     {
10243       append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
10244       result= TRUE;
10245       continue;
10246     }
10247 
10248     if (handle_as_role && is_invalid_role_name(user_name->user.str))
10249     {
10250       append_user(thd, &wrong_users, user_name);
10251       result= TRUE;
10252       continue;
10253     }
10254 
10255     if (!user_name->host.str)
10256       user_name->host= host_not_specified;
10257 
10258     if (fix_lex_user(thd, user_name))
10259     {
10260       append_user(thd, &wrong_users, user_name);
10261       result= TRUE;
10262       continue;
10263     }
10264 
10265     /*
10266       Search all in-memory structures and grant tables
10267       for a mention of the new user/role name.
10268     */
10269     if (handle_grant_data(thd, tables, 0, user_name, NULL))
10270     {
10271       if (thd->lex->create_info.or_replace())
10272       {
10273         // Drop the existing user
10274         if (handle_grant_data(thd, tables, 1, user_name, NULL) <= 0)
10275         {
10276           // DROP failed
10277           append_user(thd, &wrong_users, user_name);
10278           result= true;
10279           continue;
10280         }
10281         else
10282           some_users_dropped= true;
10283         // Proceed with the creation
10284       }
10285       else if (thd->lex->create_info.if_not_exists())
10286       {
10287         binlog= true;
10288         if (handle_as_role)
10289           push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10290                               ER_ROLE_CREATE_EXISTS,
10291                               ER_THD(thd, ER_ROLE_CREATE_EXISTS),
10292                               user_name->user.str);
10293         else
10294           push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10295                               ER_USER_CREATE_EXISTS,
10296                               ER_THD(thd, ER_USER_CREATE_EXISTS),
10297                               user_name->user.str, user_name->host.str);
10298         continue;
10299       }
10300       else
10301       {
10302         // "CREATE USER user1" for an existing user
10303         append_user(thd, &wrong_users, user_name);
10304         result= true;
10305         continue;
10306       }
10307     }
10308 
10309     if (replace_user_table(thd, tables.user_table(), *user_name, 0, 0, 1, 0))
10310     {
10311       append_user(thd, &wrong_users, user_name);
10312       result= TRUE;
10313       continue;
10314     }
10315     binlog= true;
10316 
10317     // every created role is automatically granted to its creator-admin
10318     if (handle_as_role)
10319     {
10320       ACL_USER_BASE *grantee= find_acl_user_base(thd->lex->definer->user.str,
10321                                                  thd->lex->definer->host.str);
10322       ACL_ROLE *role= find_acl_role(user_name->user.str);
10323 
10324       /*
10325         just like with routines, views, triggers, and events we allow
10326         non-existant definers here with a warning (see sp_process_definer())
10327       */
10328       if (grantee)
10329         add_role_user_mapping(grantee, role);
10330 
10331       /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
10332          Roles_mapping_table instead of TABLE directly. */
10333       if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
10334                                       &thd->lex->definer->user,
10335                                       &thd->lex->definer->host,
10336                                       &user_name->user, true,
10337                                       NULL, false))
10338       {
10339         append_user(thd, &wrong_users, user_name);
10340         if (grantee)
10341           undo_add_role_user_mapping(grantee, role);
10342         result= TRUE;
10343       }
10344       else if (grantee)
10345              update_role_mapping(&thd->lex->definer->user,
10346                                  &thd->lex->definer->host,
10347                                  &user_name->user, true, NULL, false);
10348     }
10349   }
10350 
10351   if (result && some_users_dropped && !handle_as_role)
10352   {
10353     /* Rebuild in-memory structs, since 'acl_users' has been modified */
10354     rebuild_check_host();
10355     rebuild_role_grants();
10356   }
10357 
10358   mysql_mutex_unlock(&acl_cache->lock);
10359 
10360   if (result)
10361   {
10362     my_error(ER_CANNOT_USER, MYF(0),
10363              (handle_as_role) ? "CREATE ROLE" : "CREATE USER",
10364              wrong_users.c_ptr_safe());
10365   }
10366 
10367   if (binlog)
10368     result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10369 
10370   mysql_rwlock_unlock(&LOCK_grant);
10371   DBUG_RETURN(result);
10372 }
10373 
10374 /*
10375   Drop a list of users and all their privileges.
10376 
10377   SYNOPSIS
10378     mysql_drop_user()
10379     thd                         The current thread.
10380     list                        The users to drop.
10381 
10382   RETURN
10383     FALSE       OK.
10384     TRUE        Error.
10385 */
10386 
mysql_drop_user(THD * thd,List<LEX_USER> & list,bool handle_as_role)10387 bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
10388 {
10389   int result;
10390   String wrong_users;
10391   LEX_USER *user_name, *tmp_user_name;
10392   List_iterator <LEX_USER> user_list(list);
10393   bool binlog= false;
10394   sql_mode_t old_sql_mode= thd->variables.sql_mode;
10395   DBUG_ENTER("mysql_drop_user");
10396   DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
10397 
10398   /* DROP USER may be skipped on replication client. */
10399   Grant_tables tables(Table_user | Table_db |
10400                       Table_tables_priv | Table_columns_priv |
10401                       Table_procs_priv | Table_proxies_priv |
10402                       Table_roles_mapping, TL_WRITE);
10403   if ((result= tables.open_and_lock(thd)))
10404     DBUG_RETURN(result != 1);
10405 
10406   thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
10407 
10408   mysql_rwlock_wrlock(&LOCK_grant);
10409   mysql_mutex_lock(&acl_cache->lock);
10410 
10411   while ((tmp_user_name= user_list++))
10412   {
10413     int rc;
10414     user_name= get_current_user(thd, tmp_user_name, false);
10415     if (!user_name)
10416     {
10417       thd->clear_error();
10418       append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
10419       result= TRUE;
10420       continue;
10421     }
10422 
10423     if (handle_as_role != user_name->is_role())
10424     {
10425       append_user(thd, &wrong_users, user_name);
10426       result= TRUE;
10427       continue;
10428     }
10429 
10430     if ((rc= handle_grant_data(thd, tables, 1, user_name, NULL)) > 0)
10431     {
10432       // The user or role was successfully deleted
10433       binlog= true;
10434       continue;
10435     }
10436 
10437     if (rc == 0 && thd->lex->if_exists())
10438     {
10439       // "DROP USER IF EXISTS user1" for a non-existing user or role
10440       if (handle_as_role)
10441         push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10442                             ER_ROLE_DROP_EXISTS,
10443                             ER_THD(thd, ER_ROLE_DROP_EXISTS),
10444                             user_name->user.str);
10445       else
10446         push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10447                             ER_USER_DROP_EXISTS,
10448                             ER_THD(thd, ER_USER_DROP_EXISTS),
10449                             user_name->user.str, user_name->host.str);
10450       binlog= true;
10451       continue;
10452     }
10453     // Internal error, or "DROP USER user1" for a non-existing user
10454     append_user(thd, &wrong_users, user_name);
10455     result= TRUE;
10456   }
10457 
10458   if (!handle_as_role)
10459   {
10460     /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
10461     rebuild_check_host();
10462 
10463     /*
10464       Rebuild every user's role_grants since 'acl_users' has been sorted
10465       and old pointers to ACL_USER elements are no longer valid
10466     */
10467     rebuild_role_grants();
10468   }
10469 
10470   mysql_mutex_unlock(&acl_cache->lock);
10471 
10472   if (result)
10473     my_error(ER_CANNOT_USER, MYF(0),
10474              (handle_as_role) ? "DROP ROLE" : "DROP USER",
10475              wrong_users.c_ptr_safe());
10476 
10477   if (binlog)
10478     result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10479 
10480   mysql_rwlock_unlock(&LOCK_grant);
10481   thd->variables.sql_mode= old_sql_mode;
10482   DBUG_RETURN(result);
10483 }
10484 
10485 /*
10486   Rename a user.
10487 
10488   SYNOPSIS
10489     mysql_rename_user()
10490     thd                         The current thread.
10491     list                        The user name pairs: (from, to).
10492 
10493   RETURN
10494     FALSE       OK.
10495     TRUE        Error.
10496 */
10497 
mysql_rename_user(THD * thd,List<LEX_USER> & list)10498 bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
10499 {
10500   int result;
10501   String wrong_users;
10502   LEX_USER *user_from, *tmp_user_from;
10503   LEX_USER *user_to, *tmp_user_to;
10504   List_iterator <LEX_USER> user_list(list);
10505   bool some_users_renamed= FALSE;
10506   DBUG_ENTER("mysql_rename_user");
10507 
10508   /* RENAME USER may be skipped on replication client. */
10509   Grant_tables tables(Table_user | Table_db |
10510                       Table_tables_priv | Table_columns_priv |
10511                       Table_procs_priv | Table_proxies_priv |
10512                       Table_roles_mapping, TL_WRITE);
10513   if ((result= tables.open_and_lock(thd)))
10514     DBUG_RETURN(result != 1);
10515 
10516   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
10517 
10518   mysql_rwlock_wrlock(&LOCK_grant);
10519   mysql_mutex_lock(&acl_cache->lock);
10520 
10521   while ((tmp_user_from= user_list++))
10522   {
10523     tmp_user_to= user_list++;
10524     if (!(user_from= get_current_user(thd, tmp_user_from, false)))
10525     {
10526       append_user(thd, &wrong_users, user_from);
10527       result= TRUE;
10528       continue;
10529     }
10530     if (!(user_to= get_current_user(thd, tmp_user_to, false)))
10531     {
10532       append_user(thd, &wrong_users, user_to);
10533       result= TRUE;
10534       continue;
10535     }
10536     DBUG_ASSERT(!user_from->is_role());
10537     DBUG_ASSERT(!user_to->is_role());
10538 
10539     /*
10540       Search all in-memory structures and grant tables
10541       for a mention of the new user name.
10542     */
10543     if (handle_grant_data(thd, tables, 0, user_to, NULL) ||
10544         handle_grant_data(thd, tables, 0, user_from, user_to) <= 0)
10545     {
10546       /* NOTE TODO renaming roles is not yet implemented */
10547       append_user(thd, &wrong_users, user_from);
10548       result= TRUE;
10549       continue;
10550     }
10551     some_users_renamed= TRUE;
10552   }
10553 
10554   /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
10555   rebuild_check_host();
10556 
10557   /*
10558     Rebuild every user's role_grants since 'acl_users' has been sorted
10559     and old pointers to ACL_USER elements are no longer valid
10560   */
10561   rebuild_role_grants();
10562 
10563   mysql_mutex_unlock(&acl_cache->lock);
10564 
10565   if (result)
10566     my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
10567 
10568   if (some_users_renamed && mysql_bin_log.is_open())
10569     result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10570 
10571   mysql_rwlock_unlock(&LOCK_grant);
10572   DBUG_RETURN(result);
10573 }
10574 
10575 /*
10576   Alter a user's connection and resource settings.
10577 
10578   SYNOPSIS
10579     mysql_alter_user()
10580     thd                         The current thread.
10581     list                        The users to alter.
10582 
10583   RETURN
10584     > 0         Error. Error message already sent.
10585     0           OK.
10586 */
mysql_alter_user(THD * thd,List<LEX_USER> & users_list)10587 int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
10588 {
10589   DBUG_ENTER("mysql_alter_user");
10590   int result= 0;
10591   String wrong_users;
10592   bool some_users_altered= false;
10593 
10594   /* The only table we're altering is the user table. */
10595   Grant_tables tables(Table_user, TL_WRITE);
10596   if ((result= tables.open_and_lock(thd)))
10597     DBUG_RETURN(result != 1);
10598 
10599   /* Lock ACL data structures until we finish altering all users. */
10600   mysql_rwlock_wrlock(&LOCK_grant);
10601   mysql_mutex_lock(&acl_cache->lock);
10602 
10603   LEX_USER *tmp_lex_user;
10604   List_iterator<LEX_USER> users_list_iterator(users_list);
10605   while ((tmp_lex_user= users_list_iterator++))
10606   {
10607     LEX_USER* lex_user= get_current_user(thd, tmp_lex_user, false);
10608     if (!lex_user ||
10609         fix_lex_user(thd, lex_user) ||
10610         replace_user_table(thd, tables.user_table(), *lex_user, 0,
10611                            false, false, true))
10612     {
10613       thd->clear_error();
10614       append_user(thd, &wrong_users, tmp_lex_user);
10615       result= TRUE;
10616       continue;
10617     }
10618     some_users_altered= true;
10619   }
10620 
10621   /* Unlock ACL data structures. */
10622   mysql_mutex_unlock(&acl_cache->lock);
10623   mysql_rwlock_unlock(&LOCK_grant);
10624 
10625   if (result)
10626   {
10627     /* 'if exists' flag leads to warnings instead of errors. */
10628     if (thd->lex->create_info.if_exists())
10629     {
10630       push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10631                           ER_CANNOT_USER,
10632                           ER_THD(thd, ER_CANNOT_USER),
10633                           "ALTER USER", wrong_users.c_ptr_safe());
10634       result= FALSE;
10635     }
10636     else
10637     {
10638       my_error(ER_CANNOT_USER, MYF(0),
10639                "ALTER USER",
10640                wrong_users.c_ptr_safe());
10641     }
10642   }
10643 
10644   if (some_users_altered)
10645     result|= write_bin_log(thd, FALSE, thd->query(),
10646                                      thd->query_length());
10647   DBUG_RETURN(result);
10648 }
10649 
10650 
10651 static bool
mysql_revoke_sp_privs(THD * thd,Grant_tables * tables,const Sp_handler * sph,const LEX_USER * lex_user)10652 mysql_revoke_sp_privs(THD *thd,
10653                       Grant_tables *tables,
10654                       const Sp_handler *sph,
10655                       const LEX_USER *lex_user)
10656 {
10657   bool rc= false;
10658   uint counter, revoked;
10659   do {
10660     HASH *hash= sph->get_priv_hash();
10661     for (counter= 0, revoked= 0 ; counter < hash->records ; )
10662     {
10663       const char *user,*host;
10664       GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
10665       user= safe_str(grant_proc->user);
10666       host= safe_str(grant_proc->host.hostname);
10667 
10668       if (!strcmp(lex_user->user.str, user) &&
10669           !strcmp(lex_user->host.str, host))
10670       {
10671         if (replace_routine_table(thd, grant_proc,
10672                                   tables->procs_priv_table().table(),
10673                                   *lex_user,
10674                                   grant_proc->db, grant_proc->tname,
10675                                   sph, ~(ulong)0, 1) == 0)
10676         {
10677           revoked= 1;
10678           continue;
10679         }
10680         rc= true;  // Something went wrong
10681       }
10682       counter++;
10683     }
10684   } while (revoked);
10685   return rc;
10686 }
10687 
10688 
10689 /*
10690   Revoke all privileges from a list of users.
10691 
10692   SYNOPSIS
10693     mysql_revoke_all()
10694     thd                         The current thread.
10695     list                        The users to revoke all privileges from.
10696 
10697   RETURN
10698     > 0         Error. Error message already sent.
10699     0           OK.
10700     < 0         Error. Error message not yet sent.
10701 */
10702 
mysql_revoke_all(THD * thd,List<LEX_USER> & list)10703 bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
10704 {
10705   uint counter, revoked;
10706   int result;
10707   ACL_DB *acl_db;
10708   DBUG_ENTER("mysql_revoke_all");
10709 
10710   Grant_tables tables(Table_user | Table_db |
10711                       Table_tables_priv | Table_columns_priv |
10712                       Table_procs_priv | Table_proxies_priv |
10713                       Table_roles_mapping, TL_WRITE);
10714   if ((result= tables.open_and_lock(thd)))
10715     DBUG_RETURN(result != 1);
10716 
10717   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
10718 
10719   mysql_rwlock_wrlock(&LOCK_grant);
10720   mysql_mutex_lock(&acl_cache->lock);
10721 
10722   LEX_USER *lex_user, *tmp_lex_user;
10723   List_iterator <LEX_USER> user_list(list);
10724   while ((tmp_lex_user= user_list++))
10725   {
10726     if (!(lex_user= get_current_user(thd, tmp_lex_user, false)))
10727     {
10728       result= -1;
10729       continue;
10730     }
10731 
10732     /* This is not a role and the user could not be found */
10733     if (!lex_user->is_role() &&
10734         !find_user_exact(lex_user->host.str, lex_user->user.str))
10735     {
10736       result= -1;
10737       continue;
10738     }
10739 
10740     if (replace_user_table(thd, tables.user_table(), *lex_user,
10741                            ~(ulong)0, 1, 0, 0))
10742     {
10743       result= -1;
10744       continue;
10745     }
10746 
10747     /* Remove db access privileges */
10748     /*
10749       Because acl_dbs and column_priv_hash shrink and may re-order
10750       as privileges are removed, removal occurs in a repeated loop
10751       until no more privileges are revoked.
10752      */
10753     do
10754     {
10755       for (counter= 0, revoked= 0 ; counter < acl_dbs.elements() ; )
10756       {
10757 	const char *user,*host;
10758 
10759         acl_db=&acl_dbs.at(counter);
10760 
10761         user= safe_str(acl_db->user);
10762         host= safe_str(acl_db->host.hostname);
10763 
10764 	if (!strcmp(lex_user->user.str, user) &&
10765             !strcmp(lex_user->host.str, host))
10766 	{
10767       /* TODO(cvicentiu) refactor replace_db_table to use
10768          Db_table instead of TABLE directly. */
10769 	  if (!replace_db_table(tables.db_table().table(), acl_db->db, *lex_user,
10770                             ~(ulong)0, 1))
10771 	  {
10772 	    /*
10773 	      Don't increment counter as replace_db_table deleted the
10774 	      current element in acl_dbs.
10775 	     */
10776 	    revoked= 1;
10777 	    continue;
10778 	  }
10779 	  result= -1; // Something went wrong
10780 	}
10781 	counter++;
10782       }
10783     } while (revoked);
10784 
10785     /* Remove column access */
10786     do
10787     {
10788       for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
10789       {
10790 	const char *user,*host;
10791         GRANT_TABLE *grant_table=
10792           (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
10793         user= safe_str(grant_table->user);
10794         host= safe_str(grant_table->host.hostname);
10795 
10796 	if (!strcmp(lex_user->user.str,user) &&
10797             !strcmp(lex_user->host.str, host))
10798 	{
10799       /* TODO(cvicentiu) refactor replace_db_table to use
10800          Db_table instead of TABLE directly. */
10801 	  if (replace_table_table(thd, grant_table,
10802                               tables.tables_priv_table().table(),
10803                               *lex_user, grant_table->db,
10804 				  grant_table->tname, ~(ulong)0, 0, 1))
10805 	  {
10806 	    result= -1;
10807 	  }
10808 	  else
10809 	  {
10810 	    if (!grant_table->cols)
10811 	    {
10812 	      revoked= 1;
10813 	      continue;
10814 	    }
10815 	    List<LEX_COLUMN> columns;
10816         /* TODO(cvicentiu) refactor replace_db_table to use
10817            Db_table instead of TABLE directly. */
10818 	    if (!replace_column_table(grant_table,
10819                                       tables.columns_priv_table().table(),
10820                                       *lex_user, columns, grant_table->db,
10821 				      grant_table->tname, ~(ulong)0, 1))
10822 	    {
10823 	      revoked= 1;
10824 	      continue;
10825 	    }
10826 	    result= -1;
10827 	  }
10828 	}
10829 	counter++;
10830       }
10831     } while (revoked);
10832 
10833     /* Remove procedure access */
10834     if (mysql_revoke_sp_privs(thd, &tables, &sp_handler_function, lex_user) ||
10835         mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user) ||
10836         mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_spec, lex_user) ||
10837         mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_body, lex_user))
10838       result= -1;
10839 
10840     ACL_USER_BASE *user_or_role;
10841     /* remove role grants */
10842     if (lex_user->is_role())
10843     {
10844       /* this can not fail due to get_current_user already having searched for it */
10845       user_or_role= find_acl_role(lex_user->user.str);
10846     }
10847     else
10848     {
10849       user_or_role= find_user_exact(lex_user->host.str, lex_user->user.str);
10850     }
10851     /*
10852       Find every role grant pair matching the role_grants array and remove it,
10853       both from the acl_roles_mappings and the roles_mapping table
10854     */
10855     for (counter= 0; counter < user_or_role->role_grants.elements; counter++)
10856     {
10857       ACL_ROLE *role_grant= *dynamic_element(&user_or_role->role_grants,
10858                                              counter, ACL_ROLE**);
10859       ROLE_GRANT_PAIR *pair = find_role_grant_pair(&lex_user->user,
10860                                                    &lex_user->host,
10861                                                    &role_grant->user);
10862       /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
10863          Roles_mapping_table instead of TABLE directly. */
10864       if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
10865                                       &lex_user->user, &lex_user->host,
10866                                       &role_grant->user, false, pair, true))
10867       {
10868         result= -1; //Something went wrong
10869       }
10870       update_role_mapping(&lex_user->user, &lex_user->host,
10871                           &role_grant->user, false, pair, true);
10872       /*
10873         Delete from the parent_grantee array of the roles granted,
10874         the entry pointing to this user_or_role
10875       */
10876       remove_ptr_from_dynarray(&role_grant->parent_grantee, user_or_role);
10877     }
10878     /* TODO
10879        How to handle an error in the replace_roles_mapping_table, in
10880        regards to the privileges held in memory
10881     */
10882 
10883     /* Finally, clear the role_grants array */
10884     if (counter == user_or_role->role_grants.elements)
10885     {
10886       reset_dynamic(&user_or_role->role_grants);
10887     }
10888     /*
10889       If we are revoking from a role, we need to update all the parent grantees
10890     */
10891     if (lex_user->is_role())
10892     {
10893       propagate_role_grants((ACL_ROLE *)user_or_role, PRIVS_TO_MERGE::ALL);
10894     }
10895   }
10896 
10897   mysql_mutex_unlock(&acl_cache->lock);
10898 
10899   if (result)
10900     my_message(ER_REVOKE_GRANTS, ER_THD(thd, ER_REVOKE_GRANTS), MYF(0));
10901 
10902   result= result |
10903     write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10904 
10905   mysql_rwlock_unlock(&LOCK_grant);
10906 
10907   DBUG_RETURN(result);
10908 }
10909 
10910 
10911 
10912 
10913 /**
10914   If the defining user for a routine does not exist, then the ACL lookup
10915   code should raise two errors which we should intercept.  We convert the more
10916   descriptive error into a warning, and consume the other.
10917 
10918   If any other errors are raised, then we set a flag that should indicate
10919   that there was some failure we should complain at a higher level.
10920 */
10921 class Silence_routine_definer_errors : public Internal_error_handler
10922 {
10923 public:
Silence_routine_definer_errors()10924   Silence_routine_definer_errors()
10925     : is_grave(FALSE)
10926   {}
10927 
~Silence_routine_definer_errors()10928   virtual ~Silence_routine_definer_errors()
10929   {}
10930 
10931   virtual bool handle_condition(THD *thd,
10932                                 uint sql_errno,
10933                                 const char* sqlstate,
10934                                 Sql_condition::enum_warning_level *level,
10935                                 const char* msg,
10936                                 Sql_condition ** cond_hdl);
10937 
has_errors()10938   bool has_errors() { return is_grave; }
10939 
10940 private:
10941   bool is_grave;
10942 };
10943 
10944 bool
handle_condition(THD * thd,uint sql_errno,const char *,Sql_condition::enum_warning_level * level,const char * msg,Sql_condition ** cond_hdl)10945 Silence_routine_definer_errors::handle_condition(
10946   THD *thd,
10947   uint sql_errno,
10948   const char*,
10949   Sql_condition::enum_warning_level *level,
10950   const char* msg,
10951   Sql_condition ** cond_hdl)
10952 {
10953   *cond_hdl= NULL;
10954   if (*level == Sql_condition::WARN_LEVEL_ERROR)
10955   {
10956     switch (sql_errno)
10957     {
10958       case ER_NONEXISTING_PROC_GRANT:
10959         /* Convert the error into a warning. */
10960         push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
10961                      sql_errno, msg);
10962         return TRUE;
10963       default:
10964         is_grave= TRUE;
10965     }
10966   }
10967 
10968   return FALSE;
10969 }
10970 
10971 
10972 /**
10973   Revoke privileges for all users on a stored procedure.  Use an error handler
10974   that converts errors about missing grants into warnings.
10975 
10976   @param
10977     thd                         The current thread.
10978   @param
10979     db				DB of the stored procedure
10980   @param
10981     name			Name of the stored procedure
10982 
10983   @retval
10984     0           OK.
10985   @retval
10986     < 0         Error. Error message not yet sent.
10987 */
10988 
sp_revoke_privileges(THD * thd,const char * sp_db,const char * sp_name,const Sp_handler * sph)10989 bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
10990                           const Sp_handler *sph)
10991 {
10992   uint counter, revoked;
10993   int result;
10994   HASH *hash= sph->get_priv_hash();
10995   Silence_routine_definer_errors error_handler;
10996   DBUG_ENTER("sp_revoke_privileges");
10997 
10998   Grant_tables tables(Table_user | Table_db |
10999                       Table_tables_priv | Table_columns_priv |
11000                       Table_procs_priv | Table_proxies_priv |
11001                       Table_roles_mapping, TL_WRITE);
11002   if ((result= tables.open_and_lock(thd)))
11003     DBUG_RETURN(result != 1);
11004 
11005   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
11006 
11007   /* Be sure to pop this before exiting this scope! */
11008   thd->push_internal_handler(&error_handler);
11009 
11010   mysql_rwlock_wrlock(&LOCK_grant);
11011   mysql_mutex_lock(&acl_cache->lock);
11012 
11013   /* Remove procedure access */
11014   do
11015   {
11016     for (counter= 0, revoked= 0 ; counter < hash->records ; )
11017     {
11018       GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
11019       if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
11020 	  !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
11021       {
11022         LEX_USER lex_user;
11023 	lex_user.user.str= grant_proc->user;
11024 	lex_user.user.length= strlen(grant_proc->user);
11025         lex_user.host.str= safe_str(grant_proc->host.hostname);
11026         lex_user.host.length= strlen(lex_user.host.str);
11027         if (replace_routine_table(thd, grant_proc,
11028                                   tables.procs_priv_table().table(), lex_user,
11029                                   grant_proc->db, grant_proc->tname,
11030                                   sph, ~(ulong)0, 1) == 0)
11031 	{
11032 	  revoked= 1;
11033 	  continue;
11034 	}
11035       }
11036       counter++;
11037     }
11038   } while (revoked);
11039 
11040   mysql_mutex_unlock(&acl_cache->lock);
11041   mysql_rwlock_unlock(&LOCK_grant);
11042 
11043   thd->pop_internal_handler();
11044 
11045   DBUG_RETURN(error_handler.has_errors());
11046 }
11047 
11048 
11049 /**
11050   Grant EXECUTE,ALTER privilege for a stored procedure
11051 
11052   @param thd The current thread.
11053   @param sp_db
11054   @param sp_name
11055   @param sph
11056 
11057   @return
11058     @retval FALSE Success
11059     @retval TRUE An error occurred. Error message not yet sent.
11060 */
11061 
sp_grant_privileges(THD * thd,const char * sp_db,const char * sp_name,const Sp_handler * sph)11062 bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
11063                          const Sp_handler *sph)
11064 {
11065   Security_context *sctx= thd->security_ctx;
11066   LEX_USER *combo;
11067   TABLE_LIST tables[1];
11068   List<LEX_USER> user_list;
11069   bool result;
11070   ACL_USER *au;
11071   Dummy_error_handler error_handler;
11072   DBUG_ENTER("sp_grant_privileges");
11073 
11074   if (!(combo=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
11075     DBUG_RETURN(TRUE);
11076 
11077   combo->user.str= (char *) sctx->priv_user;
11078 
11079   mysql_mutex_lock(&acl_cache->lock);
11080   if ((au= find_user_exact(combo->host.str= (char *) sctx->priv_host,
11081                            combo->user.str)))
11082     goto found_acl;
11083 
11084   mysql_mutex_unlock(&acl_cache->lock);
11085   DBUG_RETURN(TRUE);
11086 
11087  found_acl:
11088   mysql_mutex_unlock(&acl_cache->lock);
11089 
11090   bzero((char*)tables, sizeof(TABLE_LIST));
11091   user_list.empty();
11092 
11093   tables->db.str= sp_db;
11094   tables->db.length= sp_db ? strlen(sp_db) : 0;
11095   tables->table_name.str= tables->alias.str= sp_name;
11096   tables->table_name.length= tables->alias.length= sp_name ? strlen(sp_name) : 0;
11097 
11098   thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str));
11099   thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str));
11100 
11101   combo->reset_auth();
11102 
11103   if(au)
11104   {
11105     combo->plugin= au->plugin;
11106     combo->auth= au->auth_string;
11107   }
11108 
11109   if (user_list.push_back(combo, thd->mem_root))
11110     DBUG_RETURN(TRUE);
11111 
11112   thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
11113   thd->lex->ssl_cipher= thd->lex->x509_subject= thd->lex->x509_issuer= 0;
11114   bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
11115 
11116   /*
11117     Only care about whether the operation failed or succeeded
11118     as all errors will be handled later.
11119   */
11120   thd->push_internal_handler(&error_handler);
11121   result= mysql_routine_grant(thd, tables, sph, user_list,
11122                               DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
11123   thd->pop_internal_handler();
11124   DBUG_RETURN(result);
11125 }
11126 
11127 
11128 /**
11129   Validate if a user can proxy as another user
11130 
11131   @thd                     current thread
11132   @param user              the logged in user (proxy user)
11133   @param authenticated_as  the effective user a plugin is trying to
11134                            impersonate as (proxied user)
11135   @return                  proxy user definition
11136     @retval NULL           proxy user definition not found or not applicable
11137     @retval non-null       the proxy user data
11138 */
11139 
11140 static ACL_PROXY_USER *
acl_find_proxy_user(const char * user,const char * host,const char * ip,const char * authenticated_as,bool * proxy_used)11141 acl_find_proxy_user(const char *user, const char *host, const char *ip,
11142                     const char *authenticated_as, bool *proxy_used)
11143 {
11144   uint i;
11145   /* if the proxied and proxy user are the same return OK */
11146   DBUG_ENTER("acl_find_proxy_user");
11147   DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
11148                       user, host, ip, authenticated_as));
11149 
11150   if (!strcmp(authenticated_as, user))
11151   {
11152     DBUG_PRINT ("info", ("user is the same as authenticated_as"));
11153     DBUG_RETURN (NULL);
11154   }
11155 
11156   *proxy_used= TRUE;
11157   for (i=0; i < acl_proxy_users.elements; i++)
11158   {
11159     ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11160                                            ACL_PROXY_USER *);
11161     if (proxy->matches(host, user, ip, authenticated_as))
11162       DBUG_RETURN(proxy);
11163   }
11164 
11165   DBUG_RETURN(NULL);
11166 }
11167 
11168 
11169 bool
acl_check_proxy_grant_access(THD * thd,const char * host,const char * user,bool with_grant)11170 acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
11171                              bool with_grant)
11172 {
11173   DBUG_ENTER("acl_check_proxy_grant_access");
11174   DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
11175                       (int) with_grant));
11176   if (!initialized)
11177   {
11178     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
11179     DBUG_RETURN(1);
11180   }
11181 
11182   /* replication slave thread can do anything */
11183   if (thd->slave_thread)
11184   {
11185     DBUG_PRINT("info", ("replication slave"));
11186     DBUG_RETURN(FALSE);
11187   }
11188 
11189   /*
11190     one can grant proxy for self to others.
11191     Security context in THD contains two pairs of (user,host):
11192     1. (user,host) pair referring to inbound connection.
11193     2. (priv_user,priv_host) pair obtained from mysql.user table after doing
11194         authentication of incoming connection.
11195     Privileges should be checked wrt (priv_user, priv_host) tuple, because
11196     (user,host) pair obtained from inbound connection may have different
11197     values than what is actually stored in mysql.user table and while granting
11198     or revoking proxy privilege, user is expected to provide entries mentioned
11199     in mysql.user table.
11200   */
11201   if (!strcmp(thd->security_ctx->priv_user, user) &&
11202       !my_strcasecmp(system_charset_info, host,
11203                      thd->security_ctx->priv_host))
11204   {
11205     DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
11206                         thd->security_ctx->priv_user, user,
11207                         host, thd->security_ctx->priv_host));
11208     DBUG_RETURN(FALSE);
11209   }
11210 
11211   mysql_mutex_lock(&acl_cache->lock);
11212 
11213   /* check for matching WITH PROXY rights */
11214   for (uint i=0; i < acl_proxy_users.elements; i++)
11215   {
11216     ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11217                                            ACL_PROXY_USER *);
11218     if (proxy->matches(thd->security_ctx->host,
11219                        thd->security_ctx->user,
11220                        thd->security_ctx->ip,
11221                        user) &&
11222         proxy->get_with_grant())
11223     {
11224       DBUG_PRINT("info", ("found"));
11225       mysql_mutex_unlock(&acl_cache->lock);
11226       DBUG_RETURN(FALSE);
11227     }
11228   }
11229 
11230   mysql_mutex_unlock(&acl_cache->lock);
11231   my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
11232            thd->security_ctx->user,
11233            thd->security_ctx->host_or_ip);
11234   DBUG_RETURN(TRUE);
11235 }
11236 
11237 
11238 static bool
show_proxy_grants(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)11239 show_proxy_grants(THD *thd, const char *username, const char *hostname,
11240                   char *buff, size_t buffsize)
11241 {
11242   Protocol *protocol= thd->protocol;
11243   int error= 0;
11244 
11245   for (uint i=0; i < acl_proxy_users.elements; i++)
11246   {
11247     ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11248                                            ACL_PROXY_USER *);
11249     if (proxy->granted_on(hostname, username))
11250     {
11251       String global(buff, buffsize, system_charset_info);
11252       global.length(0);
11253       proxy->print_grant(&global);
11254       protocol->prepare_for_resend();
11255       protocol->store(global.ptr(), global.length(), global.charset());
11256       if (protocol->write())
11257       {
11258         error= -1;
11259         break;
11260       }
11261     }
11262   }
11263   return error;
11264 }
11265 
enabled_roles_insert(ACL_USER_BASE * role,void * context_data)11266 static int enabled_roles_insert(ACL_USER_BASE *role, void *context_data)
11267 {
11268   TABLE *table= (TABLE*) context_data;
11269   DBUG_ASSERT(role->flags & IS_ROLE);
11270 
11271   restore_record(table, s->default_values);
11272   table->field[0]->set_notnull();
11273   table->field[0]->store(role->user.str, role->user.length,
11274                          system_charset_info);
11275   if (schema_table_store_record(table->in_use, table))
11276     return -1;
11277   return 0;
11278 }
11279 
11280 struct APPLICABLE_ROLES_DATA
11281 {
11282   TABLE *table;
11283   const LEX_CSTRING host;
11284   const LEX_CSTRING user_and_host;
11285   ACL_USER *user;
11286 };
11287 
11288 static int
applicable_roles_insert(ACL_USER_BASE * grantee,ACL_ROLE * role,void * ptr)11289 applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr)
11290 {
11291   APPLICABLE_ROLES_DATA *data= (APPLICABLE_ROLES_DATA *)ptr;
11292   CHARSET_INFO *cs= system_charset_info;
11293   TABLE *table= data->table;
11294   bool is_role= grantee != data->user;
11295   const LEX_CSTRING *user_and_host= is_role ? &grantee->user
11296                                            : &data->user_and_host;
11297   const LEX_CSTRING *host= is_role ? &empty_clex_str : &data->host;
11298 
11299   restore_record(table, s->default_values);
11300   table->field[0]->store(user_and_host->str, user_and_host->length, cs);
11301   table->field[1]->store(role->user.str, role->user.length, cs);
11302 
11303   ROLE_GRANT_PAIR *pair=
11304     find_role_grant_pair(&grantee->user, host, &role->user);
11305   DBUG_ASSERT(pair);
11306 
11307   if (pair->with_admin)
11308     table->field[2]->store(STRING_WITH_LEN("YES"), cs);
11309   else
11310     table->field[2]->store(STRING_WITH_LEN("NO"), cs);
11311 
11312   /* Default role is only valid when looking at a role granted to a user. */
11313   if (!is_role)
11314   {
11315     if (data->user->default_rolename.length &&
11316         lex_string_eq(&data->user->default_rolename, &role->user))
11317       table->field[3]->store(STRING_WITH_LEN("YES"), cs);
11318     else
11319       table->field[3]->store(STRING_WITH_LEN("NO"), cs);
11320     table->field[3]->set_notnull();
11321   }
11322 
11323   if (schema_table_store_record(table->in_use, table))
11324     return -1;
11325   return 0;
11326 }
11327 
11328 /**
11329   Hash iterate function to count the number of total column privileges granted.
11330 */
count_column_grants(void * grant_table,void * current_count)11331 static my_bool count_column_grants(void *grant_table,
11332                                        void *current_count)
11333 {
11334   HASH hash_columns = ((GRANT_TABLE *)grant_table)->hash_columns;
11335   *(ulong *)current_count+= hash_columns.records;
11336   return 0;
11337 }
11338 
11339 /**
11340   SHOW function that computes the number of column grants.
11341 
11342   This must be performed under the mutex in order to make sure the
11343   iteration does not fail.
11344 */
show_column_grants(THD * thd,SHOW_VAR * var,char * buff,enum enum_var_type scope)11345 static int show_column_grants(THD *thd, SHOW_VAR *var, char *buff,
11346                               enum enum_var_type scope)
11347 {
11348   var->type= SHOW_ULONG;
11349   var->value= buff;
11350   *(ulong *)buff= 0;
11351   if (initialized)
11352   {
11353     mysql_rwlock_rdlock(&LOCK_grant);
11354     mysql_mutex_lock(&acl_cache->lock);
11355     my_hash_iterate(&column_priv_hash, count_column_grants, buff);
11356     mysql_mutex_unlock(&acl_cache->lock);
11357     mysql_rwlock_unlock(&LOCK_grant);
11358   }
11359   return 0;
11360 }
11361 
show_database_grants(THD * thd,SHOW_VAR * var,char * buff,enum enum_var_type scope)11362 static int show_database_grants(THD *thd, SHOW_VAR *var, char *buff,
11363                                 enum enum_var_type scope)
11364 {
11365   var->type= SHOW_UINT;
11366   var->value= buff;
11367   *(uint *)buff= uint(acl_dbs.elements());
11368   return 0;
11369 }
11370 
11371 #else
check_grant(THD *,ulong,TABLE_LIST *,bool,uint,bool)11372 bool check_grant(THD *, ulong, TABLE_LIST *, bool, uint, bool)
11373 {
11374   return 0;
11375 }
11376 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
11377 
11378 SHOW_VAR acl_statistics[] = {
11379 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11380   {"column_grants",    (char*)show_column_grants,          SHOW_SIMPLE_FUNC},
11381   {"database_grants",  (char*)show_database_grants,        SHOW_SIMPLE_FUNC},
11382   {"function_grants",  (char*)&func_priv_hash.records,     SHOW_ULONG},
11383   {"procedure_grants", (char*)&proc_priv_hash.records,     SHOW_ULONG},
11384   {"package_spec_grants", (char*)&package_spec_priv_hash.records, SHOW_ULONG},
11385   {"package_body_grants", (char*)&package_body_priv_hash.records, SHOW_ULONG},
11386   {"proxy_users",      (char*)&acl_proxy_users.elements,   SHOW_UINT},
11387   {"role_grants",      (char*)&acl_roles_mappings.records, SHOW_ULONG},
11388   {"roles",            (char*)&acl_roles.records,          SHOW_ULONG},
11389   {"table_grants",     (char*)&column_priv_hash.records,   SHOW_ULONG},
11390   {"users",            (char*)&acl_users.elements,         SHOW_UINT},
11391 #endif
11392   {NullS, NullS, SHOW_LONG},
11393 };
11394 
11395 /* Check if a role is granted to a user/role. We traverse the role graph
11396    and return true if we find a match.
11397 
11398    hostname == NULL means we are looking for a role as a starting point,
11399    otherwise a user.
11400 */
check_role_is_granted(const char * username,const char * hostname,const char * rolename)11401 bool check_role_is_granted(const char *username,
11402                            const char *hostname,
11403                            const char *rolename)
11404 {
11405   DBUG_ENTER("check_role_is_granted");
11406   bool result= false;
11407 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11408   ACL_USER_BASE *root;
11409   mysql_mutex_lock(&acl_cache->lock);
11410   if (hostname)
11411     root= find_user_exact(username, hostname);
11412   else
11413     root= find_acl_role(username);
11414 
11415   LEX_CSTRING role_lex;
11416   role_lex.str= rolename;
11417   role_lex.length= strlen(rolename);
11418 
11419   if (root && /* No grantee, nothing to search. */
11420       traverse_role_graph_down(root, &role_lex, check_role_is_granted_callback,
11421                                NULL) == -1)
11422   {
11423     /* We have found the role during our search. */
11424     result= true;
11425   }
11426 
11427   /* We haven't found the role or we had no initial grantee to start from. */
11428   mysql_mutex_unlock(&acl_cache->lock);
11429 #endif
11430   DBUG_RETURN(result);
11431 }
11432 
fill_schema_enabled_roles(THD * thd,TABLE_LIST * tables,COND * cond)11433 int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond)
11434 {
11435   TABLE *table= tables->table;
11436 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11437   if (thd->security_ctx->priv_role[0])
11438   {
11439     mysql_rwlock_rdlock(&LOCK_grant);
11440     mysql_mutex_lock(&acl_cache->lock);
11441     ACL_ROLE *acl_role= find_acl_role(thd->security_ctx->priv_role);
11442     if (acl_role)
11443       traverse_role_graph_down(acl_role, table, enabled_roles_insert, NULL);
11444     mysql_mutex_unlock(&acl_cache->lock);
11445     mysql_rwlock_unlock(&LOCK_grant);
11446     if (acl_role)
11447       return 0;
11448   }
11449 #endif
11450 
11451   restore_record(table, s->default_values);
11452   table->field[0]->set_null();
11453   return schema_table_store_record(table->in_use, table);
11454 }
11455 
11456 
11457 /*
11458   This shows all roles granted to current user
11459   and recursively all roles granted to those roles
11460 */
fill_schema_applicable_roles(THD * thd,TABLE_LIST * tables,COND * cond)11461 int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond)
11462 {
11463   int res= 0;
11464 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11465   if (initialized)
11466   {
11467     TABLE *table= tables->table;
11468     Security_context *sctx= thd->security_ctx;
11469     mysql_rwlock_rdlock(&LOCK_grant);
11470     mysql_mutex_lock(&acl_cache->lock);
11471     ACL_USER *user= find_user_exact(sctx->priv_host, sctx->priv_user);
11472     if (user)
11473     {
11474       char buff[USER_HOST_BUFF_SIZE+10];
11475       DBUG_ASSERT(user->user.length + user->hostname_length +2 < sizeof(buff));
11476       char *end= strxmov(buff, user->user.str, "@", user->host.hostname, NULL);
11477       APPLICABLE_ROLES_DATA data= { table,
11478         { user->host.hostname, user->hostname_length },
11479         { buff, (size_t)(end - buff) }, user
11480       };
11481 
11482       res= traverse_role_graph_down(user, &data, 0, applicable_roles_insert);
11483     }
11484 
11485     mysql_mutex_unlock(&acl_cache->lock);
11486     mysql_rwlock_unlock(&LOCK_grant);
11487   }
11488 #endif
11489 
11490   return res;
11491 }
11492 
11493 
wild_case_compare(CHARSET_INFO * cs,const char * str,const char * wildstr)11494 int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
11495 {
11496   int flag;
11497   DBUG_ENTER("wild_case_compare");
11498   DBUG_PRINT("enter",("str: '%s'  wildstr: '%s'",str,wildstr));
11499   while (*wildstr)
11500   {
11501     while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
11502     {
11503       if (*wildstr == wild_prefix && wildstr[1])
11504 	wildstr++;
11505       if (my_toupper(cs, *wildstr++) !=
11506           my_toupper(cs, *str++)) DBUG_RETURN(1);
11507     }
11508     if (! *wildstr ) DBUG_RETURN (*str != 0);
11509     if (*wildstr++ == wild_one)
11510     {
11511       if (! *str++) DBUG_RETURN (1);	/* One char; skip */
11512     }
11513     else
11514     {						/* Found '*' */
11515       if (!*wildstr) DBUG_RETURN(0);		/* '*' as last char: OK */
11516       flag=(*wildstr != wild_many && *wildstr != wild_one);
11517       do
11518       {
11519 	if (flag)
11520 	{
11521 	  char cmp;
11522 	  if ((cmp= *wildstr) == wild_prefix && wildstr[1])
11523 	    cmp=wildstr[1];
11524 	  cmp=my_toupper(cs, cmp);
11525 	  while (*str && my_toupper(cs, *str) != cmp)
11526 	    str++;
11527 	  if (!*str) DBUG_RETURN (1);
11528 	}
11529 	if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
11530       } while (*str++);
11531       DBUG_RETURN(1);
11532     }
11533   }
11534   DBUG_RETURN (*str != '\0');
11535 }
11536 
11537 
11538 #ifndef NO_EMBEDDED_ACCESS_CHECKS
update_schema_privilege(THD * thd,TABLE * table,const char * buff,const char * db,const char * t_name,const char * column,uint col_length,const char * priv,uint priv_length,const char * is_grantable)11539 static bool update_schema_privilege(THD *thd, TABLE *table, const char *buff,
11540                                     const char* db, const char* t_name,
11541                                     const char* column, uint col_length,
11542                                     const char *priv, uint priv_length,
11543                                     const char* is_grantable)
11544 {
11545   int i= 2;
11546   CHARSET_INFO *cs= system_charset_info;
11547   restore_record(table, s->default_values);
11548   table->field[0]->store(buff, (uint) strlen(buff), cs);
11549   table->field[1]->store(STRING_WITH_LEN("def"), cs);
11550   if (db)
11551     table->field[i++]->store(db, (uint) strlen(db), cs);
11552   if (t_name)
11553     table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
11554   if (column)
11555     table->field[i++]->store(column, col_length, cs);
11556   table->field[i++]->store(priv, priv_length, cs);
11557   table->field[i]->store(is_grantable, strlen(is_grantable), cs);
11558   return schema_table_store_record(thd, table);
11559 }
11560 #endif
11561 
11562 
11563 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11564 class Grantee_str
11565 {
11566   char m_buff[USER_HOST_BUFF_SIZE + 6 /* 4 quotes, @, '\0' */];
11567 public:
Grantee_str(const char * user,const char * host)11568   Grantee_str(const char *user, const char *host)
11569   {
11570     DBUG_ASSERT(strlen(user) + strlen(host) + 6 < sizeof(m_buff));
11571     strxmov(m_buff, "'", user, "'@'", host, "'", NullS);
11572   }
operator const char*() const11573   operator const char *() const { return m_buff; }
11574 };
11575 #endif
11576 
11577 
fill_schema_user_privileges(THD * thd,TABLE_LIST * tables,COND * cond)11578 int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11579 {
11580 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11581   int error= 0;
11582   uint counter;
11583   ACL_USER *acl_user;
11584   ulong want_access;
11585   TABLE *table= tables->table;
11586   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11587                                       NULL, NULL, 1, 1);
11588   char *curr_host= thd->security_ctx->priv_host_name();
11589   DBUG_ENTER("fill_schema_user_privileges");
11590 
11591   if (!initialized)
11592     DBUG_RETURN(0);
11593   mysql_mutex_lock(&acl_cache->lock);
11594 
11595   for (counter=0 ; counter < acl_users.elements ; counter++)
11596   {
11597     const char *user,*host, *is_grantable="YES";
11598     acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
11599     user= safe_str(acl_user->user.str);
11600     host= safe_str(acl_user->host.hostname);
11601 
11602     if (no_global_access &&
11603         (strcmp(thd->security_ctx->priv_user, user) ||
11604          my_strcasecmp(system_charset_info, curr_host, host)))
11605       continue;
11606 
11607     want_access= acl_user->access;
11608     if (!(want_access & GRANT_ACL))
11609       is_grantable= "NO";
11610 
11611     Grantee_str grantee(user, host);
11612     if (!(want_access & ~GRANT_ACL))
11613     {
11614       if (update_schema_privilege(thd, table, grantee, 0, 0, 0, 0,
11615                                   STRING_WITH_LEN("USAGE"), is_grantable))
11616       {
11617         error= 1;
11618         goto err;
11619       }
11620     }
11621     else
11622     {
11623       uint priv_id;
11624       ulong j,test_access= want_access & ~GRANT_ACL;
11625       for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
11626       {
11627         if (test_access & j)
11628         {
11629           if (update_schema_privilege(thd, table, grantee, 0, 0, 0, 0,
11630                                       command_array[priv_id],
11631                                       command_lengths[priv_id], is_grantable))
11632           {
11633             error= 1;
11634             goto err;
11635           }
11636         }
11637       }
11638     }
11639   }
11640 err:
11641   mysql_mutex_unlock(&acl_cache->lock);
11642 
11643   DBUG_RETURN(error);
11644 #else
11645   return(0);
11646 #endif
11647 }
11648 
11649 
fill_schema_schema_privileges(THD * thd,TABLE_LIST * tables,COND * cond)11650 int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11651 {
11652 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11653   int error= 0;
11654   uint counter;
11655   ACL_DB *acl_db;
11656   ulong want_access;
11657   TABLE *table= tables->table;
11658   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11659                                       NULL, NULL, 1, 1);
11660   char *curr_host= thd->security_ctx->priv_host_name();
11661   DBUG_ENTER("fill_schema_schema_privileges");
11662 
11663   if (!initialized)
11664     DBUG_RETURN(0);
11665   mysql_mutex_lock(&acl_cache->lock);
11666 
11667   for (counter=0 ; counter < acl_dbs.elements() ; counter++)
11668   {
11669     const char *user, *host, *is_grantable="YES";
11670 
11671     acl_db=&acl_dbs.at(counter);
11672     user= safe_str(acl_db->user);
11673     host= safe_str(acl_db->host.hostname);
11674 
11675     if (no_global_access &&
11676         (strcmp(thd->security_ctx->priv_user, user) ||
11677          my_strcasecmp(system_charset_info, curr_host, host)))
11678       continue;
11679 
11680     want_access=acl_db->access;
11681     if (want_access)
11682     {
11683       if (!(want_access & GRANT_ACL))
11684       {
11685         is_grantable= "NO";
11686       }
11687       Grantee_str grantee(user, host);
11688       if (!(want_access & ~GRANT_ACL))
11689       {
11690         if (update_schema_privilege(thd, table, grantee, acl_db->db, 0, 0,
11691                                     0, STRING_WITH_LEN("USAGE"), is_grantable))
11692         {
11693           error= 1;
11694           goto err;
11695         }
11696       }
11697       else
11698       {
11699         int cnt;
11700         ulong j,test_access= want_access & ~GRANT_ACL;
11701         for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
11702           if (test_access & j)
11703           {
11704             if (update_schema_privilege(thd, table,
11705                                         grantee, acl_db->db, 0, 0, 0,
11706                                         command_array[cnt], command_lengths[cnt],
11707                                         is_grantable))
11708             {
11709               error= 1;
11710               goto err;
11711             }
11712           }
11713       }
11714     }
11715   }
11716 err:
11717   mysql_mutex_unlock(&acl_cache->lock);
11718 
11719   DBUG_RETURN(error);
11720 #else
11721   return (0);
11722 #endif
11723 }
11724 
11725 
fill_schema_table_privileges(THD * thd,TABLE_LIST * tables,COND * cond)11726 int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11727 {
11728 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11729   int error= 0;
11730   uint index;
11731   TABLE *table= tables->table;
11732   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11733                                       NULL, NULL, 1, 1);
11734   char *curr_host= thd->security_ctx->priv_host_name();
11735   DBUG_ENTER("fill_schema_table_privileges");
11736 
11737   mysql_rwlock_rdlock(&LOCK_grant);
11738 
11739   for (index=0 ; index < column_priv_hash.records ; index++)
11740   {
11741     const char *user, *host, *is_grantable= "YES";
11742     GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
11743                                                              index);
11744     user= safe_str(grant_table->user);
11745     host= safe_str(grant_table->host.hostname);
11746 
11747     if (no_global_access &&
11748         (strcmp(thd->security_ctx->priv_user, user) ||
11749          my_strcasecmp(system_charset_info, curr_host, host)))
11750       continue;
11751 
11752     ulong table_access= grant_table->privs;
11753     if (table_access)
11754     {
11755       ulong test_access= table_access & ~GRANT_ACL;
11756       /*
11757         We should skip 'usage' privilege on table if
11758         we have any privileges on column(s) of this table
11759       */
11760       if (!test_access && grant_table->cols)
11761         continue;
11762       if (!(table_access & GRANT_ACL))
11763         is_grantable= "NO";
11764 
11765       Grantee_str grantee(user, host);
11766       if (!test_access)
11767       {
11768         if (update_schema_privilege(thd, table,
11769                                     grantee, grant_table->db,
11770                                     grant_table->tname, 0, 0,
11771                                     STRING_WITH_LEN("USAGE"), is_grantable))
11772         {
11773           error= 1;
11774           goto err;
11775         }
11776       }
11777       else
11778       {
11779         ulong j;
11780         int cnt;
11781         for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
11782         {
11783           if (test_access & j)
11784           {
11785             if (update_schema_privilege(thd, table,
11786                                         grantee, grant_table->db,
11787                                         grant_table->tname, 0, 0,
11788                                         command_array[cnt],
11789                                         command_lengths[cnt], is_grantable))
11790             {
11791               error= 1;
11792               goto err;
11793             }
11794           }
11795         }
11796       }
11797     }
11798   }
11799 err:
11800   mysql_rwlock_unlock(&LOCK_grant);
11801 
11802   DBUG_RETURN(error);
11803 #else
11804   return (0);
11805 #endif
11806 }
11807 
11808 
fill_schema_column_privileges(THD * thd,TABLE_LIST * tables,COND * cond)11809 int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
11810 {
11811 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11812   int error= 0;
11813   uint index;
11814   TABLE *table= tables->table;
11815   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
11816                                       NULL, NULL, 1, 1);
11817   char *curr_host= thd->security_ctx->priv_host_name();
11818   DBUG_ENTER("fill_schema_table_privileges");
11819 
11820   mysql_rwlock_rdlock(&LOCK_grant);
11821 
11822   for (index=0 ; index < column_priv_hash.records ; index++)
11823   {
11824     const char *user, *host, *is_grantable= "YES";
11825     GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
11826                                                           index);
11827     user= safe_str(grant_table->user);
11828     host= safe_str(grant_table->host.hostname);
11829 
11830     if (no_global_access &&
11831         (strcmp(thd->security_ctx->priv_user, user) ||
11832          my_strcasecmp(system_charset_info, curr_host, host)))
11833       continue;
11834 
11835     ulong table_access= grant_table->cols;
11836     if (table_access != 0)
11837     {
11838       if (!(grant_table->privs & GRANT_ACL))
11839         is_grantable= "NO";
11840 
11841       ulong test_access= table_access & ~GRANT_ACL;
11842       Grantee_str grantee(user, host);
11843       if (!test_access)
11844         continue;
11845       else
11846       {
11847         ulong j;
11848         int cnt;
11849         for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
11850         {
11851           if (test_access & j)
11852           {
11853             for (uint col_index=0 ;
11854                  col_index < grant_table->hash_columns.records ;
11855                  col_index++)
11856             {
11857               GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
11858                 my_hash_element(&grant_table->hash_columns,col_index);
11859               if ((grant_column->rights & j) && (table_access & j))
11860               {
11861                 if (update_schema_privilege(thd, table,
11862                                             grantee,
11863                                             grant_table->db,
11864                                             grant_table->tname,
11865                                             grant_column->column,
11866                                             grant_column->key_length,
11867                                             command_array[cnt],
11868                                             command_lengths[cnt], is_grantable))
11869                 {
11870                   error= 1;
11871                   goto err;
11872                 }
11873               }
11874             }
11875           }
11876         }
11877       }
11878     }
11879   }
11880 err:
11881   mysql_rwlock_unlock(&LOCK_grant);
11882 
11883   DBUG_RETURN(error);
11884 #else
11885   return (0);
11886 #endif
11887 }
11888 
11889 
11890 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11891 /*
11892   fill effective privileges for table
11893 
11894   SYNOPSIS
11895     fill_effective_table_privileges()
11896     thd     thread handler
11897     grant   grants table descriptor
11898     db      db name
11899     table   table name
11900 */
11901 
fill_effective_table_privileges(THD * thd,GRANT_INFO * grant,const char * db,const char * table)11902 void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
11903                                      const char *db, const char *table)
11904 {
11905   Security_context *sctx= thd->security_ctx;
11906   DBUG_ENTER("fill_effective_table_privileges");
11907   DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
11908                        sctx->priv_host, sctx->ip, sctx->priv_user, db, table));
11909   /* --skip-grants */
11910   if (!initialized)
11911   {
11912     DBUG_PRINT("info", ("skip grants"));
11913     grant->privilege= ~NO_ACCESS;             // everything is allowed
11914     DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
11915     DBUG_VOID_RETURN;
11916   }
11917 
11918   /* global privileges */
11919   grant->privilege= sctx->master_access;
11920 
11921   if (!thd->db.str || strcmp(db, thd->db.str))
11922   {
11923     /* db privileges */
11924     grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
11925     /* db privileges for role */
11926     if (sctx->priv_role[0])
11927       grant->privilege|= acl_get("", "", sctx->priv_role, db, 0);
11928   }
11929   else
11930   {
11931     grant->privilege|= sctx->db_access;
11932   }
11933 
11934   /* table privileges */
11935   mysql_rwlock_rdlock(&LOCK_grant);
11936   if (grant->version != grant_version)
11937   {
11938     grant->grant_table_user=
11939       table_hash_search(sctx->host, sctx->ip, db,
11940                         sctx->priv_user,
11941                         table, 0);              /* purecov: inspected */
11942     grant->grant_table_role=
11943       sctx->priv_role[0] ? table_hash_search("", "", db,
11944                                              sctx->priv_role,
11945                                              table, TRUE) : NULL;
11946     grant->version= grant_version;              /* purecov: inspected */
11947   }
11948   if (grant->grant_table_user != 0)
11949   {
11950     grant->privilege|= grant->grant_table_user->privs;
11951   }
11952   if (grant->grant_table_role != 0)
11953   {
11954     grant->privilege|= grant->grant_table_role->privs;
11955   }
11956   mysql_rwlock_unlock(&LOCK_grant);
11957 
11958   DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
11959   DBUG_VOID_RETURN;
11960 }
11961 
11962 #else /* NO_EMBEDDED_ACCESS_CHECKS */
11963 
11964 /****************************************************************************
11965  Dummy wrappers when we don't have any access checks
11966 ****************************************************************************/
11967 
check_routine_level_acl(THD * thd,const char * db,const char * name,const Sp_handler * sph)11968 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
11969                              const Sp_handler *sph)
11970 {
11971   return FALSE;
11972 }
11973 
11974 #endif
11975 
11976 /**
11977   Return information about user or current user.
11978 
11979   @param[in] thd          thread handler
11980   @param[in] user         user
11981   @param[in] lock         whether &acl_cache->lock mutex needs to be locked
11982 
11983   @return
11984     - On success, return a valid pointer to initialized
11985     LEX_USER, which contains user information.
11986     - On error, return 0.
11987 */
11988 
get_current_user(THD * thd,LEX_USER * user,bool lock)11989 LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock)
11990 {
11991   if (user->user.str == current_user.str)  // current_user
11992     return create_default_definer(thd, false);
11993 
11994   if (user->user.str == current_role.str)  // current_role
11995     return create_default_definer(thd, true);
11996 
11997   if (user->host.str == NULL) // Possibly a role
11998   {
11999     // to be reexecution friendly we have to make a copy
12000     LEX_USER *dup= (LEX_USER*) thd->memdup(user, sizeof(*user));
12001     if (!dup)
12002       return 0;
12003 
12004 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12005     if (has_auth(user, thd->lex))
12006     {
12007       dup->host= host_not_specified;
12008       return dup;
12009     }
12010 
12011     if (is_invalid_role_name(user->user.str))
12012       return 0;
12013 
12014     if (lock)
12015       mysql_mutex_lock(&acl_cache->lock);
12016     if (find_acl_role(dup->user.str))
12017       dup->host= empty_clex_str;
12018     else
12019       dup->host= host_not_specified;
12020     if (lock)
12021       mysql_mutex_unlock(&acl_cache->lock);
12022 #endif
12023 
12024     return dup;
12025   }
12026 
12027   return user;
12028 }
12029 
12030 struct ACL_internal_schema_registry_entry
12031 {
12032   const LEX_CSTRING *m_name;
12033   const ACL_internal_schema_access *m_access;
12034 };
12035 
12036 /**
12037   Internal schema registered.
12038   Currently, this is only:
12039   - performance_schema
12040   - information_schema,
12041   This can be reused later for:
12042   - mysql
12043 */
12044 static ACL_internal_schema_registry_entry registry_array[2];
12045 static uint m_registry_array_size= 0;
12046 
12047 /**
12048   Add an internal schema to the registry.
12049   @param name the schema name
12050   @param access the schema ACL specific rules
12051 */
register_schema(const LEX_CSTRING * name,const ACL_internal_schema_access * access)12052 void ACL_internal_schema_registry::register_schema
12053   (const LEX_CSTRING *name, const ACL_internal_schema_access *access)
12054 {
12055   DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
12056 
12057   /* Not thread safe, and does not need to be. */
12058   registry_array[m_registry_array_size].m_name= name;
12059   registry_array[m_registry_array_size].m_access= access;
12060   m_registry_array_size++;
12061 }
12062 
12063 /**
12064   Search per internal schema ACL by name.
12065   @param name a schema name
12066   @return per schema rules, or NULL
12067 */
12068 const ACL_internal_schema_access *
lookup(const char * name)12069 ACL_internal_schema_registry::lookup(const char *name)
12070 {
12071   DBUG_ASSERT(name != NULL);
12072 
12073   uint i;
12074 
12075   for (i= 0; i<m_registry_array_size; i++)
12076   {
12077     if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
12078                       name) == 0)
12079       return registry_array[i].m_access;
12080   }
12081   return NULL;
12082 }
12083 
12084 /**
12085   Get a cached internal schema access.
12086   @param grant_internal_info the cache
12087   @param schema_name the name of the internal schema
12088 */
12089 const ACL_internal_schema_access *
get_cached_schema_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name)12090 get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
12091                          const char *schema_name)
12092 {
12093   if (grant_internal_info)
12094   {
12095     if (! grant_internal_info->m_schema_lookup_done)
12096     {
12097       grant_internal_info->m_schema_access=
12098         ACL_internal_schema_registry::lookup(schema_name);
12099       grant_internal_info->m_schema_lookup_done= TRUE;
12100     }
12101     return grant_internal_info->m_schema_access;
12102   }
12103   return ACL_internal_schema_registry::lookup(schema_name);
12104 }
12105 
12106 /**
12107   Get a cached internal table access.
12108   @param grant_internal_info the cache
12109   @param schema_name the name of the internal schema
12110   @param table_name the name of the internal table
12111 */
12112 const ACL_internal_table_access *
get_cached_table_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name,const char * table_name)12113 get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
12114                         const char *schema_name,
12115                         const char *table_name)
12116 {
12117   DBUG_ASSERT(grant_internal_info);
12118   if (! grant_internal_info->m_table_lookup_done)
12119   {
12120     const ACL_internal_schema_access *schema_access;
12121     schema_access= get_cached_schema_access(grant_internal_info, schema_name);
12122     if (schema_access)
12123       grant_internal_info->m_table_access= schema_access->lookup(table_name);
12124     grant_internal_info->m_table_lookup_done= TRUE;
12125   }
12126   return grant_internal_info->m_table_access;
12127 }
12128 
12129 
12130 /****************************************************************************
12131    AUTHENTICATION CODE
12132    including initial connect handshake, invoking appropriate plugins,
12133    client-server plugin negotiation, COM_CHANGE_USER, and native
12134    MySQL authentication plugins.
12135 ****************************************************************************/
12136 
12137 /* few defines to have less ifdef's in the code below */
12138 #ifdef EMBEDDED_LIBRARY
12139 #undef HAVE_OPENSSL
12140 #ifdef NO_EMBEDDED_ACCESS_CHECKS
12141 #define initialized 0
12142 #define check_for_max_user_connections(X,Y)   0
12143 #define get_or_create_user_conn(A,B,C,D) 0
12144 #endif
12145 #endif
12146 #ifndef HAVE_OPENSSL
12147 #define ssl_acceptor_fd 0
12148 #define sslaccept(A,B,C,D) 1
12149 #endif
12150 
12151 /**
12152   The internal version of what plugins know as MYSQL_PLUGIN_VIO,
12153   basically the context of the authentication session
12154 */
12155 struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
12156 {
12157   MYSQL_SERVER_AUTH_INFO auth_info;
12158   ACL_USER *acl_user;       ///< a copy, independent from acl_users array
12159   plugin_ref plugin;        ///< what plugin we're under
12160   LEX_CSTRING db;           ///< db name from the handshake packet
12161   /** when restarting a plugin this caches the last client reply */
12162   struct {
12163     const char *plugin;
12164     char *pkt;              ///< pointer into NET::buff
12165     uint pkt_len;
12166   } cached_client_reply;
12167   /** this caches the first plugin packet for restart request on the client */
12168   struct {
12169     char *pkt;
12170     uint pkt_len;
12171   } cached_server_packet;
12172   int packets_read, packets_written; ///< counters for send/received packets
12173   bool make_it_fail;
12174   /** when plugin returns a failure this tells us what really happened */
12175   enum { SUCCESS, FAILURE, RESTART } status;
12176 };
12177 
12178 /**
12179   a helper function to report an access denied error in most proper places
12180 */
login_failed_error(THD * thd)12181 static void login_failed_error(THD *thd)
12182 {
12183   my_error(access_denied_error_code(thd->password), MYF(0),
12184            thd->main_security_ctx.user,
12185            thd->main_security_ctx.host_or_ip,
12186            thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12187   general_log_print(thd, COM_CONNECT,
12188                     ER_THD(thd, access_denied_error_code(thd->password)),
12189                     thd->main_security_ctx.user,
12190                     thd->main_security_ctx.host_or_ip,
12191                     thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12192   status_var_increment(thd->status_var.access_denied_errors);
12193   /*
12194     Log access denied messages to the error log when log-warnings = 2
12195     so that the overhead of the general query log is not required to track
12196     failed connections.
12197   */
12198   if (global_system_variables.log_warnings > 1)
12199   {
12200     sql_print_warning(ER_THD(thd, access_denied_error_code(thd->password)),
12201                       thd->main_security_ctx.user,
12202                       thd->main_security_ctx.host_or_ip,
12203                       thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12204   }
12205 }
12206 
12207 /**
12208   sends a server handshake initialization packet, the very first packet
12209   after the connection was established
12210 
12211   Packet format:
12212 
12213     Bytes       Content
12214     -----       ----
12215     1           protocol version (always 10)
12216     n           server version string, \0-terminated
12217     4           thread id
12218     8           first 8 bytes of the plugin provided data (scramble)
12219     1           \0 byte, terminating the first part of a scramble
12220     2           server capabilities (two lower bytes)
12221     1           server character set
12222     2           server status
12223     2           server capabilities (two upper bytes)
12224     1           length of the scramble
12225     10          reserved, always 0
12226     n           rest of the plugin provided data (at least 12 bytes)
12227     1           \0 byte, terminating the second part of a scramble
12228 
12229   @retval 0 ok
12230   @retval 1 error
12231 */
send_server_handshake_packet(MPVIO_EXT * mpvio,const char * data,uint data_len)12232 static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
12233                                          const char *data, uint data_len)
12234 {
12235   DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
12236   DBUG_ASSERT(data_len <= 255);
12237 
12238   THD *thd= mpvio->auth_info.thd;
12239   char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + 1 + data_len + 64);
12240   char scramble_buf[SCRAMBLE_LENGTH];
12241   char *end= buff;
12242   DBUG_ENTER("send_server_handshake_packet");
12243 
12244   *end++= protocol_version;
12245 
12246   thd->client_capabilities= CLIENT_BASIC_FLAGS;
12247 
12248   if (opt_using_transactions)
12249     thd->client_capabilities|= CLIENT_TRANSACTIONS;
12250 
12251   thd->client_capabilities|= CAN_CLIENT_COMPRESS;
12252 
12253   if (ssl_acceptor_fd)
12254   {
12255     thd->client_capabilities |= CLIENT_SSL;
12256     thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
12257   }
12258 
12259   if (data_len)
12260   {
12261     mpvio->cached_server_packet.pkt= (char*)thd->memdup(data, data_len);
12262     mpvio->cached_server_packet.pkt_len= data_len;
12263   }
12264 
12265   if (data_len < SCRAMBLE_LENGTH)
12266   {
12267     if (data_len)
12268     {
12269       /*
12270         the first packet *must* have at least 20 bytes of a scramble.
12271         if a plugin provided less, we pad it to 20 with zeros
12272       */
12273       memcpy(scramble_buf, data, data_len);
12274       bzero(scramble_buf + data_len, SCRAMBLE_LENGTH - data_len);
12275       data= scramble_buf;
12276     }
12277     else
12278     {
12279       /*
12280         if the default plugin does not provide the data for the scramble at
12281         all, we generate a scramble internally anyway, just in case the
12282         user account (that will be known only later) uses a
12283         native_password_plugin (which needs a scramble). If we don't send a
12284         scramble now - wasting 20 bytes in the packet -
12285         native_password_plugin will have to send it in a separate packet,
12286         adding one more round trip.
12287       */
12288       thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
12289       data= thd->scramble;
12290     }
12291     data_len= SCRAMBLE_LENGTH;
12292   }
12293 
12294   /* When server version is specified in config file, don't include
12295      the replication hack prefix. */
12296   if (using_custom_server_version)
12297     end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
12298   else
12299     end= strxnmov(end, SERVER_VERSION_LENGTH, RPL_VERSION_HACK, server_version, NullS) + 1;
12300 
12301   int4store((uchar*) end, mpvio->auth_info.thd->thread_id);
12302   end+= 4;
12303 
12304   /*
12305     Old clients does not understand long scrambles, but can ignore packet
12306     tail: that's why first part of the scramble is placed here, and second
12307     part at the end of packet.
12308   */
12309   end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
12310   end+= SCRAMBLE_LENGTH_323;
12311   *end++= 0;
12312 
12313   int2store(end, thd->client_capabilities);
12314   /* write server characteristics: up to 16 bytes allowed */
12315   end[2]= (char) default_charset_info->number;
12316   int2store(end+3, mpvio->auth_info.thd->server_status);
12317   int2store(end+5, thd->client_capabilities >> 16);
12318   end[7]= data_len;
12319   DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
12320   DBUG_EXECUTE_IF("increase_srv_handshake_scramble_len", end[7]= 50;);
12321   bzero(end + 8, 6);
12322   int4store(end + 14, thd->client_capabilities >> 32);
12323   end+= 18;
12324   /* write scramble tail */
12325   end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
12326                       data_len - SCRAMBLE_LENGTH_323);
12327   end+= data_len - SCRAMBLE_LENGTH_323;
12328   end= strmake(end, plugin_name(mpvio->plugin)->str,
12329                     plugin_name(mpvio->plugin)->length);
12330 
12331   int res= my_net_write(&mpvio->auth_info.thd->net, (uchar*) buff,
12332                         (size_t) (end - buff + 1)) ||
12333            net_flush(&mpvio->auth_info.thd->net);
12334   my_afree(buff);
12335   DBUG_RETURN (res);
12336 }
12337 
secure_auth(THD * thd)12338 static bool secure_auth(THD *thd)
12339 {
12340   if (!opt_secure_auth)
12341     return 0;
12342 
12343   /*
12344     If the server is running in secure auth mode, short scrambles are
12345     forbidden. Extra juggling to report the same error as the old code.
12346   */
12347   if (thd->client_capabilities & CLIENT_PROTOCOL_41)
12348   {
12349     my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
12350              thd->security_ctx->user,
12351              thd->security_ctx->host_or_ip);
12352     general_log_print(thd, COM_CONNECT,
12353                       ER_THD(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE),
12354                       thd->security_ctx->user,
12355                       thd->security_ctx->host_or_ip);
12356   }
12357   else
12358   {
12359     my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
12360     general_log_print(thd, COM_CONNECT,
12361                       ER_THD(thd, ER_NOT_SUPPORTED_AUTH_MODE));
12362   }
12363   return 1;
12364 }
12365 
12366 /**
12367   sends a "change plugin" packet, requesting a client to restart authentication
12368   using a different authentication plugin
12369 
12370   Packet format:
12371 
12372     Bytes       Content
12373     -----       ----
12374     1           byte with the value 254
12375     n           client plugin to use, \0-terminated
12376     n           plugin provided data
12377 
12378   In a special case of switching from native_password_plugin to
12379   old_password_plugin, the packet contains only one - the first - byte,
12380   plugin name is omitted, plugin data aren't needed as the scramble was
12381   already sent. This one-byte packet is identical to the "use the short
12382   scramble" packet in the protocol before plugins were introduced.
12383 
12384   @retval 0 ok
12385   @retval 1 error
12386 */
send_plugin_request_packet(MPVIO_EXT * mpvio,const uchar * data,uint data_len)12387 static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
12388                                        const uchar *data, uint data_len)
12389 {
12390   DBUG_ASSERT(mpvio->packets_written == 1);
12391   DBUG_ASSERT(mpvio->packets_read == 1);
12392   NET *net= &mpvio->auth_info.thd->net;
12393   static uchar switch_plugin_request_buf[]= { 254 };
12394   DBUG_ENTER("send_plugin_request_packet");
12395 
12396   mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
12397 
12398   const char *client_auth_plugin=
12399     ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
12400 
12401   DBUG_EXECUTE_IF("auth_disconnect", { DBUG_RETURN(1); });
12402   DBUG_EXECUTE_IF("auth_invalid_plugin", client_auth_plugin="foo/bar"; );
12403   DBUG_ASSERT(client_auth_plugin);
12404 
12405   /*
12406     we send an old "short 4.0 scramble request", if we need to request a
12407     client to use 4.0 auth plugin (short scramble) and the scramble was
12408     already sent to the client
12409 
12410     below, cached_client_reply.plugin is the plugin name that client has used,
12411     client_auth_plugin is derived from mysql.user table, for the given
12412     user account, it's the plugin that the client need to use to login.
12413   */
12414   bool switch_from_long_to_short_scramble=
12415     native_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
12416     client_auth_plugin == old_password_plugin_name.str;
12417 
12418   if (switch_from_long_to_short_scramble)
12419     DBUG_RETURN (secure_auth(mpvio->auth_info.thd) ||
12420                  my_net_write(net, switch_plugin_request_buf, 1) ||
12421                  net_flush(net));
12422 
12423   /*
12424     We never request a client to switch from a short to long scramble.
12425     Plugin-aware clients can do that, but traditionally it meant to
12426     ask an old 4.0 client to use the new 4.1 authentication protocol.
12427   */
12428   bool switch_from_short_to_long_scramble=
12429     old_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
12430     client_auth_plugin == native_password_plugin_name.str;
12431 
12432   if (switch_from_short_to_long_scramble)
12433   {
12434     my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
12435     general_log_print(mpvio->auth_info.thd, COM_CONNECT,
12436                       ER_THD(mpvio->auth_info.thd, ER_NOT_SUPPORTED_AUTH_MODE));
12437     DBUG_RETURN (1);
12438   }
12439 
12440   DBUG_PRINT("info", ("requesting client to use the %s plugin",
12441                       client_auth_plugin));
12442   DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
12443                                 (uchar*) client_auth_plugin,
12444                                 strlen(client_auth_plugin) + 1,
12445                                 (uchar*) data, data_len));
12446 }
12447 
12448 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12449 /**
12450    Finds acl entry in user database for authentication purposes.
12451 
12452    Finds a user and copies it into mpvio. Creates a fake user
12453    if no matching user account is found.
12454 
12455    @retval 0    found
12456    @retval 1    error
12457 */
find_mpvio_user(MPVIO_EXT * mpvio)12458 static bool find_mpvio_user(MPVIO_EXT *mpvio)
12459 {
12460   Security_context *sctx= mpvio->auth_info.thd->security_ctx;
12461   DBUG_ENTER("find_mpvio_user");
12462   DBUG_ASSERT(mpvio->acl_user == 0);
12463 
12464   mysql_mutex_lock(&acl_cache->lock);
12465 
12466   ACL_USER *user= find_user_or_anon(sctx->host, sctx->user, sctx->ip);
12467   if (user)
12468     mpvio->acl_user= user->copy(mpvio->auth_info.thd->mem_root);
12469 
12470   mysql_mutex_unlock(&acl_cache->lock);
12471 
12472   if (!mpvio->acl_user)
12473   {
12474     /*
12475       A matching user was not found. Fake it. Take any user, make the
12476       authentication fail later.
12477       This way we get a realistically looking failure, with occasional
12478       "change auth plugin" requests even for nonexistent users. The ratio
12479       of "change auth plugin" request will be the same for real and
12480       nonexistent users.
12481       Note, that we cannot pick any user at random, it must always be
12482       the same user account for the incoming sctx->user name.
12483     */
12484     ulong nr1=1, nr2=4;
12485     CHARSET_INFO *cs= &my_charset_latin1;
12486     cs->coll->hash_sort(cs, (uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2);
12487 
12488     mysql_mutex_lock(&acl_cache->lock);
12489     if (!acl_users.elements)
12490     {
12491       mysql_mutex_unlock(&acl_cache->lock);
12492       login_failed_error(mpvio->auth_info.thd);
12493       DBUG_RETURN(1);
12494     }
12495     uint i= nr1 % acl_users.elements;
12496     ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
12497     mpvio->acl_user= acl_user_tmp->copy(mpvio->auth_info.thd->mem_root);
12498     mysql_mutex_unlock(&acl_cache->lock);
12499 
12500     mpvio->make_it_fail= true;
12501   }
12502 
12503   /* user account requires non-default plugin and the client is too old */
12504   if (mpvio->acl_user->plugin.str != native_password_plugin_name.str &&
12505       mpvio->acl_user->plugin.str != old_password_plugin_name.str &&
12506       !(mpvio->auth_info.thd->client_capabilities & CLIENT_PLUGIN_AUTH))
12507   {
12508     DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
12509                               native_password_plugin_name.str));
12510     DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
12511                               old_password_plugin_name.str));
12512     my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
12513     general_log_print(mpvio->auth_info.thd, COM_CONNECT,
12514                       ER_THD(mpvio->auth_info.thd, ER_NOT_SUPPORTED_AUTH_MODE));
12515     DBUG_RETURN (1);
12516   }
12517 
12518   mpvio->auth_info.user_name= sctx->user;
12519   mpvio->auth_info.user_name_length= (uint)strlen(sctx->user);
12520   mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
12521   mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length;
12522   strmake_buf(mpvio->auth_info.authenticated_as, safe_str(mpvio->acl_user->user.str));
12523 
12524   DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
12525                       "plugin=%s",
12526                       mpvio->auth_info.user_name,
12527                       mpvio->auth_info.auth_string,
12528                       mpvio->auth_info.authenticated_as,
12529                       mpvio->acl_user->plugin.str));
12530   DBUG_RETURN(0);
12531 }
12532 
12533 static bool
read_client_connect_attrs(char ** ptr,char * end,CHARSET_INFO * from_cs)12534 read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs)
12535 {
12536   ulonglong length;
12537   char *ptr_save= *ptr;
12538 
12539   /* not enough bytes to hold the length */
12540   if (ptr_save >= end)
12541     return true;
12542 
12543   length= safe_net_field_length_ll((uchar **) ptr, end - ptr_save);
12544 
12545   /* cannot even read the length */
12546   if (*ptr == NULL)
12547     return true;
12548 
12549   /* length says there're more data than can fit into the packet */
12550   if (*ptr + length > end)
12551     return true;
12552 
12553   /* impose an artificial length limit of 64k */
12554   if (length > 65535)
12555     return true;
12556 
12557   if (PSI_CALL_set_thread_connect_attrs(*ptr, (uint)length, from_cs) &&
12558       current_thd->variables.log_warnings)
12559     sql_print_warning("Connection attributes of length %llu were truncated",
12560                       length);
12561   return false;
12562 }
12563 
12564 #endif
12565 
12566 /* the packet format is described in send_change_user_packet() */
parse_com_change_user_packet(MPVIO_EXT * mpvio,uint packet_length)12567 static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
12568 {
12569   THD *thd= mpvio->auth_info.thd;
12570   NET *net= &thd->net;
12571   Security_context *sctx= thd->security_ctx;
12572 
12573   char *user= (char*) net->read_pos;
12574   char *end= user + packet_length;
12575   /* Safe because there is always a trailing \0 at the end of the packet */
12576   char *passwd= strend(user) + 1;
12577   uint user_len= (uint)(passwd - user - 1);
12578   char *db= passwd;
12579   char db_buff[SAFE_NAME_LEN + 1];            // buffer to store db in utf8
12580   char user_buff[USERNAME_LENGTH + 1];	      // buffer to store user in utf8
12581   uint dummy_errors;
12582   DBUG_ENTER ("parse_com_change_user_packet");
12583 
12584   if (passwd >= end)
12585   {
12586     my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12587                MYF(0));
12588     DBUG_RETURN (1);
12589   }
12590 
12591   /*
12592     Old clients send null-terminated string as password; new clients send
12593     the size (1 byte) + string (not null-terminated). Hence in case of empty
12594     password both send '\0'.
12595 
12596     This strlen() can't be easily deleted without changing protocol.
12597 
12598     Cast *passwd to an unsigned char, so that it doesn't extend the sign for
12599     *passwd > 127 and become 2**32-127+ after casting to uint.
12600   */
12601   uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
12602                     (uchar) (*passwd++) : (uint)strlen(passwd));
12603 
12604   db+= passwd_len + 1;
12605   /*
12606     Database name is always NUL-terminated, so in case of empty database
12607     the packet must contain at least the trailing '\0'.
12608   */
12609   if (db >= end)
12610   {
12611     my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12612                MYF(0));
12613     DBUG_RETURN (1);
12614   }
12615 
12616   size_t db_len= strlen(db);
12617 
12618   char *next_field= db + db_len + 1;
12619 
12620   if (next_field + 1 < end)
12621   {
12622     if (thd_init_client_charset(thd, uint2korr(next_field)))
12623       DBUG_RETURN(1);
12624     next_field+= 2;
12625   }
12626 
12627   /* Convert database and user names to utf8 */
12628   db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
12629                            db, db_len, thd->charset(), &dummy_errors);
12630 
12631   user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
12632                              system_charset_info, user, user_len,
12633                              thd->charset(), &dummy_errors);
12634 
12635   if (!(sctx->user= my_strndup(user_buff, user_len, MYF(MY_WME))))
12636     DBUG_RETURN(1);
12637 
12638   /* Clear variables that are allocated */
12639   thd->user_connect= 0;
12640   strmake_buf(sctx->priv_user, sctx->user);
12641 
12642   if (thd->make_lex_string(&mpvio->db, db_buff, db_len) == 0)
12643     DBUG_RETURN(1); /* The error is set by make_lex_string(). */
12644 
12645   /*
12646     Clear thd->db as it points to something, that will be freed when
12647     connection is closed. We don't want to accidentally free a wrong
12648     pointer if connect failed.
12649   */
12650   thd->reset_db(&null_clex_str);
12651 
12652   if (!initialized)
12653   {
12654     // if mysqld's been started with --skip-grant-tables option
12655     mpvio->status= MPVIO_EXT::SUCCESS;
12656     DBUG_RETURN(0);
12657   }
12658 
12659 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12660   thd->password= passwd_len > 0;
12661   if (find_mpvio_user(mpvio))
12662     DBUG_RETURN(1);
12663 
12664   const char *client_plugin;
12665   if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
12666   {
12667     if (next_field >= end)
12668     {
12669       my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12670                  MYF(0));
12671       DBUG_RETURN(1);
12672     }
12673     client_plugin= fix_plugin_ptr(next_field);
12674     next_field+= strlen(next_field) + 1;
12675   }
12676   else
12677   {
12678     if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
12679       client_plugin= native_password_plugin_name.str;
12680     else
12681     {
12682       client_plugin=  old_password_plugin_name.str;
12683       /*
12684         For a passwordless accounts we use native_password_plugin.
12685         But when an old 4.0 client connects to it, we change it to
12686         old_password_plugin, otherwise MySQL will think that server
12687         and client plugins don't match.
12688       */
12689       if (mpvio->acl_user->auth_string.length == 0)
12690         mpvio->acl_user->plugin= old_password_plugin_name;
12691     }
12692   }
12693 
12694   if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
12695       read_client_connect_attrs(&next_field, end,
12696                                 thd->charset()))
12697   {
12698     my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
12699                MYF(0));
12700     DBUG_RETURN(1);
12701   }
12702 
12703   DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
12704   /*
12705     Remember the data part of the packet, to present it to plugin in
12706     read_packet()
12707   */
12708   mpvio->cached_client_reply.pkt= passwd;
12709   mpvio->cached_client_reply.pkt_len= passwd_len;
12710   mpvio->cached_client_reply.plugin= client_plugin;
12711   mpvio->status= MPVIO_EXT::RESTART;
12712 #endif
12713 
12714   DBUG_RETURN (0);
12715 }
12716 
12717 
12718 /* the packet format is described in send_client_reply_packet() */
parse_client_handshake_packet(MPVIO_EXT * mpvio,uchar ** buff,ulong pkt_len)12719 static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
12720                                            uchar **buff, ulong pkt_len)
12721 {
12722 #ifndef EMBEDDED_LIBRARY
12723   THD *thd= mpvio->auth_info.thd;
12724   NET *net= &thd->net;
12725   char *end;
12726   DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
12727 
12728   if (pkt_len < MIN_HANDSHAKE_SIZE)
12729     return packet_error;
12730 
12731   /*
12732     Protocol buffer is guaranteed to always end with \0. (see my_net_read())
12733     As the code below depends on this, lets check that.
12734   */
12735   DBUG_ASSERT(net->read_pos[pkt_len] == 0);
12736 
12737   ulonglong client_capabilities= uint2korr(net->read_pos);
12738   compile_time_assert(sizeof(client_capabilities) >= 8);
12739   if (client_capabilities & CLIENT_PROTOCOL_41)
12740   {
12741     if (pkt_len < 32)
12742       return packet_error;
12743     client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
12744     if (!(client_capabilities & CLIENT_MYSQL))
12745     {
12746       // it is client with mariadb extensions
12747       ulonglong ext_client_capabilities=
12748         (((ulonglong)uint4korr(net->read_pos + 28)) << 32);
12749       client_capabilities|= ext_client_capabilities;
12750     }
12751   }
12752 
12753   /* Disable those bits which are not supported by the client. */
12754   compile_time_assert(sizeof(thd->client_capabilities) >= 8);
12755   thd->client_capabilities&= client_capabilities;
12756 
12757   DBUG_PRINT("info", ("client capabilities: %llu", thd->client_capabilities));
12758   if (thd->client_capabilities & CLIENT_SSL)
12759   {
12760     unsigned long errptr __attribute__((unused));
12761 
12762     /* Do the SSL layering. */
12763     if (!ssl_acceptor_fd)
12764       return packet_error;
12765 
12766     DBUG_PRINT("info", ("IO layer change in progress..."));
12767     if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr))
12768     {
12769       DBUG_PRINT("error", ("Failed to accept new SSL connection"));
12770       return packet_error;
12771     }
12772 
12773     DBUG_PRINT("info", ("Reading user information over SSL layer"));
12774     pkt_len= my_net_read(net);
12775     if (unlikely(pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE))
12776     {
12777       DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
12778 			   pkt_len));
12779       return packet_error;
12780     }
12781   }
12782 
12783   if (client_capabilities & CLIENT_PROTOCOL_41)
12784   {
12785     thd->max_client_packet_length= uint4korr(net->read_pos+4);
12786     DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
12787     if (thd_init_client_charset(thd, (uint) net->read_pos[8]))
12788       return packet_error;
12789     end= (char*) net->read_pos+32;
12790   }
12791   else
12792   {
12793     if (pkt_len < 5)
12794       return packet_error;
12795     thd->max_client_packet_length= uint3korr(net->read_pos+2);
12796     end= (char*) net->read_pos+5;
12797   }
12798 
12799   if (end >= (char*) net->read_pos+ pkt_len +2)
12800     return packet_error;
12801 
12802   if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
12803     thd->variables.sql_mode|= MODE_IGNORE_SPACE;
12804   if (thd->client_capabilities & CLIENT_INTERACTIVE)
12805     thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
12806 
12807   if (end >= (char*) net->read_pos+ pkt_len +2)
12808     return packet_error;
12809 
12810   if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
12811       opt_using_transactions)
12812     net->return_status= &thd->server_status;
12813 
12814   char *user= end;
12815   char *passwd= strend(user)+1;
12816   size_t user_len= (size_t)(passwd - user - 1), db_len;
12817   char *db= passwd;
12818   char user_buff[USERNAME_LENGTH + 1];	// buffer to store user in utf8
12819   uint dummy_errors;
12820 
12821   /*
12822     Old clients send null-terminated string as password; new clients send
12823     the size (1 byte) + string (not null-terminated). Hence in case of empty
12824     password both send '\0'.
12825 
12826     This strlen() can't be easily deleted without changing protocol.
12827 
12828     Cast *passwd to an unsigned char, so that it doesn't extend the sign for
12829     *passwd > 127 and become 2**32-127+ after casting to uint.
12830   */
12831   ulonglong len;
12832   size_t passwd_len;
12833 
12834   if (!(thd->client_capabilities & CLIENT_SECURE_CONNECTION))
12835     len= strlen(passwd);
12836   else if (!(thd->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA))
12837     len= (uchar)(*passwd++);
12838   else
12839   {
12840     len= safe_net_field_length_ll((uchar**)&passwd,
12841                                       net->read_pos + pkt_len - (uchar*)passwd);
12842     if (len > pkt_len)
12843       return packet_error;
12844   }
12845 
12846   passwd_len= (size_t)len;
12847   db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
12848     db + passwd_len + 1 : 0;
12849 
12850   if (passwd == NULL ||
12851       passwd + passwd_len + MY_TEST(db) > (char*) net->read_pos + pkt_len)
12852     return packet_error;
12853 
12854   /* strlen() can't be easily deleted without changing protocol */
12855   db_len= safe_strlen(db);
12856 
12857   char *next_field;
12858   const char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0);
12859 
12860   /*
12861     Since 4.1 all database names are stored in utf8
12862     The cast is ok as copy_with_error will create a new area for db
12863   */
12864   if (unlikely(thd->copy_with_error(system_charset_info,
12865                                     (LEX_STRING*) &mpvio->db,
12866                                     thd->charset(), db, db_len)))
12867     return packet_error;
12868 
12869   user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
12870                              system_charset_info, user, user_len,
12871                              thd->charset(), &dummy_errors);
12872   user= user_buff;
12873 
12874   /* If username starts and ends in "'", chop them off */
12875   if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
12876   {
12877     user++;
12878     user_len-= 2;
12879   }
12880 
12881   /*
12882     Clip username to allowed length in characters (not bytes).  This is
12883     mostly for backward compatibility (to truncate long usernames, as
12884     old 5.1 did)
12885   */
12886   user_len= Well_formed_prefix(system_charset_info, user, user_len,
12887                                username_char_length).length();
12888   user[user_len]= '\0';
12889 
12890   Security_context *sctx= thd->security_ctx;
12891 
12892   my_free((char*) sctx->user);
12893   if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME))))
12894     return packet_error; /* The error is set by my_strdup(). */
12895 
12896 
12897   /*
12898     Clear thd->db as it points to something, that will be freed when
12899     connection is closed. We don't want to accidentally free a wrong
12900     pointer if connect failed.
12901   */
12902   thd->reset_db(&null_clex_str);
12903 
12904   if (!initialized)
12905   {
12906     // if mysqld's been started with --skip-grant-tables option
12907     mpvio->status= MPVIO_EXT::SUCCESS;
12908     return packet_error;
12909   }
12910 
12911   thd->password= passwd_len > 0;
12912   if (find_mpvio_user(mpvio))
12913     return packet_error;
12914 
12915   if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) &&
12916       (client_plugin < (char *)net->read_pos + pkt_len))
12917   {
12918     client_plugin= fix_plugin_ptr(client_plugin);
12919     next_field+= strlen(next_field) + 1;
12920   }
12921   else
12922   {
12923     /* Some clients lie. Sad, but true */
12924     thd->client_capabilities &= ~CLIENT_PLUGIN_AUTH;
12925 
12926     if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
12927       client_plugin= native_password_plugin_name.str;
12928     else
12929     {
12930       client_plugin=  old_password_plugin_name.str;
12931       /*
12932         For a passwordless accounts we use native_password_plugin.
12933         But when an old 4.0 client connects to it, we change it to
12934         old_password_plugin, otherwise MySQL will think that server
12935         and client plugins don't match.
12936       */
12937       if (mpvio->acl_user->auth_string.length == 0)
12938         mpvio->acl_user->plugin= old_password_plugin_name;
12939     }
12940   }
12941 
12942   if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
12943       read_client_connect_attrs(&next_field, ((char *)net->read_pos) + pkt_len,
12944                                 mpvio->auth_info.thd->charset()))
12945     return packet_error;
12946 
12947   /*
12948     if the acl_user needs a different plugin to authenticate
12949     (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
12950     we need to restart the authentication in the server.
12951     But perhaps the client has already used the correct plugin -
12952     in that case the authentication on the client may not need to be
12953     restarted and a server auth plugin will read the data that the client
12954     has just send. Cache them to return in the next server_mpvio_read_packet().
12955   */
12956   if (!lex_string_eq(&mpvio->acl_user->plugin, plugin_name(mpvio->plugin)))
12957   {
12958     mpvio->cached_client_reply.pkt= passwd;
12959     mpvio->cached_client_reply.pkt_len= (uint)passwd_len;
12960     mpvio->cached_client_reply.plugin= client_plugin;
12961     mpvio->status= MPVIO_EXT::RESTART;
12962     return packet_error;
12963   }
12964 
12965   /*
12966     ok, we don't need to restart the authentication on the server.
12967     but if the client used the wrong plugin, we need to restart
12968     the authentication on the client. Do it here, the server plugin
12969     doesn't need to know.
12970   */
12971   const char *client_auth_plugin=
12972     ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
12973 
12974   if (client_auth_plugin &&
12975       my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
12976   {
12977     mpvio->cached_client_reply.plugin= client_plugin;
12978     if (send_plugin_request_packet(mpvio,
12979                                    (uchar*) mpvio->cached_server_packet.pkt,
12980                                    mpvio->cached_server_packet.pkt_len))
12981       return packet_error;
12982 
12983     passwd_len= my_net_read(&thd->net);
12984     passwd= (char*)thd->net.read_pos;
12985   }
12986 
12987   *buff= (uchar*) passwd;
12988   return (ulong)passwd_len;
12989 #else
12990   return 0;
12991 #endif
12992 }
12993 
12994 
12995 /**
12996   vio->write_packet() callback method for server authentication plugins
12997 
12998   This function is called by a server authentication plugin, when it wants
12999   to send data to the client.
13000 
13001   It transparently wraps the data into a handshake packet,
13002   and handles plugin negotiation with the client. If necessary,
13003   it escapes the plugin data, if it starts with a mysql protocol packet byte.
13004 */
server_mpvio_write_packet(MYSQL_PLUGIN_VIO * param,const uchar * packet,int packet_len)13005 static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
13006                                    const uchar *packet, int packet_len)
13007 {
13008   MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
13009   int res;
13010   DBUG_ENTER("server_mpvio_write_packet");
13011 
13012   /* reset cached_client_reply */
13013   mpvio->cached_client_reply.pkt= 0;
13014 
13015   /* for the 1st packet we wrap plugin data into the handshake packet */
13016   if (mpvio->packets_written == 0)
13017     res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
13018   else if (mpvio->status == MPVIO_EXT::RESTART)
13019     res= send_plugin_request_packet(mpvio, packet, packet_len);
13020   else if (packet_len > 0 && (*packet == 1 || *packet == 255 || *packet == 254))
13021   {
13022     /*
13023       we cannot allow plugin data packet to start from 255 or 254 -
13024       as the client will treat it as an error or "change plugin" packet.
13025       We'll escape these bytes with \1. Consequently, we
13026       have to escape \1 byte too.
13027     */
13028     res= net_write_command(&mpvio->auth_info.thd->net, 1, (uchar*)"", 0,
13029                            packet, packet_len);
13030   }
13031   else
13032   {
13033     res= my_net_write(&mpvio->auth_info.thd->net, packet, packet_len) ||
13034          net_flush(&mpvio->auth_info.thd->net);
13035   }
13036   mpvio->packets_written++;
13037   DBUG_RETURN(res);
13038 }
13039 
13040 /**
13041   vio->read_packet() callback method for server authentication plugins
13042 
13043   This function is called by a server authentication plugin, when it wants
13044   to read data from the client.
13045 
13046   It transparently extracts the client plugin data, if embedded into
13047   a client authentication handshake packet, and handles plugin negotiation
13048   with the client, if necessary.
13049 */
server_mpvio_read_packet(MYSQL_PLUGIN_VIO * param,uchar ** buf)13050 static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
13051 {
13052   MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
13053   ulong pkt_len;
13054   DBUG_ENTER("server_mpvio_read_packet");
13055   if (mpvio->packets_written == 0)
13056   {
13057     /*
13058       plugin wants to read the data without sending anything first.
13059       send an empty packet to force a server handshake packet to be sent
13060     */
13061     if (server_mpvio_write_packet(mpvio, 0, 0))
13062       pkt_len= packet_error;
13063     else
13064       pkt_len= my_net_read(&mpvio->auth_info.thd->net);
13065   }
13066   else if (mpvio->cached_client_reply.pkt)
13067   {
13068     DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
13069     DBUG_ASSERT(mpvio->packets_read > 0);
13070     /*
13071       if the have the data cached from the last server_mpvio_read_packet
13072       (which can be the case if it's a restarted authentication)
13073       and a client has used the correct plugin, then we can return the
13074       cached data straight away and avoid one round trip.
13075     */
13076     const char *client_auth_plugin=
13077       ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
13078     if (client_auth_plugin == 0 ||
13079         my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
13080                       client_auth_plugin) == 0)
13081     {
13082       mpvio->status= MPVIO_EXT::FAILURE;
13083       *buf= (uchar*) mpvio->cached_client_reply.pkt;
13084       mpvio->cached_client_reply.pkt= 0;
13085       mpvio->packets_read++;
13086 
13087       DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
13088     }
13089 
13090     /*
13091       But if the client has used the wrong plugin, the cached data are
13092       useless. Furthermore, we have to send a "change plugin" request
13093       to the client.
13094     */
13095     if (server_mpvio_write_packet(mpvio, 0, 0))
13096       pkt_len= packet_error;
13097     else
13098       pkt_len= my_net_read(&mpvio->auth_info.thd->net);
13099   }
13100   else
13101     pkt_len= my_net_read(&mpvio->auth_info.thd->net);
13102 
13103   if (unlikely(pkt_len == packet_error))
13104     goto err;
13105 
13106   mpvio->packets_read++;
13107 
13108   /*
13109     the 1st packet has the plugin data wrapped into the client authentication
13110     handshake packet
13111   */
13112   if (mpvio->packets_read == 1)
13113   {
13114     pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
13115     if (unlikely(pkt_len == packet_error))
13116       goto err;
13117   }
13118   else
13119     *buf= mpvio->auth_info.thd->net.read_pos;
13120 
13121   DBUG_RETURN((int)pkt_len);
13122 
13123 err:
13124   if (mpvio->status == MPVIO_EXT::FAILURE)
13125   {
13126     if (!mpvio->auth_info.thd->is_error())
13127       my_error(ER_HANDSHAKE_ERROR, MYF(0));
13128   }
13129   DBUG_RETURN(-1);
13130 }
13131 
13132 /**
13133   fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
13134   connection
13135 */
server_mpvio_info(MYSQL_PLUGIN_VIO * vio,MYSQL_PLUGIN_VIO_INFO * info)13136 static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
13137                               MYSQL_PLUGIN_VIO_INFO *info)
13138 {
13139   MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
13140   mpvio_info(mpvio->auth_info.thd->net.vio, info);
13141 }
13142 
acl_check_ssl(THD * thd,const ACL_USER * acl_user)13143 static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
13144 {
13145 #ifdef HAVE_OPENSSL
13146   Vio *vio= thd->net.vio;
13147   SSL *ssl= (SSL *) vio->ssl_arg;
13148   X509 *cert;
13149 #endif
13150 
13151   /*
13152     At this point we know that user is allowed to connect
13153     from given host by given username/password pair. Now
13154     we check if SSL is required, if user is using SSL and
13155     if X509 certificate attributes are OK
13156   */
13157   switch (acl_user->ssl_type) {
13158   case SSL_TYPE_NOT_SPECIFIED:                  // Impossible
13159   case SSL_TYPE_NONE:                           // SSL is not required
13160     return 0;
13161 #ifdef HAVE_OPENSSL
13162   case SSL_TYPE_ANY:                            // Any kind of SSL is ok
13163     return vio_type(vio) != VIO_TYPE_SSL;
13164   case SSL_TYPE_X509: /* Client should have any valid certificate. */
13165     /*
13166       Connections with non-valid certificates are dropped already
13167       in sslaccept() anyway, so we do not check validity here.
13168 
13169       We need to check for absence of SSL because without SSL
13170       we should reject connection.
13171     */
13172     if (vio_type(vio) == VIO_TYPE_SSL &&
13173         SSL_get_verify_result(ssl) == X509_V_OK &&
13174         (cert= SSL_get_peer_certificate(ssl)))
13175     {
13176       X509_free(cert);
13177       return 0;
13178     }
13179     return 1;
13180   case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
13181     /* If a cipher name is specified, we compare it to actual cipher in use. */
13182     if (vio_type(vio) != VIO_TYPE_SSL ||
13183         SSL_get_verify_result(ssl) != X509_V_OK)
13184       return 1;
13185     if (acl_user->ssl_cipher)
13186     {
13187       DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
13188                          acl_user->ssl_cipher, SSL_get_cipher(ssl)));
13189       if (strcmp(acl_user->ssl_cipher, SSL_get_cipher(ssl)))
13190       {
13191         if (global_system_variables.log_warnings)
13192           sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
13193                             acl_user->ssl_cipher, SSL_get_cipher(ssl));
13194         return 1;
13195       }
13196     }
13197     if (!acl_user->x509_issuer && !acl_user->x509_subject)
13198       return 0; // all done
13199 
13200     /* Prepare certificate (if exists) */
13201     if (!(cert= SSL_get_peer_certificate(ssl)))
13202       return 1;
13203     /* If X509 issuer is specified, we check it... */
13204     if (acl_user->x509_issuer)
13205     {
13206       char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
13207       DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
13208                          acl_user->x509_issuer, ptr));
13209       if (strcmp(acl_user->x509_issuer, ptr))
13210       {
13211         if (global_system_variables.log_warnings)
13212           sql_print_information("X509 issuer mismatch: should be '%s' "
13213                             "but is '%s'", acl_user->x509_issuer, ptr);
13214         free(ptr);
13215         X509_free(cert);
13216         return 1;
13217       }
13218       free(ptr);
13219     }
13220     /* X509 subject is specified, we check it .. */
13221     if (acl_user->x509_subject)
13222     {
13223       char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
13224       DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
13225                          acl_user->x509_subject, ptr));
13226       if (strcmp(acl_user->x509_subject, ptr))
13227       {
13228         if (global_system_variables.log_warnings)
13229           sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
13230                           acl_user->x509_subject, ptr);
13231         free(ptr);
13232         X509_free(cert);
13233         return 1;
13234       }
13235       free(ptr);
13236     }
13237     X509_free(cert);
13238     return 0;
13239 #else  /* HAVE_OPENSSL */
13240   default:
13241     /*
13242       If we don't have SSL but SSL is required for this user the
13243       authentication should fail.
13244     */
13245     return 1;
13246 #endif /* HAVE_OPENSSL */
13247   }
13248   return 1;
13249 }
13250 
13251 
do_auth_once(THD * thd,const LEX_CSTRING * auth_plugin_name,MPVIO_EXT * mpvio)13252 static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name,
13253                         MPVIO_EXT *mpvio)
13254 {
13255   int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
13256   bool unlock_plugin= false;
13257   plugin_ref plugin= NULL;
13258 
13259   if (auth_plugin_name->str == native_password_plugin_name.str)
13260     plugin= native_password_plugin;
13261 #ifndef EMBEDDED_LIBRARY
13262   else if (auth_plugin_name->str == old_password_plugin_name.str)
13263     plugin= old_password_plugin;
13264   else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
13265                                            MYSQL_AUTHENTICATION_PLUGIN)))
13266     unlock_plugin= true;
13267 #endif
13268 
13269   mpvio->plugin= plugin;
13270   old_status= mpvio->status;
13271 
13272   if (plugin)
13273   {
13274     st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
13275     switch (auth->interface_version >> 8) {
13276     case 0x02:
13277       res= auth->authenticate_user(mpvio, &mpvio->auth_info);
13278       break;
13279     case 0x01:
13280       {
13281         MYSQL_SERVER_AUTH_INFO_0x0100 compat;
13282         compat.downgrade(&mpvio->auth_info);
13283         res= auth->authenticate_user(mpvio, (MYSQL_SERVER_AUTH_INFO *)&compat);
13284         compat.upgrade(&mpvio->auth_info);
13285       }
13286       break;
13287     default: DBUG_ASSERT(0);
13288     }
13289 
13290     if (unlock_plugin)
13291       plugin_unlock(thd, plugin);
13292   }
13293   else
13294   {
13295     /* Server cannot load the required plugin. */
13296     Host_errors errors;
13297     errors.m_no_auth_plugin= 1;
13298     inc_host_errors(mpvio->auth_info.thd->security_ctx->ip, &errors);
13299     my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
13300     res= CR_ERROR;
13301   }
13302 
13303   /*
13304     If the status was MPVIO_EXT::RESTART before the authenticate_user() call
13305     it can never be MPVIO_EXT::RESTART after the call, because any call
13306     to write_packet() or read_packet() will reset the status.
13307 
13308     But (!) if a plugin never called a read_packet() or write_packet(), the
13309     status will stay unchanged. We'll fix it, by resetting the status here.
13310   */
13311   if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART)
13312     mpvio->status= MPVIO_EXT::FAILURE; // reset to the default
13313 
13314   return res;
13315 }
13316 
13317 
13318 /**
13319   Perform the handshake, authorize the client and update thd sctx variables.
13320 
13321   @param thd                     thread handle
13322   @param com_change_user_pkt_len size of the COM_CHANGE_USER packet
13323                                  (without the first, command, byte) or 0
13324                                  if it's not a COM_CHANGE_USER (that is, if
13325                                  it's a new connection)
13326 
13327   @retval 0  success, thd is updated.
13328   @retval 1  error
13329 */
acl_authenticate(THD * thd,uint com_change_user_pkt_len)13330 bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
13331 {
13332   int res= CR_OK;
13333   MPVIO_EXT mpvio;
13334   const LEX_CSTRING *auth_plugin_name= default_auth_plugin_name;
13335   enum  enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
13336                                                              : COM_CONNECT;
13337   DBUG_ENTER("acl_authenticate");
13338 
13339   bzero(&mpvio, sizeof(mpvio));
13340   mpvio.read_packet= server_mpvio_read_packet;
13341   mpvio.write_packet= server_mpvio_write_packet;
13342   mpvio.info= server_mpvio_info;
13343   mpvio.status= MPVIO_EXT::FAILURE;
13344   mpvio.make_it_fail= false;
13345   mpvio.auth_info.thd= thd;
13346   mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
13347   mpvio.auth_info.host_or_ip_length=
13348     (unsigned int) strlen(thd->security_ctx->host_or_ip);
13349 
13350   DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
13351 
13352   if (command == COM_CHANGE_USER)
13353   {
13354     mpvio.packets_written++; // pretend that a server handshake packet was sent
13355     mpvio.packets_read++;    // take COM_CHANGE_USER packet into account
13356 
13357     if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
13358       DBUG_RETURN(1);
13359 
13360     DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
13361                 mpvio.status == MPVIO_EXT::SUCCESS);
13362   }
13363   else
13364   {
13365     /* mark the thd as having no scramble yet */
13366     thd->scramble[SCRAMBLE_LENGTH]= 1;
13367 
13368     /*
13369       perform the first authentication attempt, with the default plugin.
13370       This sends the server handshake packet, reads the client reply
13371       with a user name, and performs the authentication if everyone has used
13372       the correct plugin.
13373     */
13374 
13375     res= do_auth_once(thd, auth_plugin_name, &mpvio);
13376   }
13377 
13378   /*
13379     retry the authentication, if - after receiving the user name -
13380     we found that we need to switch to a non-default plugin
13381   */
13382   if (mpvio.status == MPVIO_EXT::RESTART)
13383   {
13384     DBUG_ASSERT(mpvio.acl_user);
13385     DBUG_ASSERT(command == COM_CHANGE_USER ||
13386                 !lex_string_eq(auth_plugin_name, &mpvio.acl_user->plugin));
13387     auth_plugin_name= &mpvio.acl_user->plugin;
13388     res= do_auth_once(thd, auth_plugin_name, &mpvio);
13389   }
13390   if (mpvio.make_it_fail && res == CR_OK)
13391   {
13392     mpvio.status= MPVIO_EXT::FAILURE;
13393     res= CR_ERROR;
13394   }
13395 
13396   Security_context *sctx= thd->security_ctx;
13397   const ACL_USER *acl_user= mpvio.acl_user;
13398 
13399   thd->password= mpvio.auth_info.password_used;  // remember for error messages
13400 
13401   /*
13402     Log the command here so that the user can check the log
13403     for the tried logins and also to detect break-in attempts.
13404 
13405     if sctx->user is unset it's protocol failure, bad packet.
13406   */
13407   if (sctx->user)
13408   {
13409     if (strcmp(sctx->priv_user, sctx->user))
13410     {
13411       general_log_print(thd, command, "%s@%s as %s on %s",
13412                         sctx->user, sctx->host_or_ip,
13413                         sctx->priv_user[0] ? sctx->priv_user : "anonymous",
13414                         safe_str(mpvio.db.str));
13415     }
13416     else
13417       general_log_print(thd, command, (char*) "%s@%s on %s",
13418                         sctx->user, sctx->host_or_ip,
13419                         safe_str(mpvio.db.str));
13420   }
13421 
13422   if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
13423   {
13424     Host_errors errors;
13425     DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
13426     switch (res)
13427     {
13428     case CR_AUTH_PLUGIN_ERROR:
13429       errors.m_auth_plugin= 1;
13430       break;
13431     case CR_AUTH_HANDSHAKE:
13432       errors.m_handshake= 1;
13433       break;
13434     case CR_AUTH_USER_CREDENTIALS:
13435       errors.m_authentication= 1;
13436       break;
13437     case CR_ERROR:
13438     default:
13439       /* Unknown of unspecified auth plugin error. */
13440       errors.m_auth_plugin= 1;
13441       break;
13442     }
13443     inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13444     if (!thd->is_error())
13445       login_failed_error(thd);
13446     DBUG_RETURN(1);
13447   }
13448 
13449   sctx->proxy_user[0]= 0;
13450 
13451   if (initialized) // if not --skip-grant-tables
13452   {
13453 #ifndef NO_EMBEDDED_ACCESS_CHECKS
13454     bool is_proxy_user= FALSE;
13455     const char *auth_user = safe_str(acl_user->user.str);
13456     ACL_PROXY_USER *proxy_user;
13457     /* check if the user is allowed to proxy as another user */
13458     proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
13459                                     mpvio.auth_info.authenticated_as,
13460                                           &is_proxy_user);
13461     if (is_proxy_user)
13462     {
13463       ACL_USER *acl_proxy_user;
13464 
13465       /* we need to find the proxy user, but there was none */
13466       if (!proxy_user)
13467       {
13468         Host_errors errors;
13469         errors.m_proxy_user= 1;
13470         inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13471         if (!thd->is_error())
13472           login_failed_error(thd);
13473         DBUG_RETURN(1);
13474       }
13475 
13476       my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
13477                   "'%s'@'%s'", auth_user,
13478                   safe_str(acl_user->host.hostname));
13479 
13480       /* we're proxying : find the proxy user definition */
13481       mysql_mutex_lock(&acl_cache->lock);
13482       acl_proxy_user= find_user_exact(safe_str(proxy_user->get_proxied_host()),
13483                                      mpvio.auth_info.authenticated_as);
13484       if (!acl_proxy_user)
13485       {
13486         mysql_mutex_unlock(&acl_cache->lock);
13487 
13488         Host_errors errors;
13489         errors.m_proxy_user_acl= 1;
13490         inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13491         if (!thd->is_error())
13492           login_failed_error(thd);
13493         DBUG_RETURN(1);
13494       }
13495       acl_user= acl_proxy_user->copy(thd->mem_root);
13496       mysql_mutex_unlock(&acl_cache->lock);
13497     }
13498 #endif
13499 
13500     sctx->master_access= acl_user->access;
13501     if (acl_user->user.str)
13502       strmake_buf(sctx->priv_user, acl_user->user.str);
13503     else
13504       *sctx->priv_user= 0;
13505 
13506     if (acl_user->host.hostname)
13507       strmake_buf(sctx->priv_host, acl_user->host.hostname);
13508     else
13509       *sctx->priv_host= 0;
13510 
13511     /*
13512       OK. Let's check the SSL. Historically it was checked after the password,
13513       as an additional layer, not instead of the password
13514       (in which case it would've been a plugin too).
13515     */
13516     if (acl_check_ssl(thd, acl_user))
13517     {
13518       Host_errors errors;
13519       errors.m_ssl= 1;
13520       inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
13521       login_failed_error(thd);
13522       DBUG_RETURN(1);
13523     }
13524 
13525     /*
13526       Don't allow the user to connect if he has done too many queries.
13527       As we are testing max_user_connections == 0 here, it means that we
13528       can't let the user change max_user_connections from 0 in the server
13529       without a restart as it would lead to wrong connect counting.
13530     */
13531     if ((acl_user->user_resource.questions ||
13532          acl_user->user_resource.updates ||
13533          acl_user->user_resource.conn_per_hour ||
13534          acl_user->user_resource.user_conn ||
13535          acl_user->user_resource.max_statement_time != 0.0 ||
13536          max_user_connections_checking) &&
13537          get_or_create_user_conn(thd,
13538            (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
13539            (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
13540            &acl_user->user_resource))
13541       DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
13542 
13543     if (acl_user->user_resource.max_statement_time != 0.0)
13544     {
13545       thd->variables.max_statement_time_double=
13546         acl_user->user_resource.max_statement_time;
13547       thd->variables.max_statement_time=
13548         (ulonglong) (thd->variables.max_statement_time_double * 1e6 + 0.1);
13549     }
13550   }
13551   else
13552     sctx->skip_grants();
13553 
13554   if (thd->user_connect &&
13555       (thd->user_connect->user_resources.conn_per_hour ||
13556        thd->user_connect->user_resources.user_conn ||
13557        max_user_connections_checking) &&
13558        check_for_max_user_connections(thd, thd->user_connect))
13559   {
13560     /* Ensure we don't decrement thd->user_connections->connections twice */
13561     thd->user_connect= 0;
13562     status_var_increment(denied_connections);
13563     DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
13564   }
13565 
13566   DBUG_PRINT("info",
13567              ("Capabilities: %llu  packet_length: %ld  Host: '%s'  "
13568               "Login user: '%s' Priv_user: '%s'  Using password: %s "
13569               "Access: %lu  db: '%s'",
13570               thd->client_capabilities, thd->max_client_packet_length,
13571               sctx->host_or_ip, sctx->user, sctx->priv_user,
13572               thd->password ? "yes": "no",
13573               sctx->master_access, mpvio.db.str));
13574 
13575   if (command == COM_CONNECT &&
13576       !(thd->main_security_ctx.master_access & SUPER_ACL))
13577   {
13578     mysql_mutex_lock(&LOCK_connection_count);
13579     bool count_ok= (*thd->scheduler->connection_count <=
13580                     *thd->scheduler->max_connections);
13581     mysql_mutex_unlock(&LOCK_connection_count);
13582     if (!count_ok)
13583     {                                         // too many connections
13584       my_error(ER_CON_COUNT_ERROR, MYF(0));
13585       DBUG_RETURN(1);
13586     }
13587   }
13588 
13589   /*
13590     This is the default access rights for the current database.  It's
13591     set to 0 here because we don't have an active database yet (and we
13592     may not have an active database to set.
13593   */
13594   sctx->db_access=0;
13595 
13596 #ifndef NO_EMBEDDED_ACCESS_CHECKS
13597   /*
13598     In case the user has a default role set, attempt to set that role
13599   */
13600   if (initialized && acl_user->default_rolename.length) {
13601     ulonglong access= 0;
13602     int result;
13603     result= acl_check_setrole(thd, acl_user->default_rolename.str, &access);
13604     if (!result)
13605       result= acl_setrole(thd, acl_user->default_rolename.str, access);
13606     if (result)
13607       thd->clear_error(); // even if the default role was not granted, do not
13608                           // close the connection
13609   }
13610 #endif
13611 
13612   /* Change a database if necessary */
13613   if (mpvio.db.length)
13614   {
13615     uint err = mysql_change_db(thd, &mpvio.db, FALSE);
13616     if(err)
13617     {
13618       if (err == ER_DBACCESS_DENIED_ERROR)
13619       {
13620         /*
13621           Got an "access denied" error, which must be handled
13622           other access denied errors (see login_failed_error()).
13623           mysql_change_db() already sent error to client, and
13624           wrote to general log, we only need to increment the counter
13625           and maybe write a warning to error log.
13626         */
13627         status_var_increment(thd->status_var.access_denied_errors);
13628         if (global_system_variables.log_warnings > 1)
13629         {
13630           Security_context* sctx = thd->security_ctx;
13631           sql_print_warning(ER_THD(thd, err),
13632             sctx->priv_user, sctx->priv_host, mpvio.db.str);
13633         }
13634       }
13635       DBUG_RETURN(1);
13636     }
13637   }
13638 
13639   thd->net.net_skip_rest_factor= 2;  // skip at most 2*max_packet_size
13640 
13641   if (mpvio.auth_info.external_user[0])
13642     sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));
13643 
13644   if (res == CR_OK_HANDSHAKE_COMPLETE)
13645     thd->get_stmt_da()->disable_status();
13646   else
13647     my_ok(thd);
13648 
13649   PSI_CALL_set_thread_user_host
13650     (thd->main_security_ctx.user, (uint)strlen(thd->main_security_ctx.user),
13651     thd->main_security_ctx.host_or_ip, (uint)strlen(thd->main_security_ctx.host_or_ip));
13652 
13653   /* Ready to handle queries */
13654   DBUG_RETURN(0);
13655 }
13656 
13657 /**
13658   MySQL Server Password Authentication Plugin
13659 
13660   In the MySQL authentication protocol:
13661   1. the server sends the random scramble to the client
13662   2. client sends the encrypted password back to the server
13663   3. the server checks the password.
13664 */
native_password_authenticate(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)13665 static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
13666                                         MYSQL_SERVER_AUTH_INFO *info)
13667 {
13668   uchar *pkt;
13669   int pkt_len;
13670   MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
13671   THD *thd=info->thd;
13672   DBUG_ENTER("native_password_authenticate");
13673 
13674   /* generate the scramble, or reuse the old one */
13675   if (thd->scramble[SCRAMBLE_LENGTH])
13676   {
13677     thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
13678     /* and send it to the client */
13679     if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
13680       DBUG_RETURN(CR_AUTH_HANDSHAKE);
13681   }
13682 
13683   /* reply and authenticate */
13684 
13685   /*
13686     <digression>
13687       This is more complex than it looks.
13688 
13689       The plugin (we) may be called right after the client was connected -
13690       and will need to send a scramble, read reply, authenticate.
13691 
13692       Or the plugin may be called after another plugin has sent a scramble,
13693       and read the reply. If the client has used the correct client-plugin,
13694       we won't need to read anything here from the client, the client
13695       has already sent a reply with everything we need for authentication.
13696 
13697       Or the plugin may be called after another plugin has sent a scramble,
13698       and read the reply, but the client has used the wrong client-plugin.
13699       We'll need to sent a "switch to another plugin" packet to the
13700       client and read the reply. "Use the short scramble" packet is a special
13701       case of "switch to another plugin" packet.
13702 
13703       Or, perhaps, the plugin may be called after another plugin has
13704       done the handshake but did not send a useful scramble. We'll need
13705       to send a scramble (and perhaps a "switch to another plugin" packet)
13706       and read the reply.
13707 
13708       Besides, a client may be an old one, that doesn't understand plugins.
13709       Or doesn't even understand 4.0 scramble.
13710 
13711       And we want to keep the same protocol on the wire  unless non-native
13712       plugins are involved.
13713 
13714       Anyway, it still looks simple from a plugin point of view:
13715       "send the scramble, read the reply and authenticate".
13716       All the magic is transparently handled by the server.
13717     </digression>
13718   */
13719 
13720   /* read the reply with the encrypted password */
13721   if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
13722     DBUG_RETURN(CR_AUTH_HANDSHAKE);
13723   DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
13724 
13725 #ifdef NO_EMBEDDED_ACCESS_CHECKS
13726   DBUG_RETURN(CR_OK);
13727 #endif
13728 
13729   DBUG_EXECUTE_IF("native_password_bad_reply", { pkt_len= 12; });
13730 
13731   if (pkt_len == 0) /* no password */
13732     DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK);
13733 
13734   info->password_used= PASSWORD_USED_YES;
13735   if (pkt_len == SCRAMBLE_LENGTH)
13736   {
13737     if (!mpvio->acl_user->salt_len)
13738       DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
13739 
13740     if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt))
13741       DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
13742     else
13743       DBUG_RETURN(CR_OK);
13744   }
13745 
13746   my_error(ER_HANDSHAKE_ERROR, MYF(0));
13747   DBUG_RETURN(CR_AUTH_HANDSHAKE);
13748 }
13749 
old_password_authenticate(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)13750 static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
13751                                      MYSQL_SERVER_AUTH_INFO *info)
13752 {
13753   uchar *pkt;
13754   int pkt_len;
13755   MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
13756   THD *thd=info->thd;
13757 
13758   /* generate the scramble, or reuse the old one */
13759   if (thd->scramble[SCRAMBLE_LENGTH])
13760   {
13761     thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
13762     /* and send it to the client */
13763     if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
13764       return CR_AUTH_HANDSHAKE;
13765   }
13766 
13767   /* read the reply and authenticate */
13768   if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
13769     return CR_AUTH_HANDSHAKE;
13770 
13771 #ifdef NO_EMBEDDED_ACCESS_CHECKS
13772   return CR_OK;
13773 #endif
13774 
13775   /*
13776     legacy: if switch_from_long_to_short_scramble,
13777     the password is sent \0-terminated, the pkt_len is always 9 bytes.
13778     We need to figure out the correct scramble length here.
13779   */
13780   if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
13781     pkt_len= (int)strnlen((char*)pkt, pkt_len);
13782 
13783   if (pkt_len == 0) /* no password */
13784     return info->auth_string[0] ? CR_AUTH_USER_CREDENTIALS : CR_OK;
13785 
13786   if (secure_auth(thd))
13787     return CR_AUTH_HANDSHAKE;
13788 
13789   info->password_used= PASSWORD_USED_YES;
13790 
13791   if (pkt_len == SCRAMBLE_LENGTH_323)
13792   {
13793     if (!mpvio->acl_user->salt_len)
13794       return CR_AUTH_USER_CREDENTIALS;
13795 
13796     return check_scramble_323(pkt, thd->scramble,
13797                              (ulong *) mpvio->acl_user->salt) ?
13798                              CR_AUTH_USER_CREDENTIALS : CR_OK;
13799   }
13800 
13801   my_error(ER_HANDSHAKE_ERROR, MYF(0));
13802   return CR_AUTH_HANDSHAKE;
13803 }
13804 
13805 static struct st_mysql_auth native_password_handler=
13806 {
13807   MYSQL_AUTHENTICATION_INTERFACE_VERSION,
13808   native_password_plugin_name.str,
13809   native_password_authenticate
13810 };
13811 
13812 static struct st_mysql_auth old_password_handler=
13813 {
13814   MYSQL_AUTHENTICATION_INTERFACE_VERSION,
13815   old_password_plugin_name.str,
13816   old_password_authenticate
13817 };
13818 
maria_declare_plugin(mysql_password)13819 maria_declare_plugin(mysql_password)
13820 {
13821   MYSQL_AUTHENTICATION_PLUGIN,                  /* type constant    */
13822   &native_password_handler,                     /* type descriptor  */
13823   native_password_plugin_name.str,              /* Name             */
13824   "R.J.Silk, Sergei Golubchik",                 /* Author           */
13825   "Native MySQL authentication",                /* Description      */
13826   PLUGIN_LICENSE_GPL,                           /* License          */
13827   NULL,                                         /* Init function    */
13828   NULL,                                         /* Deinit function  */
13829   0x0100,                                       /* Version (1.0)    */
13830   NULL,                                         /* status variables */
13831   NULL,                                         /* system variables */
13832   "1.0",                                        /* String version   */
13833   MariaDB_PLUGIN_MATURITY_STABLE                /* Maturity         */
13834 },
13835 {
13836   MYSQL_AUTHENTICATION_PLUGIN,                  /* type constant    */
13837   &old_password_handler,                        /* type descriptor  */
13838   old_password_plugin_name.str,                 /* Name             */
13839   "R.J.Silk, Sergei Golubchik",                 /* Author           */
13840   "Old MySQL-4.0 authentication",               /* Description      */
13841   PLUGIN_LICENSE_GPL,                           /* License          */
13842   NULL,                                         /* Init function    */
13843   NULL,                                         /* Deinit function  */
13844   0x0100,                                       /* Version (1.0)    */
13845   NULL,                                         /* status variables */
13846   NULL,                                         /* system variables */
13847   "1.0",                                        /* String version   */
13848   MariaDB_PLUGIN_MATURITY_STABLE                /* Maturity         */
13849 }
13850 maria_declare_plugin_end;
13851