1 /* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
2    Copyright (c) 2009, 2021, MariaDB
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA */
16 
17 
18 /*
19   The privileges are saved in the following tables:
20   mysql/user	 ; super user who are allowed to do almost anything
21   mysql/host	 ; host privileges. This is used if host is empty in mysql/db.
22   mysql/db	 ; database privileges / user
23 
24   data in tables is sorted according to how many not-wild-cards there is
25   in the relevant fields. Empty strings comes last.
26 */
27 
28 #include "mariadb.h"                          /* NO_EMBEDDED_ACCESS_CHECKS */
29 #include "sql_priv.h"
30 #include "sql_acl.h"         // MYSQL_DB_FIELD_COUNT, ACL_ACCESS
31 #include "sql_base.h"                           // close_mysql_tables
32 #include "key.h"             // key_copy, key_cmp_if_same, key_restore
33 #include "sql_show.h"        // append_identifier
34 #include "sql_table.h"                         // write_bin_log
35 #include "hash_filo.h"
36 #include "sql_parse.h"                          // check_access
37 #include "sql_view.h"                           // VIEW_ANY_ACL
38 #include "records.h"              // READ_RECORD, read_record_info,
39                                   // init_read_record, end_read_record
40 #include "rpl_filter.h"           // rpl_filter
41 #include "rpl_rli.h"
42 #include <m_ctype.h>
43 #include <stdarg.h>
44 #include "sp_head.h"
45 #include "sp.h"
46 #include "transaction.h"
47 #include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
48 #include <sql_common.h>
49 #include <mysql/plugin_auth.h>
50 #include <mysql/plugin_password_validation.h>
51 #include "sql_connect.h"
52 #include "hostname.h"
53 #include "sql_db.h"
54 #include "sql_array.h"
55 #include "sql_hset.h"
56 #include "password.h"
57 
58 #include "sql_plugin_compat.h"
59 
60 #define MAX_SCRAMBLE_LENGTH 1024
61 
62 bool mysql_user_table_is_in_short_password_format= false;
63 bool using_global_priv_table= true;
64 
65 // set that from field length in acl_load?
66 #ifndef NO_EMBEDDED_ACCESS_CHECKS
67 const uint max_hostname_length= 60;
68 const uint max_dbname_length= 64;
69 #endif
70 
safe_vio_type_name(Vio * vio)71 const char *safe_vio_type_name(Vio *vio)
72 {
73   size_t unused;
74 #ifdef EMBEDDED_LIBRARY
75   if (!vio) return "Internal";
76 #endif
77   return vio_type_name(vio_type(vio), &unused);
78 }
79 
80 #include "sql_acl_getsort.ic"
81 
82 static LEX_CSTRING native_password_plugin_name= {
83   STRING_WITH_LEN("mysql_native_password")
84 };
85 
86 static LEX_CSTRING old_password_plugin_name= {
87   STRING_WITH_LEN("mysql_old_password")
88 };
89 
90 /// @todo make it configurable
91 LEX_CSTRING *default_auth_plugin_name= &native_password_plugin_name;
92 
93 /*
94   Wildcard host, matches any hostname
95 */
96 LEX_CSTRING host_not_specified= { STRING_WITH_LEN("%") };
97 
98 /*
99   Constants, used in the SHOW GRANTS command.
100   Their actual string values are irrelevant, they're always compared
101   as pointers to these string constants.
102 */
103 LEX_CSTRING current_user= { STRING_WITH_LEN("*current_user") };
104 LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role") };
105 LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role") };
106 
107 
108 static plugin_ref old_password_plugin;
109 static plugin_ref native_password_plugin;
110 
get_auth_plugin(THD * thd,const LEX_CSTRING & name,bool * locked)111 static plugin_ref get_auth_plugin(THD *thd, const LEX_CSTRING &name, bool *locked)
112 {
113   if (name.str == native_password_plugin_name.str)
114     return native_password_plugin;
115   else if (name.str == old_password_plugin_name.str)
116     return old_password_plugin;
117   *locked=true;
118   return my_plugin_lock_by_name(thd, &name, MYSQL_AUTHENTICATION_PLUGIN);
119 }
120 
121 /* Classes */
122 
123 struct acl_host_and_ip
124 {
125   char *hostname;
126   long ip, ip_mask;                      // Used with masked ip:s
127 };
128 
129 #ifndef NO_EMBEDDED_ACCESS_CHECKS
130 static bool compare_hostname(const acl_host_and_ip *, const char *, const char *);
131 #else
132 #define compare_hostname(X,Y,Z) 0
133 #endif
134 
135 class ACL_ACCESS {
136 public:
137   ulonglong sort;
138   privilege_t access;
ACL_ACCESS()139   ACL_ACCESS()
140    :sort(0), access(NO_ACL)
141   { }
142 };
143 
144 /* ACL_HOST is used if no host is specified */
145 
146 class ACL_HOST :public ACL_ACCESS
147 {
148 public:
149   acl_host_and_ip host;
150   char *db;
151 };
152 
153 class ACL_USER_BASE :public ACL_ACCESS, public Sql_alloc
154 {
155 
156 public:
ACL_USER_BASE()157   ACL_USER_BASE()
158    :flags(0), user(null_clex_str)
159   {
160     bzero(&role_grants, sizeof(role_grants));
161   }
162   uchar flags;           // field used to store various state information
163   LEX_CSTRING user;
164   /* list to hold references to granted roles (ACL_ROLE instances) */
165   DYNAMIC_ARRAY role_grants;
get_username()166   const char *get_username() { return user.str; }
167 };
168 
169 class ACL_USER_PARAM
170 {
171 public:
ACL_USER_PARAM()172   ACL_USER_PARAM()
173   {
174     bzero(this, sizeof(*this));
175   }
176   acl_host_and_ip host;
177   size_t hostname_length;
178   USER_RESOURCES user_resource;
179   enum SSL_type ssl_type;
180   uint password_errors;
181   const char *ssl_cipher, *x509_issuer, *x509_subject;
182   LEX_CSTRING default_rolename;
183   struct AUTH { LEX_CSTRING plugin, auth_string, salt; } *auth;
184   uint nauth;
185   bool account_locked;
186   bool password_expired;
187   my_time_t password_last_changed;
188   longlong password_lifetime;
189 
alloc_auth(MEM_ROOT * root,uint n)190   bool alloc_auth(MEM_ROOT *root, uint n)
191   {
192     return !(auth= (AUTH*) alloc_root(root, (nauth= n)*sizeof(AUTH)));
193   }
194 };
195 
196 
197 class ACL_USER :public ACL_USER_BASE,
198                 public ACL_USER_PARAM
199 {
200 public:
201 
ACL_USER()202   ACL_USER() { }
203   ACL_USER(THD *thd, const LEX_USER &combo,
204            const Account_options &options,
205            const privilege_t privileges);
206 
copy(MEM_ROOT * root)207   ACL_USER *copy(MEM_ROOT *root)
208   {
209     ACL_USER *dst;
210     AUTH *dauth;
211     if (!multi_alloc_root(root, &dst, sizeof(ACL_USER),
212                                 &dauth, sizeof(AUTH)*nauth, NULL))
213       return 0;
214     *dst= *this;
215     dst->user= safe_lexcstrdup_root(root, user);
216     dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
217     dst->x509_issuer= safe_strdup_root(root, x509_issuer);
218     dst->x509_subject= safe_strdup_root(root, x509_subject);
219     dst->auth= dauth;
220     for (uint i=0; i < nauth; i++, dauth++)
221     {
222       if (auth[i].plugin.str == native_password_plugin_name.str ||
223           auth[i].plugin.str == old_password_plugin_name.str)
224         dauth->plugin= auth[i].plugin;
225       else
226         dauth->plugin= safe_lexcstrdup_root(root, auth[i].plugin);
227       dauth->auth_string= safe_lexcstrdup_root(root, auth[i].auth_string);
228       if (auth[i].salt.length == 0)
229         dauth->salt= auth[i].salt;
230       else
231         dauth->salt= safe_lexcstrdup_root(root, auth[i].salt);
232     }
233     dst->host.hostname= safe_strdup_root(root, host.hostname);
234     dst->default_rolename= safe_lexcstrdup_root(root, default_rolename);
235     bzero(&dst->role_grants, sizeof(role_grants));
236     return dst;
237   }
238 
cmp(const char * user2,const char * host2)239   int cmp(const char *user2, const char *host2)
240   {
241     CHARSET_INFO *cs= system_charset_info;
242     int res;
243     res= strcmp(user.str, user2);
244     if (!res)
245       res= my_strcasecmp(cs, host.hostname, host2);
246     return res;
247   }
248 
eq(const char * user2,const char * host2)249   bool eq(const char *user2, const char *host2) { return !cmp(user2, host2); }
250 
wild_eq(const char * user2,const char * host2,const char * ip2)251   bool wild_eq(const char *user2, const char *host2, const char *ip2)
252   {
253     if (strcmp(user.str, user2))
254       return false;
255 
256     return compare_hostname(&host, host2, ip2 ? ip2 : host2);
257   }
258 };
259 
260 class ACL_ROLE :public ACL_USER_BASE
261 {
262 public:
263   /*
264     In case of granting a role to a role, the access bits are merged together
265     via a bit OR operation and placed in the ACL_USER::access field.
266 
267     When rebuilding role_grants via the rebuild_role_grant function,
268     the ACL_USER::access field needs to be reset first. The field
269     initial_role_access holds initial grants, as granted directly to the role
270   */
271   privilege_t initial_role_access;
272   /*
273     In subgraph traversal, when we need to traverse only a part of the graph
274     (e.g. all direct and indirect grantees of a role X), the counter holds the
275     number of affected neighbour nodes.
276     See also propagate_role_grants()
277   */
278   uint  counter;
279   DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted
280 
281   ACL_ROLE(ACL_USER * user, MEM_ROOT *mem);
282   ACL_ROLE(const char * rolename, privilege_t privileges, MEM_ROOT *mem);
283 
284 };
285 
286 class ACL_DB :public ACL_ACCESS
287 {
288 public:
ACL_DB()289   ACL_DB() :initial_access(NO_ACL) { }
290   acl_host_and_ip host;
291   const char *user,*db;
292   privilege_t initial_access; /* access bits present in the table */
293 
get_username()294   const char *get_username() { return user; }
295 };
296 
297 #ifndef DBUG_OFF
298 /* status variables, only visible in SHOW STATUS after -#d,role_merge_stats */
299 ulong role_global_merges= 0, role_db_merges= 0, role_table_merges= 0,
300       role_column_merges= 0, role_routine_merges= 0;
301 #endif
302 
303 #ifndef NO_EMBEDDED_ACCESS_CHECKS
304 static void update_hostname(acl_host_and_ip *host, const char *hostname);
305 static bool show_proxy_grants (THD *, const char *, const char *,
306                                char *, size_t);
307 static bool show_role_grants(THD *, const char *,
308                              ACL_USER_BASE *, char *, size_t);
309 static bool show_default_role(THD *, ACL_USER *, char *, size_t);
310 static bool show_global_privileges(THD *, ACL_USER_BASE *,
311                                    bool, char *, size_t);
312 static bool show_database_privileges(THD *, const char *, const char *,
313                                      char *, size_t);
314 static bool show_table_and_column_privileges(THD *, const char *, const char *,
315                                              char *, size_t);
316 static int show_routine_grants(THD *, const char *, const char *,
317                                const Sp_handler *sph, char *, int);
318 
319 class Grant_tables;
320 class User_table;
321 class Proxies_priv_table;
322 
323 class ACL_PROXY_USER :public ACL_ACCESS
324 {
325   acl_host_and_ip host;
326   const char *user;
327   acl_host_and_ip proxied_host;
328   const char *proxied_user;
329   bool with_grant;
330 
331   typedef enum {
332     MYSQL_PROXIES_PRIV_HOST,
333     MYSQL_PROXIES_PRIV_USER,
334     MYSQL_PROXIES_PRIV_PROXIED_HOST,
335     MYSQL_PROXIES_PRIV_PROXIED_USER,
336     MYSQL_PROXIES_PRIV_WITH_GRANT,
337     MYSQL_PROXIES_PRIV_GRANTOR,
338     MYSQL_PROXIES_PRIV_TIMESTAMP } proxy_table_fields;
339 public:
ACL_PROXY_USER()340   ACL_PROXY_USER () {};
341 
init(const char * host_arg,const char * user_arg,const char * proxied_host_arg,const char * proxied_user_arg,bool with_grant_arg)342   void init(const char *host_arg, const char *user_arg,
343        const char *proxied_host_arg, const char *proxied_user_arg,
344        bool with_grant_arg)
345   {
346     user= user_arg;
347     update_hostname (&host, (host_arg && *host_arg) ? host_arg : NULL);
348     proxied_user= proxied_user_arg;
349     update_hostname (&proxied_host,
350                      (proxied_host_arg && *proxied_host_arg) ?
351                      proxied_host_arg : NULL);
352     with_grant= with_grant_arg;
353     sort= get_magic_sort("huhu", host.hostname, user, proxied_host.hostname,
354                          proxied_user);
355   }
356 
init(MEM_ROOT * mem,const char * host_arg,const char * user_arg,const char * proxied_host_arg,const char * proxied_user_arg,bool with_grant_arg)357   void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
358        const char *proxied_host_arg, const char *proxied_user_arg,
359        bool with_grant_arg)
360   {
361     init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
362           strdup_root (mem, user_arg),
363           (proxied_host_arg && *proxied_host_arg) ?
364             strdup_root (mem, proxied_host_arg) : NULL,
365           strdup_root (mem, proxied_user_arg),
366           with_grant_arg);
367   }
368 
369   void init(const Proxies_priv_table& proxies_priv_table, MEM_ROOT *mem);
370 
get_with_grant()371   bool get_with_grant() { return with_grant; }
get_user()372   const char *get_user() { return user; }
get_host()373   const char *get_host() { return host.hostname; }
get_proxied_user()374   const char *get_proxied_user() { return proxied_user; }
get_proxied_host()375   const char *get_proxied_host() { return proxied_host.hostname; }
set_user(MEM_ROOT * mem,const char * user_arg)376   void set_user(MEM_ROOT *mem, const char *user_arg)
377   {
378     user= *user_arg ? strdup_root(mem, user_arg) : "";
379   }
set_host(MEM_ROOT * mem,const char * host_arg)380   void set_host(MEM_ROOT *mem, const char *host_arg)
381   {
382     update_hostname(&host, safe_strdup_root(mem, host_arg));
383   }
384 
check_validity(bool check_no_resolve)385   bool check_validity(bool check_no_resolve)
386   {
387     if (check_no_resolve &&
388         (hostname_requires_resolving(host.hostname) ||
389          hostname_requires_resolving(proxied_host.hostname)))
390     {
391       sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
392                         "ignored in --skip-name-resolve mode.",
393                         proxied_user,
394                         safe_str(proxied_host.hostname), user,
395                         safe_str(host.hostname));
396       return TRUE;
397     }
398     return FALSE;
399   }
400 
matches(const char * host_arg,const char * user_arg,const char * ip_arg,const char * proxied_user_arg)401   bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
402                 const char *proxied_user_arg)
403   {
404     DBUG_ENTER("ACL_PROXY_USER::matches");
405     DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
406                         "compare_hostname(%s,%s,%s) &&"
407                         "wild_compare (%s,%s) &&"
408                         "wild_compare (%s,%s)",
409                         host.hostname, host_arg, ip_arg, proxied_host.hostname,
410                         host_arg, ip_arg, user_arg, user,
411                         proxied_user_arg, proxied_user));
412     DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
413                 compare_hostname(&proxied_host, host_arg, ip_arg) &&
414                 (!*user || !strcmp(user_arg, user)) &&
415                 (!*proxied_user || !strcmp(proxied_user_arg, proxied_user)));
416   }
417 
418 
auth_element_equals(const char * a,const char * b)419   inline static bool auth_element_equals(const char *a, const char *b)
420   {
421     return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
422   }
423 
424 
pk_equals(ACL_PROXY_USER * grant)425   bool pk_equals(ACL_PROXY_USER *grant)
426   {
427     DBUG_ENTER("pk_equals");
428     DBUG_PRINT("info", ("strcmp(%s,%s) &&"
429                         "strcmp(%s,%s) &&"
430                         "wild_compare (%s,%s) &&"
431                         "wild_compare (%s,%s)",
432                         user, grant->user, proxied_user, grant->proxied_user,
433                         host.hostname, grant->host.hostname,
434                         proxied_host.hostname, grant->proxied_host.hostname));
435 
436     bool res= auth_element_equals(user, grant->user) &&
437               auth_element_equals(proxied_user, grant->proxied_user) &&
438               auth_element_equals(host.hostname, grant->host.hostname) &&
439               auth_element_equals(proxied_host.hostname,
440                                   grant->proxied_host.hostname);
441     DBUG_RETURN(res);
442   }
443 
444 
granted_on(const char * host_arg,const char * user_arg)445   bool granted_on(const char *host_arg, const char *user_arg)
446   {
447     return (!strcmp(user, user_arg) &&
448             ((!host.hostname && (!host_arg || !host_arg[0])) ||
449              (host.hostname && host_arg && !strcmp(host.hostname, host_arg))));
450   }
451 
452 
print_grant(String * str)453   void print_grant(String *str)
454   {
455     str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
456     str->append(proxied_user);
457     str->append(STRING_WITH_LEN("'@'"));
458     if (proxied_host.hostname)
459       str->append(proxied_host.hostname, strlen(proxied_host.hostname));
460     str->append(STRING_WITH_LEN("' TO '"));
461     str->append(user);
462     str->append(STRING_WITH_LEN("'@'"));
463     if (host.hostname)
464       str->append(host.hostname);
465     str->append(STRING_WITH_LEN("'"));
466     if (with_grant)
467       str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
468   }
469 
set_data(ACL_PROXY_USER * grant)470   void set_data(ACL_PROXY_USER *grant)
471   {
472     with_grant= grant->with_grant;
473   }
474 
store_pk(TABLE * table,const LEX_CSTRING * host,const LEX_CSTRING * user,const LEX_CSTRING * proxied_host,const LEX_CSTRING * proxied_user)475   static int store_pk(TABLE *table,
476                       const LEX_CSTRING *host,
477                       const LEX_CSTRING *user,
478                       const LEX_CSTRING *proxied_host,
479                       const LEX_CSTRING *proxied_user)
480   {
481     DBUG_ENTER("ACL_PROXY_USER::store_pk");
482     DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
483                         host->str, user->str,
484                         proxied_host->str, proxied_user->str));
485     if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
486                                                    host->length,
487                                                    system_charset_info))
488       DBUG_RETURN(TRUE);
489     if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
490                                                    user->length,
491                                                    system_charset_info))
492       DBUG_RETURN(TRUE);
493     if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str,
494                                                            proxied_host->length,
495                                                            system_charset_info))
496       DBUG_RETURN(TRUE);
497     if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str,
498                                                            proxied_user->length,
499                                                            system_charset_info))
500       DBUG_RETURN(TRUE);
501 
502     DBUG_RETURN(FALSE);
503   }
504 
store_data_record(TABLE * table,const LEX_CSTRING * host,const LEX_CSTRING * user,const LEX_CSTRING * proxied_host,const LEX_CSTRING * proxied_user,bool with_grant,const char * grantor)505   static int store_data_record(TABLE *table,
506                                const LEX_CSTRING *host,
507                                const LEX_CSTRING *user,
508                                const LEX_CSTRING *proxied_host,
509                                const LEX_CSTRING *proxied_user,
510                                bool with_grant,
511                                const char *grantor)
512   {
513     DBUG_ENTER("ACL_PROXY_USER::store_pk");
514     if (store_pk(table,  host, user, proxied_host, proxied_user))
515       DBUG_RETURN(TRUE);
516     DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
517     if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
518                                                            TRUE))
519       DBUG_RETURN(TRUE);
520     if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
521                                                         strlen(grantor),
522                                                         system_charset_info))
523       DBUG_RETURN(TRUE);
524 
525     DBUG_RETURN(FALSE);
526   }
527 };
528 
529 #define FIRST_NON_YN_FIELD 26
530 
531 class acl_entry :public hash_filo_element
532 {
533 public:
534   privilege_t access;
535   uint16 length;
536   char key[1];					// Key will be stored here
537 };
538 
539 
acl_entry_get_key(acl_entry * entry,size_t * length,my_bool not_used)540 static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
541                                 my_bool not_used __attribute__((unused)))
542 {
543   *length=(uint) entry->length;
544   return (uchar*) entry->key;
545 }
546 
acl_role_get_key(ACL_ROLE * entry,size_t * length,my_bool not_used)547 static uchar* acl_role_get_key(ACL_ROLE *entry, size_t *length,
548                                my_bool not_used __attribute__((unused)))
549 {
550   *length=(uint) entry->user.length;
551   return (uchar*) entry->user.str;
552 }
553 
554 struct ROLE_GRANT_PAIR : public Sql_alloc
555 {
556   char *u_uname;
557   char *u_hname;
558   char *r_uname;
559   LEX_STRING hashkey;
560   bool with_admin;
561 
562   bool init(MEM_ROOT *mem, const char *username, const char *hostname,
563             const char *rolename, bool with_admin_option);
564 };
565 
acl_role_map_get_key(ROLE_GRANT_PAIR * entry,size_t * length,my_bool not_used)566 static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length,
567                                   my_bool not_used __attribute__((unused)))
568 {
569   *length=(uint) entry->hashkey.length;
570   return (uchar*) entry->hashkey.str;
571 }
572 
init(MEM_ROOT * mem,const char * username,const char * hostname,const char * rolename,bool with_admin_option)573 bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, const char *username,
574                            const char *hostname, const char *rolename,
575                            bool with_admin_option)
576 {
577   size_t uname_l = safe_strlen(username);
578   size_t hname_l = safe_strlen(hostname);
579   size_t rname_l = safe_strlen(rolename);
580   /*
581     Create a buffer that holds all 3 NULL terminated strings in succession
582     To save memory space, the same buffer is used as the hashkey
583   */
584   size_t bufflen = uname_l + hname_l + rname_l + 3; //add the '\0' aswell
585   char *buff= (char *)alloc_root(mem, bufflen);
586   if (!buff)
587     return true;
588 
589   /*
590     Offsets in the buffer for all 3 strings
591   */
592   char *username_pos= buff;
593   char *hostname_pos= buff + uname_l + 1;
594   char *rolename_pos= buff + uname_l + hname_l + 2;
595 
596   if (username) //prevent undefined behaviour
597     memcpy(username_pos, username, uname_l);
598   username_pos[uname_l]= '\0';         //#1 string terminator
599   u_uname= username_pos;
600 
601   if (hostname) //prevent undefined behaviour
602     memcpy(hostname_pos, hostname, hname_l);
603   hostname_pos[hname_l]= '\0';         //#2 string terminator
604   u_hname= hostname_pos;
605 
606   if (rolename) //prevent undefined behaviour
607     memcpy(rolename_pos, rolename, rname_l);
608   rolename_pos[rname_l]= '\0';         //#3 string terminator
609   r_uname= rolename_pos;
610 
611   hashkey.str = buff;
612   hashkey.length = bufflen;
613 
614   with_admin= with_admin_option;
615 
616   return false;
617 }
618 
619 #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
620 #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
621                         1 + USERNAME_LENGTH + 1)
622 
623 #if defined(HAVE_OPENSSL)
624 /*
625   Without SSL the handshake consists of one packet. This packet
626   has both client capabilities and scrambled password.
627   With SSL the handshake might consist of two packets. If the first
628   packet (client capabilities) has CLIENT_SSL flag set, we have to
629   switch to SSL and read the second packet. The scrambled password
630   is in the second packet and client_capabilities field will be ignored.
631   Maybe it is better to accept flags other than CLIENT_SSL from the
632   second packet?
633 */
634 #define SSL_HANDSHAKE_SIZE      2
635 #define MIN_HANDSHAKE_SIZE      2
636 #else
637 #define MIN_HANDSHAKE_SIZE      6
638 #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
639 #define NORMAL_HANDSHAKE_SIZE   6
640 
641 #define ROLE_ASSIGN_COLUMN_IDX  44
642 #define DEFAULT_ROLE_COLUMN_IDX 45
643 #define MAX_STATEMENT_TIME_COLUMN_IDX 46
644 
645 /* various flags valid for ACL_USER */
646 #define IS_ROLE                 (1L << 0)
647 /* Flag to mark that a ROLE is on the recursive DEPTH_FIRST_SEARCH stack */
648 #define ROLE_ON_STACK            (1L << 1)
649 /*
650   Flag to mark that a ROLE and all it's neighbours have
651   been visited
652 */
653 #define ROLE_EXPLORED           (1L << 2)
654 /* Flag to mark that on_node was already called for this role */
655 #define ROLE_OPENED             (1L << 3)
656 
657 static DYNAMIC_ARRAY acl_hosts, acl_users, acl_proxy_users;
658 static Dynamic_array<ACL_DB> acl_dbs(PSI_INSTRUMENT_MEM, 0U, 50U);
659 typedef Dynamic_array<ACL_DB>::CMP_FUNC acl_dbs_cmp;
660 static HASH acl_roles;
661 /*
662   An hash containing mappings user <--> role
663 
664   A hash is used so as to make updates quickly
665   The hashkey used represents all the entries combined
666 */
667 static HASH acl_roles_mappings;
668 static MEM_ROOT acl_memroot, grant_memroot;
669 static bool initialized=0;
670 static bool allow_all_hosts=1;
671 static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
672 static HASH package_spec_priv_hash, package_body_priv_hash;
673 static DYNAMIC_ARRAY acl_wild_hosts;
674 static Hash_filo<acl_entry> *acl_cache;
675 static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
676 static privilege_t get_access(TABLE *form, uint fieldnr, uint *next_field=0);
677 static int acl_compare(const ACL_ACCESS *a, const ACL_ACCESS *b);
678 static int acl_user_compare(const ACL_USER *a, const ACL_USER *b);
679 static void rebuild_acl_users();
680 static int acl_db_compare(const ACL_DB *a, const ACL_DB *b);
681 static void rebuild_acl_dbs();
682 static void init_check_host(void);
683 static void rebuild_check_host(void);
684 static void rebuild_role_grants(void);
685 static ACL_USER *find_user_exact(const char *host, const char *user);
686 static ACL_USER *find_user_wild(const char *host, const char *user, const char *ip= 0);
687 static ACL_ROLE *find_acl_role(const char *user);
688 static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, const LEX_CSTRING *h, const LEX_CSTRING *r);
689 static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host);
690 static bool update_user_table_password(THD *, const User_table&, const ACL_USER&);
691 static bool acl_load(THD *thd, const Grant_tables& grant_tables);
692 static inline void get_grantor(THD *thd, char* grantor);
693 static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname);
694 static bool get_YN_as_bool(Field *field);
695 
696 #define ROLE_CYCLE_FOUND 2
697 static int traverse_role_graph_up(ACL_ROLE *, void *,
698                                   int (*) (ACL_ROLE *, void *),
699                                   int (*) (ACL_ROLE *, ACL_ROLE *, void *));
700 
701 static int traverse_role_graph_down(ACL_USER_BASE *, void *,
702                              int (*) (ACL_USER_BASE *, void *),
703                              int (*) (ACL_USER_BASE *, ACL_ROLE *, void *));
704 
705 
get_priv_hash() const706 HASH *Sp_handler_procedure::get_priv_hash() const
707 {
708   return &proc_priv_hash;
709 }
710 
711 
get_priv_hash() const712 HASH *Sp_handler_function::get_priv_hash() const
713 {
714   return &func_priv_hash;
715 }
716 
717 
get_priv_hash() const718 HASH *Sp_handler_package_spec::get_priv_hash() const
719 {
720   return &package_spec_priv_hash;
721 }
722 
723 
get_priv_hash() const724 HASH *Sp_handler_package_body::get_priv_hash() const
725 {
726   return &package_body_priv_hash;
727 }
728 
729 
730 /*
731  Enumeration of ACL/GRANT tables in the mysql database
732 */
733 enum enum_acl_tables
734 {
735   DB_TABLE,
736   TABLES_PRIV_TABLE,
737   COLUMNS_PRIV_TABLE,
738 #define FIRST_OPTIONAL_TABLE HOST_TABLE
739   HOST_TABLE,
740   PROCS_PRIV_TABLE,
741   PROXIES_PRIV_TABLE,
742   ROLES_MAPPING_TABLE,
743   USER_TABLE // <== always the last
744 };
745 
746 static const int Table_user= 1 << USER_TABLE;
747 static const int Table_db= 1 << DB_TABLE;
748 static const int Table_tables_priv= 1 << TABLES_PRIV_TABLE;
749 static const int Table_columns_priv= 1 << COLUMNS_PRIV_TABLE;
750 static const int Table_host= 1 << HOST_TABLE;
751 static const int Table_procs_priv= 1 << PROCS_PRIV_TABLE;
752 static const int Table_proxies_priv= 1 << PROXIES_PRIV_TABLE;
753 static const int Table_roles_mapping= 1 << ROLES_MAPPING_TABLE;
754 
755 static LEX_CSTRING MYSQL_TABLE_NAME[USER_TABLE+1]= {
756   {STRING_WITH_LEN("db")},
757   {STRING_WITH_LEN("tables_priv")},
758   {STRING_WITH_LEN("columns_priv")},
759   {STRING_WITH_LEN("host")},
760   {STRING_WITH_LEN("procs_priv")},
761   {STRING_WITH_LEN("proxies_priv")},
762   {STRING_WITH_LEN("roles_mapping")},
763   {STRING_WITH_LEN("global_priv")}
764 };
765 static LEX_CSTRING MYSQL_TABLE_NAME_USER={STRING_WITH_LEN("user")};
766 
767 /**
768   Choose from either native or old password plugins when assigning a password
769 */
770 
guess_auth_plugin(THD * thd,size_t password_len)771 static LEX_CSTRING &guess_auth_plugin(THD *thd, size_t password_len)
772 {
773   if (thd->variables.old_passwords == 1 ||
774       password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
775     return old_password_plugin_name;
776   else
777     return native_password_plugin_name;
778 }
779 
780 /**
781   Base class representing a generic grant table from the mysql database.
782 
783   The potential tables that this class can represent are:
784   user, db, columns_priv, tables_priv, host, procs_priv, proxies_priv,
785   roles_mapping
786 
787   Objects belonging to this parent class can only be constructed by the
788   Grants_table class. This ensures the correct initialization of the objects.
789 */
790 class Grant_table_base
791 {
792  public:
793   /* Number of fields for this Grant Table. */
num_fields() const794   uint num_fields() const { return m_table->s->fields; }
795   /* Check if the table exists after an attempt to open it was made.
796      Some tables, such as the host table in MySQL 5.6.7+ are missing. */
table_exists() const797   bool table_exists() const { return m_table; };
798   /* Initializes the READ_RECORD structure provided as a parameter
799      to read through the whole table, with all columns available. Cleaning up
800      is the caller's job. */
init_read_record(READ_RECORD * info) const801   bool init_read_record(READ_RECORD* info) const
802   {
803     DBUG_ASSERT(m_table);
804 
805     if (num_fields() < min_columns)
806     {
807       my_printf_error(ER_UNKNOWN_ERROR, "Fatal error: mysql.%s table is "
808                       "damaged or in unsupported 3.20 format",
809                       MYF(ME_ERROR_LOG), m_table->s->table_name.str);
810       return 1;
811     }
812 
813     bool result= ::init_read_record(info, m_table->in_use, m_table,
814                                     NULL, NULL, 1, true, false);
815     if (!result)
816       m_table->use_all_columns();
817     return result;
818   }
819 
820   /* Return the underlying TABLE handle. */
table() const821   TABLE* table() const { return m_table; }
822 
get_access() const823   privilege_t get_access() const
824   {
825     ulonglong access_bits= 0, bit= 1;
826     for (uint i = start_priv_columns; i < end_priv_columns; i++, bit<<=1)
827     {
828       if (get_YN_as_bool(m_table->field[i]))
829         access_bits|= bit;
830     }
831     return ALL_KNOWN_ACL & access_bits;
832   }
833 
834  protected:
835   friend class Grant_tables;
836 
Grant_table_base()837   Grant_table_base() : min_columns(3), start_priv_columns(0), end_priv_columns(0), m_table(0)
838   { }
839 
840   /* Compute how many privilege columns this table has. This method
841      can only be called after the table has been opened.
842 
843      IMPLEMENTATION
844      A privilege column is of type enum('Y', 'N'). Privilege columns are
845      expected to be one after another.
846   */
set_table(TABLE * table)847   void set_table(TABLE *table)
848   {
849     if (!(m_table= table)) // Table does not exist or not opened.
850       return;
851 
852     for (end_priv_columns= 0; end_priv_columns < num_fields(); end_priv_columns++)
853     {
854       Field *field= m_table->field[end_priv_columns];
855       if (field->real_type() == MYSQL_TYPE_ENUM &&
856           static_cast<Field_enum*>(field)->typelib->count == 2)
857       {
858         if (!start_priv_columns)
859           start_priv_columns= end_priv_columns;
860       }
861       else if (start_priv_columns)
862           break;
863     }
864   }
865 
866 
867   /* the min number of columns a table should have */
868   uint min_columns;
869   /* The index at which privilege columns start. */
870   uint start_priv_columns;
871   /* The index after the last privilege column */
872   uint end_priv_columns;
873 
874   TABLE *m_table;
875 };
876 
877 class User_table: public Grant_table_base
878 {
879  public:
init_read_record(READ_RECORD * info) const880   bool init_read_record(READ_RECORD* info) const
881   {
882     return Grant_table_base::init_read_record(info) || setup_sysvars();
883   }
884 
885   virtual LEX_CSTRING& name() const = 0;
886   virtual int get_auth(THD *, MEM_ROOT *, ACL_USER *u) const= 0;
887   virtual bool set_auth(const ACL_USER &u) const = 0;
888   virtual privilege_t get_access() const = 0;
889   virtual void set_access(const privilege_t rights, bool revoke) const = 0;
890 
get_host(MEM_ROOT * root) const891   char *get_host(MEM_ROOT *root) const
892   { return ::get_field(root, m_table->field[0]); }
set_host(const char * s,size_t l) const893   int set_host(const char *s, size_t l) const
894   { return m_table->field[0]->store(s, l, system_charset_info); };
get_user(MEM_ROOT * root) const895   char *get_user(MEM_ROOT *root) const
896   { return ::get_field(root, m_table->field[1]); }
set_user(const char * s,size_t l) const897   int set_user(const char *s, size_t l) const
898   { return m_table->field[1]->store(s, l, system_charset_info); };
899 
900   virtual SSL_type get_ssl_type () const = 0;
901   virtual int set_ssl_type (SSL_type x) const = 0;
902   virtual const char* get_ssl_cipher (MEM_ROOT *root) const = 0;
903   virtual int set_ssl_cipher (const char *s, size_t l) const = 0;
904   virtual const char* get_x509_issuer (MEM_ROOT *root) const = 0;
905   virtual int set_x509_issuer (const char *s, size_t l) const = 0;
906   virtual const char* get_x509_subject (MEM_ROOT *root) const = 0;
907   virtual int set_x509_subject (const char *s, size_t l) const = 0;
908   virtual longlong get_max_questions () const = 0;
909   virtual int set_max_questions (longlong x) const = 0;
910   virtual longlong get_max_updates () const = 0;
911   virtual int set_max_updates (longlong x) const = 0;
912   virtual longlong get_max_connections () const = 0;
913   virtual int set_max_connections (longlong x) const = 0;
914   virtual longlong get_max_user_connections () const = 0;
915   virtual int set_max_user_connections (longlong x) const = 0;
916   virtual double get_max_statement_time () const = 0;
917   virtual int set_max_statement_time (double x) const = 0;
918   virtual bool get_is_role () const = 0;
919   virtual int set_is_role (bool x) const = 0;
920   virtual const char* get_default_role (MEM_ROOT *root) const = 0;
921   virtual int set_default_role (const char *s, size_t l) const = 0;
922   virtual bool get_account_locked () const = 0;
923   virtual int set_account_locked (bool x) const = 0;
924   virtual bool get_password_expired () const = 0;
925   virtual int set_password_expired (bool x) const = 0;
926   virtual my_time_t get_password_last_changed () const = 0;
927   virtual int set_password_last_changed (my_time_t x) const = 0;
928   virtual longlong get_password_lifetime () const = 0;
929   virtual int set_password_lifetime (longlong x) const = 0;
930 
~User_table()931   virtual ~User_table() {}
932  private:
933   friend class Grant_tables;
934   virtual int setup_sysvars() const = 0;
935 };
936 
937 /* MySQL-3.23 to MariaDB 10.3 `user` table */
938 class User_table_tabular: public User_table
939 {
940  public:
941 
name() const942   LEX_CSTRING& name() const { return MYSQL_TABLE_NAME_USER; }
943 
get_auth(THD * thd,MEM_ROOT * root,ACL_USER * u) const944   int get_auth(THD *thd, MEM_ROOT *root, ACL_USER *u) const
945   {
946     u->alloc_auth(root, 1);
947     if (have_password())
948     {
949       const char *as= safe_str(::get_field(&acl_memroot, password()));
950       u->auth->auth_string.str= as;
951       u->auth->auth_string.length= strlen(as);
952       u->auth->plugin= guess_auth_plugin(thd, u->auth->auth_string.length);
953     }
954     else
955     {
956       u->auth->plugin= native_password_plugin_name;
957       u->auth->auth_string= empty_clex_str;
958     }
959     if (plugin() && authstr())
960     {
961       char *tmpstr= ::get_field(&acl_memroot, plugin());
962       if (tmpstr)
963       {
964         const char *pw= u->auth->auth_string.str;
965         const char *as= safe_str(::get_field(&acl_memroot, authstr()));
966         if (*pw)
967         {
968           if (*as && strcmp(as, pw))
969           {
970             sql_print_warning("'user' entry '%s@%s' has both a password and an "
971               "authentication plugin specified. The password will be ignored.",
972               safe_str(get_user(thd->mem_root)), safe_str(get_host(thd->mem_root)));
973           }
974           else
975             as= pw;
976         }
977         u->auth->plugin.str= tmpstr;
978         u->auth->plugin.length= strlen(tmpstr);
979         u->auth->auth_string.str= as;
980         u->auth->auth_string.length= strlen(as);
981       }
982     }
983     return 0;
984   }
985 
set_auth(const ACL_USER & u) const986   bool set_auth(const ACL_USER &u) const
987   {
988     if (u.nauth != 1)
989       return 1;
990     if (plugin())
991     {
992       if (have_password())
993         password()->reset();
994       plugin()->store(u.auth->plugin.str, u.auth->plugin.length, system_charset_info);
995       authstr()->store(u.auth->auth_string.str, u.auth->auth_string.length, system_charset_info);
996     }
997     else
998     {
999       if (u.auth->plugin.str != native_password_plugin_name.str &&
1000           u.auth->plugin.str != old_password_plugin_name.str)
1001         return 1;
1002       password()->store(u.auth->auth_string.str, u.auth->auth_string.length, system_charset_info);
1003     }
1004     return 0;
1005   }
1006 
get_access() const1007   privilege_t get_access() const
1008   {
1009     privilege_t access(Grant_table_base::get_access());
1010     if ((num_fields() <= 13) && (access & CREATE_ACL))
1011       access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
1012 
1013     if (num_fields() <= 18)
1014     {
1015       access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
1016       if (access & FILE_ACL)
1017         access|= BINLOG_MONITOR_ACL | REPL_SLAVE_ACL | BINLOG_ADMIN_ACL |
1018                  BINLOG_REPLAY_ACL;
1019       if (access & PROCESS_ACL)
1020         access|= SUPER_ACL | EXECUTE_ACL;
1021     }
1022 
1023     if (num_fields() <= 31 && (access & CREATE_ACL))
1024       access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
1025 
1026     if (num_fields() <= 33)
1027     {
1028       if (access & CREATE_ACL)
1029         access|= CREATE_PROC_ACL;
1030       if (access & ALTER_ACL)
1031         access|= ALTER_PROC_ACL;
1032     }
1033 
1034     if (num_fields() <= 36 && (access & GRANT_ACL))
1035       access|= CREATE_USER_ACL;
1036 
1037     if (num_fields() <= 37 && (access & SUPER_ACL))
1038       access|= EVENT_ACL;
1039 
1040     if (num_fields() <= 38 && (access & SUPER_ACL))
1041       access|= TRIGGER_ACL;
1042 
1043     if (num_fields() <= 46 && (access & DELETE_ACL))
1044       access|= DELETE_HISTORY_ACL;
1045 
1046     if (access & SUPER_ACL)
1047       access|= GLOBAL_SUPER_ADDED_SINCE_USER_TABLE_ACLS;
1048 
1049     /*
1050       The SHOW SLAVE HOSTS statement :
1051       - required REPLICATION SLAVE privilege prior to 10.5.2
1052       - requires REPLICATION MASTER ADMIN privilege since 10.5.2
1053       There is no a way to GRANT MASTER ADMIN with User_table_tabular.
1054       So let's automatically add REPLICATION MASTER ADMIN for all users
1055       that had REPLICATION SLAVE. This will allow to do SHOW SLAVE HOSTS.
1056     */
1057     if (access & REPL_SLAVE_ACL)
1058       access|= REPL_MASTER_ADMIN_ACL;
1059 
1060     if (access & REPL_SLAVE_ACL)
1061       access|= SLAVE_MONITOR_ACL;
1062 
1063     return access & GLOBAL_ACLS;
1064   }
1065 
set_access(const privilege_t rights,bool revoke) const1066   void set_access(const privilege_t rights, bool revoke) const
1067   {
1068     ulonglong priv(SELECT_ACL);
1069     for (uint i= start_priv_columns; i < end_priv_columns; i++, priv <<= 1)
1070     {
1071       if (priv & rights)
1072         m_table->field[i]->store(1 + !revoke, 0);
1073     }
1074   }
1075 
get_ssl_type() const1076   SSL_type get_ssl_type () const
1077   {
1078     Field *f= get_field(end_priv_columns, MYSQL_TYPE_ENUM);
1079     return (SSL_type)(f ? f->val_int()-1 : 0);
1080   }
set_ssl_type(SSL_type x) const1081   int set_ssl_type (SSL_type x) const
1082   {
1083     if (Field *f= get_field(end_priv_columns, MYSQL_TYPE_ENUM))
1084       return f->store(x+1, 0);
1085     else
1086       return 1;
1087   }
get_ssl_cipher(MEM_ROOT * root) const1088   const char* get_ssl_cipher (MEM_ROOT *root) const
1089   {
1090     Field *f= get_field(end_priv_columns + 1, MYSQL_TYPE_BLOB);
1091     return f ? ::get_field(root,f) : 0;
1092   }
set_ssl_cipher(const char * s,size_t l) const1093   int set_ssl_cipher (const char *s, size_t l) const
1094   {
1095     if (Field *f= get_field(end_priv_columns + 1, MYSQL_TYPE_BLOB))
1096       return f->store(s, l, &my_charset_latin1);
1097     else
1098       return 1;
1099   }
get_x509_issuer(MEM_ROOT * root) const1100   const char* get_x509_issuer (MEM_ROOT *root) const
1101   {
1102     Field *f= get_field(end_priv_columns + 2, MYSQL_TYPE_BLOB);
1103     return f ? ::get_field(root,f) : 0;
1104   }
set_x509_issuer(const char * s,size_t l) const1105   int set_x509_issuer (const char *s, size_t l) const
1106   {
1107     if (Field *f= get_field(end_priv_columns + 2, MYSQL_TYPE_BLOB))
1108       return f->store(s, l, &my_charset_latin1);
1109     else
1110       return 1;
1111   }
get_x509_subject(MEM_ROOT * root) const1112   const char* get_x509_subject (MEM_ROOT *root) const
1113   {
1114     Field *f= get_field(end_priv_columns + 3, MYSQL_TYPE_BLOB);
1115     return f ? ::get_field(root,f) : 0;
1116   }
set_x509_subject(const char * s,size_t l) const1117   int set_x509_subject (const char *s, size_t l) const
1118   {
1119     if (Field *f= get_field(end_priv_columns + 3, MYSQL_TYPE_BLOB))
1120       return f->store(s, l, &my_charset_latin1);
1121     else
1122       return 1;
1123   }
get_max_questions() const1124   longlong get_max_questions () const
1125   {
1126     Field *f= get_field(end_priv_columns + 4, MYSQL_TYPE_LONG);
1127     return f ? f->val_int() : 0;
1128   }
set_max_questions(longlong x) const1129   int set_max_questions (longlong x) const
1130   {
1131     if (Field *f= get_field(end_priv_columns + 4, MYSQL_TYPE_LONG))
1132       return f->store(x, 0);
1133     else
1134       return 1;
1135   }
get_max_updates() const1136   longlong get_max_updates () const
1137   {
1138     Field *f= get_field(end_priv_columns + 5, MYSQL_TYPE_LONG);
1139     return f ? f->val_int() : 0;
1140   }
set_max_updates(longlong x) const1141   int set_max_updates (longlong x) const
1142   {
1143     if (Field *f= get_field(end_priv_columns + 5, MYSQL_TYPE_LONG))
1144       return f->store(x, 0);
1145     else
1146       return 1;
1147   }
get_max_connections() const1148   longlong get_max_connections () const
1149   {
1150     Field *f= get_field(end_priv_columns + 6, MYSQL_TYPE_LONG);
1151     return f ? f->val_int() : 0;
1152   }
set_max_connections(longlong x) const1153   int set_max_connections (longlong x) const
1154   {
1155     if (Field *f= get_field(end_priv_columns + 6, MYSQL_TYPE_LONG))
1156       return f->store(x, 0);
1157     else
1158       return 1;
1159   }
get_max_user_connections() const1160   longlong get_max_user_connections () const
1161   {
1162     Field *f= get_field(end_priv_columns + 7, MYSQL_TYPE_LONG);
1163     return f ? f->val_int() : 0;
1164   }
set_max_user_connections(longlong x) const1165   int set_max_user_connections (longlong x) const
1166   {
1167     if (Field *f= get_field(end_priv_columns + 7, MYSQL_TYPE_LONG))
1168       return f->store(x, 0);
1169     else
1170       return 1;
1171   }
get_max_statement_time() const1172   double get_max_statement_time () const
1173   {
1174     Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_NEWDECIMAL);
1175     return f ? f->val_real() : 0;
1176   }
set_max_statement_time(double x) const1177   int set_max_statement_time (double x) const
1178   {
1179     if (Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_NEWDECIMAL))
1180       return f->store(x);
1181     else
1182       return 1;
1183   }
get_is_role() const1184   bool get_is_role () const
1185   {
1186     Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_ENUM);
1187     return f ? f->val_int()-1 : 0;
1188   }
set_is_role(bool x) const1189   int set_is_role (bool x) const
1190   {
1191     if (Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_ENUM))
1192       return f->store(x+1, 0);
1193     else
1194       return 1;
1195   }
get_default_role(MEM_ROOT * root) const1196   const char* get_default_role (MEM_ROOT *root) const
1197   {
1198     Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_STRING);
1199     return f ? ::get_field(root,f) : 0;
1200   }
set_default_role(const char * s,size_t l) const1201   int set_default_role (const char *s, size_t l) const
1202   {
1203     if (Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_STRING))
1204       return f->store(s, l, system_charset_info);
1205     else
1206       return 1;
1207   }
1208   /* On a MariaDB 10.3 user table, the account locking accessors will try to
1209      get the content of the max_statement_time column, but they will fail due
1210      to the typecheck in get_field. */
get_account_locked() const1211   bool get_account_locked () const
1212   {
1213     Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_ENUM);
1214     return f ? f->val_int()-1 : 0;
1215   }
set_account_locked(bool x) const1216   int set_account_locked (bool x) const
1217   {
1218     if (Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_ENUM))
1219       return f->store(x+1, 0);
1220 
1221     return 1;
1222   }
1223 
get_password_expired() const1224   bool get_password_expired () const
1225   {
1226     uint field_num= end_priv_columns + 10;
1227 
1228     Field *f= get_field(field_num, MYSQL_TYPE_ENUM);
1229     return f ? f->val_int()-1 : 0;
1230   }
set_password_expired(bool x) const1231   int set_password_expired (bool x) const
1232   {
1233     uint field_num= end_priv_columns + 10;
1234 
1235     if (Field *f= get_field(field_num, MYSQL_TYPE_ENUM))
1236       return f->store(x+1, 0);
1237     return 1;
1238   }
get_password_last_changed() const1239   my_time_t get_password_last_changed () const
1240   {
1241     ulong unused_dec;
1242     if (Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_TIMESTAMP2))
1243       return f->get_timestamp(&unused_dec);
1244     return 0;
1245   }
set_password_last_changed(my_time_t x) const1246   int set_password_last_changed (my_time_t x) const
1247   {
1248     if (Field *f= get_field(end_priv_columns + 11, MYSQL_TYPE_TIMESTAMP2))
1249     {
1250       f->set_notnull();
1251       return f->store_timestamp(x, 0);
1252     }
1253     return 1;
1254   }
get_password_lifetime() const1255   longlong get_password_lifetime () const
1256   {
1257     if (Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_SHORT))
1258     {
1259       if (f->is_null())
1260         return -1;
1261       return f->val_int();
1262     }
1263     return 0;
1264   }
set_password_lifetime(longlong x) const1265   int set_password_lifetime (longlong x) const
1266   {
1267     if (Field *f= get_field(end_priv_columns + 12, MYSQL_TYPE_SHORT))
1268     {
1269       if (x < 0)
1270       {
1271         f->set_null();
1272         return 0;
1273       }
1274       f->set_notnull();
1275       return f->store(x, 0);
1276     }
1277     return 1;
1278   }
1279 
~User_table_tabular()1280   virtual ~User_table_tabular() {}
1281  private:
1282   friend class Grant_tables;
1283 
1284   /* Only Grant_tables can instantiate this class. */
User_table_tabular()1285   User_table_tabular() { min_columns= 13; /* As in 3.20.13 */ }
1286 
1287   /* The user table is a bit different compared to the other Grant tables.
1288      Usually, we only add columns to the grant tables when adding functionality.
1289      This makes it easy to test which version of the table we are using, by
1290      just looking at the number of fields present in the table.
1291 
1292      In MySQL 5.7.6 the Password column was removed. We need to guard for that.
1293      The field-fetching methods for the User table return NULL if the field
1294      doesn't exist. This simplifies checking of table "version", as we don't
1295      have to make use of num_fields() any more.
1296   */
get_field(uint field_num,enum enum_field_types type) const1297   inline Field* get_field(uint field_num, enum enum_field_types type) const
1298   {
1299     if (field_num >= num_fields())
1300       return NULL;
1301     Field *f= m_table->field[field_num];
1302     return f->real_type() == type ? f : NULL;
1303   }
1304 
setup_sysvars() const1305   int setup_sysvars() const
1306   {
1307     username_char_length= MY_MIN(m_table->field[1]->char_length(),
1308                                  USERNAME_CHAR_LENGTH);
1309     using_global_priv_table= false;
1310 
1311     if (have_password()) // Password column might be missing. (MySQL 5.7.6+)
1312     {
1313       int password_length= password()->field_length /
1314                            password()->charset()->mbmaxlen;
1315       if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1316       {
1317         sql_print_error("Fatal error: mysql.user table is damaged or in "
1318                         "unsupported 3.20 format.");
1319         return 1;
1320       }
1321 
1322       mysql_mutex_lock(&LOCK_global_system_variables);
1323       if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
1324       {
1325         if (opt_secure_auth)
1326         {
1327           mysql_mutex_unlock(&LOCK_global_system_variables);
1328           sql_print_error("Fatal error: mysql.user table is in old format, "
1329                           "but server started with --secure-auth option.");
1330           return 1;
1331         }
1332         mysql_user_table_is_in_short_password_format= true;
1333         if (global_system_variables.old_passwords)
1334           mysql_mutex_unlock(&LOCK_global_system_variables);
1335         else
1336         {
1337           extern sys_var *Sys_old_passwords_ptr;
1338           Sys_old_passwords_ptr->value_origin= sys_var::AUTO;
1339           global_system_variables.old_passwords= 1;
1340           mysql_mutex_unlock(&LOCK_global_system_variables);
1341           sql_print_warning("mysql.user table is not updated to new password format; "
1342                             "Disabling new password usage until "
1343                             "mysql_fix_privilege_tables is run");
1344         }
1345         m_table->in_use->variables.old_passwords= 1;
1346       }
1347       else
1348       {
1349         mysql_user_table_is_in_short_password_format= false;
1350         mysql_mutex_unlock(&LOCK_global_system_variables);
1351       }
1352     }
1353     return 0;
1354   }
1355 
1356   /* Normally password column is the third column in the table. If privileges
1357      start on the third column instead, we are missing the password column.
1358      This means we are using a MySQL 5.7.6+ data directory. */
have_password() const1359   bool have_password() const { return start_priv_columns == 3; }
1360 
password() const1361   Field* password() const { return m_table->field[2]; }
plugin() const1362   Field* plugin() const   { return get_field(end_priv_columns + 8, MYSQL_TYPE_STRING); }
authstr() const1363   Field* authstr() const  { return get_field(end_priv_columns + 9, MYSQL_TYPE_BLOB); }
1364 };
1365 
1366 /*
1367   MariaDB 10.4 and up `global_priv` table
1368 
1369   TODO possible optimizations:
1370   * update json in-place if the new value can fit
1371   * don't repeat get_value for every key, but use a streaming parser
1372     to convert json into in-memory object (ACL_USER?) in one json scan.
1373     - this makes sense for acl_load(), but hardly for GRANT
1374   * similarly, pack ACL_USER (?) into json in one go.
1375     - doesn't make sense? GRANT rarely updates more than one field.
1376 */
1377 class User_table_json: public User_table
1378 {
name() const1379   LEX_CSTRING& name() const { return MYSQL_TABLE_NAME[USER_TABLE]; }
1380 
get_auth(THD * thd,MEM_ROOT * root,ACL_USER * u) const1381   int get_auth(THD *thd, MEM_ROOT *root, ACL_USER *u) const
1382   {
1383     size_t array_len;
1384     const char *array;
1385     int vl;
1386     const char *v;
1387 
1388     if (get_value("auth_or", JSV_ARRAY, &array, &array_len))
1389     {
1390       u->alloc_auth(root, 1);
1391       return get_auth1(thd, root, u, 0);
1392     }
1393 
1394     if (json_get_array_item(array, array + array_len, (int)array_len,
1395                             &v, &vl) != JSV_NOTHING)
1396       return 1;
1397     u->alloc_auth(root, vl);
1398     for (uint i=0; i < u->nauth; i++)
1399     {
1400       if (json_get_array_item(array, array + array_len, i, &v, &vl) != JSV_OBJECT)
1401         return 1;
1402 
1403       const char *p, *a;
1404       int pl, al;
1405       switch (json_get_object_key(v, v + vl, "plugin", &p, &pl)) {
1406       case JSV_STRING: u->auth[i].plugin.str= strmake_root(root, p, pl);
1407                        u->auth[i].plugin.length= pl;
1408                        break;
1409       case JSV_NOTHING: if (get_auth1(thd, root, u, i))
1410                           return 1;
1411                         else
1412                           continue;
1413       default: return 1;
1414       }
1415       switch (json_get_object_key(v, v + vl, "authentication_string", &a, &al)) {
1416       case JSV_NOTHING: u->auth[i].auth_string= empty_clex_str;
1417                         break;
1418       case JSV_STRING: u->auth[i].auth_string.str= strmake_root(root, a, al);
1419                        u->auth[i].auth_string.length= al;
1420                        break;
1421       default: return 1;
1422       }
1423     }
1424     return 0;
1425   }
1426 
get_auth1(THD * thd,MEM_ROOT * root,ACL_USER * u,uint n) const1427   int get_auth1(THD *thd, MEM_ROOT *root, ACL_USER *u, uint n) const
1428   {
1429     const char *authstr= get_str_value(root, "authentication_string");
1430     const char *plugin= get_str_value(root, "plugin");
1431     if (plugin && authstr)
1432     {
1433       if (plugin && *plugin)
1434       {
1435         u->auth[n].plugin.str= plugin;
1436         u->auth[n].plugin.length= strlen(plugin);
1437       }
1438       else
1439         u->auth[n].plugin= native_password_plugin_name;
1440       u->auth[n].auth_string.str= authstr;
1441       u->auth[n].auth_string.length= strlen(authstr);
1442       return 0;
1443     }
1444     return 1;
1445   }
1446 
append_str_value(String * to,const LEX_CSTRING & str) const1447   bool append_str_value(String *to, const LEX_CSTRING &str) const
1448   {
1449     to->append('"');
1450     to->reserve(str.length*2);
1451     int len= json_escape(system_charset_info, (uchar*)str.str, (uchar*)str.str + str.length,
1452                          to->charset(), (uchar*)to->end(), (uchar*)to->end() + str.length*2);
1453     if (len < 0)
1454       return 1;
1455     to->length(to->length() + len);
1456     to->append('"');
1457     return 0;
1458   }
1459 
set_auth(const ACL_USER & u) const1460   bool set_auth(const ACL_USER &u) const
1461   {
1462     size_t array_len;
1463     const char *array;
1464     if (u.nauth == 1 && get_value("auth_or", JSV_ARRAY, &array, &array_len))
1465       return set_auth1(u, 0);
1466 
1467     StringBuffer<JSON_SIZE> json(m_table->field[2]->charset());
1468     bool top_done = false;
1469     json.append('[');
1470     for (uint i=0; i < u.nauth; i++)
1471     {
1472       ACL_USER::AUTH * const auth= u.auth + i;
1473       if (i)
1474         json.append(',');
1475       json.append('{');
1476       if (!top_done &&
1477           (auth->plugin.str == native_password_plugin_name.str ||
1478            auth->plugin.str == old_password_plugin_name.str ||
1479            i == u.nauth - 1))
1480       {
1481         if (set_auth1(u, i))
1482           return 1;
1483         top_done= true;
1484       }
1485       else
1486       {
1487         json.append(STRING_WITH_LEN("\"plugin\":"));
1488         if (append_str_value(&json, auth->plugin))
1489           return 1;
1490         if (auth->auth_string.length)
1491         {
1492           json.append(STRING_WITH_LEN(",\"authentication_string\":"));
1493           if (append_str_value(&json, auth->auth_string))
1494             return 1;
1495         }
1496       }
1497       json.append('}');
1498     }
1499     json.append(']');
1500     return set_value("auth_or", json.ptr(), json.length(), false) == JSV_BAD_JSON;
1501   }
set_auth1(const ACL_USER & u,uint i) const1502   bool set_auth1(const ACL_USER &u, uint i) const
1503   {
1504     return set_str_value("plugin",
1505                          u.auth[i].plugin.str, u.auth[i].plugin.length) ||
1506             set_str_value("authentication_string",
1507                          u.auth[i].auth_string.str, u.auth[i].auth_string.length);
1508   }
1509 
print_warning_bad_version_id(ulonglong version_id) const1510   void print_warning_bad_version_id(ulonglong version_id) const
1511   {
1512     sql_print_warning("'user' entry '%s@%s' has a wrong 'version_id' value %lld",
1513                       safe_str(get_user(current_thd->mem_root)),
1514                       safe_str(get_host(current_thd->mem_root)),
1515                       version_id);
1516   }
1517 
print_warning_bad_access(ulonglong version_id,privilege_t mask,ulonglong access) const1518   void print_warning_bad_access(ulonglong version_id,
1519                                 privilege_t mask,
1520                                 ulonglong access) const
1521   {
1522     sql_print_warning("'user' entry '%s@%s' "
1523                       "has a wrong 'access' value 0x%llx "
1524                       "(allowed mask is 0x%llx, version_id=%lld)",
1525                       safe_str(get_user(current_thd->mem_root)),
1526                       safe_str(get_host(current_thd->mem_root)),
1527                       access, mask, version_id);
1528   }
1529 
adjust_access(ulonglong version_id,ulonglong access) const1530   privilege_t adjust_access(ulonglong version_id, ulonglong access) const
1531   {
1532     privilege_t mask= ALL_KNOWN_ACL_100304;
1533     ulonglong orig_access= access;
1534     if (version_id >= 100509)
1535     {
1536       mask= ALL_KNOWN_ACL_100509;
1537     }
1538     else if (version_id >= 100502)
1539     {
1540       if (version_id >= 100508)
1541         mask= ALL_KNOWN_ACL_100508;
1542       else
1543         mask= ALL_KNOWN_ACL_100502;
1544       if (access & REPL_SLAVE_ADMIN_ACL)
1545         access|= SLAVE_MONITOR_ACL;
1546     }
1547     else // 100501 or earlier
1548     {
1549       /*
1550         Address changes in SUPER and REPLICATION SLAVE made in 10.5.2.
1551         This also covers a special case: if the user had ALL PRIVILEGES before
1552         the upgrade, it gets ALL PRIVILEGES after the upgrade.
1553       */
1554       if (access & SUPER_ACL)
1555       {
1556         if (access & REPL_SLAVE_ACL)
1557         {
1558           /*
1559             The user could do both before the upgrade:
1560             - set global variables       (because of SUPER_ACL)
1561             - execute "SHOW SLAVE HOSTS" (because of REPL_SLAVE_ACL)
1562             Grant all new privileges that were splitted from SUPER (in 10.5.2),
1563             and REPLICATION MASTER ADMIN, so it still can do "SHOW SLAVE HOSTS".
1564           */
1565           access|= REPL_MASTER_ADMIN_ACL;
1566         }
1567         access|= GLOBAL_SUPER_ADDED_SINCE_USER_TABLE_ACLS;
1568       }
1569       /*
1570         REPLICATION_CLIENT(BINLOG_MONITOR_ACL) should allow SHOW SLAVE STATUS
1571         REPLICATION SLAVE should allow SHOW RELAYLOG EVENTS
1572       */
1573       if (access & BINLOG_MONITOR_ACL || access & REPL_SLAVE_ACL)
1574         access|= SLAVE_MONITOR_ACL;
1575     }
1576 
1577     if (orig_access & ~mask)
1578     {
1579       print_warning_bad_access(version_id, mask, orig_access);
1580       return NO_ACL;
1581     }
1582     return access & ALL_KNOWN_ACL;
1583   }
1584 
get_access() const1585   privilege_t get_access() const
1586   {
1587     ulonglong version_id= (ulonglong) get_int_value("version_id");
1588     ulonglong access= (ulonglong) get_int_value("access");
1589 
1590     /*
1591       Special case:
1592       mysql_system_tables_data.sql populates "ALL PRIVILEGES"
1593       for the super user this way:
1594             {"access":18446744073709551615}
1595     */
1596     if (access == (ulonglong) ~0)
1597       return GLOBAL_ACLS;
1598 
1599     /*
1600       Reject obviously bad (negative and too large) version_id values.
1601       Also reject versions before 10.4.0 (when JSON table was added).
1602     */
1603     if ((longlong) version_id < 0 || version_id > 999999 ||
1604         (version_id > 0 && version_id < 100400))
1605     {
1606       print_warning_bad_version_id(version_id);
1607       return NO_ACL;
1608     }
1609     return adjust_access(version_id, access) & GLOBAL_ACLS;
1610   }
1611 
set_access(const privilege_t rights,bool revoke) const1612   void set_access(const privilege_t rights, bool revoke) const
1613   {
1614     privilege_t access= get_access();
1615     if (revoke)
1616       access&= ~rights;
1617     else
1618       access|= rights;
1619     set_int_value("access", (longlong) (access & GLOBAL_ACLS));
1620     set_int_value("version_id", (longlong) MYSQL_VERSION_ID);
1621   }
unsafe_str(const char * s) const1622   const char *unsafe_str(const char *s) const
1623   { return s[0] ? s : NULL; }
1624 
get_ssl_type() const1625   SSL_type get_ssl_type () const
1626   { return (SSL_type)get_int_value("ssl_type"); }
set_ssl_type(SSL_type x) const1627   int set_ssl_type (SSL_type x) const
1628   { return set_int_value("ssl_type", x); }
get_ssl_cipher(MEM_ROOT * root) const1629   const char* get_ssl_cipher (MEM_ROOT *root) const
1630   { return unsafe_str(get_str_value(root, "ssl_cipher")); }
set_ssl_cipher(const char * s,size_t l) const1631   int set_ssl_cipher (const char *s, size_t l) const
1632   { return set_str_value("ssl_cipher", s, l); }
get_x509_issuer(MEM_ROOT * root) const1633   const char* get_x509_issuer (MEM_ROOT *root) const
1634   { return unsafe_str(get_str_value(root, "x509_issuer")); }
set_x509_issuer(const char * s,size_t l) const1635   int set_x509_issuer (const char *s, size_t l) const
1636   { return set_str_value("x509_issuer", s, l); }
get_x509_subject(MEM_ROOT * root) const1637   const char* get_x509_subject (MEM_ROOT *root) const
1638   { return unsafe_str(get_str_value(root, "x509_subject")); }
set_x509_subject(const char * s,size_t l) const1639   int set_x509_subject (const char *s, size_t l) const
1640   { return set_str_value("x509_subject", s, l); }
get_max_questions() const1641   longlong get_max_questions () const
1642   { return get_int_value("max_questions"); }
set_max_questions(longlong x) const1643   int set_max_questions (longlong x) const
1644   { return set_int_value("max_questions", x); }
get_max_updates() const1645   longlong get_max_updates () const
1646   { return get_int_value("max_updates"); }
set_max_updates(longlong x) const1647   int set_max_updates (longlong x) const
1648   { return set_int_value("max_updates", x); }
get_max_connections() const1649   longlong get_max_connections () const
1650   { return get_int_value("max_connections"); }
set_max_connections(longlong x) const1651   int set_max_connections (longlong x) const
1652   { return set_int_value("max_connections", x); }
get_max_user_connections() const1653   longlong get_max_user_connections () const
1654   { return get_int_value("max_user_connections"); }
set_max_user_connections(longlong x) const1655   int set_max_user_connections (longlong x) const
1656   { return set_int_value("max_user_connections", x); }
get_max_statement_time() const1657   double get_max_statement_time () const
1658   { return get_double_value("max_statement_time"); }
set_max_statement_time(double x) const1659   int set_max_statement_time (double x) const
1660   { return set_double_value("max_statement_time", x); }
get_is_role() const1661   bool get_is_role () const
1662   { return get_bool_value("is_role"); }
set_is_role(bool x) const1663   int set_is_role (bool x) const
1664   { return set_bool_value("is_role", x); }
get_default_role(MEM_ROOT * root) const1665   const char* get_default_role (MEM_ROOT *root) const
1666   { return get_str_value(root, "default_role"); }
set_default_role(const char * s,size_t l) const1667   int set_default_role (const char *s, size_t l) const
1668   { return set_str_value("default_role", s, l); }
get_account_locked() const1669   bool get_account_locked () const
1670   { return get_bool_value("account_locked"); }
set_account_locked(bool x) const1671   int set_account_locked (bool x) const
1672   { return set_bool_value("account_locked", x); }
get_password_last_changed() const1673   my_time_t get_password_last_changed () const
1674   { return static_cast<my_time_t>(get_int_value("password_last_changed")); }
set_password_last_changed(my_time_t x) const1675   int set_password_last_changed (my_time_t x) const
1676   { return set_int_value("password_last_changed", static_cast<longlong>(x)); }
set_password_lifetime(longlong x) const1677   int set_password_lifetime (longlong x) const
1678   { return set_int_value("password_lifetime", x); }
get_password_lifetime() const1679   longlong get_password_lifetime () const
1680   { return get_int_value("password_lifetime", -1); }
1681   /*
1682      password_last_changed=0 means the password is manually expired.
1683      In MySQL 5.7+ this state is described using the password_expired column
1684      in mysql.user
1685   */
get_password_expired() const1686   bool get_password_expired () const
1687   { return get_int_value("password_last_changed", -1) == 0; }
set_password_expired(bool x) const1688   int set_password_expired (bool x) const
1689   { return x ? set_password_last_changed(0) : 0; }
1690 
~User_table_json()1691   ~User_table_json() {}
1692  private:
1693   friend class Grant_tables;
1694   static const uint JSON_SIZE=1024;
setup_sysvars() const1695   int setup_sysvars() const
1696   {
1697     using_global_priv_table= true;
1698     username_char_length= MY_MIN(m_table->field[1]->char_length(),
1699                                  USERNAME_CHAR_LENGTH);
1700     return 0;
1701   }
get_value(const char * key,enum json_types vt,const char ** v,size_t * vl) const1702   bool get_value(const char *key,
1703                  enum json_types vt, const char **v, size_t *vl) const
1704   {
1705     enum json_types value_type;
1706     int int_vl;
1707     String str, *res= m_table->field[2]->val_str(&str);
1708     if (!res ||
1709         (value_type= json_get_object_key(res->ptr(), res->end(), key,
1710                                              v, &int_vl)) == JSV_BAD_JSON)
1711       return 1; // invalid
1712     *vl= int_vl;
1713     return value_type != vt;
1714   }
get_str_value(MEM_ROOT * root,const char * key) const1715   const char *get_str_value(MEM_ROOT *root, const char *key) const
1716   {
1717     size_t value_len;
1718     const char *value_start;
1719     if (get_value(key, JSV_STRING, &value_start, &value_len))
1720       return "";
1721     char *ptr= (char*)alloca(value_len);
1722     int len= json_unescape(m_table->field[2]->charset(),
1723                            (const uchar*)value_start,
1724                            (const uchar*)value_start + value_len,
1725                            system_charset_info,
1726                            (uchar*)ptr, (uchar*)ptr + value_len);
1727     if (len < 0)
1728       return NULL;
1729     return strmake_root(root, ptr, len);
1730   }
get_int_value(const char * key,longlong def_val=0) const1731   longlong get_int_value(const char *key, longlong def_val= 0) const
1732   {
1733     int err;
1734     size_t value_len;
1735     const char *value_start;
1736     if (get_value(key, JSV_NUMBER, &value_start, &value_len))
1737       return def_val;
1738     const char *value_end= value_start + value_len;
1739     return my_strtoll10(value_start, (char**)&value_end, &err);
1740   }
get_double_value(const char * key) const1741   double get_double_value(const char *key) const
1742   {
1743     int err;
1744     size_t value_len;
1745     const char *value_start;
1746     if (get_value(key, JSV_NUMBER, &value_start, &value_len))
1747       return 0;
1748     const char *value_end= value_start + value_len;
1749     return my_strtod(value_start, (char**)&value_end, &err);
1750   }
get_bool_value(const char * key) const1751   bool get_bool_value(const char *key) const
1752   {
1753     size_t value_len;
1754     const char *value_start;
1755     if (get_value(key, JSV_TRUE, &value_start, &value_len))
1756       return false;
1757     return true;
1758   }
set_value(const char * key,const char * val,size_t vlen,bool string) const1759   enum json_types set_value(const char *key,
1760                             const char *val, size_t vlen, bool string) const
1761   {
1762     int value_len;
1763     const char *value_start;
1764     enum json_types value_type;
1765     String str, *res= m_table->field[2]->val_str(&str);
1766     if (!res || !res->length())
1767       (res= &str)->set(STRING_WITH_LEN("{}"), m_table->field[2]->charset());
1768     value_type= json_get_object_key(res->ptr(), res->end(), key,
1769                                     &value_start, &value_len);
1770     if (value_type == JSV_BAD_JSON)
1771       return value_type; // invalid
1772     StringBuffer<JSON_SIZE> json(res->charset());
1773     json.copy(res->ptr(), value_start - res->ptr(), res->charset());
1774     if (value_type == JSV_NOTHING)
1775     {
1776       if (value_len)
1777         json.append(',');
1778       json.append('"');
1779       json.append(key);
1780       json.append(STRING_WITH_LEN("\":"));
1781       if (string)
1782         json.append('"');
1783     }
1784     else
1785       value_start+= value_len;
1786     json.append(val, vlen);
1787     if (!value_type && string)
1788       json.append('"');
1789     json.append(value_start, res->end() - value_start);
1790     DBUG_ASSERT(json_valid(json.ptr(), json.length(), json.charset()));
1791     m_table->field[2]->store(json.ptr(), json.length(), json.charset());
1792     return value_type;
1793   }
set_str_value(const char * key,const char * val,size_t vlen) const1794   bool set_str_value(const char *key, const char *val, size_t vlen) const
1795   {
1796     char buf[JSON_SIZE];
1797     int blen= json_escape(system_charset_info,
1798                           (const uchar*)val, (const uchar*)val + vlen,
1799                           m_table->field[2]->charset(),
1800                           (uchar*)buf, (uchar*)buf+sizeof(buf));
1801     if (blen < 0)
1802       return 1;
1803     return set_value(key, buf, blen, true) == JSV_BAD_JSON;
1804   }
set_int_value(const char * key,longlong val) const1805   bool set_int_value(const char *key, longlong val) const
1806   {
1807     char v[MY_INT64_NUM_DECIMAL_DIGITS+1];
1808     size_t vlen= longlong10_to_str(val, v, -10) - v;
1809     return set_value(key, v, vlen, false) == JSV_BAD_JSON;
1810   }
set_double_value(const char * key,double val) const1811   bool set_double_value(const char *key, double val) const
1812   {
1813     char v[FLOATING_POINT_BUFFER+1];
1814     size_t vlen= my_fcvt(val, TIME_SECOND_PART_DIGITS, v, NULL);
1815     return set_value(key, v, vlen, false) == JSV_BAD_JSON;
1816   }
set_bool_value(const char * key,bool val) const1817   bool set_bool_value(const char *key, bool val) const
1818   {
1819     return set_value(key, val ? "true" : "false", val ? 4 : 5, false) == JSV_BAD_JSON;
1820   }
1821 };
1822 
1823 class Db_table: public Grant_table_base
1824 {
1825  public:
host() const1826   Field* host() const { return m_table->field[0]; }
db() const1827   Field* db() const { return m_table->field[1]; }
user() const1828   Field* user() const { return m_table->field[2]; }
1829 
1830  private:
1831   friend class Grant_tables;
1832 
Db_table()1833   Db_table() { min_columns= 9; /* as in 3.20.13 */ }
1834 };
1835 
1836 class Tables_priv_table: public Grant_table_base
1837 {
1838  public:
host() const1839   Field* host() const { return m_table->field[0]; }
db() const1840   Field* db() const { return m_table->field[1]; }
user() const1841   Field* user() const { return m_table->field[2]; }
table_name() const1842   Field* table_name() const { return m_table->field[3]; }
grantor() const1843   Field* grantor() const { return m_table->field[4]; }
timestamp() const1844   Field* timestamp() const { return m_table->field[5]; }
table_priv() const1845   Field* table_priv() const { return m_table->field[6]; }
column_priv() const1846   Field* column_priv() const { return m_table->field[7]; }
1847 
1848  private:
1849   friend class Grant_tables;
1850 
Tables_priv_table()1851   Tables_priv_table() { min_columns= 8; /* as in 3.22.26a */ }
1852 };
1853 
1854 class Columns_priv_table: public Grant_table_base
1855 {
1856  public:
host() const1857   Field* host() const { return m_table->field[0]; }
db() const1858   Field* db() const { return m_table->field[1]; }
user() const1859   Field* user() const { return m_table->field[2]; }
table_name() const1860   Field* table_name() const { return m_table->field[3]; }
column_name() const1861   Field* column_name() const { return m_table->field[4]; }
timestamp() const1862   Field* timestamp() const { return m_table->field[5]; }
column_priv() const1863   Field* column_priv() const { return m_table->field[6]; }
1864 
1865  private:
1866   friend class Grant_tables;
1867 
Columns_priv_table()1868   Columns_priv_table() { min_columns= 7; /* as in 3.22.26a */ }
1869 };
1870 
1871 class Host_table: public Grant_table_base
1872 {
1873  public:
host() const1874   Field* host() const { return m_table->field[0]; }
db() const1875   Field* db() const { return m_table->field[1]; }
1876 
1877  private:
1878   friend class Grant_tables;
1879 
Host_table()1880   Host_table() { min_columns= 8; /* as in 3.20.13 */ }
1881 };
1882 
1883 class Procs_priv_table: public Grant_table_base
1884 {
1885  public:
host() const1886   Field* host() const { return m_table->field[0]; }
db() const1887   Field* db() const { return m_table->field[1]; }
user() const1888   Field* user() const { return m_table->field[2]; }
routine_name() const1889   Field* routine_name() const { return m_table->field[3]; }
routine_type() const1890   Field* routine_type() const { return m_table->field[4]; }
grantor() const1891   Field* grantor() const { return m_table->field[5]; }
proc_priv() const1892   Field* proc_priv() const { return m_table->field[6]; }
timestamp() const1893   Field* timestamp() const { return m_table->field[7]; }
1894 
1895  private:
1896   friend class Grant_tables;
1897 
Procs_priv_table()1898   Procs_priv_table() { min_columns=8; }
1899 };
1900 
1901 class Proxies_priv_table: public Grant_table_base
1902 {
1903  public:
host() const1904   Field* host() const { return m_table->field[0]; }
user() const1905   Field* user() const { return m_table->field[1]; }
proxied_host() const1906   Field* proxied_host() const { return m_table->field[2]; }
proxied_user() const1907   Field* proxied_user() const { return m_table->field[3]; }
with_grant() const1908   Field* with_grant() const { return m_table->field[4]; }
grantor() const1909   Field* grantor() const { return m_table->field[5]; }
timestamp() const1910   Field* timestamp() const { return m_table->field[6]; }
1911 
1912  private:
1913   friend class Grant_tables;
1914 
Proxies_priv_table()1915   Proxies_priv_table() { min_columns= 7; }
1916 };
1917 
1918 class Roles_mapping_table: public Grant_table_base
1919 {
1920  public:
host() const1921   Field* host() const { return m_table->field[0]; }
user() const1922   Field* user() const { return m_table->field[1]; }
role() const1923   Field* role() const { return m_table->field[2]; }
admin_option() const1924   Field* admin_option() const { return m_table->field[3]; }
1925 
1926  private:
1927   friend class Grant_tables;
1928 
Roles_mapping_table()1929   Roles_mapping_table() { min_columns= 4; }
1930 };
1931 
1932 /**
1933   Class that represents a collection of grant tables.
1934 */
1935 class Grant_tables
1936 {
1937  public:
Grant_tables()1938   Grant_tables() : p_user_table(&m_user_table_json) { }
1939 
open_and_lock(THD * thd,int which_tables,enum thr_lock_type lock_type)1940   int open_and_lock(THD *thd, int which_tables, enum thr_lock_type lock_type)
1941   {
1942     DBUG_ENTER("Grant_tables::open_and_lock");
1943     TABLE_LIST tables[USER_TABLE+1], *first= NULL;
1944 
1945     DBUG_ASSERT(which_tables); /* At least one table must be opened. */
1946     /*
1947        We can read privilege tables even when !initialized.
1948        This can be acl_load() - server startup or FLUSH PRIVILEGES
1949        */
1950     if (lock_type >= TL_WRITE_ALLOW_WRITE && !initialized)
1951     {
1952       my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1953       DBUG_RETURN(-1);
1954     }
1955 
1956     for (int i=USER_TABLE; i >=0; i--)
1957     {
1958       TABLE_LIST *tl= tables + i;
1959       if (which_tables & (1 << i))
1960       {
1961         tl->init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLE_NAME[i],
1962                            NULL, lock_type);
1963         tl->open_type= OT_BASE_ONLY;
1964         tl->i_s_requested_object= OPEN_TABLE_ONLY;
1965         tl->updating= lock_type >= TL_WRITE_ALLOW_WRITE;
1966         if (i >= FIRST_OPTIONAL_TABLE)
1967           tl->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
1968         tl->next_global= tl->next_local= first;
1969         first= tl;
1970       }
1971       else
1972         tl->table= NULL;
1973     }
1974 
1975     uint counter;
1976     int res= really_open(thd, first, &counter);
1977 
1978     /* if User_table_json wasn't found, let's try User_table_tabular */
1979     if (!res && (which_tables & Table_user) && !tables[USER_TABLE].table)
1980     {
1981       uint unused;
1982       TABLE_LIST *tl= tables + USER_TABLE;
1983       TABLE *backup_open_tables= thd->open_tables;
1984       thd->set_open_tables(NULL);
1985 
1986       tl->init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLE_NAME_USER,
1987                          NULL, lock_type);
1988       tl->open_type= OT_BASE_ONLY;
1989       tl->i_s_requested_object= OPEN_TABLE_ONLY;
1990       tl->updating= lock_type >= TL_WRITE_ALLOW_WRITE;
1991       p_user_table= &m_user_table_tabular;
1992       counter++;
1993       res= really_open(thd, tl, &unused);
1994       thd->set_open_tables(backup_open_tables);
1995       if (tables[USER_TABLE].table)
1996       {
1997         tables[USER_TABLE].table->next= backup_open_tables;
1998         thd->set_open_tables(tables[USER_TABLE].table);
1999       }
2000     }
2001     if (res)
2002       DBUG_RETURN(res);
2003 
2004     if (lock_tables(thd, first, counter,
2005                     MYSQL_LOCK_IGNORE_TIMEOUT |
2006                     MYSQL_OPEN_IGNORE_LOGGING_FORMAT))
2007       DBUG_RETURN(-1);
2008 
2009     p_user_table->set_table(tables[USER_TABLE].table);
2010     m_db_table.set_table(tables[DB_TABLE].table);
2011     m_tables_priv_table.set_table(tables[TABLES_PRIV_TABLE].table);
2012     m_columns_priv_table.set_table(tables[COLUMNS_PRIV_TABLE].table);
2013     m_host_table.set_table(tables[HOST_TABLE].table);
2014     m_procs_priv_table.set_table(tables[PROCS_PRIV_TABLE].table);
2015     m_proxies_priv_table.set_table(tables[PROXIES_PRIV_TABLE].table);
2016     m_roles_mapping_table.set_table(tables[ROLES_MAPPING_TABLE].table);
2017     DBUG_RETURN(0);
2018   }
2019 
user_table() const2020   inline const User_table& user_table() const
2021   { return *p_user_table; }
2022 
db_table() const2023   inline const Db_table& db_table() const
2024   { return m_db_table; }
2025 
tables_priv_table() const2026   inline const Tables_priv_table& tables_priv_table() const
2027   { return m_tables_priv_table; }
2028 
columns_priv_table() const2029   inline const Columns_priv_table& columns_priv_table() const
2030   { return m_columns_priv_table; }
2031 
host_table() const2032   inline const Host_table& host_table() const
2033   { return m_host_table; }
2034 
procs_priv_table() const2035   inline const Procs_priv_table& procs_priv_table() const
2036   { return m_procs_priv_table; }
2037 
proxies_priv_table() const2038   inline const Proxies_priv_table& proxies_priv_table() const
2039   { return m_proxies_priv_table; }
2040 
roles_mapping_table() const2041   inline const Roles_mapping_table& roles_mapping_table() const
2042   { return m_roles_mapping_table; }
2043 
2044  private:
2045 
2046   /* Before any operation is possible on grant tables, they must be opened.
2047 
2048      @retval  1 replication filters matched. Abort the operation,
2049                 but return OK (!)
2050      @retval  0 tables were opened successfully
2051      @retval -1 error, tables could not be opened
2052   */
really_open(THD * thd,TABLE_LIST * tables,uint * counter)2053   int really_open(THD *thd, TABLE_LIST* tables, uint *counter)
2054   {
2055     DBUG_ENTER("Grant_tables::really_open:");
2056 #ifdef HAVE_REPLICATION
2057     if (tables->lock_type >= TL_WRITE_ALLOW_WRITE &&
2058         thd->slave_thread && !thd->spcont)
2059     {
2060       /*
2061         GRANT and REVOKE are applied the slave in/exclusion rules as they are
2062         some kind of updates to the mysql.% tables.
2063       */
2064       Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
2065       if (rpl_filter->is_on() && !rpl_filter->tables_ok(0, tables))
2066         DBUG_RETURN(1);
2067     }
2068 #endif
2069     if (open_tables(thd, &tables, counter, MYSQL_LOCK_IGNORE_TIMEOUT))
2070       DBUG_RETURN(-1);
2071     DBUG_RETURN(0);
2072   }
2073 
2074   User_table *p_user_table;
2075   User_table_json m_user_table_json;
2076   User_table_tabular m_user_table_tabular;
2077   Db_table m_db_table;
2078   Tables_priv_table m_tables_priv_table;
2079   Columns_priv_table m_columns_priv_table;
2080   Host_table m_host_table;
2081   Procs_priv_table m_procs_priv_table;
2082   Proxies_priv_table m_proxies_priv_table;
2083   Roles_mapping_table m_roles_mapping_table;
2084 };
2085 
2086 
init(const Proxies_priv_table & proxies_priv_table,MEM_ROOT * mem)2087 void ACL_PROXY_USER::init(const Proxies_priv_table& proxies_priv_table,
2088                           MEM_ROOT *mem)
2089 {
2090   init(get_field(mem, proxies_priv_table.host()),
2091        safe_str(get_field(mem, proxies_priv_table.user())),
2092        get_field(mem, proxies_priv_table.proxied_host()),
2093        safe_str(get_field(mem, proxies_priv_table.proxied_user())),
2094        proxies_priv_table.with_grant()->val_int() != 0);
2095 }
2096 
2097 
2098 /*
2099  Enumeration of various ACL's and Hashes used in handle_grant_struct()
2100 */
2101 enum enum_acl_lists
2102 {
2103   USER_ACL= 0,
2104   ROLE_ACL,
2105   DB_ACL,
2106   COLUMN_PRIVILEGES_HASH,
2107   PROC_PRIVILEGES_HASH,
2108   FUNC_PRIVILEGES_HASH,
2109   PACKAGE_SPEC_PRIVILEGES_HASH,
2110   PACKAGE_BODY_PRIVILEGES_HASH,
2111   PROXY_USERS_ACL,
2112   ROLES_MAPPINGS_HASH
2113 };
2114 
ACL_ROLE(ACL_USER * user,MEM_ROOT * root)2115 ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root)
2116  :
2117   /* set initial role access the same as the table row privileges */
2118   initial_role_access(user->access),
2119   counter(0)
2120 {
2121   access= user->access;
2122   this->user= user->user;
2123   bzero(&parent_grantee, sizeof(parent_grantee));
2124   flags= IS_ROLE;
2125 }
2126 
ACL_ROLE(const char * rolename,privilege_t privileges,MEM_ROOT * root)2127 ACL_ROLE::ACL_ROLE(const char * rolename, privilege_t privileges,
2128                    MEM_ROOT *root) :
2129   initial_role_access(privileges), counter(0)
2130 {
2131   this->access= initial_role_access;
2132   this->user.str= safe_strdup_root(root, rolename);
2133   this->user.length= strlen(rolename);
2134   bzero(&parent_grantee, sizeof(parent_grantee));
2135   flags= IS_ROLE;
2136 }
2137 
2138 
is_invalid_role_name(const char * str)2139 static bool is_invalid_role_name(const char *str)
2140 {
2141   if (*str && strcasecmp(str, "PUBLIC") && strcasecmp(str, "NONE"))
2142     return false;
2143 
2144   my_error(ER_INVALID_ROLE, MYF(0), str);
2145   return true;
2146 }
2147 
2148 
free_acl_user(ACL_USER * user)2149 static void free_acl_user(ACL_USER *user)
2150 {
2151   delete_dynamic(&(user->role_grants));
2152 }
2153 
free_acl_role(ACL_ROLE * role)2154 static void free_acl_role(ACL_ROLE *role)
2155 {
2156   delete_dynamic(&(role->role_grants));
2157   delete_dynamic(&(role->parent_grantee));
2158 }
2159 
check_if_exists(THD *,plugin_ref,void *)2160 static my_bool check_if_exists(THD *, plugin_ref, void *)
2161 {
2162   return TRUE;
2163 }
2164 
has_validation_plugins()2165 static bool has_validation_plugins()
2166 {
2167   return plugin_foreach(NULL, check_if_exists,
2168                         MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL);
2169 }
2170 
2171 struct validation_data { const LEX_CSTRING *user, *password; };
2172 
do_validate(THD *,plugin_ref plugin,void * arg)2173 static my_bool do_validate(THD *, plugin_ref plugin, void *arg)
2174 {
2175   struct validation_data *data= (struct validation_data *)arg;
2176   struct st_mariadb_password_validation *handler=
2177     (st_mariadb_password_validation *)plugin_decl(plugin)->info;
2178   return handler->validate_password(data->user, data->password);
2179 }
2180 
2181 
validate_password(THD * thd,const LEX_CSTRING & user,const LEX_CSTRING & pwtext,bool has_hash)2182 static bool validate_password(THD *thd, const LEX_CSTRING &user,
2183                               const LEX_CSTRING &pwtext, bool has_hash)
2184 {
2185   if (pwtext.length || !has_hash)
2186   {
2187     struct validation_data data= { &user,
2188                                    pwtext.str ? &pwtext : &empty_clex_str };
2189     if (plugin_foreach(NULL, do_validate,
2190                        MariaDB_PASSWORD_VALIDATION_PLUGIN, &data))
2191     {
2192       my_error(ER_NOT_VALID_PASSWORD, MYF(0));
2193       return true;
2194     }
2195   }
2196   else
2197   {
2198     if (!thd->slave_thread &&
2199         strict_password_validation && has_validation_plugins()
2200 #ifdef WITH_WSREP
2201         && !thd->wsrep_applier
2202 #endif
2203        )
2204     {
2205       my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--strict-password-validation");
2206       return true;
2207     }
2208   }
2209   return false;
2210 }
2211 
set_user_salt(ACL_USER::AUTH * auth,plugin_ref plugin)2212 static int set_user_salt(ACL_USER::AUTH *auth, plugin_ref plugin)
2213 {
2214   st_mysql_auth *info= (st_mysql_auth *) plugin_decl(plugin)->info;
2215   if (info->interface_version >= 0x0202 && info->preprocess_hash &&
2216       auth->auth_string.length)
2217   {
2218     uchar buf[MAX_SCRAMBLE_LENGTH];
2219     size_t len= sizeof(buf);
2220     if (info->preprocess_hash(auth->auth_string.str,
2221                               auth->auth_string.length, buf, &len))
2222       return 1;
2223     auth->salt.str= (char*)memdup_root(&acl_memroot, buf, len);
2224     auth->salt.length= len;
2225   }
2226   else
2227     auth->salt= safe_lexcstrdup_root(&acl_memroot, auth->auth_string);
2228 
2229   return 0;
2230 }
2231 
2232 /**
2233   Fills in ACL_USER::auth_string and ACL_USER::salt fields, as needed
2234 
2235   hashes the plain-text password (if provided) to auth_string,
2236   converts auth_string to salt.
2237 
2238   Fails if the plain-text password fails validation, if the plugin is
2239   not loaded, if the auth_string is invalid, if the password is not applicable
2240 */
set_user_auth(THD * thd,const LEX_CSTRING & user,ACL_USER::AUTH * auth,const LEX_CSTRING & pwtext)2241 static int set_user_auth(THD *thd, const LEX_CSTRING &user,
2242                          ACL_USER::AUTH *auth, const LEX_CSTRING &pwtext)
2243 {
2244   const char *plugin_name= auth->plugin.str;
2245   bool unlock_plugin= false;
2246   plugin_ref plugin= get_auth_plugin(thd, auth->plugin, &unlock_plugin);
2247   int res= 1;
2248 
2249   if (!plugin)
2250   {
2251     push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
2252                         ER_PLUGIN_IS_NOT_LOADED,
2253                         ER_THD(thd, ER_PLUGIN_IS_NOT_LOADED), plugin_name);
2254     return ER_PLUGIN_IS_NOT_LOADED;
2255   }
2256 
2257   auth->salt= auth->auth_string;
2258 
2259   st_mysql_auth *info= (st_mysql_auth *) plugin_decl(plugin)->info;
2260   if (info->interface_version < 0x0202)
2261   {
2262     res= pwtext.length ? ER_SET_PASSWORD_AUTH_PLUGIN : 0;
2263     goto end;
2264   }
2265 
2266   if (thd->lex->sql_command == SQLCOM_SET_OPTION && !info->hash_password)
2267   {
2268     res= ER_SET_PASSWORD_AUTH_PLUGIN;
2269     goto end;
2270   }
2271 
2272   if (info->hash_password &&
2273       validate_password(thd, user, pwtext, auth->auth_string.length))
2274   {
2275     res= ER_NOT_VALID_PASSWORD;
2276     goto end;
2277   }
2278   if (pwtext.length)
2279   {
2280     if (info->hash_password)
2281     {
2282       char buf[MAX_SCRAMBLE_LENGTH];
2283       size_t len= sizeof(buf) - 1;
2284       if (info->hash_password(pwtext.str, pwtext.length, buf, &len))
2285       {
2286         res= ER_OUTOFMEMORY;
2287         goto end;
2288       }
2289       buf[len] = 0;
2290       auth->auth_string.str= (char*)memdup_root(&acl_memroot, buf, len+1);
2291       auth->auth_string.length= len;
2292     }
2293     else
2294     {
2295       res= ER_SET_PASSWORD_AUTH_PLUGIN;
2296       goto end;
2297     }
2298   }
2299   if (set_user_salt(auth, plugin))
2300   {
2301     res= ER_PASSWD_LENGTH;
2302     goto end;
2303   }
2304 
2305   res= 0;
2306 end:
2307   if (unlock_plugin)
2308     plugin_unlock(thd, plugin);
2309   return res;
2310 }
2311 
2312 
2313 /**
2314   Lazily computes user's salt from the password hash
2315 */
set_user_salt_if_needed(ACL_USER * user_copy,int curr_auth,plugin_ref plugin)2316 static bool set_user_salt_if_needed(ACL_USER *user_copy, int curr_auth,
2317                                     plugin_ref plugin)
2318 {
2319   ACL_USER::AUTH *auth_copy= user_copy->auth + curr_auth;
2320   DBUG_ASSERT(!strcasecmp(auth_copy->plugin.str, plugin_name(plugin)->str));
2321 
2322   if (auth_copy->salt.str)
2323     return 0; // already done
2324 
2325   if (set_user_salt(auth_copy, plugin))
2326     return 1;
2327 
2328   mysql_mutex_lock(&acl_cache->lock);
2329   ACL_USER *user= find_user_exact(user_copy->host.hostname, user_copy->user.str);
2330   // make sure the user wasn't altered or dropped meanwhile
2331   if (user)
2332   {
2333     ACL_USER::AUTH *auth= user->auth + curr_auth;
2334     if (!auth->salt.str && auth->plugin.length == auth_copy->plugin.length &&
2335         auth->auth_string.length == auth_copy->auth_string.length &&
2336         !memcmp(auth->plugin.str, auth_copy->plugin.str, auth->plugin.length) &&
2337         !memcmp(auth->auth_string.str, auth_copy->auth_string.str, auth->auth_string.length))
2338       auth->salt= auth_copy->salt;
2339   }
2340   mysql_mutex_unlock(&acl_cache->lock);
2341   return 0;
2342 }
2343 
2344 
2345 /**
2346   Fix ACL::plugin pointer to point to a hard-coded string, if appropriate
2347 
2348   Make sure that if ACL_USER's plugin is a built-in, then it points
2349   to a hard coded string, not to an allocated copy. Run-time, for
2350   authentication, we want to be able to detect built-ins by comparing
2351   pointers, not strings.
2352 
2353   @retval 0 the pointers were fixed
2354   @retval 1 this ACL_USER uses a not built-in plugin
2355 */
fix_user_plugin_ptr(ACL_USER::AUTH * auth)2356 static bool fix_user_plugin_ptr(ACL_USER::AUTH *auth)
2357 {
2358   if (lex_string_eq(&auth->plugin, &native_password_plugin_name))
2359     auth->plugin= native_password_plugin_name;
2360   else
2361   if (lex_string_eq(&auth->plugin, &old_password_plugin_name))
2362     auth->plugin= old_password_plugin_name;
2363   else
2364     return true;
2365   return false;
2366 }
2367 
2368 
get_YN_as_bool(Field * field)2369 static bool get_YN_as_bool(Field *field)
2370 {
2371   char buff[2];
2372   String res(buff,sizeof(buff),&my_charset_latin1);
2373   field->val_str(&res);
2374   return res[0] == 'Y' || res[0] == 'y';
2375 }
2376 
2377 
2378 /*
2379   Initialize structures responsible for user/db-level privilege checking and
2380   load privilege information for them from tables in the 'mysql' database.
2381 
2382   SYNOPSIS
2383     acl_init()
2384       dont_read_acl_tables  TRUE if we want to skip loading data from
2385                             privilege tables and disable privilege checking.
2386 
2387   NOTES
2388     This function is mostly responsible for preparatory steps, main work
2389     on initialization and grants loading is done in acl_reload().
2390 
2391   RETURN VALUES
2392     0	ok
2393     1	Could not initialize grant's
2394 */
2395 
acl_init(bool dont_read_acl_tables)2396 bool acl_init(bool dont_read_acl_tables)
2397 {
2398   THD  *thd;
2399   bool return_val;
2400   DBUG_ENTER("acl_init");
2401 
2402   acl_cache= new Hash_filo<acl_entry>(key_memory_acl_cache, ACL_CACHE_SIZE, 0, 0,
2403                            (my_hash_get_key) acl_entry_get_key,
2404                            (my_hash_free_key) my_free,
2405                            &my_charset_utf8mb3_bin);
2406 
2407   /*
2408     cache built-in native authentication plugins,
2409     to avoid hash searches and a global mutex lock on every connect
2410   */
2411   native_password_plugin= my_plugin_lock_by_name(0,
2412            &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
2413   old_password_plugin= my_plugin_lock_by_name(0,
2414            &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
2415 
2416   if (!native_password_plugin || !old_password_plugin)
2417     DBUG_RETURN(1);
2418 
2419   if (dont_read_acl_tables)
2420   {
2421     DBUG_RETURN(0); /* purecov: tested */
2422   }
2423 
2424   /*
2425     To be able to run this from boot, we allocate a temporary THD
2426   */
2427   if (!(thd=new THD(0)))
2428     DBUG_RETURN(1); /* purecov: inspected */
2429   thd->thread_stack= (char*) &thd;
2430   thd->store_globals();
2431   /*
2432     It is safe to call acl_reload() since acl_* arrays and hashes which
2433     will be freed there are global static objects and thus are initialized
2434     by zeros at startup.
2435   */
2436   return_val= acl_reload(thd);
2437   delete thd;
2438   DBUG_RETURN(return_val);
2439 }
2440 
push_new_user(const ACL_USER & user)2441 static void push_new_user(const ACL_USER &user)
2442 {
2443   push_dynamic(&acl_users, &user);
2444   if (!user.host.hostname ||
2445       (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
2446     allow_all_hosts=1;                  // Anyone can connect
2447 }
2448 
2449 
2450 /*
2451   Initialize structures responsible for user/db-level privilege checking
2452   and load information about grants from open privilege tables.
2453 
2454   SYNOPSIS
2455     acl_load()
2456       thd     Current thread
2457       tables  List containing open "mysql.host", "mysql.user",
2458               "mysql.db", "mysql.proxies_priv" and "mysql.roles_mapping"
2459               tables.
2460 
2461   RETURN VALUES
2462     FALSE  Success
2463     TRUE   Error
2464 */
2465 
acl_load(THD * thd,const Grant_tables & tables)2466 static bool acl_load(THD *thd, const Grant_tables& tables)
2467 {
2468   READ_RECORD read_record_info;
2469   bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
2470   char tmp_name[SAFE_NAME_LEN+1];
2471   Sql_mode_save old_mode_save(thd);
2472   DBUG_ENTER("acl_load");
2473 
2474   thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
2475 
2476   grant_version++; /* Privileges updated */
2477 
2478   const Host_table& host_table= tables.host_table();
2479   init_sql_alloc(key_memory_acl_mem, &acl_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
2480   if (host_table.table_exists()) // "host" table may not exist (e.g. in MySQL 5.6.7+)
2481   {
2482     if (host_table.init_read_record(&read_record_info))
2483       DBUG_RETURN(true);
2484     while (!(read_record_info.read_record()))
2485     {
2486       ACL_HOST host;
2487       update_hostname(&host.host, get_field(&acl_memroot, host_table.host()));
2488       host.db= get_field(&acl_memroot, host_table.db());
2489       if (lower_case_table_names && host.db)
2490       {
2491         /*
2492           convert db to lower case and give a warning if the db wasn't
2493           already in lower case
2494         */
2495         char *end = strnmov(tmp_name, host.db, sizeof(tmp_name));
2496         if (end >= tmp_name + sizeof(tmp_name))
2497         {
2498           sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), host.db);
2499           continue;
2500         }
2501         my_casedn_str(files_charset_info, host.db);
2502         if (strcmp(host.db, tmp_name) != 0)
2503           sql_print_warning("'host' entry '%s|%s' had database in mixed "
2504                             "case that has been forced to lowercase because "
2505                             "lower_case_table_names is set. It will not be "
2506                             "possible to remove this privilege using REVOKE.",
2507                             host.host.hostname, host.db);
2508       }
2509       host.access= host_table.get_access();
2510       host.access= fix_rights_for_db(host.access);
2511       host.sort= get_magic_sort("hd", host.host.hostname, host.db);
2512       if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
2513       {
2514         sql_print_warning("'host' entry '%s|%s' "
2515                         "ignored in --skip-name-resolve mode.",
2516                          safe_str(host.host.hostname),
2517                          safe_str(host.db));
2518         continue;
2519       }
2520 #ifndef TO_BE_REMOVED
2521       if (host_table.num_fields() == 8)
2522       {						// Without grant
2523         if (host.access & CREATE_ACL)
2524           host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
2525       }
2526 #endif
2527       (void) push_dynamic(&acl_hosts,(uchar*) &host);
2528     }
2529     my_qsort((uchar*) dynamic_element(&acl_hosts, 0, ACL_HOST*),
2530              acl_hosts.elements, sizeof(ACL_HOST),(qsort_cmp) acl_compare);
2531     end_read_record(&read_record_info);
2532   }
2533   freeze_size(&acl_hosts);
2534 
2535   const User_table& user_table= tables.user_table();
2536   if (user_table.init_read_record(&read_record_info))
2537     DBUG_RETURN(true);
2538 
2539   allow_all_hosts=0;
2540   while (!(read_record_info.read_record()))
2541   {
2542     ACL_USER user;
2543     bool is_role= FALSE;
2544     update_hostname(&user.host, user_table.get_host(&acl_memroot));
2545     char *username= safe_str(user_table.get_user(&acl_memroot));
2546     user.user.str= username;
2547     user.user.length= strlen(username);
2548 
2549     is_role= user_table.get_is_role();
2550 
2551     user.access= user_table.get_access();
2552 
2553     user.sort= get_magic_sort("hu", user.host.hostname, user.user.str);
2554     user.hostname_length= safe_strlen(user.host.hostname);
2555 
2556     my_init_dynamic_array(key_memory_acl_mem, &user.role_grants,
2557                           sizeof(ACL_ROLE *), 0, 8, MYF(0));
2558 
2559     user.account_locked= user_table.get_account_locked();
2560 
2561     user.password_expired= user_table.get_password_expired();
2562     user.password_last_changed= user_table.get_password_last_changed();
2563     user.password_lifetime= user_table.get_password_lifetime();
2564 
2565     if (is_role)
2566     {
2567       if (is_invalid_role_name(username))
2568       {
2569         thd->clear_error(); // the warning is still issued
2570         continue;
2571       }
2572 
2573       ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot);
2574       entry->role_grants = user.role_grants;
2575       my_init_dynamic_array(key_memory_acl_mem, &entry->parent_grantee,
2576                             sizeof(ACL_USER_BASE *), 0, 8, MYF(0));
2577       my_hash_insert(&acl_roles, (uchar *)entry);
2578 
2579       continue;
2580     }
2581     else
2582     {
2583       if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
2584       {
2585         sql_print_warning("'user' entry '%s@%s' "
2586                           "ignored in --skip-name-resolve mode.", user.user.str,
2587                           safe_str(user.host.hostname));
2588         continue;
2589       }
2590 
2591       if (user_table.get_auth(thd, &acl_memroot, &user))
2592         continue;
2593       for (uint i= 0; i < user.nauth; i++)
2594       {
2595         ACL_USER::AUTH *auth= user.auth + i;
2596         auth->salt= null_clex_str;
2597         fix_user_plugin_ptr(auth);
2598       }
2599 
2600       user.ssl_type=     user_table.get_ssl_type();
2601       user.ssl_cipher=   user_table.get_ssl_cipher(&acl_memroot);
2602       user.x509_issuer=  safe_str(user_table.get_x509_issuer(&acl_memroot));
2603       user.x509_subject= safe_str(user_table.get_x509_subject(&acl_memroot));
2604       user.user_resource.questions= (uint)user_table.get_max_questions();
2605       user.user_resource.updates= (uint)user_table.get_max_updates();
2606       user.user_resource.conn_per_hour= (uint)user_table.get_max_connections();
2607       if (user.user_resource.questions || user.user_resource.updates ||
2608           user.user_resource.conn_per_hour)
2609         mqh_used=1;
2610 
2611       user.user_resource.user_conn= (int)user_table.get_max_user_connections();
2612       user.user_resource.max_statement_time= user_table.get_max_statement_time();
2613 
2614       user.default_rolename.str= user_table.get_default_role(&acl_memroot);
2615       user.default_rolename.length= safe_strlen(user.default_rolename.str);
2616     }
2617     push_new_user(user);
2618   }
2619   rebuild_acl_users();
2620   end_read_record(&read_record_info);
2621   freeze_size(&acl_users);
2622 
2623   const Db_table& db_table= tables.db_table();
2624   if (db_table.init_read_record(&read_record_info))
2625     DBUG_RETURN(TRUE);
2626   while (!(read_record_info.read_record()))
2627   {
2628     ACL_DB db;
2629     char *db_name;
2630     db.user=safe_str(get_field(&acl_memroot, db_table.user()));
2631     const char *hostname= get_field(&acl_memroot, db_table.host());
2632     if (!hostname && find_acl_role(db.user))
2633       hostname= "";
2634     update_hostname(&db.host, hostname);
2635     db.db= db_name= get_field(&acl_memroot, db_table.db());
2636     if (!db.db)
2637     {
2638       sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
2639       continue;
2640     }
2641     if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
2642     {
2643       sql_print_warning("'db' entry '%s %s@%s' "
2644                         "ignored in --skip-name-resolve mode.",
2645 		        db.db, db.user, safe_str(db.host.hostname));
2646       continue;
2647     }
2648     db.access= db_table.get_access();
2649     db.access=fix_rights_for_db(db.access);
2650     db.initial_access= db.access;
2651     if (lower_case_table_names)
2652     {
2653       /*
2654         convert db to lower case and give a warning if the db wasn't
2655         already in lower case
2656       */
2657       char *end = strnmov(tmp_name, db.db, sizeof(tmp_name));
2658       if (end >= tmp_name + sizeof(tmp_name))
2659       {
2660         sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), db.db);
2661         continue;
2662       }
2663       my_casedn_str(files_charset_info, db_name);
2664       if (strcmp(db_name, tmp_name) != 0)
2665       {
2666         sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
2667                           "case that has been forced to lowercase because "
2668                           "lower_case_table_names is set. It will not be "
2669                           "possible to remove this privilege using REVOKE.",
2670 		          db.db, db.user, safe_str(db.host.hostname));
2671       }
2672     }
2673     db.sort=get_magic_sort("hdu", db.host.hostname, db.db, db.user);
2674 #ifndef TO_BE_REMOVED
2675     if (db_table.num_fields() <=  9)
2676     {						// Without grant
2677       if (db.access & CREATE_ACL)
2678 	db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
2679     }
2680 #endif
2681     acl_dbs.push(db);
2682   }
2683   end_read_record(&read_record_info);
2684   rebuild_acl_dbs();
2685   acl_dbs.freeze();
2686 
2687   const Proxies_priv_table& proxies_priv_table= tables.proxies_priv_table();
2688   if (proxies_priv_table.table_exists())
2689   {
2690     if (proxies_priv_table.init_read_record(&read_record_info))
2691       DBUG_RETURN(TRUE);
2692     while (!(read_record_info.read_record()))
2693     {
2694       ACL_PROXY_USER proxy;
2695       proxy.init(proxies_priv_table, &acl_memroot);
2696       if (proxy.check_validity(check_no_resolve))
2697         continue;
2698       if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
2699         DBUG_RETURN(TRUE);
2700     }
2701     my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
2702              acl_proxy_users.elements,
2703              sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
2704     end_read_record(&read_record_info);
2705   }
2706   else
2707   {
2708     sql_print_error("Missing system table mysql.proxies_priv; "
2709                     "please run mysql_upgrade to create it");
2710   }
2711   freeze_size(&acl_proxy_users);
2712 
2713   const Roles_mapping_table& roles_mapping_table= tables.roles_mapping_table();
2714   if (roles_mapping_table.table_exists())
2715   {
2716     if (roles_mapping_table.init_read_record(&read_record_info))
2717       DBUG_RETURN(TRUE);
2718 
2719     MEM_ROOT temp_root;
2720     init_alloc_root(key_memory_acl_mem, &temp_root, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
2721     while (!(read_record_info.read_record()))
2722     {
2723       char *hostname= safe_str(get_field(&temp_root, roles_mapping_table.host()));
2724       char *username= safe_str(get_field(&temp_root, roles_mapping_table.user()));
2725       char *rolename= safe_str(get_field(&temp_root, roles_mapping_table.role()));
2726       bool with_grant_option= get_YN_as_bool(roles_mapping_table.admin_option());
2727 
2728       if (add_role_user_mapping(username, hostname, rolename)) {
2729         sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'",
2730                         username, hostname, rolename);
2731         continue;
2732       }
2733 
2734       ROLE_GRANT_PAIR *mapping= new (&acl_memroot) ROLE_GRANT_PAIR;
2735 
2736       if (mapping->init(&acl_memroot, username, hostname, rolename, with_grant_option))
2737         continue;
2738 
2739       my_hash_insert(&acl_roles_mappings, (uchar*) mapping);
2740     }
2741 
2742     free_root(&temp_root, MYF(0));
2743     end_read_record(&read_record_info);
2744   }
2745   else
2746   {
2747     sql_print_error("Missing system table mysql.roles_mapping; "
2748                     "please run mysql_upgrade to create it");
2749   }
2750 
2751   init_check_host();
2752 
2753   thd->bootstrap= !initialized; // keep FLUSH PRIVILEGES connection special
2754   initialized=1;
2755   DBUG_RETURN(FALSE);
2756 }
2757 
2758 
acl_free(bool end)2759 void acl_free(bool end)
2760 {
2761   my_hash_free(&acl_roles);
2762   free_root(&acl_memroot,MYF(0));
2763   delete_dynamic(&acl_hosts);
2764   delete_dynamic_with_callback(&acl_users, (FREE_FUNC) free_acl_user);
2765   acl_dbs.free_memory();
2766   delete_dynamic(&acl_wild_hosts);
2767   delete_dynamic(&acl_proxy_users);
2768   my_hash_free(&acl_check_hosts);
2769   my_hash_free(&acl_roles_mappings);
2770   if (!end)
2771     acl_cache->clear(1); /* purecov: inspected */
2772   else
2773   {
2774     plugin_unlock(0, native_password_plugin);
2775     plugin_unlock(0, old_password_plugin);
2776     delete acl_cache;
2777     acl_cache=0;
2778   }
2779 }
2780 
2781 
2782 /*
2783   Forget current user/db-level privileges and read new privileges
2784   from the privilege tables.
2785 
2786   SYNOPSIS
2787     acl_reload()
2788       thd  Current thread
2789 
2790   NOTE
2791     All tables of calling thread which were open and locked by LOCK TABLES
2792     statement will be unlocked and closed.
2793     This function is also used for initialization of structures responsible
2794     for user/db-level privilege checking.
2795 
2796   RETURN VALUE
2797     FALSE  Success
2798     TRUE   Failure
2799 */
2800 
acl_reload(THD * thd)2801 bool acl_reload(THD *thd)
2802 {
2803   DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_proxy_users;
2804   Dynamic_array<ACL_DB> old_acl_dbs(0U,0U);
2805   HASH old_acl_roles, old_acl_roles_mappings;
2806   MEM_ROOT old_mem;
2807   int result;
2808   DBUG_ENTER("acl_reload");
2809 
2810   Grant_tables tables;
2811   /*
2812     To avoid deadlocks we should obtain table locks before
2813     obtaining acl_cache->lock mutex.
2814   */
2815   const uint tables_to_open= Table_host | Table_user | Table_db |
2816                              Table_proxies_priv | Table_roles_mapping;
2817   if ((result= tables.open_and_lock(thd, tables_to_open, TL_READ)))
2818   {
2819     DBUG_ASSERT(result <= 0);
2820     /*
2821       Execution might have been interrupted; only print the error message
2822       if an error condition has been raised.
2823     */
2824     if (thd->get_stmt_da()->is_error())
2825       sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
2826                       thd->get_stmt_da()->message());
2827     goto end;
2828   }
2829 
2830   acl_cache->clear(0);
2831   mysql_mutex_lock(&acl_cache->lock);
2832 
2833   old_acl_hosts= acl_hosts;
2834   old_acl_users= acl_users;
2835   old_acl_roles= acl_roles;
2836   old_acl_roles_mappings= acl_roles_mappings;
2837   old_acl_proxy_users= acl_proxy_users;
2838   old_acl_dbs= acl_dbs;
2839   my_init_dynamic_array(key_memory_acl_mem, &acl_hosts, sizeof(ACL_HOST), 20, 50, MYF(0));
2840   my_init_dynamic_array(key_memory_acl_mem, &acl_users, sizeof(ACL_USER), 50, 100, MYF(0));
2841   acl_dbs.init(key_memory_acl_mem, 50, 100);
2842   my_init_dynamic_array(key_memory_acl_mem, &acl_proxy_users, sizeof(ACL_PROXY_USER), 50, 100, MYF(0));
2843   my_hash_init2(key_memory_acl_mem, &acl_roles,50, &my_charset_utf8mb3_bin,
2844                 0, 0, 0, (my_hash_get_key) acl_role_get_key, 0,
2845                 (void (*)(void *))free_acl_role, 0);
2846   my_hash_init2(key_memory_acl_mem, &acl_roles_mappings, 50,
2847                 &my_charset_utf8mb3_bin, 0, 0, 0, (my_hash_get_key)
2848                 acl_role_map_get_key, 0, 0, 0);
2849   old_mem= acl_memroot;
2850   delete_dynamic(&acl_wild_hosts);
2851   my_hash_free(&acl_check_hosts);
2852 
2853   if ((result= acl_load(thd, tables)))
2854   {					// Error. Revert to old list
2855     DBUG_PRINT("error",("Reverting to old privileges"));
2856     acl_free();				/* purecov: inspected */
2857     acl_hosts= old_acl_hosts;
2858     acl_users= old_acl_users;
2859     acl_roles= old_acl_roles;
2860     acl_roles_mappings= old_acl_roles_mappings;
2861     acl_proxy_users= old_acl_proxy_users;
2862     acl_dbs= old_acl_dbs;
2863     old_acl_dbs.init(0,0);
2864     acl_memroot= old_mem;
2865     init_check_host();
2866   }
2867   else
2868   {
2869     my_hash_free(&old_acl_roles);
2870     free_root(&old_mem,MYF(0));
2871     delete_dynamic(&old_acl_hosts);
2872     delete_dynamic_with_callback(&old_acl_users, (FREE_FUNC) free_acl_user);
2873     delete_dynamic(&old_acl_proxy_users);
2874     my_hash_free(&old_acl_roles_mappings);
2875   }
2876   mysql_mutex_unlock(&acl_cache->lock);
2877 end:
2878   close_mysql_tables(thd);
2879   DBUG_RETURN(result);
2880 }
2881 
2882 /*
2883   Get all access bits from table after fieldnr
2884 
2885   IMPLEMENTATION
2886   We know that the access privileges ends when there is no more fields
2887   or the field is not an enum with two elements.
2888 
2889   SYNOPSIS
2890     get_access()
2891     form        an open table to read privileges from.
2892                 The record should be already read in table->record[0]
2893     fieldnr     number of the first privilege (that is ENUM('N','Y') field
2894     next_field  on return - number of the field next to the last ENUM
2895                 (unless next_field == 0)
2896 
2897   RETURN VALUE
2898     privilege mask
2899 */
2900 
get_access(TABLE * form,uint fieldnr,uint * next_field)2901 static privilege_t get_access(TABLE *form, uint fieldnr, uint *next_field)
2902 {
2903   ulonglong access_bits=0,bit;
2904   char buff[2];
2905   String res(buff,sizeof(buff),&my_charset_latin1);
2906   Field **pos;
2907 
2908   for (pos=form->field+fieldnr, bit=1;
2909        *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
2910 	 ((Field_enum*) (*pos))->typelib->count == 2 ;
2911        pos++, fieldnr++, bit<<=1)
2912   {
2913     if (get_YN_as_bool(*pos))
2914       access_bits|= bit;
2915   }
2916   if (next_field)
2917     *next_field=fieldnr;
2918   return ALL_KNOWN_ACL & access_bits;
2919 }
2920 
2921 
acl_compare(const ACL_ACCESS * a,const ACL_ACCESS * b)2922 static int acl_compare(const ACL_ACCESS *a, const ACL_ACCESS *b)
2923 {
2924   if (a->sort > b->sort)
2925     return -1;
2926   if (a->sort < b->sort)
2927     return 1;
2928   return 0;
2929 }
2930 
acl_user_compare(const ACL_USER * a,const ACL_USER * b)2931 static int acl_user_compare(const ACL_USER *a, const ACL_USER *b)
2932 {
2933   int res= strcmp(a->user.str, b->user.str);
2934   if (res)
2935     return res;
2936 
2937   res= acl_compare(a, b);
2938   if (res)
2939     return res;
2940 
2941   /*
2942     For more deterministic results, resolve ambiguity between
2943     "localhost" and "127.0.0.1"/"::1" by sorting "localhost" before
2944     loopback addresses.
2945     Test suite (on Windows) expects "root@localhost", even if
2946     root@::1 would also match.
2947   */
2948   return -strcmp(a->host.hostname, b->host.hostname);
2949 }
2950 
acl_db_compare(const ACL_DB * a,const ACL_DB * b)2951 static int acl_db_compare(const ACL_DB *a, const ACL_DB *b)
2952 {
2953   int res= strcmp(a->user, b->user);
2954   if (res)
2955     return res;
2956 
2957   return acl_compare(a, b);
2958 }
2959 
rebuild_acl_users()2960 static void rebuild_acl_users()
2961 {
2962    my_qsort((uchar*)dynamic_element(&acl_users, 0, ACL_USER*), acl_users.elements,
2963      sizeof(ACL_USER), (qsort_cmp)acl_user_compare);
2964 }
2965 
rebuild_acl_dbs()2966 static void rebuild_acl_dbs()
2967 {
2968   acl_dbs.sort(acl_db_compare);
2969 }
2970 
2971 
2972 /*
2973   Return index of the first entry with given user in the array,
2974   or SIZE_T_MAX if not found.
2975 
2976   Assumes the array is sorted by get_username
2977 */
find_first_user(T * arr,size_t len,const char * user)2978 template<typename T> size_t find_first_user(T* arr, size_t len, const char *user)
2979 {
2980   size_t low= 0;
2981   size_t high= len;
2982   size_t mid;
2983 
2984   bool found= false;
2985   if(!len)
2986     return  SIZE_T_MAX;
2987 
2988 #ifndef DBUG_OFF
2989   for (uint i = 0; i < len - 1; i++)
2990     DBUG_ASSERT(strcmp(arr[i].get_username(), arr[i + 1].get_username()) <= 0);
2991 #endif
2992   while (low < high)
2993   {
2994     mid= low + (high - low) / 2;
2995     int cmp= strcmp(arr[mid].get_username(),user);
2996     if (cmp == 0)
2997       found= true;
2998 
2999     if (cmp >= 0 )
3000       high= mid;
3001     else
3002       low= mid + 1;
3003   }
3004   return  (!found || low == len || strcmp(arr[low].get_username(), user)!=0 )?SIZE_T_MAX:low;
3005 }
3006 
acl_find_user_by_name(const char * user)3007 static size_t acl_find_user_by_name(const char *user)
3008 {
3009   return find_first_user<ACL_USER>((ACL_USER *)acl_users.buffer,acl_users.elements,user);
3010 }
3011 
acl_find_db_by_username(const char * user)3012 static size_t acl_find_db_by_username(const char *user)
3013 {
3014   return find_first_user<ACL_DB>(acl_dbs.front(), acl_dbs.elements(), user);
3015 }
3016 
match_db(ACL_DB * acl_db,const char * db,my_bool db_is_pattern)3017 static bool match_db(ACL_DB *acl_db, const char *db, my_bool db_is_pattern)
3018 {
3019   return !acl_db->db || (db && !wild_compare(db, acl_db->db, db_is_pattern));
3020 }
3021 
3022 
3023 /*
3024   Lookup in the acl_users or acl_dbs for the best matching entry corresponding to
3025   given user, host and ip parameters (also db, in case of ACL_DB)
3026 
3027   Historical note:
3028 
3029   In the past, both arrays were sorted just by ACL_ENTRY::sort field and were
3030   searched linearly, until the first match of (username,host) pair was found.
3031 
3032   This function uses optimizations (binary search by username), yet preserves the
3033   historical behavior, i.e the returns a match with highest ACL_ENTRY::sort.
3034 */
find_by_username_or_anon(T * arr,size_t len,const char * user,const char * host,const char * ip,const char * db,my_bool db_is_pattern,bool (* match_db_func)(T *,const char *,my_bool))3035 template <typename T> T* find_by_username_or_anon(T* arr, size_t len, const char *user,
3036   const char *host, const char *ip,
3037   const char *db, my_bool db_is_pattern, bool (*match_db_func)(T*,const char *,my_bool))
3038 {
3039   size_t i;
3040   T *ret = NULL;
3041 
3042   // Check  entries matching user name.
3043   size_t start = find_first_user(arr, len, user);
3044   for (i= start; i < len; i++)
3045   {
3046     T *entry= &arr[i];
3047     if (i > start && strcmp(user, entry->get_username()))
3048       break;
3049 
3050     if (compare_hostname(&entry->host, host, ip) && (!match_db_func || match_db_func(entry, db, db_is_pattern)))
3051     {
3052       ret= entry;
3053       break;
3054     }
3055   }
3056 
3057   // Look also for anonymous user (username is empty string)
3058   // Due to sort by name, entries for anonymous user start at the start of array.
3059   for (i= 0; i < len; i++)
3060   {
3061     T *entry = &arr[i];
3062     if (*entry->get_username() || (ret && acl_compare(entry, ret) >= 0))
3063       break;
3064     if (compare_hostname(&entry->host, host, ip) && (!match_db_func || match_db_func(entry, db, db_is_pattern)))
3065     {
3066       ret= entry;
3067       break;
3068     }
3069   }
3070   return ret;
3071 }
3072 
acl_db_find(const char * db,const char * user,const char * host,const char * ip,my_bool db_is_pattern)3073 static ACL_DB *acl_db_find(const char *db, const char *user, const char *host, const char *ip, my_bool db_is_pattern)
3074 {
3075   return find_by_username_or_anon(acl_dbs.front(), acl_dbs.elements(),
3076                                   user, host, ip, db, db_is_pattern, match_db);
3077 }
3078 
3079 
3080 /*
3081   Gets user credentials without authentication and resource limit checks.
3082 
3083   SYNOPSIS
3084     acl_getroot()
3085       sctx               Context which should be initialized
3086       user               user name
3087       host               host name
3088       ip                 IP
3089       db                 current data base name
3090 
3091   RETURN
3092     FALSE  OK
3093     TRUE   Error
3094 */
3095 
acl_getroot(Security_context * sctx,const char * user,const char * host,const char * ip,const char * db)3096 bool acl_getroot(Security_context *sctx, const char *user, const char *host,
3097                  const char *ip, const char *db)
3098 {
3099   int res= 1;
3100   ACL_USER *acl_user= 0;
3101   DBUG_ENTER("acl_getroot");
3102 
3103   DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
3104                        host, ip, user, db));
3105   sctx->init();
3106   sctx->user= *user ? user : NULL;
3107   sctx->host= host;
3108   sctx->ip= ip;
3109   sctx->host_or_ip= host ? host : (safe_str(ip));
3110 
3111   if (!initialized)
3112   {
3113     /*
3114       here if mysqld's been started with --skip-grant-tables option.
3115     */
3116     sctx->skip_grants();
3117     DBUG_RETURN(FALSE);
3118   }
3119 
3120   mysql_mutex_lock(&acl_cache->lock);
3121 
3122   sctx->db_access= NO_ACL;
3123 
3124   if (host[0]) // User, not Role
3125   {
3126     acl_user= find_user_wild(host, user, ip);
3127 
3128     if (acl_user)
3129     {
3130       res= 0;
3131       if (ACL_DB *acl_db= acl_db_find(db, user, host, ip, FALSE))
3132         sctx->db_access= acl_db->access;
3133 
3134       sctx->master_access= acl_user->access;
3135 
3136       strmake_buf(sctx->priv_user, user);
3137 
3138       if (acl_user->host.hostname)
3139         strmake_buf(sctx->priv_host, acl_user->host.hostname);
3140     }
3141   }
3142   else // Role, not User
3143   {
3144     ACL_ROLE *acl_role= find_acl_role(user);
3145     if (acl_role)
3146     {
3147       res= 0;
3148       if (ACL_DB *acl_db= acl_db_find(db, user, "", "", FALSE))
3149         sctx->db_access = acl_db->access;
3150 
3151       sctx->master_access= acl_role->access;
3152 
3153       strmake_buf(sctx->priv_role, user);
3154     }
3155   }
3156 
3157   mysql_mutex_unlock(&acl_cache->lock);
3158   DBUG_RETURN(res);
3159 }
3160 
check_role_is_granted_callback(ACL_USER_BASE * grantee,void * data)3161 static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data)
3162 {
3163   LEX_CSTRING *rolename= static_cast<LEX_CSTRING *>(data);
3164   if (rolename->length == grantee->user.length &&
3165       !strcmp(rolename->str, grantee->user.str))
3166     return -1; // End search, we've found our role.
3167 
3168   /* Keep looking, we haven't found our role yet. */
3169   return 0;
3170 }
3171 
3172 /*
3173   unlike find_user_exact and find_user_wild,
3174   this function finds anonymous users too, it's when a
3175   user is not empty, but priv_user (acl_user->user) is empty.
3176 */
find_user_or_anon(const char * host,const char * user,const char * ip)3177 static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip)
3178 {
3179   return find_by_username_or_anon<ACL_USER>
3180     (reinterpret_cast<ACL_USER*>(acl_users.buffer), acl_users.elements,
3181      user, host, ip, NULL, FALSE, NULL);
3182 }
3183 
3184 
check_user_can_set_role(THD * thd,const char * user,const char * host,const char * ip,const char * rolename,privilege_t * access)3185 static int check_user_can_set_role(THD *thd, const char *user,
3186                                    const char *host, const char *ip,
3187                                    const char *rolename, privilege_t *access)
3188 {
3189   ACL_ROLE *role;
3190   ACL_USER_BASE *acl_user_base;
3191   ACL_USER *UNINIT_VAR(acl_user);
3192   bool is_granted= FALSE;
3193   int result= 0;
3194 
3195   /* clear role privileges */
3196   mysql_mutex_lock(&acl_cache->lock);
3197 
3198   if (!strcasecmp(rolename, "NONE"))
3199   {
3200     /* have to clear the privileges */
3201     /* get the current user */
3202     acl_user= find_user_wild(host, user, ip);
3203     if (acl_user == NULL)
3204       result= ER_INVALID_CURRENT_USER;
3205     else if (access)
3206       *access= acl_user->access;
3207 
3208     goto end;
3209   }
3210 
3211   role= find_acl_role(rolename);
3212 
3213   /* According to SQL standard, the same error message must be presented */
3214   if (role == NULL)
3215   {
3216     result= ER_INVALID_ROLE;
3217     goto end;
3218   }
3219 
3220   for (uint i=0 ; i < role->parent_grantee.elements ; i++)
3221   {
3222     acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**));
3223     if (acl_user_base->flags & IS_ROLE)
3224       continue;
3225 
3226     acl_user= (ACL_USER *)acl_user_base;
3227     if (acl_user->wild_eq(user, host, ip))
3228     {
3229       is_granted= TRUE;
3230       break;
3231     }
3232   }
3233 
3234   /* According to SQL standard, the same error message must be presented */
3235   if (!is_granted)
3236   {
3237     result= 1;
3238     goto end;
3239   }
3240 
3241   if (access)
3242   {
3243     *access = acl_user->access | role->access;
3244   }
3245 
3246 end:
3247   mysql_mutex_unlock(&acl_cache->lock);
3248 
3249   /* We present different error messages depending if the user has sufficient
3250      privileges to know if the INVALID_ROLE exists. */
3251   switch (result)
3252   {
3253     case ER_INVALID_CURRENT_USER:
3254       my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename);
3255       break;
3256     case ER_INVALID_ROLE:
3257       /* Role doesn't exist at all */
3258       my_error(ER_INVALID_ROLE, MYF(0), rolename);
3259       break;
3260     case 1:
3261       LEX_CSTRING role_lex;
3262       /* First, check if current user can see mysql database. */
3263       bool read_access= !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 1);
3264 
3265       role_lex.str= rolename;
3266       role_lex.length= strlen(rolename);
3267       mysql_mutex_lock(&acl_cache->lock);
3268       ACL_USER *cur_user= find_user_or_anon(thd->security_ctx->priv_host,
3269                                             thd->security_ctx->priv_user,
3270                                             thd->security_ctx->ip);
3271 
3272       /* If the current user does not have select priv to mysql database,
3273          see if the current user can discover the role if it was granted to him.
3274       */
3275       if (cur_user && (read_access ||
3276                        traverse_role_graph_down(cur_user, &role_lex,
3277                                                 check_role_is_granted_callback,
3278                                                 NULL) == -1))
3279       {
3280         /* Role is not granted but current user can see the role */
3281         my_printf_error(ER_INVALID_ROLE, "User %`s@%`s has not been granted role %`s",
3282                         MYF(0), thd->security_ctx->priv_user,
3283                         thd->security_ctx->priv_host, rolename);
3284       }
3285       else
3286       {
3287         /* Role is not granted and current user cannot see the role */
3288         my_error(ER_INVALID_ROLE, MYF(0), rolename);
3289       }
3290       mysql_mutex_unlock(&acl_cache->lock);
3291       break;
3292   }
3293 
3294   return result;
3295 }
3296 
3297 
acl_check_setrole(THD * thd,const char * rolename,privilege_t * access)3298 int acl_check_setrole(THD *thd, const char *rolename, privilege_t *access)
3299 {
3300   if (!initialized)
3301   {
3302     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3303     return 1;
3304   }
3305 
3306   return check_user_can_set_role(thd, thd->security_ctx->priv_user,
3307            thd->security_ctx->host, thd->security_ctx->ip, rolename, access);
3308 }
3309 
3310 
acl_setrole(THD * thd,const char * rolename,privilege_t access)3311 int acl_setrole(THD *thd, const char *rolename, privilege_t access)
3312 {
3313   /* merge the privileges */
3314   Security_context *sctx= thd->security_ctx;
3315   sctx->master_access= access;
3316   if (thd->db.str)
3317     sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db.str, FALSE);
3318 
3319   if (!strcasecmp(rolename, "NONE"))
3320   {
3321     thd->security_ctx->priv_role[0]= 0;
3322   }
3323   else
3324   {
3325     if (thd->db.str)
3326       sctx->db_access|= acl_get("", "", rolename, thd->db.str, FALSE);
3327     /* mark the current role */
3328     strmake_buf(thd->security_ctx->priv_role, rolename);
3329   }
3330   return 0;
3331 }
3332 
check_get_key(ACL_USER * buff,size_t * length,my_bool not_used)3333 static uchar* check_get_key(ACL_USER *buff, size_t *length,
3334                             my_bool not_used __attribute__((unused)))
3335 {
3336   *length=buff->hostname_length;
3337   return (uchar*) buff->host.hostname;
3338 }
3339 
3340 
acl_update_role(const char * rolename,const privilege_t privileges)3341 static void acl_update_role(const char *rolename, const privilege_t privileges)
3342 {
3343   ACL_ROLE *role= find_acl_role(rolename);
3344   if (role)
3345     role->initial_role_access= role->access= privileges;
3346 }
3347 
3348 
ACL_USER(THD * thd,const LEX_USER & combo,const Account_options & options,const privilege_t privileges)3349 ACL_USER::ACL_USER(THD *thd, const LEX_USER &combo,
3350                    const Account_options &options,
3351                    const privilege_t privileges)
3352 {
3353   user= safe_lexcstrdup_root(&acl_memroot, combo.user);
3354   update_hostname(&host, safe_strdup_root(&acl_memroot, combo.host.str));
3355   hostname_length= combo.host.length;
3356   sort= get_magic_sort("hu", host.hostname, user.str);
3357   password_last_changed= thd->query_start();
3358   password_lifetime= -1;
3359   my_init_dynamic_array(PSI_INSTRUMENT_ME, &role_grants, sizeof(ACL_USER *), 0, 8, MYF(0));
3360 }
3361 
3362 
acl_user_update(THD * thd,ACL_USER * acl_user,uint nauth,const LEX_USER & combo,const Account_options & options,const privilege_t privileges)3363 static int acl_user_update(THD *thd, ACL_USER *acl_user, uint nauth,
3364                            const LEX_USER &combo,
3365                            const Account_options &options,
3366                            const privilege_t privileges)
3367 {
3368   ACL_USER_PARAM::AUTH *work_copy= NULL;
3369   if (nauth)
3370   {
3371     if (!(work_copy= (ACL_USER_PARAM::AUTH*)
3372             alloc_root(thd->mem_root, nauth * sizeof(ACL_USER_PARAM::AUTH))))
3373       return 1;
3374 
3375     USER_AUTH *auth= combo.auth;
3376     for (uint i= 0; i < nauth; i++, auth= auth->next)
3377     {
3378       work_copy[i].plugin= auth->plugin;
3379       work_copy[i].auth_string= safe_lexcstrdup_root(&acl_memroot,
3380                                                      auth->auth_str);
3381       if (fix_user_plugin_ptr(work_copy + i))
3382         work_copy[i].plugin= safe_lexcstrdup_root(&acl_memroot, auth->plugin);
3383       if (set_user_auth(thd, acl_user->user, work_copy + i, auth->pwtext))
3384         return 1;
3385     }
3386   }
3387 
3388   acl_user->access= privileges;
3389   if (options.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
3390     acl_user->user_resource.questions= options.questions;
3391   if (options.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
3392     acl_user->user_resource.updates= options.updates;
3393   if (options.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
3394     acl_user->user_resource.conn_per_hour= options.conn_per_hour;
3395   if (options.specified_limits & USER_RESOURCES::USER_CONNECTIONS)
3396     acl_user->user_resource.user_conn= options.user_conn;
3397   if (options.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
3398     acl_user->user_resource.max_statement_time= options.max_statement_time;
3399   if (options.ssl_type != SSL_TYPE_NOT_SPECIFIED)
3400   {
3401     acl_user->ssl_type= options.ssl_type;
3402     acl_user->ssl_cipher= safe_strdup_root(&acl_memroot, options.ssl_cipher.str);
3403     acl_user->x509_issuer= safe_strdup_root(&acl_memroot,
3404                                             safe_str(options.x509_issuer.str));
3405     acl_user->x509_subject= safe_strdup_root(&acl_memroot,
3406                                              safe_str(options.x509_subject.str));
3407   }
3408   if (options.account_locked != ACCOUNTLOCK_UNSPECIFIED)
3409     acl_user->account_locked= options.account_locked == ACCOUNTLOCK_LOCKED;
3410 
3411   if (thd->is_error())
3412   {
3413     // If something went wrong (including OOM) we will not spoil acl cache
3414     return 1;
3415   }
3416   /* Unexpire the user password and copy AUTH (when no more errors possible)*/
3417   if (nauth)
3418   {
3419     acl_user->password_expired= false;
3420     acl_user->password_last_changed= thd->query_start();
3421 
3422     if (acl_user->nauth >= nauth)
3423     {
3424       acl_user->nauth= nauth;
3425     }
3426     else
3427     {
3428       if (acl_user->alloc_auth(&acl_memroot, nauth))
3429       {
3430         /*
3431           acl_user is a copy, so NULL assigned in case of an error do not
3432           change the acl cache
3433         */
3434         return 1;
3435       }
3436     }
3437     DBUG_ASSERT(work_copy); // allocated under the same condinition
3438     memcpy(acl_user->auth, work_copy,  nauth * sizeof(ACL_USER_PARAM::AUTH));
3439   }
3440 
3441   switch (options.password_expire) {
3442   case PASSWORD_EXPIRE_UNSPECIFIED:
3443     break;
3444   case PASSWORD_EXPIRE_NOW:
3445     acl_user->password_expired= true;
3446     break;
3447   case PASSWORD_EXPIRE_NEVER:
3448     acl_user->password_lifetime= 0;
3449     break;
3450   case PASSWORD_EXPIRE_DEFAULT:
3451     acl_user->password_lifetime= -1;
3452     break;
3453   case PASSWORD_EXPIRE_INTERVAL:
3454     acl_user->password_lifetime= options.num_expiration_days;
3455     break;
3456   }
3457 
3458   return 0;
3459 }
3460 
3461 
acl_insert_role(const char * rolename,privilege_t privileges)3462 static void acl_insert_role(const char *rolename, privilege_t privileges)
3463 {
3464   ACL_ROLE *entry;
3465 
3466   mysql_mutex_assert_owner(&acl_cache->lock);
3467   entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot);
3468   my_init_dynamic_array(key_memory_acl_mem, &entry->parent_grantee,
3469                         sizeof(ACL_USER_BASE *), 0, 8, MYF(0));
3470   my_init_dynamic_array(key_memory_acl_mem, &entry->role_grants,
3471                         sizeof(ACL_ROLE *), 0, 8, MYF(0));
3472 
3473   my_hash_insert(&acl_roles, (uchar *)entry);
3474 }
3475 
3476 
acl_update_db(const char * user,const char * host,const char * db,privilege_t privileges)3477 static bool acl_update_db(const char *user, const char *host, const char *db,
3478                           privilege_t privileges)
3479 {
3480   mysql_mutex_assert_owner(&acl_cache->lock);
3481 
3482   bool updated= false;
3483 
3484   for (size_t i= acl_find_db_by_username(user); i < acl_dbs.elements(); i++)
3485   {
3486     ACL_DB *acl_db= &acl_dbs.at(i);
3487     if (!strcmp(user,acl_db->user))
3488     {
3489       if ((!acl_db->host.hostname && !host[0]) ||
3490           (acl_db->host.hostname && !strcmp(host, acl_db->host.hostname)))
3491       {
3492         if ((!acl_db->db && !db[0]) ||
3493             (acl_db->db && !strcmp(db,acl_db->db)))
3494 
3495         {
3496           if (privileges)
3497           {
3498             acl_db->access= privileges;
3499             acl_db->initial_access= acl_db->access;
3500           }
3501           else
3502             acl_dbs.del(i);
3503           updated= true;
3504         }
3505       }
3506     }
3507     else
3508      break;
3509   }
3510 
3511   return updated;
3512 }
3513 
3514 
3515 /*
3516   Insert a user/db/host combination into the global acl_cache
3517 
3518   SYNOPSIS
3519     acl_insert_db()
3520     user		User name
3521     host		Host name
3522     db			Database name
3523     privileges		Bitmap of privileges
3524 
3525   NOTES
3526     acl_cache->lock must be locked when calling this
3527 */
3528 
acl_insert_db(const char * user,const char * host,const char * db,const privilege_t privileges)3529 static void acl_insert_db(const char *user, const char *host, const char *db,
3530                           const privilege_t privileges)
3531 {
3532   ACL_DB acl_db;
3533   mysql_mutex_assert_owner(&acl_cache->lock);
3534   acl_db.user=strdup_root(&acl_memroot,user);
3535   update_hostname(&acl_db.host, safe_strdup_root(&acl_memroot, host));
3536   acl_db.db=strdup_root(&acl_memroot,db);
3537   acl_db.initial_access= acl_db.access= privileges;
3538   acl_db.sort=get_magic_sort("hdu", acl_db.host.hostname, acl_db.db, acl_db.user);
3539   acl_dbs.push(acl_db);
3540   rebuild_acl_dbs();
3541 }
3542 
3543 
3544 /*
3545   Get privilege for a host, user and db combination
3546 
3547   as db_is_pattern changes the semantics of comparison,
3548   acl_cache is not used if db_is_pattern is set.
3549 */
3550 
acl_get(const char * host,const char * ip,const char * user,const char * db,my_bool db_is_pattern)3551 privilege_t acl_get(const char *host, const char *ip,
3552                     const char *user, const char *db, my_bool db_is_pattern)
3553 {
3554   privilege_t host_access(ALL_KNOWN_ACL), db_access(NO_ACL);
3555   uint i;
3556   size_t key_length;
3557   char key[ACL_KEY_LENGTH],*tmp_db,*end;
3558   acl_entry *entry;
3559   DBUG_ENTER("acl_get");
3560 
3561   tmp_db= strmov(strmov(key, safe_str(ip)) + 1, user) + 1;
3562   end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db);
3563 
3564   if (end >= key + sizeof(key)) // db name was truncated
3565     DBUG_RETURN(NO_ACL);        // no privileges for an invalid db name
3566 
3567   if (lower_case_table_names)
3568   {
3569     my_casedn_str(files_charset_info, tmp_db);
3570     db=tmp_db;
3571   }
3572   key_length= (size_t) (end-key);
3573 
3574   mysql_mutex_lock(&acl_cache->lock);
3575   if (!db_is_pattern && (entry=acl_cache->search((uchar*) key, key_length)))
3576   {
3577     db_access=entry->access;
3578     mysql_mutex_unlock(&acl_cache->lock);
3579     DBUG_PRINT("exit", ("access: 0x%llx",  (longlong) db_access));
3580     DBUG_RETURN(db_access);
3581   }
3582 
3583   /*
3584     Check if there are some access rights for database and user
3585   */
3586   if (ACL_DB *acl_db= acl_db_find(db,user, host, ip, db_is_pattern))
3587   {
3588     db_access= acl_db->access;
3589     if (acl_db->host.hostname)
3590       goto exit; // Fully specified. Take it
3591     /* the host table is not used for roles */
3592     if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user))
3593       goto exit;
3594   }
3595 
3596   if (!db_access)
3597     goto exit;					// Can't be better
3598 
3599   /*
3600     No host specified for user. Get hostdata from host table
3601   */
3602   host_access= NO_ACL;                          // Host must be found
3603   for (i=0 ; i < acl_hosts.elements ; i++)
3604   {
3605     ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
3606     if (compare_hostname(&acl_host->host,host,ip))
3607     {
3608       if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
3609       {
3610 	host_access=acl_host->access;		// Fully specified. Take it
3611 	break;
3612       }
3613     }
3614   }
3615 exit:
3616   /* Save entry in cache for quick retrieval */
3617   if (!db_is_pattern &&
3618       (entry= (acl_entry*) my_malloc(key_memory_acl_cache,
3619                                      sizeof(acl_entry)+key_length, MYF(MY_WME))))
3620   {
3621     entry->access=(db_access & host_access);
3622     DBUG_ASSERT(key_length < 0xffff);
3623     entry->length=(uint16)key_length;
3624     memcpy((uchar*) entry->key,key,key_length);
3625     acl_cache->add(entry);
3626   }
3627   mysql_mutex_unlock(&acl_cache->lock);
3628   DBUG_PRINT("exit", ("access: 0x%llx", (longlong) (db_access & host_access)));
3629   DBUG_RETURN(db_access & host_access);
3630 }
3631 
3632 /*
3633   Check if there are any possible matching entries for this host
3634 
3635   NOTES
3636     All host names without wild cards are stored in a hash table,
3637     entries with wildcards are stored in a dynamic array
3638 */
3639 
init_check_host(void)3640 static void init_check_host(void)
3641 {
3642   DBUG_ENTER("init_check_host");
3643   (void) my_init_dynamic_array(key_memory_acl_mem, &acl_wild_hosts,
3644                                sizeof(struct acl_host_and_ip),
3645                                acl_users.elements, 1, MYF(0));
3646   (void) my_hash_init(key_memory_acl_mem, &acl_check_hosts,system_charset_info,
3647                       acl_users.elements, 0, 0,
3648                       (my_hash_get_key) check_get_key, 0, 0);
3649   if (!allow_all_hosts)
3650   {
3651     for (uint i=0 ; i < acl_users.elements ; i++)
3652     {
3653       ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
3654       if (strchr(acl_user->host.hostname,wild_many) ||
3655 	  strchr(acl_user->host.hostname,wild_one) ||
3656 	  acl_user->host.ip_mask)
3657       {						// Has wildcard
3658 	uint j;
3659 	for (j=0 ; j < acl_wild_hosts.elements ; j++)
3660 	{					// Check if host already exists
3661 	  acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j,
3662 					       acl_host_and_ip *);
3663 	  if (!my_strcasecmp(system_charset_info,
3664                              acl_user->host.hostname, acl->hostname))
3665 	    break;				// already stored
3666 	}
3667 	if (j == acl_wild_hosts.elements)	// If new
3668 	  (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
3669       }
3670       else if (!my_hash_search(&acl_check_hosts,(uchar*)
3671                                acl_user->host.hostname,
3672                                strlen(acl_user->host.hostname)))
3673       {
3674 	if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
3675 	{					// End of memory
3676 	  allow_all_hosts=1;			// Should never happen
3677 	  DBUG_VOID_RETURN;
3678 	}
3679       }
3680     }
3681   }
3682   freeze_size(&acl_wild_hosts);
3683   freeze_size(&acl_check_hosts.array);
3684   DBUG_VOID_RETURN;
3685 }
3686 
3687 
3688 /*
3689   Rebuild lists used for checking of allowed hosts
3690 
3691   We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
3692   dropping or renaming user, since they contain pointers to elements of
3693   'acl_user' array, which are invalidated by drop operation, and use
3694   ACL_USER::host::hostname as a key, which is changed by rename.
3695 */
rebuild_check_host(void)3696 static void rebuild_check_host(void)
3697 {
3698   delete_dynamic(&acl_wild_hosts);
3699   my_hash_free(&acl_check_hosts);
3700   init_check_host();
3701 }
3702 
3703 /*
3704   Reset a role role_grants dynamic array.
3705   Also, the role's access bits are reset to the ones present in the table.
3706 */
acl_role_reset_role_arrays(void * ptr,void * not_used)3707 static my_bool acl_role_reset_role_arrays(void *ptr,
3708                                     void * not_used __attribute__((unused)))
3709 {
3710   ACL_ROLE *role= (ACL_ROLE *)ptr;
3711   reset_dynamic(&role->role_grants);
3712   reset_dynamic(&role->parent_grantee);
3713   role->counter= 0;
3714   return 0;
3715 }
3716 
3717 /*
3718    Add a the coresponding pointers present in the mapping to the entries in
3719    acl_users and acl_roles
3720 */
add_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role)3721 static bool add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
3722 {
3723   return push_dynamic(&grantee->role_grants, (uchar*) &role)
3724       || push_dynamic(&role->parent_grantee, (uchar*) &grantee);
3725 
3726 }
3727 
3728 /*
3729   Revert the last add_role_user_mapping() action
3730 */
undo_add_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role)3731 static void undo_add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
3732 {
3733   void *pop __attribute__((unused));
3734 
3735   pop= pop_dynamic(&grantee->role_grants);
3736   DBUG_ASSERT(role == *(ACL_ROLE**)pop);
3737 
3738   pop= pop_dynamic(&role->parent_grantee);
3739   DBUG_ASSERT(grantee == *(ACL_USER_BASE**)pop);
3740 }
3741 
3742 /*
3743   this helper is used when building role_grants and parent_grantee arrays
3744   from scratch.
3745 
3746   this happens either on initial loading of data from tables, in acl_load().
3747   or in rebuild_role_grants after acl_role_reset_role_arrays().
3748 */
add_role_user_mapping(const char * uname,const char * hname,const char * rname)3749 static bool add_role_user_mapping(const char *uname, const char *hname,
3750                                   const char *rname)
3751 {
3752   ACL_USER_BASE *grantee= find_acl_user_base(uname, hname);
3753   ACL_ROLE *role= find_acl_role(rname);
3754 
3755   if (grantee == NULL || role == NULL)
3756     return 1;
3757 
3758   /*
3759     because all arrays are rebuilt completely, and counters were also reset,
3760     we can increment them here, and after the rebuild all counters will
3761     have correct values (equal to the number of roles granted).
3762   */
3763   if (grantee->flags & IS_ROLE)
3764     ((ACL_ROLE*)grantee)->counter++;
3765   return add_role_user_mapping(grantee, role);
3766 }
3767 
3768 /*
3769   This helper function is used to removes roles and grantees
3770   from the corresponding cross-reference arrays. see remove_role_user_mapping().
3771   as such, it asserts that an element to delete is present in the array,
3772   and is present only once.
3773 */
remove_ptr_from_dynarray(DYNAMIC_ARRAY * array,void * ptr)3774 static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr)
3775 {
3776   bool found __attribute__((unused))= false;
3777   for (uint i= 0; i < array->elements; i++)
3778   {
3779     if (ptr == *dynamic_element(array, i, void**))
3780     {
3781       DBUG_ASSERT(!found);
3782       delete_dynamic_element(array, i);
3783       IF_DBUG_ASSERT(found= true, break);
3784     }
3785   }
3786   DBUG_ASSERT(found);
3787 }
3788 
remove_role_user_mapping(ACL_USER_BASE * grantee,ACL_ROLE * role,int grantee_idx=-1,int role_idx=-1)3789 static void remove_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role,
3790                                      int grantee_idx=-1, int role_idx=-1)
3791 {
3792   remove_ptr_from_dynarray(&grantee->role_grants, role);
3793   remove_ptr_from_dynarray(&role->parent_grantee, grantee);
3794 }
3795 
3796 
add_role_user_mapping_action(void * ptr,void * unused)3797 static my_bool add_role_user_mapping_action(void *ptr, void *unused __attribute__((unused)))
3798 {
3799   ROLE_GRANT_PAIR *pair= (ROLE_GRANT_PAIR*)ptr;
3800   bool status __attribute__((unused));
3801   status= add_role_user_mapping(pair->u_uname, pair->u_hname, pair->r_uname);
3802   /*
3803      The invariant chosen is that acl_roles_mappings should _always_
3804      only contain valid entries, referencing correct user and role grants.
3805      If add_role_user_mapping detects an invalid entry, it will not add
3806      the mapping into the ACL_USER::role_grants array.
3807   */
3808   DBUG_ASSERT(status == 0);
3809   return 0;
3810 }
3811 
3812 
3813 /*
3814   Rebuild the role grants every time the acl_users is modified
3815 
3816   The role grants in the ACL_USER class need to be rebuilt, as they contain
3817   pointers to elements of the acl_users array.
3818 */
3819 
rebuild_role_grants(void)3820 static void rebuild_role_grants(void)
3821 {
3822   DBUG_ENTER("rebuild_role_grants");
3823   /*
3824     Reset every user's and role's role_grants array
3825   */
3826   for (uint i=0; i < acl_users.elements; i++) {
3827     ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *);
3828     reset_dynamic(&user->role_grants);
3829   }
3830   my_hash_iterate(&acl_roles, acl_role_reset_role_arrays, NULL);
3831 
3832   /* Rebuild the direct links between users and roles in ACL_USER::role_grants */
3833   my_hash_iterate(&acl_roles_mappings, add_role_user_mapping_action, NULL);
3834 
3835   DBUG_VOID_RETURN;
3836 }
3837 
3838 
3839 /* Return true if there is no users that can match the given host */
acl_check_host(const char * host,const char * ip)3840 bool acl_check_host(const char *host, const char *ip)
3841 {
3842   if (allow_all_hosts)
3843     return 0;
3844   mysql_mutex_lock(&acl_cache->lock);
3845 
3846   if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
3847       (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
3848   {
3849     mysql_mutex_unlock(&acl_cache->lock);
3850     return 0;					// Found host
3851   }
3852   for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
3853   {
3854     acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
3855     if (compare_hostname(acl, host, ip))
3856     {
3857       mysql_mutex_unlock(&acl_cache->lock);
3858       return 0;					// Host ok
3859     }
3860   }
3861   mysql_mutex_unlock(&acl_cache->lock);
3862   if (ip != NULL)
3863   {
3864     /* Increment HOST_CACHE.COUNT_HOST_ACL_ERRORS. */
3865     Host_errors errors;
3866     errors.m_host_acl= 1;
3867     inc_host_errors(ip, &errors);
3868   }
3869   return 1;					// Host is not allowed
3870 }
3871 
3872 /**
3873   Check if the user is allowed to alter the mysql.user table
3874 
3875  @param thd              THD
3876  @param host             Hostname for the user
3877  @param user             User name
3878 
3879  @return Error status
3880    @retval 0 OK
3881    @retval 1 Error
3882 */
3883 
check_alter_user(THD * thd,const char * host,const char * user)3884 static int check_alter_user(THD *thd, const char *host, const char *user)
3885 {
3886   int error = 1;
3887   if (!initialized)
3888   {
3889     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3890     goto end;
3891   }
3892 
3893   if (IF_WSREP((!WSREP(thd) || !thd->wsrep_applier), 1) &&
3894       !thd->slave_thread && !thd->security_ctx->priv_user[0] &&
3895       !thd->bootstrap)
3896   {
3897     my_message(ER_PASSWORD_ANONYMOUS_USER,
3898                ER_THD(thd, ER_PASSWORD_ANONYMOUS_USER),
3899                MYF(0));
3900     goto end;
3901   }
3902   if (!host) // Role
3903   {
3904     my_error(ER_PASSWORD_NO_MATCH, MYF(0));
3905     goto end;
3906   }
3907 
3908   if (!thd->slave_thread &&
3909       IF_WSREP((!WSREP(thd) || !thd->wsrep_applier),1) &&
3910       !thd->security_ctx->is_priv_user(user, host))
3911   {
3912     if (thd->security_ctx->password_expired)
3913     {
3914       my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
3915       goto end;
3916     }
3917     if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
3918       goto end;
3919   }
3920 
3921   error = 0;
3922 
3923 end:
3924   return error;
3925 }
3926 /**
3927   Check if the user is allowed to change password
3928 
3929  @param thd              THD
3930  @param user             User, hostname, new password or password hash
3931 
3932  @return Error status
3933    @retval 0 OK
3934    @retval 1 ERROR; In this case the error is sent to the client.
3935 */
3936 
check_change_password(THD * thd,LEX_USER * user)3937 bool check_change_password(THD *thd, LEX_USER *user)
3938 {
3939   LEX_USER *real_user= get_current_user(thd, user);
3940   user->user= real_user->user;
3941   user->host= real_user->host;
3942   return check_alter_user(thd, user->host.str, user->user.str);
3943 }
3944 
3945 
3946 /**
3947   Change a password for a user.
3948 
3949   @param thd            THD
3950   @param user           User, hostname, new password hash
3951 
3952   @return Error code
3953    @retval 0 ok
3954    @retval 1 ERROR; In this case the error is sent to the client.
3955 */
change_password(THD * thd,LEX_USER * user)3956 bool change_password(THD *thd, LEX_USER *user)
3957 {
3958   Grant_tables tables;
3959   /* Buffer should be extended when password length is extended. */
3960   char buff[512];
3961   ulong query_length= 0;
3962   enum_binlog_format save_binlog_format;
3963   bool result= false, acl_cache_is_locked= false;
3964   ACL_USER *acl_user;
3965   ACL_USER::AUTH auth;
3966   const char *password_plugin= 0;
3967   const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
3968   DBUG_ENTER("change_password");
3969   DBUG_PRINT("enter",("host: '%s'  user: '%s'  new_password: '%s'",
3970 		      user->host.str, user->user.str, user->auth->auth_str.str));
3971   DBUG_ASSERT(user->host.str != 0);                     // Ensured by caller
3972 
3973   /*
3974     This statement will be replicated as a statement, even when using
3975     row-based replication.  The flag will be reset at the end of the
3976     statement.
3977     This has to be handled here as it's called by set_var.cc, which is
3978     not automaticly handled by sql_parse.cc
3979   */
3980   save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
3981 
3982   if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
3983     WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
3984 
3985   if ((result= tables.open_and_lock(thd, Table_user, TL_WRITE)))
3986     DBUG_RETURN(result != 1);
3987 
3988   acl_cache_is_locked= 1;
3989   mysql_mutex_lock(&acl_cache->lock);
3990 
3991   if (!(acl_user= find_user_exact(user->host.str, user->user.str)))
3992   {
3993     my_error(ER_PASSWORD_NO_MATCH, MYF(0));
3994     goto end;
3995   }
3996 
3997   if (acl_user->nauth == 1 &&
3998       (acl_user->auth[0].plugin.str == native_password_plugin_name.str ||
3999        acl_user->auth[0].plugin.str == old_password_plugin_name.str))
4000   {
4001     /* historical hack of auto-changing the plugin */
4002     acl_user->auth[0].plugin= guess_auth_plugin(thd, user->auth->auth_str.length);
4003   }
4004 
4005   for (uint i=0; i < acl_user->nauth; i++)
4006   {
4007     auth= acl_user->auth[i];
4008     auth.auth_string= safe_lexcstrdup_root(&acl_memroot, user->auth->auth_str);
4009     int r= set_user_auth(thd, user->user, &auth, user->auth->pwtext);
4010     if (r == ER_SET_PASSWORD_AUTH_PLUGIN)
4011       password_plugin= auth.plugin.str;
4012     else if (r)
4013       goto end;
4014     else
4015     {
4016       acl_user->auth[i]= auth;
4017       password_plugin= 0;
4018       break;
4019     }
4020   }
4021   if (password_plugin)
4022   {
4023     my_error(ER_SET_PASSWORD_AUTH_PLUGIN, MYF(0), password_plugin);
4024     goto end;
4025   }
4026 
4027   /* Update the acl password expired state of user */
4028   acl_user->password_last_changed= thd->query_start();
4029   acl_user->password_expired= false;
4030 
4031   /* If user is the connected user, reset the password expired field on sctx
4032      and allow the user to exit sandbox mode */
4033   if (thd->security_ctx->is_priv_user(user->user.str, user->host.str))
4034     thd->security_ctx->password_expired= false;
4035 
4036   if (update_user_table_password(thd, tables.user_table(), *acl_user))
4037     goto end;
4038 
4039   acl_cache->clear(1);                          // Clear locked hostname cache
4040   mysql_mutex_unlock(&acl_cache->lock);
4041   result= acl_cache_is_locked= 0;
4042   if (mysql_bin_log.is_open())
4043   {
4044     query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
4045            user->user.str, safe_str(user->host.str), auth.auth_string.str);
4046     DBUG_ASSERT(query_length);
4047     thd->clear_error();
4048     result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
4049                               FALSE, FALSE, FALSE, 0) > 0;
4050   }
4051 end:
4052   if (acl_cache_is_locked)
4053     mysql_mutex_unlock(&acl_cache->lock);
4054   close_mysql_tables(thd);
4055 
4056 #ifdef WITH_WSREP
4057 wsrep_error_label:
4058   if (WSREP(thd) && !thd->wsrep_applier)
4059   {
4060     WSREP_TO_ISOLATION_END;
4061 
4062     thd->set_query(query_save);
4063   }
4064 #endif /* WITH_WSREP */
4065   thd->restore_stmt_binlog_format(save_binlog_format);
4066 
4067   DBUG_RETURN(result);
4068 }
4069 
acl_check_set_default_role(THD * thd,const char * host,const char * user,const char * role)4070 int acl_check_set_default_role(THD *thd, const char *host, const char *user,
4071                                const char *role)
4072 {
4073   DBUG_ENTER("acl_check_set_default_role");
4074   DBUG_RETURN(check_alter_user(thd, host, user) ||
4075               check_user_can_set_role(thd, user, host, NULL, role, NULL));
4076 }
4077 
acl_set_default_role(THD * thd,const char * host,const char * user,const char * rolename)4078 int acl_set_default_role(THD *thd, const char *host, const char *user,
4079                          const char *rolename)
4080 {
4081   Grant_tables tables;
4082   char user_key[MAX_KEY_LENGTH];
4083   int result= 1;
4084   int error;
4085   ulong query_length= 0;
4086   bool clear_role= FALSE;
4087   char buff[512];
4088   enum_binlog_format save_binlog_format= thd->get_current_stmt_binlog_format();
4089   const CSET_STRING query_save __attribute__((unused)) = thd->query_string;
4090 
4091   DBUG_ENTER("acl_set_default_role");
4092   DBUG_PRINT("enter",("host: '%s'  user: '%s'  rolename: '%s'",
4093                       user, safe_str(host), safe_str(rolename)));
4094 
4095   if (!strcasecmp(rolename, "NONE"))
4096     clear_role= TRUE;
4097 
4098   if (mysql_bin_log.is_open() ||
4099       (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)))
4100   {
4101     query_length=
4102       sprintf(buff,"SET DEFAULT ROLE '%-.120s' FOR '%-.120s'@'%-.120s'",
4103               safe_str(rolename), user, safe_str(host));
4104   }
4105 
4106   /*
4107     This statement will be replicated as a statement, even when using
4108     row-based replication.  The flag will be reset at the end of the
4109     statement.
4110     This has to be handled here as it's called by set_var.cc, which is
4111     not automaticly handled by sql_parse.cc
4112   */
4113   save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
4114 
4115   if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))
4116   {
4117     thd->set_query(buff, query_length, system_charset_info);
4118     // Attention!!! here is implicit goto error;
4119     WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
4120   }
4121 
4122   /*
4123     Extra block due to WSREP_TO_ISOLATION_BEGIN using goto.
4124     TODO(cvicentiu) Should move  this block out in a new function.
4125   */
4126   {
4127     if ((result= tables.open_and_lock(thd, Table_user, TL_WRITE)))
4128       DBUG_RETURN(result != 1);
4129 
4130     const User_table& user_table= tables.user_table();
4131     TABLE *table= user_table.table();
4132 
4133     result= 1;
4134 
4135     mysql_mutex_lock(&acl_cache->lock);
4136     ACL_USER *acl_user;
4137     if (!(acl_user= find_user_exact(host, user)))
4138     {
4139       mysql_mutex_unlock(&acl_cache->lock);
4140       my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
4141                  MYF(0));
4142       goto end;
4143     }
4144 
4145     if (!clear_role)
4146     {
4147       /* set new default_rolename */
4148       acl_user->default_rolename.str= safe_strdup_root(&acl_memroot, rolename);
4149       acl_user->default_rolename.length= strlen(rolename);
4150     }
4151     else
4152     {
4153       /* clear the default_rolename */
4154       acl_user->default_rolename.str = NULL;
4155       acl_user->default_rolename.length = 0;
4156     }
4157 
4158     /* update the mysql.user table with the new default role */
4159     tables.user_table().table()->use_all_columns();
4160     user_table.set_host(host, strlen(host));
4161     user_table.set_user(user, strlen(user));
4162     key_copy((uchar *) user_key, table->record[0], table->key_info,
4163              table->key_info->key_length);
4164 
4165     if (table->file->ha_index_read_idx_map(table->record[0], 0,
4166                                            (uchar *) user_key, HA_WHOLE_KEY,
4167                                            HA_READ_KEY_EXACT))
4168     {
4169       mysql_mutex_unlock(&acl_cache->lock);
4170       my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
4171                  MYF(0));
4172       goto end;
4173     }
4174     store_record(table, record[1]);
4175     user_table.set_default_role(acl_user->default_rolename.str,
4176                                 acl_user->default_rolename.length);
4177     if (unlikely(error= table->file->ha_update_row(table->record[1],
4178                                                    table->record[0])) &&
4179         error != HA_ERR_RECORD_IS_THE_SAME)
4180     {
4181       mysql_mutex_unlock(&acl_cache->lock);
4182       table->file->print_error(error,MYF(0));	/* purecov: deadcode */
4183       goto end;
4184     }
4185 
4186     acl_cache->clear(1);
4187     mysql_mutex_unlock(&acl_cache->lock);
4188     result= 0;
4189     if (mysql_bin_log.is_open())
4190     {
4191       DBUG_ASSERT(query_length);
4192       thd->clear_error();
4193       result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
4194                                 FALSE, FALSE, FALSE, 0) > 0;
4195     }
4196   end:
4197     close_mysql_tables(thd);
4198   }
4199 
4200 #ifdef WITH_WSREP
4201 wsrep_error_label:
4202   if (WSREP(thd) && !thd->wsrep_applier)
4203   {
4204     WSREP_TO_ISOLATION_END;
4205 
4206     thd->set_query(query_save);
4207   }
4208 #endif /* WITH_WSREP */
4209 
4210   thd->restore_stmt_binlog_format(save_binlog_format);
4211 
4212   DBUG_RETURN(result);
4213 }
4214 
4215 
4216 /*
4217   Find user in ACL
4218 
4219   SYNOPSIS
4220     is_acl_user()
4221     host                 host name
4222     user                 user name
4223 
4224   RETURN
4225    FALSE  user not fond
4226    TRUE   there is such user
4227 */
4228 
is_acl_user(const char * host,const char * user)4229 bool is_acl_user(const char *host, const char *user)
4230 {
4231   bool res;
4232 
4233   /* --skip-grants */
4234   if (!initialized)
4235     return TRUE;
4236 
4237   mysql_mutex_lock(&acl_cache->lock);
4238 
4239   if (*host) // User
4240     res= find_user_exact(host, user) != NULL;
4241   else // Role
4242     res= find_acl_role(user) != NULL;
4243 
4244   mysql_mutex_unlock(&acl_cache->lock);
4245   return res;
4246 }
4247 
4248 
4249 /*
4250   Find first entry that matches the specified user@host pair
4251 */
find_user_exact(const char * host,const char * user)4252 static ACL_USER *find_user_exact(const char *host, const char *user)
4253 {
4254   mysql_mutex_assert_owner(&acl_cache->lock);
4255   size_t start= acl_find_user_by_name(user);
4256 
4257   for (size_t i= start; i < acl_users.elements; i++)
4258   {
4259     ACL_USER *acl_user= dynamic_element(&acl_users, i, ACL_USER*);
4260     if (i > start && strcmp(acl_user->user.str, user))
4261       return 0;
4262 
4263     if (!my_strcasecmp(system_charset_info, acl_user->host.hostname, host))
4264       return acl_user;
4265   }
4266   return 0;
4267 }
4268 
4269 /*
4270   Find first entry that matches the specified user@host pair
4271 */
find_user_wild(const char * host,const char * user,const char * ip)4272 static ACL_USER * find_user_wild(const char *host, const char *user, const char *ip)
4273 {
4274   mysql_mutex_assert_owner(&acl_cache->lock);
4275 
4276   size_t start = acl_find_user_by_name(user);
4277 
4278   for (size_t i= start; i < acl_users.elements; i++)
4279   {
4280     ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
4281     if (i > start && strcmp(acl_user->user.str, user))
4282       break;
4283     if (compare_hostname(&acl_user->host, host, ip ? ip : host))
4284       return acl_user;
4285   }
4286   return 0;
4287 }
4288 
4289 /*
4290   Find a role with the specified name
4291 */
find_acl_role(const char * role)4292 static ACL_ROLE *find_acl_role(const char *role)
4293 {
4294   DBUG_ENTER("find_acl_role");
4295   DBUG_PRINT("enter",("role: '%s'", role));
4296   DBUG_PRINT("info", ("Hash elements: %ld", acl_roles.records));
4297 
4298   mysql_mutex_assert_owner(&acl_cache->lock);
4299 
4300   ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role,
4301                                           strlen(role));
4302   DBUG_RETURN(r);
4303 }
4304 
4305 
find_acl_user_base(const char * user,const char * host)4306 static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host)
4307 {
4308   if (*host)
4309     return find_user_exact(host, user);
4310 
4311   return find_acl_role(user);
4312 }
4313 
4314 
4315 /*
4316   Comparing of hostnames
4317 
4318   NOTES
4319   A hostname may be of type:
4320   hostname   (May include wildcards);   monty.pp.sci.fi
4321   ip	   (May include wildcards);   192.168.0.0
4322   ip/netmask			      192.168.0.0/255.255.255.0
4323 
4324   A net mask of 0.0.0.0 is not allowed.
4325 */
4326 
calc_ip(const char * ip,long * val,char end)4327 static const char *calc_ip(const char *ip, long *val, char end)
4328 {
4329   long ip_val,tmp;
4330   if (!(ip=str2int(ip,10,0,255,&ip_val)) || *ip != '.')
4331     return 0;
4332   ip_val<<=24;
4333   if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
4334     return 0;
4335   ip_val+=tmp<<16;
4336   if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
4337     return 0;
4338   ip_val+=tmp<<8;
4339   if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != end)
4340     return 0;
4341   *val=ip_val+tmp;
4342   return ip;
4343 }
4344 
4345 
update_hostname(acl_host_and_ip * host,const char * hostname)4346 static void update_hostname(acl_host_and_ip *host, const char *hostname)
4347 {
4348   // fix historical undocumented convention that empty host is the same as '%'
4349   hostname=const_cast<char*>(hostname ? hostname : host_not_specified.str);
4350   host->hostname=(char*) hostname;             // This will not be modified!
4351   if (!(hostname= calc_ip(hostname,&host->ip,'/')) ||
4352       !(hostname= calc_ip(hostname+1,&host->ip_mask,'\0')))
4353   {
4354     host->ip= host->ip_mask=0;			// Not a masked ip
4355   }
4356 }
4357 
4358 
compare_hostname(const acl_host_and_ip * host,const char * hostname,const char * ip)4359 static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
4360 			     const char *ip)
4361 {
4362   long tmp;
4363   if (host->ip_mask && ip && calc_ip(ip,&tmp,'\0'))
4364   {
4365     return (tmp & host->ip_mask) == host->ip;
4366   }
4367   return (!host->hostname ||
4368 	  (hostname && !wild_case_compare(system_charset_info,
4369                                           hostname, host->hostname)) ||
4370 	  (ip && !wild_compare(ip, host->hostname, 0)));
4371 }
4372 
4373 /**
4374   Check if the given host name needs to be resolved or not.
4375   Host name has to be resolved if it actually contains *name*.
4376 
4377   For example:
4378     192.168.1.1               --> FALSE
4379     192.168.1.0/255.255.255.0 --> FALSE
4380     %                         --> FALSE
4381     192.168.1.%               --> FALSE
4382     AB%                       --> FALSE
4383 
4384     AAAAFFFF                  --> TRUE (Hostname)
4385     AAAA:FFFF:1234:5678       --> FALSE
4386     ::1                       --> FALSE
4387 
4388   This function does not check if the given string is a valid host name or
4389   not. It assumes that the argument is a valid host name.
4390 
4391   @param hostname   the string to check.
4392 
4393   @return a flag telling if the argument needs to be resolved or not.
4394   @retval TRUE the argument is a host name and needs to be resolved.
4395   @retval FALSE the argument is either an IP address, or a patter and
4396           should not be resolved.
4397 */
4398 
hostname_requires_resolving(const char * hostname)4399 bool hostname_requires_resolving(const char *hostname)
4400 {
4401   if (!hostname)
4402     return FALSE;
4403 
4404   /* Check if hostname is the localhost. */
4405 
4406   size_t hostname_len= strlen(hostname);
4407   size_t localhost_len= strlen(my_localhost);
4408 
4409   if (hostname == my_localhost ||
4410       (hostname_len == localhost_len &&
4411        !system_charset_info->strnncoll(
4412                      (const uchar *) hostname,  hostname_len,
4413                      (const uchar *) my_localhost, strlen(my_localhost))))
4414   {
4415     return FALSE;
4416   }
4417 
4418   /*
4419     If the string contains any of {':', '%', '_', '/'}, it is definitely
4420     not a host name:
4421       - ':' means that the string is an IPv6 address;
4422       - '%' or '_' means that the string is a pattern;
4423       - '/' means that the string is an IPv4 network address;
4424   */
4425 
4426   for (const char *p= hostname; *p; ++p)
4427   {
4428     switch (*p) {
4429       case ':':
4430       case '%':
4431       case '_':
4432       case '/':
4433         return FALSE;
4434     }
4435   }
4436 
4437   /*
4438     Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
4439     (12.34.56.78). The assumption is that if the string contains only
4440     digits and dots, it is an IPv4 address. Otherwise -- a host name.
4441   */
4442 
4443   for (const char *p= hostname; *p; ++p)
4444   {
4445     if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
4446       return TRUE; /* a "letter" has been found. */
4447   }
4448 
4449   return FALSE; /* all characters are either dots or digits. */
4450 }
4451 
4452 
4453 /**
4454   Update record for user in mysql.user privilege table with new password.
4455 
4456   @see change_password
4457 */
4458 
update_user_table_password(THD * thd,const User_table & user_table,const ACL_USER & user)4459 static bool update_user_table_password(THD *thd, const User_table& user_table,
4460                                        const ACL_USER &user)
4461 {
4462   char user_key[MAX_KEY_LENGTH];
4463   int error;
4464   DBUG_ENTER("update_user_table_password");
4465 
4466   TABLE *table= user_table.table();
4467   table->use_all_columns();
4468   user_table.set_host(user.host.hostname, user.hostname_length);
4469   user_table.set_user(user.user.str, user.user.length);
4470   key_copy((uchar *) user_key, table->record[0], table->key_info,
4471            table->key_info->key_length);
4472 
4473   if (table->file->ha_index_read_idx_map(table->record[0], 0,
4474                                          (uchar *) user_key, HA_WHOLE_KEY,
4475                                          HA_READ_KEY_EXACT))
4476   {
4477     my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
4478                MYF(0));
4479     DBUG_RETURN(1);
4480   }
4481   store_record(table, record[1]);
4482 
4483   if (user_table.set_auth(user))
4484   {
4485     my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
4486              user_table.name().str, 3, user_table.num_fields(),
4487              static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
4488     DBUG_RETURN(1);
4489   }
4490 
4491   user_table.set_password_expired(user.password_expired);
4492   user_table.set_password_last_changed(user.password_last_changed);
4493 
4494   if (unlikely(error= table->file->ha_update_row(table->record[1],
4495                                                  table->record[0])) &&
4496       error != HA_ERR_RECORD_IS_THE_SAME)
4497   {
4498     table->file->print_error(error,MYF(0));
4499     DBUG_RETURN(1);
4500   }
4501 
4502   DBUG_RETURN(0);
4503 }
4504 
4505 
4506 /*
4507   Return 1 if we are allowed to create new users
4508   the logic here is: INSERT_ACL is sufficient.
4509   It's also a requirement in opt_safe_user_create,
4510   otherwise CREATE_USER_ACL is enough.
4511 */
4512 
test_if_create_new_users(THD * thd)4513 static bool test_if_create_new_users(THD *thd)
4514 {
4515   Security_context *sctx= thd->security_ctx;
4516   bool create_new_users= MY_TEST(sctx->master_access & INSERT_ACL) ||
4517                          (!opt_safe_user_create &&
4518                           MY_TEST(sctx->master_access & CREATE_USER_ACL));
4519   if (!create_new_users)
4520   {
4521     TABLE_LIST tl;
4522     privilege_t db_access(NO_ACL);
4523     tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLE_NAME[USER_TABLE],
4524                       NULL, TL_WRITE);
4525     create_new_users= 1;
4526 
4527     db_access=acl_get(sctx->host, sctx->ip,
4528 		      sctx->priv_user, tl.db.str, 0);
4529     if (sctx->priv_role[0])
4530       db_access|= acl_get("", "", sctx->priv_role, tl.db.str, 0);
4531     if (!(db_access & INSERT_ACL))
4532     {
4533       if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
4534         create_new_users=0;
4535     }
4536   }
4537   return create_new_users;
4538 }
4539 
4540 
4541 /****************************************************************************
4542   Handle GRANT commands
4543 ****************************************************************************/
4544 static USER_AUTH auth_no_password;
4545 
replace_user_table(THD * thd,const User_table & user_table,LEX_USER * const combo,privilege_t rights,const bool revoke_grant,const bool can_create_user,const bool no_auto_create)4546 static int replace_user_table(THD *thd, const User_table &user_table,
4547                               LEX_USER * const combo, privilege_t rights,
4548                               const bool revoke_grant,
4549                               const bool can_create_user,
4550                               const bool no_auto_create)
4551 {
4552   int error = -1;
4553   uint nauth= 0;
4554   bool old_row_exists=0;
4555   uchar user_key[MAX_KEY_LENGTH];
4556   bool handle_as_role= combo->is_role();
4557   LEX *lex= thd->lex;
4558   TABLE *table= user_table.table();
4559   ACL_USER new_acl_user, *old_acl_user= 0;
4560   DBUG_ENTER("replace_user_table");
4561 
4562   mysql_mutex_assert_owner(&acl_cache->lock);
4563 
4564   table->use_all_columns();
4565   user_table.set_host(combo->host.str,combo->host.length);
4566   user_table.set_user(combo->user.str,combo->user.length);
4567   key_copy(user_key, table->record[0], table->key_info,
4568            table->key_info->key_length);
4569 
4570   if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
4571                                          HA_WHOLE_KEY, HA_READ_KEY_EXACT))
4572   {
4573     if (revoke_grant)
4574     {
4575       if (combo->host.length)
4576         my_error(ER_NONEXISTING_GRANT, MYF(0), combo->user.str,
4577                  combo->host.str);
4578       else
4579         my_error(ER_INVALID_ROLE, MYF(0), combo->user.str);
4580       goto end;
4581     }
4582     /*
4583       There are four options which affect the process of creation of
4584       a new user (mysqld option --safe-create-user, 'insert' privilege
4585       on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
4586       SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
4587       how it should work.
4588       if (safe-user-create && ! INSERT_priv) => reject
4589       else if (identified_by) => create
4590       else if (no_auto_create_user) => reject
4591       else create
4592 
4593       see also test_if_create_new_users()
4594     */
4595     else if (!combo->has_auth() && no_auto_create)
4596     {
4597       my_error(ER_PASSWORD_NO_MATCH, MYF(0));
4598       goto end;
4599     }
4600     else if (!can_create_user)
4601     {
4602       my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
4603       goto end;
4604     }
4605 
4606     if (!combo->auth)
4607       combo->auth= &auth_no_password;
4608 
4609     old_row_exists = 0;
4610     restore_record(table, s->default_values);
4611     user_table.set_host(combo->host.str, combo->host.length);
4612     user_table.set_user(combo->user.str, combo->user.length);
4613   }
4614   else
4615   {
4616     old_row_exists = 1;
4617     store_record(table,record[1]);			// Save copy for update
4618   }
4619 
4620   for (USER_AUTH *auth= combo->auth; auth; auth= auth->next)
4621   {
4622     nauth++;
4623     if (auth->plugin.length)
4624     {
4625       if (!plugin_is_ready(&auth->plugin, MYSQL_AUTHENTICATION_PLUGIN))
4626       {
4627         my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth->plugin.str);
4628         goto end;
4629       }
4630     }
4631     else
4632       auth->plugin= guess_auth_plugin(thd, auth->auth_str.length);
4633   }
4634 
4635   /* Update table columns with new privileges */
4636   user_table.set_access(rights, revoke_grant);
4637   rights= user_table.get_access();
4638 
4639   if (handle_as_role)
4640   {
4641     if (old_row_exists && !user_table.get_is_role())
4642     {
4643       goto end;
4644     }
4645     if (user_table.set_is_role(true))
4646     {
4647       my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
4648                user_table.name().str,
4649                ROLE_ASSIGN_COLUMN_IDX + 1, user_table.num_fields(),
4650                static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
4651       goto end;
4652     }
4653   }
4654   else
4655   {
4656     old_acl_user= find_user_exact(combo->host.str, combo->user.str);
4657     if ((old_acl_user != NULL) != old_row_exists)
4658     {
4659       my_error(ER_PASSWORD_NO_MATCH, MYF(0));
4660       goto end;
4661     }
4662     new_acl_user= old_row_exists ? *old_acl_user :
4663                   ACL_USER(thd, *combo, lex->account_options, rights);
4664     if (acl_user_update(thd, &new_acl_user, nauth,
4665                         *combo, lex->account_options, rights))
4666       goto end;
4667 
4668     if (user_table.set_auth(new_acl_user))
4669     {
4670       my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
4671                user_table.name().str, 3, user_table.num_fields(),
4672                static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
4673       DBUG_RETURN(1);
4674     }
4675 
4676     switch (lex->account_options.ssl_type) {
4677     case SSL_TYPE_NOT_SPECIFIED:
4678       break;
4679     case SSL_TYPE_NONE:
4680     case SSL_TYPE_ANY:
4681     case SSL_TYPE_X509:
4682       user_table.set_ssl_type(lex->account_options.ssl_type);
4683       user_table.set_ssl_cipher("", 0);
4684       user_table.set_x509_issuer("", 0);
4685       user_table.set_x509_subject("", 0);
4686       break;
4687     case SSL_TYPE_SPECIFIED:
4688       user_table.set_ssl_type(lex->account_options.ssl_type);
4689       if (lex->account_options.ssl_cipher.str)
4690         user_table.set_ssl_cipher(lex->account_options.ssl_cipher.str,
4691                                   lex->account_options.ssl_cipher.length);
4692       else
4693         user_table.set_ssl_cipher("", 0);
4694       if (lex->account_options.x509_issuer.str)
4695         user_table.set_x509_issuer(lex->account_options.x509_issuer.str,
4696                                    lex->account_options.x509_issuer.length);
4697       else
4698         user_table.set_x509_issuer("", 0);
4699       if (lex->account_options.x509_subject.str)
4700         user_table.set_x509_subject(lex->account_options.x509_subject.str,
4701                                     lex->account_options.x509_subject.length);
4702       else
4703         user_table.set_x509_subject("", 0);
4704       break;
4705     }
4706 
4707     if (lex->account_options.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
4708       user_table.set_max_questions(lex->account_options.questions);
4709     if (lex->account_options.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
4710       user_table.set_max_updates(lex->account_options.updates);
4711     if (lex->account_options.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
4712       user_table.set_max_connections(lex->account_options.conn_per_hour);
4713     if (lex->account_options.specified_limits & USER_RESOURCES::USER_CONNECTIONS)
4714       user_table.set_max_user_connections(lex->account_options.user_conn);
4715     if (lex->account_options.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME)
4716       user_table.set_max_statement_time(lex->account_options.max_statement_time);
4717 
4718     mqh_used= (mqh_used || lex->account_options.questions || lex->account_options.updates ||
4719                lex->account_options.conn_per_hour || lex->account_options.user_conn ||
4720                lex->account_options.max_statement_time != 0.0);
4721 
4722     if (lex->account_options.account_locked != ACCOUNTLOCK_UNSPECIFIED)
4723       user_table.set_account_locked(new_acl_user.account_locked);
4724 
4725     if (nauth)
4726       user_table.set_password_last_changed(new_acl_user.password_last_changed);
4727     if (lex->account_options.password_expire != PASSWORD_EXPIRE_UNSPECIFIED)
4728     {
4729       user_table.set_password_lifetime(new_acl_user.password_lifetime);
4730       user_table.set_password_expired(new_acl_user.password_expired);
4731     }
4732   }
4733 
4734   if (old_row_exists)
4735   {
4736     /*
4737       We should NEVER delete from the user table, as a uses can still
4738       use mysqld even if he doesn't have any privileges in the user table!
4739     */
4740     if (cmp_record(table, record[1]))
4741     {
4742       if (unlikely(error= table->file->ha_update_row(table->record[1],
4743                                                      table->record[0])) &&
4744           error != HA_ERR_RECORD_IS_THE_SAME)
4745       {                                         // This should never happen
4746         table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4747         error= -1;                              /* purecov: deadcode */
4748         goto end;                               /* purecov: deadcode */
4749       }
4750       else
4751         error= 0;
4752     }
4753   }
4754   else if (unlikely(error=table->file->ha_write_row(table->record[0])))
4755   {
4756     // This should never happen
4757     if (table->file->is_fatal_error(error, HA_CHECK_DUP))
4758     {
4759       table->file->print_error(error,MYF(0));	/* purecov: deadcode */
4760       error= -1;				/* purecov: deadcode */
4761       goto end;					/* purecov: deadcode */
4762     }
4763   }
4764   error=0;					// Privileges granted / revoked
4765 
4766 end:
4767   if (likely(!error))
4768   {
4769     acl_cache->clear(1);			// Clear privilege cache
4770     if (handle_as_role)
4771     {
4772       if (old_row_exists)
4773         acl_update_role(combo->user.str, rights);
4774       else
4775         acl_insert_role(combo->user.str, rights);
4776     }
4777     else
4778     {
4779       if (old_acl_user)
4780         *old_acl_user= new_acl_user;
4781       else
4782       {
4783         push_new_user(new_acl_user);
4784         rebuild_acl_users();
4785 
4786         /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
4787         rebuild_check_host();
4788 
4789         /*
4790           Rebuild every user's role_grants since 'acl_users' has been sorted
4791           and old pointers to ACL_USER elements are no longer valid
4792         */
4793         rebuild_role_grants();
4794       }
4795     }
4796   }
4797   DBUG_RETURN(error);
4798 }
4799 
4800 
4801 /*
4802   change grants in the mysql.db table
4803 */
4804 
replace_db_table(TABLE * table,const char * db,const LEX_USER & combo,privilege_t rights,const bool revoke_grant)4805 static int replace_db_table(TABLE *table, const char *db,
4806 			    const LEX_USER &combo,
4807 			    privilege_t rights, const bool revoke_grant)
4808 {
4809   uint i;
4810   ulonglong priv;
4811   privilege_t store_rights(NO_ACL);
4812   bool old_row_exists=0;
4813   int error;
4814   char what= revoke_grant ? 'N' : 'Y';
4815   uchar user_key[MAX_KEY_LENGTH];
4816   DBUG_ENTER("replace_db_table");
4817 
4818   /* Check if there is such a user in user table in memory? */
4819   if (!find_user_wild(combo.host.str,combo.user.str))
4820   {
4821     /* The user could be a role, check if the user is registered as a role */
4822     if (!combo.host.length && !find_acl_role(combo.user.str))
4823     {
4824       my_message(ER_PASSWORD_NO_MATCH, ER_THD(table->in_use,
4825                                               ER_PASSWORD_NO_MATCH), MYF(0));
4826       DBUG_RETURN(-1);
4827     }
4828   }
4829 
4830   table->use_all_columns();
4831   table->field[0]->store(combo.host.str,combo.host.length,
4832                          system_charset_info);
4833   table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4834   table->field[2]->store(combo.user.str,combo.user.length,
4835                          system_charset_info);
4836   key_copy(user_key, table->record[0], table->key_info,
4837            table->key_info->key_length);
4838 
4839   if (table->file->ha_index_read_idx_map(table->record[0],0, user_key,
4840                                          HA_WHOLE_KEY,
4841                                          HA_READ_KEY_EXACT))
4842   {
4843     if (revoke_grant)
4844     { // no row, no revoke
4845       my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
4846       goto abort;
4847     }
4848     old_row_exists = 0;
4849     restore_record(table, s->default_values);
4850     table->field[0]->store(combo.host.str,combo.host.length,
4851                            system_charset_info);
4852     table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4853     table->field[2]->store(combo.user.str,combo.user.length,
4854                            system_charset_info);
4855   }
4856   else
4857   {
4858     old_row_exists = 1;
4859     store_record(table,record[1]);
4860   }
4861 
4862   store_rights=get_rights_for_db(rights);
4863   for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
4864   {
4865     if (priv & store_rights)			// do it if priv is chosen
4866       table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
4867   }
4868   rights=get_access(table,3);
4869   rights=fix_rights_for_db(rights);
4870 
4871   if (old_row_exists)
4872   {
4873     /* update old existing row */
4874     if (rights)
4875     {
4876       if (unlikely((error= table->file->ha_update_row(table->record[1],
4877                                                       table->record[0]))) &&
4878           error != HA_ERR_RECORD_IS_THE_SAME)
4879 	goto table_error;			/* purecov: deadcode */
4880     }
4881     else	/* must have been a revoke of all privileges */
4882     {
4883       if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4884 	goto table_error;			/* purecov: deadcode */
4885     }
4886   }
4887   else if (rights &&
4888            (unlikely(error= table->file->ha_write_row(table->record[0]))))
4889   {
4890     if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4891       goto table_error; /* purecov: deadcode */
4892   }
4893 
4894   acl_cache->clear(1);				// Clear privilege cache
4895   if (old_row_exists)
4896     acl_update_db(combo.user.str,combo.host.str,db,rights);
4897   else if (rights)
4898   {
4899     /*
4900        If we did not have an already existing row, for users, we must always
4901        insert an ACL_DB entry. For roles however, it is possible that one was
4902        already created when DB privileges were propagated from other granted
4903        roles onto the current role. For this case, first try to update the
4904        existing entry, otherwise insert a new one.
4905     */
4906     if (!combo.is_role() ||
4907         !acl_update_db(combo.user.str, combo.host.str, db, rights))
4908     {
4909       acl_insert_db(combo.user.str,combo.host.str,db,rights);
4910     }
4911   }
4912   DBUG_RETURN(0);
4913 
4914   /* This could only happen if the grant tables got corrupted */
4915 table_error:
4916   table->file->print_error(error,MYF(0));	/* purecov: deadcode */
4917 
4918 abort:
4919   DBUG_RETURN(-1);
4920 }
4921 
4922 /**
4923   Updates the mysql.roles_mapping table
4924 
4925   @param table          TABLE to update
4926   @param user           user name of the grantee
4927   @param host           host name of the grantee
4928   @param role           role name to grant
4929   @param with_admin     WITH ADMIN OPTION flag
4930   @param existing       the entry in the acl_roles_mappings hash or NULL.
4931                         it is never NULL if revoke_grant is true.
4932                         it is NULL when a new pair is added, it's not NULL
4933                         when an existing pair is updated.
4934   @param revoke_grant   true for REVOKE, false for GRANT
4935 */
4936 static int
replace_roles_mapping_table(TABLE * table,LEX_CSTRING * user,LEX_CSTRING * host,LEX_CSTRING * role,bool with_admin,ROLE_GRANT_PAIR * existing,bool revoke_grant)4937 replace_roles_mapping_table(TABLE *table, LEX_CSTRING *user, LEX_CSTRING *host,
4938                             LEX_CSTRING *role, bool with_admin,
4939                             ROLE_GRANT_PAIR *existing, bool revoke_grant)
4940 {
4941   DBUG_ENTER("replace_roles_mapping_table");
4942 
4943   uchar row_key[MAX_KEY_LENGTH];
4944   int error;
4945   table->use_all_columns();
4946   restore_record(table, s->default_values);
4947   table->field[0]->store(host->str, host->length, system_charset_info);
4948   table->field[1]->store(user->str, user->length, system_charset_info);
4949   table->field[2]->store(role->str, role->length, system_charset_info);
4950 
4951   DBUG_ASSERT(!revoke_grant || existing);
4952 
4953   if (existing) // delete or update
4954   {
4955     key_copy(row_key, table->record[0], table->key_info,
4956              table->key_info->key_length);
4957     if (table->file->ha_index_read_idx_map(table->record[1], 0, row_key,
4958                                            HA_WHOLE_KEY, HA_READ_KEY_EXACT))
4959     {
4960       /* No match */
4961       DBUG_RETURN(1);
4962     }
4963     if (revoke_grant && !with_admin)
4964     {
4965       if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
4966       {
4967         DBUG_PRINT("info", ("error deleting row '%s' '%s' '%s'",
4968                             host->str, user->str, role->str));
4969         goto table_error;
4970       }
4971     }
4972     else if (with_admin)
4973     {
4974       table->field[3]->store(!revoke_grant + 1);
4975 
4976       if (unlikely((error= table->file->ha_update_row(table->record[1],
4977                                                       table->record[0]))))
4978       {
4979         DBUG_PRINT("info", ("error updating row '%s' '%s' '%s'",
4980                             host->str, user->str, role->str));
4981         goto table_error;
4982       }
4983     }
4984     DBUG_RETURN(0);
4985   }
4986 
4987   table->field[3]->store(with_admin + 1);
4988 
4989   if (unlikely((error= table->file->ha_write_row(table->record[0]))))
4990   {
4991     DBUG_PRINT("info", ("error inserting row '%s' '%s' '%s'",
4992                         host->str, user->str, role->str));
4993     goto table_error;
4994   }
4995 
4996   /* all ok */
4997   DBUG_RETURN(0);
4998 
4999 table_error:
5000   DBUG_PRINT("info", ("table error"));
5001   table->file->print_error(error, MYF(0));
5002   DBUG_RETURN(1);
5003 }
5004 
5005 
5006 /**
5007   Updates the acl_roles_mappings hash
5008 
5009   @param user           user name of the grantee
5010   @param host           host name of the grantee
5011   @param role           role name to grant
5012   @param with_admin     WITH ADMIN OPTION flag
5013   @param existing       the entry in the acl_roles_mappings hash or NULL.
5014                         it is never NULL if revoke_grant is true.
5015                         it is NULL when a new pair is added, it's not NULL
5016                         when an existing pair is updated.
5017   @param revoke_grant   true for REVOKE, false for GRANT
5018 */
5019 static int
update_role_mapping(LEX_CSTRING * user,LEX_CSTRING * host,LEX_CSTRING * role,bool with_admin,ROLE_GRANT_PAIR * existing,bool revoke_grant)5020 update_role_mapping(LEX_CSTRING *user, LEX_CSTRING *host, LEX_CSTRING *role,
5021                     bool with_admin, ROLE_GRANT_PAIR *existing, bool revoke_grant)
5022 {
5023   if (revoke_grant)
5024   {
5025     if (with_admin)
5026     {
5027       existing->with_admin= false;
5028       return 0;
5029     }
5030     return my_hash_delete(&acl_roles_mappings, (uchar*)existing);
5031   }
5032 
5033   if (existing)
5034   {
5035     existing->with_admin|= with_admin;
5036     return 0;
5037   }
5038 
5039   /* allocate a new entry that will go in the hash */
5040   ROLE_GRANT_PAIR *hash_entry= new (&acl_memroot) ROLE_GRANT_PAIR;
5041   if (hash_entry->init(&acl_memroot, user->str, host->str,
5042                        role->str, with_admin))
5043     return 1;
5044   return my_hash_insert(&acl_roles_mappings, (uchar*) hash_entry);
5045 }
5046 
5047 static void
acl_update_proxy_user(ACL_PROXY_USER * new_value,bool is_revoke)5048 acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
5049 {
5050   mysql_mutex_assert_owner(&acl_cache->lock);
5051 
5052   DBUG_ENTER("acl_update_proxy_user");
5053   for (uint i= 0; i < acl_proxy_users.elements; i++)
5054   {
5055     ACL_PROXY_USER *acl_user=
5056       dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
5057 
5058     if (acl_user->pk_equals(new_value))
5059     {
5060       if (is_revoke)
5061       {
5062         DBUG_PRINT("info", ("deleting ACL_PROXY_USER"));
5063         delete_dynamic_element(&acl_proxy_users, i);
5064       }
5065       else
5066       {
5067         DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
5068         acl_user->set_data(new_value);
5069       }
5070       break;
5071     }
5072   }
5073   DBUG_VOID_RETURN;
5074 }
5075 
5076 
5077 static void
acl_insert_proxy_user(ACL_PROXY_USER * new_value)5078 acl_insert_proxy_user(ACL_PROXY_USER *new_value)
5079 {
5080   DBUG_ENTER("acl_insert_proxy_user");
5081   mysql_mutex_assert_owner(&acl_cache->lock);
5082   (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
5083   my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
5084            acl_proxy_users.elements,
5085            sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
5086   DBUG_VOID_RETURN;
5087 }
5088 
5089 
5090 static int
replace_proxies_priv_table(THD * thd,TABLE * table,const LEX_USER * user,const LEX_USER * proxied_user,bool with_grant_arg,bool revoke_grant)5091 replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
5092                          const LEX_USER *proxied_user, bool with_grant_arg,
5093                          bool revoke_grant)
5094 {
5095   bool old_row_exists= 0;
5096   int error;
5097   uchar user_key[MAX_KEY_LENGTH];
5098   ACL_PROXY_USER new_grant;
5099   char grantor[USER_HOST_BUFF_SIZE];
5100 
5101   DBUG_ENTER("replace_proxies_priv_table");
5102 
5103   if (!table)
5104   {
5105     my_error(ER_NO_SUCH_TABLE, MYF(0), MYSQL_SCHEMA_NAME.str,
5106              MYSQL_TABLE_NAME[PROXIES_PRIV_TABLE].str);
5107     DBUG_RETURN(-1);
5108   }
5109 
5110   /* Check if there is such a user in user table in memory? */
5111   if (!find_user_wild(user->host.str,user->user.str))
5112   {
5113     my_message(ER_PASSWORD_NO_MATCH,
5114                ER_THD(thd, ER_PASSWORD_NO_MATCH), MYF(0));
5115     DBUG_RETURN(-1);
5116   }
5117 
5118   table->use_all_columns();
5119   ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
5120                             &proxied_user->host, &proxied_user->user);
5121 
5122   key_copy(user_key, table->record[0], table->key_info,
5123            table->key_info->key_length);
5124 
5125   get_grantor(thd, grantor);
5126 
5127   if (unlikely((error= table->file->ha_index_init(0, 1))))
5128   {
5129     table->file->print_error(error, MYF(0));
5130     DBUG_PRINT("info", ("ha_index_init error"));
5131     DBUG_RETURN(-1);
5132   }
5133 
5134   if (table->file->ha_index_read_map(table->record[0], user_key,
5135                                      HA_WHOLE_KEY,
5136                                      HA_READ_KEY_EXACT))
5137   {
5138     DBUG_PRINT ("info", ("Row not found"));
5139     if (revoke_grant)
5140     { // no row, no revoke
5141       my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
5142       goto abort;
5143     }
5144     old_row_exists= 0;
5145     restore_record(table, s->default_values);
5146     ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
5147                                       &proxied_user->host,
5148                                       &proxied_user->user,
5149                                       with_grant_arg,
5150                                       grantor);
5151   }
5152   else
5153   {
5154     DBUG_PRINT("info", ("Row found"));
5155     old_row_exists= 1;
5156     store_record(table, record[1]);
5157   }
5158 
5159   if (old_row_exists)
5160   {
5161     /* update old existing row */
5162     if (!revoke_grant)
5163     {
5164       if (unlikely(error= table->file->ha_update_row(table->record[1],
5165                                                      table->record[0])) &&
5166           error != HA_ERR_RECORD_IS_THE_SAME)
5167 	goto table_error;			/* purecov: inspected */
5168     }
5169     else
5170     {
5171       if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
5172 	goto table_error;			/* purecov: inspected */
5173     }
5174   }
5175   else if (unlikely((error= table->file->ha_write_row(table->record[0]))))
5176   {
5177     DBUG_PRINT("info", ("error inserting the row"));
5178     if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
5179       goto table_error; /* purecov: inspected */
5180   }
5181 
5182   acl_cache->clear(1);				// Clear privilege cache
5183   if (old_row_exists)
5184   {
5185     new_grant.init(user->host.str, user->user.str,
5186                    proxied_user->host.str, proxied_user->user.str,
5187                    with_grant_arg);
5188     acl_update_proxy_user(&new_grant, revoke_grant);
5189   }
5190   else
5191   {
5192     new_grant.init(&acl_memroot, user->host.str, user->user.str,
5193                    proxied_user->host.str, proxied_user->user.str,
5194                    with_grant_arg);
5195     acl_insert_proxy_user(&new_grant);
5196   }
5197 
5198   table->file->ha_index_end();
5199   DBUG_RETURN(0);
5200 
5201   /* This could only happen if the grant tables got corrupted */
5202 table_error:
5203   DBUG_PRINT("info", ("table error"));
5204   table->file->print_error(error, MYF(0));	/* purecov: inspected */
5205 
5206 abort:
5207   DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
5208   table->file->ha_index_end();
5209   DBUG_RETURN(-1);
5210 }
5211 
5212 
5213 class GRANT_COLUMN :public Sql_alloc
5214 {
5215 public:
5216   char *column;
5217   privilege_t rights;
5218   privilege_t init_rights;
5219   uint key_length;
GRANT_COLUMN(String & c,privilege_t y)5220   GRANT_COLUMN(String &c, privilege_t y) :rights (y), init_rights(y)
5221   {
5222     column= (char*) memdup_root(&grant_memroot,c.ptr(), key_length=c.length());
5223   }
5224 
5225   /* this constructor assumes thas source->column is allocated in grant_memroot */
GRANT_COLUMN(GRANT_COLUMN * source)5226   GRANT_COLUMN(GRANT_COLUMN *source) : column(source->column),
5227     rights (source->rights), init_rights(NO_ACL), key_length(source->key_length) { }
5228 };
5229 
5230 
get_key_column(GRANT_COLUMN * buff,size_t * length,my_bool not_used)5231 static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
5232 			    my_bool not_used __attribute__((unused)))
5233 {
5234   *length=buff->key_length;
5235   return (uchar*) buff->column;
5236 }
5237 
5238 class GRANT_NAME :public Sql_alloc
5239 {
5240 public:
5241   acl_host_and_ip host;
5242   char *db, *user, *tname, *hash_key;
5243   privilege_t privs;
5244   privilege_t init_privs; /* privileges found in physical table */
5245   ulonglong sort;
5246   size_t key_length;
5247   GRANT_NAME(const char *h, const char *d,const char *u,
5248              const char *t, privilege_t p, bool is_routine);
5249   GRANT_NAME (TABLE *form, bool is_routine);
~GRANT_NAME()5250   virtual ~GRANT_NAME() {};
ok()5251   virtual bool ok() { return privs != NO_ACL; }
5252   void set_user_details(const char *h, const char *d,
5253                         const char *u, const char *t,
5254                         bool is_routine);
5255 };
5256 
5257 
get_access_value_from_val_int(Field * field)5258 static privilege_t get_access_value_from_val_int(Field *field)
5259 {
5260   return privilege_t(ALL_KNOWN_ACL & (ulonglong) field->val_int());
5261 }
5262 
5263 
5264 class GRANT_TABLE :public GRANT_NAME
5265 {
5266 public:
5267   privilege_t cols;
5268   privilege_t init_cols; /* privileges found in physical table */
5269   HASH hash_columns;
5270 
5271   GRANT_TABLE(const char *h, const char *d,const char *u,
5272               const char *t, privilege_t p, privilege_t c);
5273   GRANT_TABLE (TABLE *form, TABLE *col_privs);
5274   ~GRANT_TABLE();
ok()5275   bool ok() { return privs != NO_ACL || cols != NO_ACL; }
init_hash()5276   void init_hash()
5277   {
5278     my_hash_init2(key_memory_acl_memex, &hash_columns, 4, system_charset_info,
5279                   0, 0, 0, (my_hash_get_key) get_key_column, 0, 0, 0);
5280   }
5281 };
5282 
5283 
set_user_details(const char * h,const char * d,const char * u,const char * t,bool is_routine)5284 void GRANT_NAME::set_user_details(const char *h, const char *d,
5285                                   const char *u, const char *t,
5286                                   bool is_routine)
5287 {
5288   /* Host given by user */
5289   update_hostname(&host, strdup_root(&grant_memroot, h));
5290   if (db != d)
5291   {
5292     db= strdup_root(&grant_memroot, d);
5293     if (lower_case_table_names)
5294       my_casedn_str(files_charset_info, db);
5295   }
5296   user = strdup_root(&grant_memroot,u);
5297   sort=  get_magic_sort("hdu", host.hostname, db, user);
5298   if (tname != t)
5299   {
5300     tname= strdup_root(&grant_memroot, t);
5301     if (lower_case_table_names || is_routine)
5302       my_casedn_str(files_charset_info, tname);
5303   }
5304   key_length= strlen(d) + strlen(u)+ strlen(t)+3;
5305   hash_key=   (char*) alloc_root(&grant_memroot,key_length);
5306   strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
5307 }
5308 
GRANT_NAME(const char * h,const char * d,const char * u,const char * t,privilege_t p,bool is_routine)5309 GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
5310                        const char *t, privilege_t p, bool is_routine)
5311   :db(0), tname(0), privs(p), init_privs(p)
5312 {
5313   set_user_details(h, d, u, t, is_routine);
5314 }
5315 
GRANT_TABLE(const char * h,const char * d,const char * u,const char * t,privilege_t p,privilege_t c)5316 GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
5317                          const char *t, privilege_t p, privilege_t c)
5318   :GRANT_NAME(h,d,u,t,p, FALSE), cols(c), init_cols(NO_ACL)
5319 {
5320   init_hash();
5321 }
5322 
5323 /*
5324   create a new GRANT_TABLE entry for role inheritance. init_* fields are set
5325   to 0
5326 */
GRANT_NAME(TABLE * form,bool is_routine)5327 GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
5328  :privs(NO_ACL), init_privs(NO_ACL)
5329 {
5330   user= safe_str(get_field(&grant_memroot,form->field[2]));
5331 
5332   const char *hostname= get_field(&grant_memroot, form->field[0]);
5333   mysql_mutex_lock(&acl_cache->lock);
5334   if (!hostname && find_acl_role(user))
5335     hostname= "";
5336   mysql_mutex_unlock(&acl_cache->lock);
5337   update_hostname(&host, hostname);
5338 
5339   db=    get_field(&grant_memroot,form->field[1]);
5340   tname= get_field(&grant_memroot,form->field[3]);
5341   if (!db || !tname)
5342   {
5343     /* Wrong table row; Ignore it */
5344     return;					/* purecov: inspected */
5345   }
5346   sort=  get_magic_sort("hdu", host.hostname, db, user);
5347   if (lower_case_table_names)
5348   {
5349     my_casedn_str(files_charset_info, db);
5350   }
5351   if (lower_case_table_names || is_routine)
5352   {
5353     my_casedn_str(files_charset_info, tname);
5354   }
5355   key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
5356   hash_key=   (char*) alloc_root(&grant_memroot, key_length);
5357   strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
5358   privs = get_access_value_from_val_int(form->field[6]);
5359   privs = fix_rights_for_table(privs);
5360   init_privs= privs;
5361 }
5362 
5363 
GRANT_TABLE(TABLE * form,TABLE * col_privs)5364 GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
5365   :GRANT_NAME(form, FALSE), cols(NO_ACL), init_cols(NO_ACL)
5366 {
5367   uchar key[MAX_KEY_LENGTH];
5368 
5369   if (!db || !tname)
5370   {
5371     /* Wrong table row; Ignore it */
5372     my_hash_clear(&hash_columns);               /* allow for destruction */
5373     cols= NO_ACL;
5374     return;
5375   }
5376   cols= get_access_value_from_val_int(form->field[7]);
5377   cols= fix_rights_for_column(cols);
5378   /*
5379     Initial columns privileges are the same as column privileges on creation.
5380     In case of roles, the cols privilege bits can get inherited and thus
5381     cause the cols field to change. The init_cols field is always the same
5382     as the physical table entry
5383   */
5384   init_cols= cols;
5385 
5386   init_hash();
5387 
5388   if (cols)
5389   {
5390     uint key_prefix_len;
5391     KEY_PART_INFO *key_part= col_privs->key_info->key_part;
5392     col_privs->field[0]->store(host.hostname,
5393                                (uint) safe_strlen(host.hostname),
5394                                system_charset_info);
5395     col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
5396     col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
5397     col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
5398 
5399     key_prefix_len= (key_part[0].store_length +
5400                      key_part[1].store_length +
5401                      key_part[2].store_length +
5402                      key_part[3].store_length);
5403     key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
5404     col_privs->field[4]->store("",0, &my_charset_latin1);
5405 
5406     if (col_privs->file->ha_index_init(0, 1))
5407     {
5408       cols= NO_ACL;
5409       init_cols= NO_ACL;
5410       return;
5411     }
5412 
5413     if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
5414                                            (key_part_map)15,
5415                                            HA_READ_KEY_EXACT))
5416     {
5417       cols= NO_ACL; /* purecov: deadcode */
5418       init_cols= NO_ACL;
5419       col_privs->file->ha_index_end();
5420       return;
5421     }
5422     do
5423     {
5424       String *res,column_name;
5425       GRANT_COLUMN *mem_check;
5426       /* As column name is a string, we don't have to supply a buffer */
5427       res=col_privs->field[4]->val_str(&column_name);
5428       privilege_t priv= get_access_value_from_val_int(col_privs->field[6]);
5429       if (!(mem_check = new GRANT_COLUMN(*res,
5430                                          fix_rights_for_column(priv))))
5431       {
5432         /* Don't use this entry */
5433         privs= cols= init_privs= init_cols= NO_ACL;   /* purecov: deadcode */
5434         return;				/* purecov: deadcode */
5435       }
5436       if (my_hash_insert(&hash_columns, (uchar *) mem_check))
5437       {
5438         /* Invalidate this entry */
5439         privs= cols= init_privs= init_cols= NO_ACL;
5440         return;
5441       }
5442     } while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
5443              !key_cmp_if_same(col_privs,key,0,key_prefix_len));
5444     col_privs->file->ha_index_end();
5445   }
5446 }
5447 
5448 
~GRANT_TABLE()5449 GRANT_TABLE::~GRANT_TABLE()
5450 {
5451   my_hash_free(&hash_columns);
5452 }
5453 
5454 
get_grant_table(GRANT_NAME * buff,size_t * length,my_bool not_used)5455 static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
5456 			     my_bool not_used __attribute__((unused)))
5457 {
5458   *length=buff->key_length;
5459   return (uchar*) buff->hash_key;
5460 }
5461 
5462 
free_grant_table(GRANT_TABLE * grant_table)5463 static void free_grant_table(GRANT_TABLE *grant_table)
5464 {
5465   grant_table->~GRANT_TABLE();
5466 }
5467 
5468 
5469 /* Search after a matching grant. Prefer exact grants before not exact ones */
5470 
name_hash_search(HASH * name_hash,const char * host,const char * ip,const char * db,const char * user,const char * tname,bool exact,bool name_tolower)5471 static GRANT_NAME *name_hash_search(HASH *name_hash,
5472                                     const char *host,const char* ip,
5473                                     const char *db,
5474                                     const char *user, const char *tname,
5475                                     bool exact, bool name_tolower)
5476 {
5477   char helping[SAFE_NAME_LEN*2+USERNAME_LENGTH+3];
5478   char *hend = helping + sizeof(helping);
5479   uint len;
5480   GRANT_NAME *grant_name,*found=0;
5481   HASH_SEARCH_STATE state;
5482 
5483   char *db_ptr= strmov(helping, user) + 1;
5484   char *tname_ptr= strnmov(db_ptr, db, hend - db_ptr) + 1;
5485   if (tname_ptr > hend)
5486     return 0; // invalid name = not found
5487   char *end= strnmov(tname_ptr, tname, hend - tname_ptr) + 1;
5488   if (end > hend)
5489     return 0; // invalid name = not found
5490 
5491   len  = (uint) (end - helping);
5492   if (name_tolower)
5493     my_casedn_str(files_charset_info, tname_ptr);
5494   for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
5495                                                len, &state);
5496        grant_name ;
5497        grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
5498                                               len, &state))
5499   {
5500     if (exact)
5501     {
5502       if (!grant_name->host.hostname ||
5503           (host &&
5504 	   !my_strcasecmp(system_charset_info, host,
5505                           grant_name->host.hostname)) ||
5506 	  (ip && !strcmp(ip, grant_name->host.hostname)))
5507 	return grant_name;
5508     }
5509     else
5510     {
5511       if (compare_hostname(&grant_name->host, host, ip) &&
5512           (!found || found->sort < grant_name->sort))
5513 	found=grant_name;					// Host ok
5514     }
5515   }
5516   return found;
5517 }
5518 
5519 
5520 static GRANT_NAME *
routine_hash_search(const char * host,const char * ip,const char * db,const char * user,const char * tname,const Sp_handler * sph,bool exact)5521 routine_hash_search(const char *host, const char *ip, const char *db,
5522                     const char *user, const char *tname, const Sp_handler *sph,
5523                     bool exact)
5524 {
5525   return (GRANT_NAME*)
5526     name_hash_search(sph->get_priv_hash(),
5527 		     host, ip, db, user, tname, exact, TRUE);
5528 }
5529 
5530 
5531 static GRANT_TABLE *
table_hash_search(const char * host,const char * ip,const char * db,const char * user,const char * tname,bool exact)5532 table_hash_search(const char *host, const char *ip, const char *db,
5533 		  const char *user, const char *tname, bool exact)
5534 {
5535   return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
5536 					 user, tname, exact, FALSE);
5537 }
5538 
column_priv_insert(GRANT_TABLE * grant)5539 static bool column_priv_insert(GRANT_TABLE *grant)
5540 {
5541   return my_hash_insert(&column_priv_hash,(uchar*) grant);
5542 }
5543 
5544 static GRANT_COLUMN *
column_hash_search(GRANT_TABLE * t,const char * cname,size_t length)5545 column_hash_search(GRANT_TABLE *t, const char *cname, size_t length)
5546 {
5547   if (!my_hash_inited(&t->hash_columns))
5548     return (GRANT_COLUMN*) 0;
5549   return (GRANT_COLUMN*)my_hash_search(&t->hash_columns, (uchar*)cname, length);
5550 }
5551 
5552 
replace_column_table(GRANT_TABLE * g_t,TABLE * table,const LEX_USER & combo,List<LEX_COLUMN> & columns,const char * db,const char * table_name,privilege_t rights,bool revoke_grant)5553 static int replace_column_table(GRANT_TABLE *g_t,
5554 				TABLE *table, const LEX_USER &combo,
5555 				List <LEX_COLUMN> &columns,
5556 				const char *db, const char *table_name,
5557 				privilege_t rights, bool revoke_grant)
5558 {
5559   int result=0;
5560   uchar key[MAX_KEY_LENGTH];
5561   uint key_prefix_length;
5562   KEY_PART_INFO *key_part= table->key_info->key_part;
5563   DBUG_ENTER("replace_column_table");
5564 
5565   table->use_all_columns();
5566   table->field[0]->store(combo.host.str,combo.host.length,
5567                          system_charset_info);
5568   table->field[1]->store(db,(uint) strlen(db),
5569                          system_charset_info);
5570   table->field[2]->store(combo.user.str,combo.user.length,
5571                          system_charset_info);
5572   table->field[3]->store(table_name,(uint) strlen(table_name),
5573                          system_charset_info);
5574 
5575   /* Get length of 4 first key parts */
5576   key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
5577                       key_part[2].store_length + key_part[3].store_length);
5578   key_copy(key, table->record[0], table->key_info, key_prefix_length);
5579 
5580   rights&= COL_ACLS;				// Only ACL for columns
5581 
5582   /* first fix privileges for all columns in column list */
5583 
5584   List_iterator <LEX_COLUMN> iter(columns);
5585   class LEX_COLUMN *column;
5586 
5587   int error= table->file->ha_index_init(0, 1);
5588   if (unlikely(error))
5589   {
5590     table->file->print_error(error, MYF(0));
5591     DBUG_RETURN(-1);
5592   }
5593 
5594   while ((column= iter++))
5595   {
5596     privilege_t privileges= column->rights;
5597     bool old_row_exists=0;
5598     uchar user_key[MAX_KEY_LENGTH];
5599 
5600     key_restore(table->record[0],key,table->key_info,
5601                 key_prefix_length);
5602     table->field[4]->store(column->column.ptr(), column->column.length(),
5603                            system_charset_info);
5604     /* Get key for the first 4 columns */
5605     key_copy(user_key, table->record[0], table->key_info,
5606              table->key_info->key_length);
5607 
5608     if (table->file->ha_index_read_map(table->record[0], user_key,
5609                                        HA_WHOLE_KEY, HA_READ_KEY_EXACT))
5610     {
5611       if (revoke_grant)
5612       {
5613 	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
5614                  combo.user.str, combo.host.str,
5615                  table_name);                   /* purecov: inspected */
5616 	result= -1;                             /* purecov: inspected */
5617 	continue;                               /* purecov: inspected */
5618       }
5619       old_row_exists = 0;
5620       restore_record(table, s->default_values);		// Get empty record
5621       key_restore(table->record[0],key,table->key_info,
5622                   key_prefix_length);
5623       table->field[4]->store(column->column.ptr(),column->column.length(),
5624                              system_charset_info);
5625     }
5626     else
5627     {
5628       privilege_t tmp= get_access_value_from_val_int(table->field[6]);
5629       tmp=fix_rights_for_column(tmp);
5630 
5631       if (revoke_grant)
5632 	privileges = tmp & ~(privileges | rights);
5633       else
5634 	privileges |= tmp;
5635       old_row_exists = 1;
5636       store_record(table,record[1]);			// copy original row
5637     }
5638 
5639     table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
5640 
5641     if (old_row_exists)
5642     {
5643       GRANT_COLUMN *grant_column;
5644       if (privileges)
5645 	error=table->file->ha_update_row(table->record[1],table->record[0]);
5646       else
5647 	error=table->file->ha_delete_row(table->record[1]);
5648       if (unlikely(error) && error != HA_ERR_RECORD_IS_THE_SAME)
5649       {
5650 	table->file->print_error(error,MYF(0)); /* purecov: inspected */
5651 	result= -1;				/* purecov: inspected */
5652 	goto end;				/* purecov: inspected */
5653       }
5654       else
5655         error= 0;
5656       grant_column= column_hash_search(g_t, column->column.ptr(),
5657                                        column->column.length());
5658       if (grant_column)				// Should always be true
5659 	grant_column->rights= privileges;	// Update hash
5660     }
5661     else					// new grant
5662     {
5663       GRANT_COLUMN *grant_column;
5664       if (unlikely((error=table->file->ha_write_row(table->record[0]))))
5665       {
5666 	table->file->print_error(error,MYF(0)); /* purecov: inspected */
5667 	result= -1;				/* purecov: inspected */
5668 	goto end;				/* purecov: inspected */
5669       }
5670       grant_column= new GRANT_COLUMN(column->column,privileges);
5671       if (my_hash_insert(&g_t->hash_columns,(uchar*) grant_column))
5672       {
5673         result= -1;
5674         goto end;
5675       }
5676     }
5677   }
5678 
5679   /*
5680     If revoke of privileges on the table level, remove all such privileges
5681     for all columns
5682   */
5683 
5684   if (revoke_grant)
5685   {
5686     uchar user_key[MAX_KEY_LENGTH];
5687     key_copy(user_key, table->record[0], table->key_info,
5688              key_prefix_length);
5689 
5690     if (table->file->ha_index_read_map(table->record[0], user_key,
5691                                        (key_part_map)15,
5692                                        HA_READ_KEY_EXACT))
5693       goto end;
5694 
5695     /* Scan through all rows with the same host,db,user and table */
5696     do
5697     {
5698       privilege_t privileges = get_access_value_from_val_int(table->field[6]);
5699       privileges=fix_rights_for_column(privileges);
5700       store_record(table,record[1]);
5701 
5702       if (privileges & rights)	// is in this record the priv to be revoked ??
5703       {
5704 	GRANT_COLUMN *grant_column = NULL;
5705 	char  colum_name_buf[HOSTNAME_LENGTH+1];
5706 	String column_name(colum_name_buf,sizeof(colum_name_buf),
5707                            system_charset_info);
5708 
5709 	privileges&= ~rights;
5710 	table->field[6]->store((longlong)
5711 	                       get_rights_for_column(privileges), TRUE);
5712 	table->field[4]->val_str(&column_name);
5713 	grant_column = column_hash_search(g_t,
5714 					  column_name.ptr(),
5715 					  column_name.length());
5716 	if (privileges)
5717 	{
5718 	  int tmp_error;
5719 	  if (unlikely(tmp_error=
5720                        table->file->ha_update_row(table->record[1],
5721                                                   table->record[0])) &&
5722               tmp_error != HA_ERR_RECORD_IS_THE_SAME)
5723 	  {					/* purecov: deadcode */
5724 	    table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
5725 	    result= -1;				/* purecov: deadcode */
5726 	    goto end;				/* purecov: deadcode */
5727 	  }
5728 	  if (grant_column)
5729           {
5730             grant_column->rights  = privileges; // Update hash
5731             grant_column->init_rights = privileges;
5732           }
5733 	}
5734 	else
5735 	{
5736 	  int tmp_error;
5737 	  if (unlikely((tmp_error=
5738                         table->file->ha_delete_row(table->record[1]))))
5739 	  {					/* purecov: deadcode */
5740 	    table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
5741 	    result= -1;				/* purecov: deadcode */
5742 	    goto end;				/* purecov: deadcode */
5743 	  }
5744 	  if (grant_column)
5745 	    my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
5746 	}
5747       }
5748     } while (!table->file->ha_index_next(table->record[0]) &&
5749 	     !key_cmp_if_same(table, key, 0, key_prefix_length));
5750   }
5751 
5752 end:
5753   table->file->ha_index_end();
5754   DBUG_RETURN(result);
5755 }
5756 
get_grantor(THD * thd,char * grantor)5757 static inline void get_grantor(THD *thd, char *grantor)
5758 {
5759   const char *user= thd->security_ctx->user;
5760   const char *host= thd->security_ctx->host_or_ip;
5761 
5762 #if defined(HAVE_REPLICATION)
5763   if (thd->slave_thread && thd->has_invoker())
5764   {
5765     user= thd->get_invoker_user().str;
5766     host= thd->get_invoker_host().str;
5767   }
5768 #endif
5769   strxmov(grantor, user, "@", host, NullS);
5770 }
5771 
5772 
5773 /**
5774    Revoke rights from a grant table entry.
5775 
5776    @return 0  ok
5777    @return 1  fatal error (error given)
5778    @return -1 grant table was revoked
5779 */
5780 
replace_table_table(THD * thd,GRANT_TABLE * grant_table,TABLE * table,const LEX_USER & combo,const char * db,const char * table_name,privilege_t rights,privilege_t col_rights,bool revoke_grant)5781 static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
5782 			       TABLE *table, const LEX_USER &combo,
5783 			       const char *db, const char *table_name,
5784 			       privilege_t rights, privilege_t col_rights,
5785 			       bool revoke_grant)
5786 {
5787   char grantor[USER_HOST_BUFF_SIZE];
5788   int old_row_exists = 1;
5789   int error=0;
5790   privilege_t store_table_rights(NO_ACL), store_col_rights(NO_ACL);
5791   uchar user_key[MAX_KEY_LENGTH];
5792   DBUG_ENTER("replace_table_table");
5793 
5794   get_grantor(thd, grantor);
5795   /*
5796     The following should always succeed as new users are created before
5797     this function is called!
5798   */
5799   if (!find_user_wild(combo.host.str,combo.user.str))
5800   {
5801     if (!combo.host.length && !find_acl_role(combo.user.str))
5802     {
5803       my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH),
5804                  MYF(0)); /* purecov: deadcode */
5805       DBUG_RETURN(1);                            /* purecov: deadcode */
5806     }
5807   }
5808 
5809   table->use_all_columns();
5810   restore_record(table, s->default_values);     // Get empty record
5811   table->field[0]->store(combo.host.str,combo.host.length,
5812                          system_charset_info);
5813   table->field[1]->store(db,(uint) strlen(db), system_charset_info);
5814   table->field[2]->store(combo.user.str,combo.user.length,
5815                          system_charset_info);
5816   table->field[3]->store(table_name,(uint) strlen(table_name),
5817                          system_charset_info);
5818   store_record(table,record[1]);			// store at pos 1
5819   key_copy(user_key, table->record[0], table->key_info,
5820            table->key_info->key_length);
5821 
5822   if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
5823                                          HA_WHOLE_KEY,
5824                                          HA_READ_KEY_EXACT))
5825   {
5826     /*
5827       The following should never happen as we first check the in memory
5828       grant tables for the user.  There is however always a small change that
5829       the user has modified the grant tables directly.
5830     */
5831     if (revoke_grant)
5832     { // no row, no revoke
5833       my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
5834                combo.user.str, combo.host.str,
5835                table_name);		        /* purecov: deadcode */
5836       DBUG_RETURN(1);				/* purecov: deadcode */
5837     }
5838     old_row_exists = 0;
5839     restore_record(table,record[1]);			// Get saved record
5840   }
5841 
5842   store_table_rights= get_rights_for_table(rights);
5843   store_col_rights=   get_rights_for_column(col_rights);
5844   if (old_row_exists)
5845   {
5846     store_record(table,record[1]);
5847     privilege_t j= get_access_value_from_val_int(table->field[6]);
5848     privilege_t k= get_access_value_from_val_int(table->field[7]);
5849 
5850     if (revoke_grant)
5851     {
5852       /* column rights are already fixed in mysql_table_grant */
5853       store_table_rights=j & ~store_table_rights;
5854     }
5855     else
5856     {
5857       store_table_rights|= j;
5858       store_col_rights|=   k;
5859     }
5860   }
5861 
5862   table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
5863   table->field[6]->store((longlong) store_table_rights, TRUE);
5864   table->field[7]->store((longlong) store_col_rights, TRUE);
5865   rights=fix_rights_for_table(store_table_rights);
5866   col_rights=fix_rights_for_column(store_col_rights);
5867 
5868   if (old_row_exists)
5869   {
5870     if (store_table_rights || store_col_rights)
5871     {
5872       if (unlikely(error=table->file->ha_update_row(table->record[1],
5873                                                     table->record[0])) &&
5874           error != HA_ERR_RECORD_IS_THE_SAME)
5875 	goto table_error;			/* purecov: deadcode */
5876     }
5877     else if (unlikely((error = table->file->ha_delete_row(table->record[1]))))
5878       goto table_error;				/* purecov: deadcode */
5879   }
5880   else
5881   {
5882     error=table->file->ha_write_row(table->record[0]);
5883     if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
5884       goto table_error;				/* purecov: deadcode */
5885   }
5886 
5887   if (rights | col_rights)
5888   {
5889     grant_table->init_privs= rights;
5890     grant_table->init_cols=  col_rights;
5891 
5892     grant_table->privs= rights;
5893     grant_table->cols=	col_rights;
5894   }
5895   else
5896   {
5897     my_hash_delete(&column_priv_hash,(uchar*) grant_table);
5898     DBUG_RETURN(-1);                            // Entry revoked
5899   }
5900   DBUG_RETURN(0);
5901 
5902   /* This should never happen */
5903 table_error:
5904   table->file->print_error(error,MYF(0)); /* purecov: deadcode */
5905   DBUG_RETURN(1); /* purecov: deadcode */
5906 }
5907 
5908 
5909 /**
5910   @retval       0  success
5911   @retval      -1  error
5912 */
replace_routine_table(THD * thd,GRANT_NAME * grant_name,TABLE * table,const LEX_USER & combo,const char * db,const char * routine_name,const Sp_handler * sph,privilege_t rights,bool revoke_grant)5913 static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
5914 			      TABLE *table, const LEX_USER &combo,
5915 			      const char *db, const char *routine_name,
5916 			      const Sp_handler *sph,
5917 			      privilege_t rights, bool revoke_grant)
5918 {
5919   char grantor[USER_HOST_BUFF_SIZE];
5920   int old_row_exists= 1;
5921   int error=0;
5922   HASH *hash= sph->get_priv_hash();
5923   DBUG_ENTER("replace_routine_table");
5924 
5925   if (!table)
5926   {
5927     my_error(ER_NO_SUCH_TABLE, MYF(0), MYSQL_SCHEMA_NAME.str,
5928              MYSQL_TABLE_NAME[PROCS_PRIV_TABLE].str);
5929     DBUG_RETURN(-1);
5930   }
5931 
5932   if (revoke_grant && !grant_name->init_privs) // only inherited role privs
5933   {
5934     my_hash_delete(hash, (uchar*) grant_name);
5935     DBUG_RETURN(0);
5936   }
5937 
5938   get_grantor(thd, grantor);
5939   /*
5940     New users are created before this function is called.
5941 
5942     There may be some cases where a routine's definer is removed but the
5943     routine remains.
5944   */
5945 
5946   table->use_all_columns();
5947   restore_record(table, s->default_values);            // Get empty record
5948   table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
5949   table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
5950   table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
5951   table->field[3]->store(routine_name,(uint) strlen(routine_name),
5952                          &my_charset_latin1);
5953   table->field[4]->store((longlong) sph->type(), true);
5954   store_record(table,record[1]);			// store at pos 1
5955 
5956   if (table->file->ha_index_read_idx_map(table->record[0], 0,
5957                                          (uchar*) table->field[0]->ptr,
5958                                          HA_WHOLE_KEY,
5959                                          HA_READ_KEY_EXACT))
5960   {
5961     /*
5962       The following should never happen as we first check the in memory
5963       grant tables for the user.  There is however always a small change that
5964       the user has modified the grant tables directly.
5965 
5966       Also, there is also a second posibility that this routine entry
5967       is created for a role by being inherited from a granted role.
5968     */
5969     if (revoke_grant)
5970     { // no row, no revoke
5971       my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
5972                combo.user.str, combo.host.str, routine_name);
5973       DBUG_RETURN(-1);
5974     }
5975     old_row_exists= 0;
5976     restore_record(table,record[1]);			// Get saved record
5977   }
5978 
5979   privilege_t store_proc_rights= get_rights_for_procedure(rights);
5980   if (old_row_exists)
5981   {
5982     store_record(table,record[1]);
5983     privilege_t j= get_access_value_from_val_int(table->field[6]);
5984 
5985     if (revoke_grant)
5986     {
5987       /* column rights are already fixed in mysql_table_grant */
5988       store_proc_rights=j & ~store_proc_rights;
5989     }
5990     else
5991     {
5992       store_proc_rights|= j;
5993     }
5994   }
5995 
5996   table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
5997   table->field[6]->store((longlong) store_proc_rights, TRUE);
5998   rights=fix_rights_for_procedure(store_proc_rights);
5999 
6000   if (old_row_exists)
6001   {
6002     if (store_proc_rights)
6003     {
6004       if (unlikely(error=table->file->ha_update_row(table->record[1],
6005                                                     table->record[0])) &&
6006                    error != HA_ERR_RECORD_IS_THE_SAME)
6007 	goto table_error;
6008     }
6009     else if (unlikely((error= table->file->ha_delete_row(table->record[1]))))
6010       goto table_error;
6011   }
6012   else
6013   {
6014     error=table->file->ha_write_row(table->record[0]);
6015     if (unlikely(table->file->is_fatal_error(error, HA_CHECK_DUP_KEY)))
6016       goto table_error;
6017   }
6018 
6019   if (rights)
6020   {
6021     grant_name->init_privs= rights;
6022     grant_name->privs= rights;
6023   }
6024   else
6025   {
6026     my_hash_delete(hash, (uchar*) grant_name);
6027   }
6028   DBUG_RETURN(0);
6029 
6030   /* This should never happen */
6031 table_error:
6032   table->file->print_error(error,MYF(0));
6033   DBUG_RETURN(-1);
6034 }
6035 
6036 
6037 /*****************************************************************
6038   Role privilege propagation and graph traversal functionality
6039 
6040   According to the SQL standard, a role can be granted to a role,
6041   thus role grants can create an arbitrarily complex directed acyclic
6042   graph (a standard explicitly specifies that cycles are not allowed).
6043 
6044   When a privilege is granted to a role, it becomes available to all grantees.
6045   The code below recursively traverses a DAG of role grants, propagating
6046   privilege changes.
6047 
6048   The traversal function can work both ways, from roles to grantees or
6049   from grantees to roles. The first is used for privilege propagation,
6050   the second - for SHOW GRANTS and I_S.APPLICABLE_ROLES
6051 
6052   The role propagation code is smart enough to propagate only privilege
6053   changes to one specific database, table, or routine, if only they
6054   were changed (like in GRANT ... ON ... TO ...) or it can propagate
6055   everything (on startup or after FLUSH PRIVILEGES).
6056 
6057   It traverses only a subgraph that's accessible from the modified role,
6058   only visiting roles that can be possibly affected by the GRANT statement.
6059 
6060   Additionally, it stops traversal early, if this particular GRANT statement
6061   didn't result in any changes of privileges (e.g. both role1 and role2
6062   are granted to the role3, both role1 and role2 have SELECT privilege.
6063   if SELECT is revoked from role1 it won't change role3 privileges,
6064   so we won't traverse from role3 to its grantees).
6065 ******************************************************************/
6066 struct PRIVS_TO_MERGE
6067 {
6068   enum what
6069   {
6070     ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC, PACKAGE_SPEC, PACKAGE_BODY
6071   } what;
6072   const char *db, *name;
6073 };
6074 
6075 
sp_privs_to_merge(enum_sp_type type)6076 static enum PRIVS_TO_MERGE::what sp_privs_to_merge(enum_sp_type type)
6077 {
6078   switch (type) {
6079   case SP_TYPE_FUNCTION:
6080     return PRIVS_TO_MERGE::FUNC;
6081   case SP_TYPE_PROCEDURE:
6082     return PRIVS_TO_MERGE::PROC;
6083   case SP_TYPE_PACKAGE:
6084     return PRIVS_TO_MERGE::PACKAGE_SPEC;
6085   case SP_TYPE_PACKAGE_BODY:
6086     return PRIVS_TO_MERGE::PACKAGE_BODY;
6087   case SP_TYPE_EVENT:
6088   case SP_TYPE_TRIGGER:
6089     break;
6090   }
6091   DBUG_ASSERT(0);
6092   return PRIVS_TO_MERGE::PROC;
6093 }
6094 
6095 
init_role_for_merging(ACL_ROLE * role,void * context)6096 static int init_role_for_merging(ACL_ROLE *role, void *context)
6097 {
6098   role->counter= 0;
6099   return 0;
6100 }
6101 
count_subgraph_nodes(ACL_ROLE * role,ACL_ROLE * grantee,void * context)6102 static int count_subgraph_nodes(ACL_ROLE *role, ACL_ROLE *grantee, void *context)
6103 {
6104   grantee->counter++;
6105   return 0;
6106 }
6107 
6108 static int merge_role_privileges(ACL_ROLE *, ACL_ROLE *, void *);
6109 
6110 /**
6111   rebuild privileges of all affected roles
6112 
6113   entry point into role privilege propagation. after privileges of the
6114   'role' were changed, this function rebuilds privileges of all affected roles
6115   as necessary.
6116 */
propagate_role_grants(ACL_ROLE * role,enum PRIVS_TO_MERGE::what what,const char * db=0,const char * name=0)6117 static void propagate_role_grants(ACL_ROLE *role,
6118                                   enum PRIVS_TO_MERGE::what what,
6119                                   const char *db= 0, const char *name= 0)
6120 {
6121   if (!role)
6122     return;
6123 
6124   mysql_mutex_assert_owner(&acl_cache->lock);
6125   PRIVS_TO_MERGE data= { what, db, name };
6126 
6127   /*
6128      Changing privileges of a role causes all other roles that had
6129      this role granted to them to have their rights invalidated.
6130 
6131      We need to rebuild all roles' related access bits.
6132 
6133      This cannot be a simple depth-first search, instead we have to merge
6134      privieges for all roles granted to a specific grantee, *before*
6135      merging privileges for this grantee. In other words, we must visit all
6136      parent nodes of a specific node, before descencing into this node.
6137 
6138      For example, if role1 is granted to role2 and role3, and role3 is
6139      granted to role2, after "GRANT ... role1", we cannot merge privileges
6140      for role2, until role3 is merged.  The counter will be 0 for role1, 2
6141      for role2, 1 for role3. Traversal will start from role1, go to role2,
6142      decrement the counter, backtrack, go to role3, merge it, go to role2
6143      again, merge it.
6144 
6145      And the counter is not just "all parent nodes", but only parent nodes
6146      that are part of the subgraph we're interested in. For example, if
6147      both roleA and roleB are granted to roleC, then roleC has two parent
6148      nodes. But when granting a privilege to roleA, we're only looking at a
6149      subgraph that includes roleA and roleC (roleB cannot be possibly
6150      affected by that grant statement). In this subgraph roleC has only one
6151      parent.
6152 
6153      (on the other hand, in acl_load we want to update all roles, and
6154      the counter is exactly equal to the number of all parent nodes)
6155 
6156      Thus, we do two graph traversals here. First we only count parents
6157      that are part of the subgraph. On the second traversal we decrement
6158      the counter and actually merge privileges for a node when a counter
6159      drops to zero.
6160   */
6161   traverse_role_graph_up(role, &data, init_role_for_merging, count_subgraph_nodes);
6162   traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
6163 }
6164 
6165 
6166 // State of a node during a Depth First Search exploration
6167 struct NODE_STATE
6168 {
6169   ACL_USER_BASE *node_data; /* pointer to the node data */
6170   uint neigh_idx;           /* the neighbour that needs to be evaluated next */
6171 };
6172 
6173 /**
6174   Traverse the role grant graph and invoke callbacks at the specified points.
6175 
6176   @param user           user or role to start traversal from
6177   @param context        opaque parameter to pass to callbacks
6178   @param offset         offset to ACL_ROLE::parent_grantee or to
6179                         ACL_USER_BASE::role_grants. Depending on this value,
6180                         traversal will go from roles to grantees or from
6181                         grantees to roles.
6182   @param on_node        called when a node is visited for the first time.
6183                         Returning a value <0 will abort the traversal.
6184   @param on_edge        called for every edge in the graph, when traversal
6185                         goes from a node to a neighbour node.
6186                         Returning <0 will abort the traversal. Returning >0
6187                         will make the traversal not to follow this edge.
6188 
6189   @note
6190   The traverse method is a DEPTH FIRST SEARCH, but callbacks can influence
6191   that (on_edge returning >0 value).
6192 
6193   @note
6194   This function should not be called directly, use
6195   traverse_role_graph_up() and traverse_role_graph_down() instead.
6196 
6197   @retval 0                 traversal finished successfully
6198   @retval ROLE_CYCLE_FOUND  traversal aborted, cycle detected
6199   @retval <0                traversal was aborted, because a callback returned
6200                             this error code
6201 */
traverse_role_graph_impl(ACL_USER_BASE * user,void * context,off_t offset,int (* on_node)(ACL_USER_BASE * role,void * context),int (* on_edge)(ACL_USER_BASE * current,ACL_ROLE * neighbour,void * context))6202 static int traverse_role_graph_impl(ACL_USER_BASE *user, void *context,
6203        off_t offset,
6204        int (*on_node) (ACL_USER_BASE *role, void *context),
6205        int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
6206 {
6207   DBUG_ENTER("traverse_role_graph_impl");
6208   DBUG_ASSERT(user);
6209   DBUG_PRINT("enter",("role: '%s'", user->user.str));
6210   /*
6211      The search operation should always leave the ROLE_ON_STACK and
6212      ROLE_EXPLORED flags clean for all nodes involved in the search
6213   */
6214   DBUG_ASSERT(!(user->flags & ROLE_ON_STACK));
6215   DBUG_ASSERT(!(user->flags & ROLE_EXPLORED));
6216   mysql_mutex_assert_owner(&acl_cache->lock);
6217 
6218   /*
6219      Stack used to simulate the recursive calls of DFS.
6220      It uses a Dynamic_array to reduce the number of
6221      malloc calls to a minimum
6222   */
6223   Dynamic_array<NODE_STATE> stack(20,50);
6224   Dynamic_array<ACL_USER_BASE *> to_clear(20,50);
6225   NODE_STATE state;     /* variable used to insert elements in the stack */
6226   int result= 0;
6227 
6228   state.neigh_idx= 0;
6229   state.node_data= user;
6230   user->flags|= ROLE_ON_STACK;
6231 
6232   stack.push(state);
6233   to_clear.push(user);
6234 
6235   user->flags|= ROLE_OPENED;
6236   if (on_node && ((result= on_node(user, context)) < 0))
6237     goto end;
6238 
6239   while (stack.elements())
6240   {
6241     NODE_STATE *curr_state= stack.back();
6242 
6243     DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
6244 
6245     ACL_USER_BASE *current= curr_state->node_data;
6246     ACL_USER_BASE *neighbour= NULL;
6247     DBUG_PRINT("info", ("Examining role %s", current->user.str));
6248     /*
6249       Iterate through the neighbours until a first valid jump-to
6250       neighbour is found
6251     */
6252     bool found= FALSE;
6253     uint i;
6254     DYNAMIC_ARRAY *array= (DYNAMIC_ARRAY *)(((char*)current) + offset);
6255 
6256     DBUG_ASSERT(array == &current->role_grants || current->flags & IS_ROLE);
6257     for (i= curr_state->neigh_idx; i < array->elements; i++)
6258     {
6259       neighbour= *(dynamic_element(array, i, ACL_ROLE**));
6260       if (!(neighbour->flags & IS_ROLE))
6261         continue;
6262 
6263       DBUG_PRINT("info", ("Examining neighbour role %s", neighbour->user.str));
6264 
6265       /* check if it forms a cycle */
6266       if (neighbour->flags & ROLE_ON_STACK)
6267       {
6268         DBUG_PRINT("info", ("Found cycle"));
6269         result= ROLE_CYCLE_FOUND;
6270         goto end;
6271       }
6272 
6273       if (!(neighbour->flags & ROLE_OPENED))
6274       {
6275         neighbour->flags|= ROLE_OPENED;
6276         to_clear.push(neighbour);
6277         if (on_node && ((result= on_node(neighbour, context)) < 0))
6278           goto end;
6279       }
6280 
6281       if (on_edge)
6282       {
6283         result= on_edge(current, (ACL_ROLE*)neighbour, context);
6284         if (result < 0)
6285           goto end;
6286         if (result > 0)
6287           continue;
6288       }
6289 
6290       /* Check if it was already explored, in that case, move on */
6291       if (neighbour->flags & ROLE_EXPLORED)
6292         continue;
6293 
6294       found= TRUE;
6295       break;
6296     }
6297 
6298     /* found states that we have found a node to jump next into */
6299     if (found)
6300     {
6301       curr_state->neigh_idx= i + 1;
6302 
6303       /* some sanity checks */
6304       DBUG_ASSERT(!(neighbour->flags & ROLE_ON_STACK));
6305 
6306       /* add the neighbour on the stack */
6307       neighbour->flags|= ROLE_ON_STACK;
6308       state.neigh_idx= 0;
6309       state.node_data= neighbour;
6310       stack.push(state);
6311     }
6312     else
6313     {
6314       /* Make sure we got a correct node */
6315       DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
6316       /* Finished with exploring the current node, pop it off the stack */
6317       curr_state= &stack.pop();
6318       curr_state->node_data->flags&= ~ROLE_ON_STACK; /* clear the on-stack bit */
6319       curr_state->node_data->flags|= ROLE_EXPLORED;
6320     }
6321   }
6322 
6323 end:
6324   /* Cleanup */
6325   for (uint i= 0; i < to_clear.elements(); i++)
6326   {
6327     ACL_USER_BASE *current= to_clear.at(i);
6328     DBUG_ASSERT(current->flags & (ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED));
6329     current->flags&= ~(ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED);
6330   }
6331   DBUG_RETURN(result);
6332 }
6333 
6334 /**
6335   Traverse the role grant graph, going from a role to its grantees.
6336 
6337   This is used to propagate changes in privileges, for example,
6338   when GRANT or REVOKE is issued for a role.
6339 */
6340 
traverse_role_graph_up(ACL_ROLE * role,void * context,int (* on_node)(ACL_ROLE * role,void * context),int (* on_edge)(ACL_ROLE * current,ACL_ROLE * neighbour,void * context))6341 static int traverse_role_graph_up(ACL_ROLE *role, void *context,
6342        int (*on_node) (ACL_ROLE *role, void *context),
6343        int (*on_edge) (ACL_ROLE *current, ACL_ROLE *neighbour, void *context))
6344 {
6345   return traverse_role_graph_impl(role, context,
6346                     my_offsetof(ACL_ROLE, parent_grantee),
6347                     (int (*)(ACL_USER_BASE *, void *))on_node,
6348                     (int (*)(ACL_USER_BASE *, ACL_ROLE *, void *))on_edge);
6349 }
6350 
6351 /**
6352   Traverse the role grant graph, going from a user or a role to granted roles.
6353 
6354   This is used, for example, to print all grants available to a user or a role
6355   (as in SHOW GRANTS).
6356 */
6357 
traverse_role_graph_down(ACL_USER_BASE * user,void * context,int (* on_node)(ACL_USER_BASE * role,void * context),int (* on_edge)(ACL_USER_BASE * current,ACL_ROLE * neighbour,void * context))6358 static int traverse_role_graph_down(ACL_USER_BASE *user, void *context,
6359        int (*on_node) (ACL_USER_BASE *role, void *context),
6360        int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
6361 {
6362   return traverse_role_graph_impl(user, context,
6363                              my_offsetof(ACL_USER_BASE, role_grants),
6364                              on_node, on_edge);
6365 }
6366 
6367 /*
6368   To find all db/table/routine privilege for a specific role
6369   we need to scan the array of privileges. It can be big.
6370   But the set of privileges granted to a role in question (or
6371   to roles directly granted to the role in question) is supposedly
6372   much smaller.
6373 
6374   We put a role and all roles directly granted to it in a hash, and iterate
6375   the (suposedly long) array of privileges, filtering out "interesting"
6376   entries using the role hash. We put all these "interesting"
6377   entries in a (suposedly small) dynamic array and them use it for merging.
6378 */
role_key(const ACL_ROLE * role,size_t * klen,my_bool)6379 static uchar* role_key(const ACL_ROLE *role, size_t *klen, my_bool)
6380 {
6381   *klen= role->user.length;
6382   return (uchar*) role->user.str;
6383 }
6384 typedef Hash_set<ACL_ROLE> role_hash_t;
6385 
merge_role_global_privileges(ACL_ROLE * grantee)6386 static bool merge_role_global_privileges(ACL_ROLE *grantee)
6387 {
6388   privilege_t old= grantee->access;
6389   grantee->access= grantee->initial_role_access;
6390 
6391   DBUG_EXECUTE_IF("role_merge_stats", role_global_merges++;);
6392 
6393   for (uint i= 0; i < grantee->role_grants.elements; i++)
6394   {
6395     ACL_ROLE *r= *dynamic_element(&grantee->role_grants, i, ACL_ROLE**);
6396     grantee->access|= r->access;
6397   }
6398   return old != grantee->access;
6399 }
6400 
db_name_sort(const int * db1,const int * db2)6401 static int db_name_sort(const int *db1, const int *db2)
6402 {
6403   return strcmp(acl_dbs.at(*db1).db, acl_dbs.at(*db2).db);
6404 }
6405 
6406 /**
6407   update ACL_DB for given database and a given role with merged privileges
6408 
6409   @param merged ACL_DB of the role in question (or -1 if it wasn't found)
6410   @param first  first ACL_DB in an array for the database in question
6411   @param access new privileges for the given role on the gived database
6412   @param role   the name of the given role
6413 
6414   @return a bitmap of
6415           1 - privileges were changed
6416           2 - ACL_DB was added
6417           4 - ACL_DB was deleted
6418 */
update_role_db(int merged,int first,privilege_t access,const char * role)6419 static int update_role_db(int merged, int first, privilege_t access,
6420                           const char *role)
6421 {
6422   if (first < 0)
6423     return 0;
6424 
6425   DBUG_EXECUTE_IF("role_merge_stats", role_db_merges++;);
6426 
6427   if (merged < 0)
6428   {
6429     /*
6430       there's no ACL_DB for this role (all db grants come from granted roles)
6431       we need to create it
6432 
6433       Note that we cannot use acl_insert_db() now:
6434       1. it'll sort elements in the acl_dbs, so the pointers will become invalid
6435       2. we may need many of them, no need to sort every time
6436     */
6437     DBUG_ASSERT(access);
6438     ACL_DB acl_db;
6439     acl_db.user= role;
6440     acl_db.host.hostname= const_cast<char*>("");
6441     acl_db.host.ip= acl_db.host.ip_mask= 0;
6442     acl_db.db= acl_dbs.at(first).db;
6443     acl_db.access= access;
6444     acl_db.initial_access= NO_ACL;
6445     acl_db.sort= get_magic_sort("hdu", "", acl_db.db, role);
6446     acl_dbs.push(acl_db);
6447     return 2;
6448   }
6449   else if (access == NO_ACL)
6450   {
6451     /*
6452       there is ACL_DB but the role has no db privileges granted
6453       (all privileges were coming from granted roles, and now those roles
6454       were dropped or had their privileges revoked).
6455       we need to remove this ACL_DB entry
6456 
6457       Note, that we cannot delete now:
6458       1. it'll shift elements in the acl_dbs, so the pointers will become invalid
6459       2. it's O(N) operation, and we may need many of them
6460       so we only mark elements deleted and will delete later.
6461     */
6462     acl_dbs.at(merged).sort= 0; // lower than any valid ACL_DB sort value, will be sorted last
6463     return 4;
6464   }
6465   else if (acl_dbs.at(merged).access != access)
6466   {
6467     /* this is easy */
6468     acl_dbs.at(merged).access= access;
6469     return 1;
6470   }
6471   return 0;
6472 }
6473 
6474 /**
6475   merges db privileges from roles granted to the role 'grantee'.
6476 
6477   @return true if database privileges of the 'grantee' were changed
6478 
6479 */
merge_role_db_privileges(ACL_ROLE * grantee,const char * dbname,role_hash_t * rhash)6480 static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname,
6481                                      role_hash_t *rhash)
6482 {
6483   Dynamic_array<int> dbs(PSI_INSTRUMENT_MEM);
6484 
6485   /*
6486     Supposedly acl_dbs can be huge, but only a handful of db grants
6487     apply to grantee or roles directly granted to grantee.
6488 
6489     Collect these applicable db grants.
6490   */
6491   for (uint i=0 ; i < acl_dbs.elements() ; i++)
6492   {
6493     ACL_DB *db= &acl_dbs.at(i);
6494     if (db->host.hostname[0])
6495       continue;
6496     if (dbname && strcmp(db->db, dbname))
6497       continue;
6498     ACL_ROLE *r= rhash->find(db->user, strlen(db->user));
6499     if (!r)
6500       continue;
6501     dbs.append(i);
6502   }
6503   dbs.sort(db_name_sort);
6504 
6505   /*
6506     Because dbs array is sorted by the db name, all grants for the same db
6507     (that should be merged) are sorted together. The grantee's ACL_DB element
6508     is not necessarily the first and may be not present at all.
6509   */
6510   int first= -1, merged= -1;
6511   privilege_t access(NO_ACL);
6512   ulong update_flags= 0;
6513   for (int *p= dbs.front(); p <= dbs.back(); p++)
6514   {
6515     if (first<0 || (!dbname && strcmp(acl_dbs.at(p[0]).db, acl_dbs.at(p[-1]).db)))
6516     { // new db name series
6517       update_flags|= update_role_db(merged, first, access, grantee->user.str);
6518       merged= -1;
6519       access= NO_ACL;
6520       first= *p;
6521     }
6522     if (strcmp(acl_dbs.at(*p).user, grantee->user.str) == 0)
6523       access|= acl_dbs.at(merged= *p).initial_access;
6524     else
6525       access|= acl_dbs.at(*p).access;
6526   }
6527   update_flags|= update_role_db(merged, first, access, grantee->user.str);
6528 
6529   if (update_flags & 4)
6530   {
6531     // Remove elements marked for deletion.
6532     uint count= 0;
6533     for(uint i= 0; i < acl_dbs.elements(); i++)
6534     {
6535       ACL_DB *acl_db= &acl_dbs.at(i);
6536       if (acl_db->sort)
6537       {
6538         if (i > count)
6539           acl_dbs.set(count, *acl_db);
6540         count++;
6541       }
6542     }
6543     acl_dbs.elements(count);
6544   }
6545 
6546 
6547   if (update_flags & 2)
6548   { // inserted, need to sort
6549     rebuild_acl_dbs();
6550   }
6551 
6552   return update_flags;
6553 }
6554 
table_name_sort(GRANT_TABLE * const * tbl1,GRANT_TABLE * const * tbl2)6555 static int table_name_sort(GRANT_TABLE * const *tbl1, GRANT_TABLE * const *tbl2)
6556 {
6557   int res = strcmp((*tbl1)->db, (*tbl2)->db);
6558   if (res) return res;
6559   return strcmp((*tbl1)->tname, (*tbl2)->tname);
6560 }
6561 
6562 /**
6563   merges column privileges for the entry 'merged'
6564 
6565   @param merged GRANT_TABLE to merge the privileges into
6566   @param cur    first entry in the array of GRANT_TABLE's for a given table
6567   @param last   last entry in the array of GRANT_TABLE's for a given table,
6568                 all entries between cur and last correspond to the *same* table
6569 
6570   @return 1 if the _set of columns_ in 'merged' was changed
6571           (not if the _set of privileges_ was changed).
6572 */
update_role_columns(GRANT_TABLE * merged,GRANT_TABLE ** cur,GRANT_TABLE ** last)6573 static int update_role_columns(GRANT_TABLE *merged,
6574                                GRANT_TABLE **cur, GRANT_TABLE **last)
6575 
6576 {
6577   privilege_t rights __attribute__((unused)) (NO_ACL);
6578   int changed= 0;
6579   if (!merged->cols)
6580   {
6581     changed= merged->hash_columns.records > 0;
6582     my_hash_reset(&merged->hash_columns);
6583     return changed;
6584   }
6585 
6586   DBUG_EXECUTE_IF("role_merge_stats", role_column_merges++;);
6587 
6588   HASH *mh= &merged->hash_columns;
6589   for (uint i=0 ; i < mh->records ; i++)
6590   {
6591     GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
6592     col->rights= col->init_rights;
6593   }
6594 
6595   for (; cur < last; cur++)
6596   {
6597     if (*cur == merged)
6598       continue;
6599     HASH *ch= &cur[0]->hash_columns;
6600     for (uint i=0 ; i < ch->records ; i++)
6601     {
6602       GRANT_COLUMN *ccol = (GRANT_COLUMN *)my_hash_element(ch, i);
6603       GRANT_COLUMN *mcol = (GRANT_COLUMN *)my_hash_search(mh,
6604                                   (uchar *)ccol->column, ccol->key_length);
6605       if (mcol)
6606         mcol->rights|= ccol->rights;
6607       else
6608       {
6609         changed= 1;
6610         my_hash_insert(mh, (uchar*)new (&grant_memroot) GRANT_COLUMN(ccol));
6611       }
6612     }
6613   }
6614 
6615   for (uint i=0 ; i < mh->records ; i++)
6616   {
6617     GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
6618     rights|= col->rights;
6619     if (!col->rights)
6620     {
6621       changed= 1;
6622       my_hash_delete(mh, (uchar*)col);
6623     }
6624   }
6625   DBUG_ASSERT(rights == merged->cols);
6626   return changed;
6627 }
6628 
6629 /**
6630   update GRANT_TABLE for a given table and a given role with merged privileges
6631 
6632   @param merged GRANT_TABLE of the role in question (or NULL if it wasn't found)
6633   @param first  first GRANT_TABLE in an array for the table in question
6634   @param last   last entry in the array of GRANT_TABLE's for a given table,
6635                 all entries between first and last correspond to the *same* table
6636   @param privs  new table-level privileges for 'merged'
6637   @param cols   new OR-ed column-level privileges for 'merged'
6638   @param role   the name of the given role
6639 
6640   @return a bitmap of
6641           1 - privileges were changed
6642           2 - GRANT_TABLE was added
6643           4 - GRANT_TABLE was deleted
6644 */
update_role_table_columns(GRANT_TABLE * merged,GRANT_TABLE ** first,GRANT_TABLE ** last,privilege_t privs,privilege_t cols,const char * role)6645 static int update_role_table_columns(GRANT_TABLE *merged,
6646                                      GRANT_TABLE **first, GRANT_TABLE **last,
6647                                      privilege_t privs, privilege_t cols,
6648                                      const char *role)
6649 {
6650   if (!first)
6651     return 0;
6652 
6653   DBUG_EXECUTE_IF("role_merge_stats", role_table_merges++;);
6654 
6655   if (merged == NULL)
6656   {
6657     /*
6658       there's no GRANT_TABLE for this role (all table grants come from granted
6659       roles) we need to create it
6660     */
6661     DBUG_ASSERT(privs | cols);
6662     merged= new (&grant_memroot) GRANT_TABLE("", first[0]->db, role, first[0]->tname,
6663                                      privs, cols);
6664     merged->init_privs= merged->init_cols= NO_ACL;
6665     update_role_columns(merged, first, last);
6666     column_priv_insert(merged);
6667     return 2;
6668   }
6669   else if ((privs | cols) == NO_ACL)
6670   {
6671     /*
6672       there is GRANT_TABLE object but the role has no table or column
6673       privileges granted (all privileges were coming from granted roles, and
6674       now those roles were dropped or had their privileges revoked).
6675       we need to remove this GRANT_TABLE
6676     */
6677     DBUG_EXECUTE_IF("role_merge_stats",
6678                     role_column_merges+= MY_TEST(merged->cols););
6679     my_hash_delete(&column_priv_hash,(uchar*) merged);
6680     return 4;
6681   }
6682   else
6683   {
6684     bool changed= merged->cols != cols || merged->privs != privs;
6685     merged->cols= cols;
6686     merged->privs= privs;
6687     if (update_role_columns(merged, first, last))
6688       changed= true;
6689     return changed;
6690   }
6691 }
6692 
6693 /**
6694   merges table privileges from roles granted to the role 'grantee'.
6695 
6696   @return true if table privileges of the 'grantee' were changed
6697 
6698 */
merge_role_table_and_column_privileges(ACL_ROLE * grantee,const char * db,const char * tname,role_hash_t * rhash)6699 static bool merge_role_table_and_column_privileges(ACL_ROLE *grantee,
6700                         const char *db, const char *tname, role_hash_t *rhash)
6701 {
6702   Dynamic_array<GRANT_TABLE *> grants(PSI_INSTRUMENT_MEM);
6703   DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
6704 
6705   /*
6706     first, collect table/column privileges granted to
6707     roles in question.
6708   */
6709   for (uint i=0 ; i < column_priv_hash.records ; i++)
6710   {
6711     GRANT_TABLE *grant= (GRANT_TABLE *) my_hash_element(&column_priv_hash, i);
6712     if (grant->host.hostname[0])
6713       continue;
6714     if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
6715       continue;
6716     ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
6717     if (!r)
6718       continue;
6719     grants.append(grant);
6720   }
6721   grants.sort(table_name_sort);
6722 
6723   GRANT_TABLE **first= NULL, *merged= NULL, **cur;
6724   privilege_t privs(NO_ACL), cols(NO_ACL);
6725   ulong update_flags= 0;
6726   for (cur= grants.front(); cur <= grants.back(); cur++)
6727   {
6728     if (!first ||
6729         (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
6730                    strcmp(cur[0]->tname, cur[-1]->tname))))
6731     { // new db.tname series
6732       update_flags|= update_role_table_columns(merged, first, cur,
6733                                                privs, cols, grantee->user.str);
6734       merged= NULL;
6735       privs= cols= NO_ACL;
6736       first= cur;
6737     }
6738     if (strcmp(cur[0]->user, grantee->user.str) == 0)
6739     {
6740       merged= cur[0];
6741       cols|= cur[0]->init_cols;
6742       privs|= cur[0]->init_privs;
6743     }
6744     else
6745     {
6746       cols|= cur[0]->cols;
6747       privs|= cur[0]->privs;
6748     }
6749   }
6750   update_flags|= update_role_table_columns(merged, first, cur,
6751                                            privs, cols, grantee->user.str);
6752 
6753   return update_flags;
6754 }
6755 
routine_name_sort(GRANT_NAME * const * r1,GRANT_NAME * const * r2)6756 static int routine_name_sort(GRANT_NAME * const *r1, GRANT_NAME * const *r2)
6757 {
6758   int res= strcmp((*r1)->db, (*r2)->db);
6759   if (res) return res;
6760   return strcmp((*r1)->tname, (*r2)->tname);
6761 }
6762 
6763 /**
6764   update GRANT_NAME for a given routine and a given role with merged privileges
6765 
6766   @param merged GRANT_NAME of the role in question (or NULL if it wasn't found)
6767   @param first  first GRANT_NAME in an array for the routine in question
6768   @param privs  new routine-level privileges for 'merged'
6769   @param role   the name of the given role
6770   @param hash   proc_priv_hash or func_priv_hash
6771 
6772   @return a bitmap of
6773           1 - privileges were changed
6774           2 - GRANT_NAME was added
6775           4 - GRANT_NAME was deleted
6776 */
update_role_routines(GRANT_NAME * merged,GRANT_NAME ** first,privilege_t privs,const char * role,HASH * hash)6777 static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first,
6778                                 privilege_t privs, const char *role, HASH *hash)
6779 {
6780   if (!first)
6781     return 0;
6782 
6783   DBUG_EXECUTE_IF("role_merge_stats", role_routine_merges++;);
6784 
6785   if (merged == NULL)
6786   {
6787     /*
6788       there's no GRANT_NAME for this role (all routine grants come from granted
6789       roles) we need to create it
6790     */
6791     DBUG_ASSERT(privs);
6792     merged= new (&grant_memroot) GRANT_NAME("", first[0]->db, role, first[0]->tname,
6793                                     privs, true);
6794     merged->init_privs= NO_ACL; // all privs are inherited
6795     my_hash_insert(hash, (uchar *)merged);
6796     return 2;
6797   }
6798   else if (privs == NO_ACL)
6799   {
6800     /*
6801       there is GRANT_NAME but the role has no privileges granted
6802       (all privileges were coming from granted roles, and now those roles
6803       were dropped or had their privileges revoked).
6804       we need to remove this entry
6805     */
6806     my_hash_delete(hash, (uchar*)merged);
6807     return 4;
6808   }
6809   else if (merged->privs != privs)
6810   {
6811     /* this is easy */
6812     merged->privs= privs;
6813     return 1;
6814   }
6815   return 0;
6816 }
6817 
6818 /**
6819   merges routine privileges from roles granted to the role 'grantee'.
6820 
6821   @return true if routine privileges of the 'grantee' were changed
6822 
6823 */
merge_role_routine_grant_privileges(ACL_ROLE * grantee,const char * db,const char * tname,role_hash_t * rhash,HASH * hash)6824 static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee,
6825             const char *db, const char *tname, role_hash_t *rhash, HASH *hash)
6826 {
6827   ulong update_flags= 0;
6828 
6829   DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
6830 
6831   Dynamic_array<GRANT_NAME *> grants(PSI_INSTRUMENT_MEM);
6832 
6833   /* first, collect routine privileges granted to roles in question */
6834   for (uint i=0 ; i < hash->records ; i++)
6835   {
6836     GRANT_NAME *grant= (GRANT_NAME *) my_hash_element(hash, i);
6837     if (grant->host.hostname[0])
6838       continue;
6839     if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
6840       continue;
6841     ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
6842     if (!r)
6843       continue;
6844     grants.append(grant);
6845   }
6846   grants.sort(routine_name_sort);
6847 
6848   GRANT_NAME **first= NULL, *merged= NULL;
6849   privilege_t privs(NO_ACL);
6850   for (GRANT_NAME **cur= grants.front(); cur <= grants.back(); cur++)
6851   {
6852     if (!first ||
6853         (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
6854                     strcmp(cur[0]->tname, cur[-1]->tname))))
6855     { // new db.tname series
6856       update_flags|= update_role_routines(merged, first, privs,
6857                                           grantee->user.str, hash);
6858       merged= NULL;
6859       privs= NO_ACL;
6860       first= cur;
6861     }
6862     if (strcmp(cur[0]->user, grantee->user.str) == 0)
6863     {
6864       merged= cur[0];
6865       privs|= cur[0]->init_privs;
6866     }
6867     else
6868     {
6869       privs|= cur[0]->privs;
6870     }
6871   }
6872   update_flags|= update_role_routines(merged, first, privs,
6873                                       grantee->user.str, hash);
6874   return update_flags;
6875 }
6876 
6877 /**
6878   update privileges of the 'grantee' from all roles, granted to it
6879 */
merge_role_privileges(ACL_ROLE * role,ACL_ROLE * grantee,void * context)6880 static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
6881                                  ACL_ROLE *grantee, void *context)
6882 {
6883   PRIVS_TO_MERGE *data= (PRIVS_TO_MERGE *)context;
6884 
6885   DBUG_ASSERT(grantee->counter > 0);
6886   if (--grantee->counter)
6887     return 1; // don't recurse into grantee just yet
6888 
6889   grantee->counter= 1; // Mark the grantee as merged.
6890 
6891   /* if we'll do db/table/routine privileges, create a hash of role names */
6892   role_hash_t role_hash(PSI_INSTRUMENT_MEM, role_key);
6893   if (data->what != PRIVS_TO_MERGE::GLOBAL)
6894   {
6895     role_hash.insert(grantee);
6896     for (uint i= 0; i < grantee->role_grants.elements; i++)
6897       role_hash.insert(*dynamic_element(&grantee->role_grants, i, ACL_ROLE**));
6898   }
6899 
6900   bool all= data->what == PRIVS_TO_MERGE::ALL;
6901   bool changed= false;
6902   if (all || data->what == PRIVS_TO_MERGE::GLOBAL)
6903     changed|= merge_role_global_privileges(grantee);
6904   if (all || data->what == PRIVS_TO_MERGE::DB)
6905     changed|= merge_role_db_privileges(grantee, data->db, &role_hash);
6906   if (all || data->what == PRIVS_TO_MERGE::TABLE_COLUMN)
6907     changed|= merge_role_table_and_column_privileges(grantee,
6908                                              data->db, data->name, &role_hash);
6909   if (all || data->what == PRIVS_TO_MERGE::PROC)
6910     changed|= merge_role_routine_grant_privileges(grantee,
6911                             data->db, data->name, &role_hash, &proc_priv_hash);
6912   if (all || data->what == PRIVS_TO_MERGE::FUNC)
6913     changed|= merge_role_routine_grant_privileges(grantee,
6914                             data->db, data->name, &role_hash, &func_priv_hash);
6915   if (all || data->what == PRIVS_TO_MERGE::PACKAGE_SPEC)
6916     changed|= merge_role_routine_grant_privileges(grantee,
6917                             data->db, data->name, &role_hash,
6918                             &package_spec_priv_hash);
6919   if (all || data->what == PRIVS_TO_MERGE::PACKAGE_BODY)
6920     changed|= merge_role_routine_grant_privileges(grantee,
6921                             data->db, data->name, &role_hash,
6922                             &package_body_priv_hash);
6923   return !changed; // don't recurse into the subgraph if privs didn't change
6924 }
6925 
merge_one_role_privileges(ACL_ROLE * grantee)6926 static bool merge_one_role_privileges(ACL_ROLE *grantee)
6927 {
6928   PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
6929   grantee->counter= 1;
6930   return merge_role_privileges(0, grantee, &data);
6931 }
6932 
6933 /*****************************************************************
6934   End of the role privilege propagation and graph traversal code
6935 ******************************************************************/
6936 
has_auth(LEX_USER * user,LEX * lex)6937 static bool has_auth(LEX_USER *user, LEX *lex)
6938 {
6939   return user->has_auth() ||
6940          lex->account_options.ssl_type != SSL_TYPE_NOT_SPECIFIED ||
6941          lex->account_options.ssl_cipher.str ||
6942          lex->account_options.x509_issuer.str ||
6943          lex->account_options.x509_subject.str ||
6944          lex->account_options.specified_limits;
6945 }
6946 
copy_and_check_auth(LEX_USER * to,LEX_USER * from,THD * thd)6947 static bool copy_and_check_auth(LEX_USER *to, LEX_USER *from, THD *thd)
6948 {
6949   to->auth= from->auth;
6950 
6951   // if changing auth for an existing user
6952   if (has_auth(to, thd->lex) && find_user_exact(to->host.str, to->user.str))
6953   {
6954     mysql_mutex_unlock(&acl_cache->lock);
6955     bool res= check_alter_user(thd, to->host.str, to->user.str);
6956     mysql_mutex_lock(&acl_cache->lock);
6957     return res;
6958   }
6959 
6960   return false;
6961 }
6962 
6963 
6964 /*
6965   Store table level and column level grants in the privilege tables
6966 
6967   SYNOPSIS
6968     mysql_table_grant()
6969     thd			Thread handle
6970     table_list		List of tables to give grant
6971     user_list		List of users to give grant
6972     columns		List of columns to give grant
6973     rights		Table level grant
6974     revoke_grant	Set to 1 if this is a REVOKE command
6975 
6976   RETURN
6977     FALSE ok
6978     TRUE  error
6979 */
6980 
mysql_table_grant(THD * thd,TABLE_LIST * table_list,List<LEX_USER> & user_list,List<LEX_COLUMN> & columns,privilege_t rights,bool revoke_grant)6981 int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
6982 		      List <LEX_USER> &user_list,
6983 		      List <LEX_COLUMN> &columns, privilege_t rights,
6984 		      bool revoke_grant)
6985 {
6986   privilege_t column_priv(NO_ACL);
6987   int result;
6988   List_iterator <LEX_USER> str_list (user_list);
6989   LEX_USER *Str, *tmp_Str;
6990   bool create_new_users=0;
6991   const char *db_name, *table_name;
6992   DBUG_ENTER("mysql_table_grant");
6993 
6994   if (rights & ~TABLE_ACLS)
6995   {
6996     my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
6997                ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
6998                MYF(0));
6999     DBUG_RETURN(TRUE);
7000   }
7001 
7002   if (!revoke_grant)
7003   {
7004     if (columns.elements)
7005     {
7006       class LEX_COLUMN *column;
7007       List_iterator <LEX_COLUMN> column_iter(columns);
7008 
7009       if (open_normal_and_derived_tables(thd, table_list, 0, DT_PREPARE))
7010         DBUG_RETURN(TRUE);
7011 
7012       while ((column = column_iter++))
7013       {
7014         uint unused_field_idx= NO_CACHED_FIELD_INDEX;
7015         TABLE_LIST *dummy;
7016         Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
7017                                          column->column.length(),
7018                                          column->column.ptr(), NULL, NULL,
7019                                          NULL, TRUE, FALSE,
7020                                          &unused_field_idx, FALSE, &dummy);
7021         if (unlikely(f == (Field*)0))
7022         {
7023           my_error(ER_BAD_FIELD_ERROR, MYF(0),
7024                    column->column.c_ptr(), table_list->alias.str);
7025           DBUG_RETURN(TRUE);
7026         }
7027         if (unlikely(f == (Field *)-1))
7028           DBUG_RETURN(TRUE);
7029         column_priv|= column->rights;
7030       }
7031       close_mysql_tables(thd);
7032     }
7033     else
7034     {
7035       if (!(rights & CREATE_ACL))
7036       {
7037         if (!ha_table_exists(thd, &table_list->db, &table_list->table_name))
7038         {
7039           my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db.str,
7040                    table_list->alias.str);
7041           DBUG_RETURN(TRUE);
7042         }
7043       }
7044       if (table_list->grant.want_privilege)
7045       {
7046         char command[128];
7047         get_privilege_desc(command, sizeof(command),
7048                            table_list->grant.want_privilege);
7049         my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
7050                  command, thd->security_ctx->priv_user,
7051                  thd->security_ctx->host_or_ip, table_list->alias.str);
7052         DBUG_RETURN(-1);
7053       }
7054     }
7055   }
7056 
7057   /*
7058     Open the mysql.user and mysql.tables_priv tables.
7059     Don't open column table if we don't need it !
7060   */
7061   int tables_to_open= Table_user | Table_tables_priv;
7062   if (column_priv ||
7063       (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
7064     tables_to_open|= Table_columns_priv;
7065 
7066   /*
7067     The lock api is depending on the thd->lex variable which needs to be
7068     re-initialized.
7069   */
7070   Query_tables_list backup;
7071   thd->lex->reset_n_backup_query_tables_list(&backup);
7072   /*
7073     Restore Query_tables_list::sql_command value, which was reset
7074     above, as the code writing query to the binary log assumes that
7075     this value corresponds to the statement being executed.
7076   */
7077   thd->lex->sql_command= backup.sql_command;
7078 
7079   Grant_tables tables;
7080   if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
7081   {
7082     thd->lex->restore_backup_query_tables_list(&backup);
7083     DBUG_RETURN(result != 1);
7084   }
7085 
7086   if (!revoke_grant)
7087     create_new_users= test_if_create_new_users(thd);
7088   mysql_rwlock_wrlock(&LOCK_grant);
7089   mysql_mutex_lock(&acl_cache->lock);
7090   MEM_ROOT *old_root= thd->mem_root;
7091   thd->mem_root= &grant_memroot;
7092   grant_version++;
7093 
7094   while ((tmp_Str = str_list++))
7095   {
7096     int error;
7097     GRANT_TABLE *grant_table;
7098     if (!(Str= get_current_user(thd, tmp_Str, false)))
7099     {
7100       result= TRUE;
7101       continue;
7102     }
7103     /* Create user if needed */
7104     error= copy_and_check_auth(Str, tmp_Str, thd) ||
7105            replace_user_table(thd, tables.user_table(), Str,
7106                                NO_ACL, revoke_grant, create_new_users,
7107                                MY_TEST(thd->variables.sql_mode &
7108                                        MODE_NO_AUTO_CREATE_USER));
7109     if (unlikely(error))
7110     {
7111       result= TRUE;				// Remember error
7112       continue;					// Add next user
7113     }
7114 
7115     db_name= table_list->get_db_name();
7116     table_name= table_list->get_table_name();
7117 
7118     /* Find/create cached table grant */
7119     grant_table= table_hash_search(Str->host.str, NullS, db_name,
7120 				   Str->user.str, table_name, 1);
7121     if (!grant_table)
7122     {
7123       if (revoke_grant)
7124       {
7125 	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
7126                  Str->user.str, Str->host.str, table_list->table_name.str);
7127 	result= TRUE;
7128 	continue;
7129       }
7130       grant_table= new (&grant_memroot) GRANT_TABLE(Str->host.str, db_name,
7131                                                     Str->user.str, table_name,
7132                                                     rights,
7133                                                     column_priv);
7134       if (!grant_table ||
7135           column_priv_insert(grant_table))
7136       {
7137 	result= TRUE;				/* purecov: deadcode */
7138 	continue;				/* purecov: deadcode */
7139       }
7140     }
7141 
7142     /* If revoke_grant, calculate the new column privilege for tables_priv */
7143     if (revoke_grant)
7144     {
7145       class LEX_COLUMN *column;
7146       List_iterator <LEX_COLUMN> column_iter(columns);
7147       GRANT_COLUMN *grant_column;
7148 
7149       /* Fix old grants */
7150       while ((column = column_iter++))
7151       {
7152 	grant_column = column_hash_search(grant_table,
7153 					  column->column.ptr(),
7154 					  column->column.length());
7155 	if (grant_column)
7156 	  grant_column->rights&= ~(column->rights | rights);
7157       }
7158       /* scan trough all columns to get new column grant */
7159       column_priv= NO_ACL;
7160       for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
7161       {
7162         grant_column= (GRANT_COLUMN*)
7163           my_hash_element(&grant_table->hash_columns, idx);
7164 	grant_column->rights&= ~rights;		// Fix other columns
7165 	column_priv|= grant_column->rights;
7166       }
7167     }
7168     else
7169     {
7170       column_priv|= grant_table->cols;
7171     }
7172 
7173 
7174     /* update table and columns */
7175 
7176     /* TODO(cvicentiu) refactor replace_table_table to use Tables_priv_table
7177        instead of TABLE directly. */
7178     if (tables.columns_priv_table().table_exists())
7179     {
7180       /* TODO(cvicentiu) refactor replace_column_table to use Columns_priv_table
7181          instead of TABLE directly. */
7182       if (replace_column_table(grant_table, tables.columns_priv_table().table(),
7183                                *Str, columns, db_name, table_name, rights,
7184                                revoke_grant))
7185 	result= TRUE;
7186     }
7187     if (int res= replace_table_table(thd, grant_table,
7188                                      tables.tables_priv_table().table(),
7189                                      *Str, db_name, table_name,
7190                                      rights, column_priv, revoke_grant))
7191     {
7192       if (res > 0)
7193       {
7194         /* Should only happen if table is crashed */
7195         result= TRUE;			       /* purecov: deadcode */
7196       }
7197     }
7198     if (Str->is_role())
7199       propagate_role_grants(find_acl_role(Str->user.str),
7200                             PRIVS_TO_MERGE::TABLE_COLUMN, db_name, table_name);
7201   }
7202 
7203   thd->mem_root= old_root;
7204   mysql_mutex_unlock(&acl_cache->lock);
7205 
7206   if (!result) /* success */
7207     result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7208 
7209   mysql_rwlock_unlock(&LOCK_grant);
7210 
7211   if (!result) /* success */
7212     my_ok(thd);
7213 
7214   thd->lex->restore_backup_query_tables_list(&backup);
7215   DBUG_RETURN(result);
7216 }
7217 
7218 
7219 /**
7220   Store routine level grants in the privilege tables
7221 
7222   @param thd Thread handle
7223   @param table_list List of routines to give grant
7224   @param sph SP handler
7225   @param user_list List of users to give grant
7226   @param rights Table level grant
7227   @param revoke_grant Is this is a REVOKE command?
7228 
7229   @return
7230     @retval FALSE Success.
7231     @retval TRUE An error occurred.
7232 */
7233 
mysql_routine_grant(THD * thd,TABLE_LIST * table_list,const Sp_handler * sph,List<LEX_USER> & user_list,privilege_t rights,bool revoke_grant,bool write_to_binlog)7234 bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list,
7235                          const Sp_handler *sph,
7236 			 List <LEX_USER> &user_list, privilege_t rights,
7237 			 bool revoke_grant, bool write_to_binlog)
7238 {
7239   List_iterator <LEX_USER> str_list (user_list);
7240   LEX_USER *Str, *tmp_Str;
7241   bool create_new_users= 0;
7242   int result;
7243   const char *db_name, *table_name;
7244   DBUG_ENTER("mysql_routine_grant");
7245 
7246   if (rights & ~PROC_ACLS)
7247   {
7248     my_message(ER_ILLEGAL_GRANT_FOR_TABLE,
7249                ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
7250                MYF(0));
7251     DBUG_RETURN(TRUE);
7252   }
7253 
7254   if (!revoke_grant)
7255   {
7256     if (sph->sp_exist_routines(thd, table_list))
7257       DBUG_RETURN(TRUE);
7258   }
7259 
7260   Grant_tables tables;
7261   if ((result= tables.open_and_lock(thd, Table_user | Table_procs_priv, TL_WRITE)))
7262     DBUG_RETURN(result != 1);
7263 
7264   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7265 
7266   if (!revoke_grant)
7267     create_new_users= test_if_create_new_users(thd);
7268   mysql_rwlock_wrlock(&LOCK_grant);
7269   mysql_mutex_lock(&acl_cache->lock);
7270   MEM_ROOT *old_root= thd->mem_root;
7271   thd->mem_root= &grant_memroot;
7272 
7273   DBUG_PRINT("info",("now time to iterate and add users"));
7274 
7275   while ((tmp_Str= str_list++))
7276   {
7277     GRANT_NAME *grant_name;
7278     if (!(Str= get_current_user(thd, tmp_Str, false)))
7279     {
7280       result= TRUE;
7281       continue;
7282     }
7283     /* Create user if needed */
7284     if (copy_and_check_auth(Str, tmp_Str, thd) ||
7285         replace_user_table(thd, tables.user_table(), Str,
7286 			   NO_ACL, revoke_grant, create_new_users,
7287                            MY_TEST(thd->variables.sql_mode &
7288                                      MODE_NO_AUTO_CREATE_USER)))
7289     {
7290       result= TRUE;
7291       continue;
7292     }
7293 
7294     db_name= table_list->db.str;
7295     table_name= table_list->table_name.str;
7296     grant_name= routine_hash_search(Str->host.str, NullS, db_name,
7297                                     Str->user.str, table_name, sph, 1);
7298     if (!grant_name || !grant_name->init_privs)
7299     {
7300       if (revoke_grant)
7301       {
7302         my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
7303 	         Str->user.str, Str->host.str, table_name);
7304 	result= TRUE;
7305 	continue;
7306       }
7307       grant_name= new GRANT_NAME(Str->host.str, db_name,
7308 				 Str->user.str, table_name,
7309 				 rights, TRUE);
7310       if (!grant_name ||
7311         my_hash_insert(sph->get_priv_hash(), (uchar*) grant_name))
7312       {
7313         result= TRUE;
7314 	continue;
7315       }
7316     }
7317 
7318     if (replace_routine_table(thd, grant_name, tables.procs_priv_table().table(),
7319           *Str, db_name, table_name, sph, rights, revoke_grant) != 0)
7320     {
7321       result= TRUE;
7322       continue;
7323     }
7324     if (Str->is_role())
7325       propagate_role_grants(find_acl_role(Str->user.str),
7326                             sp_privs_to_merge(sph->type()),
7327                             db_name, table_name);
7328   }
7329   thd->mem_root= old_root;
7330   mysql_mutex_unlock(&acl_cache->lock);
7331 
7332   if (write_to_binlog)
7333   {
7334     if (write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
7335       result= TRUE;
7336   }
7337 
7338   mysql_rwlock_unlock(&LOCK_grant);
7339 
7340   /* Tables are automatically closed */
7341   DBUG_RETURN(result);
7342 }
7343 
7344 /**
7345   append a user or role name to a buffer that will be later used as an error message
7346 */
append_user(THD * thd,String * str,const LEX_CSTRING * u,const LEX_CSTRING * h)7347 static void append_user(THD *thd, String *str,
7348                         const LEX_CSTRING *u, const LEX_CSTRING *h)
7349 {
7350   if (str->length())
7351     str->append(',');
7352   append_query_string(system_charset_info, str, u->str, u->length,
7353                       thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
7354   /* hostname part is not relevant for roles, it is always empty */
7355   if (u->length == 0 || h->length != 0)
7356   {
7357     str->append('@');
7358     append_query_string(system_charset_info, str, h->str, h->length,
7359                         thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
7360   }
7361 }
7362 
append_user(THD * thd,String * str,LEX_USER * user)7363 static void append_user(THD *thd, String *str, LEX_USER *user)
7364 {
7365   append_user(thd, str, & user->user, & user->host);
7366 }
7367 
7368 /**
7369   append a string to a buffer that will be later used as an error message
7370 
7371   @note
7372   a string can be either CURRENT_USER or CURRENT_ROLE or NONE, it should be
7373   neither quoted nor escaped.
7374 */
append_str(String * str,const char * s,size_t l)7375 static void append_str(String *str, const char *s, size_t l)
7376 {
7377   if (str->length())
7378     str->append(',');
7379   str->append(s, l);
7380 }
7381 
can_grant_role_callback(ACL_USER_BASE * grantee,ACL_ROLE * role,void * data)7382 static int can_grant_role_callback(ACL_USER_BASE *grantee,
7383                                    ACL_ROLE *role, void *data)
7384 {
7385   ROLE_GRANT_PAIR *pair;
7386 
7387   if (role != (ACL_ROLE*)data)
7388     return 0; // keep searching
7389 
7390   if (grantee->flags & IS_ROLE)
7391     pair= find_role_grant_pair(&grantee->user, &empty_clex_str, &role->user);
7392   else
7393   {
7394     ACL_USER *user= (ACL_USER *)grantee;
7395     LEX_CSTRING host= { user->host.hostname, user->hostname_length };
7396     pair= find_role_grant_pair(&user->user, &host, &role->user);
7397   }
7398   if (!pair->with_admin)
7399     return 0; // keep searching
7400 
7401   return -1; // abort the traversal
7402 }
7403 
7404 
7405 /*
7406   One can only grant a role if SELECT * FROM I_S.APPLICABLE_ROLES shows this
7407   role as grantable.
7408 
7409   What this really means - we need to traverse role graph for the current user
7410   looking for our role being granted with the admin option.
7411 */
can_grant_role(THD * thd,ACL_ROLE * role)7412 static bool can_grant_role(THD *thd, ACL_ROLE *role)
7413 {
7414   Security_context *sctx= thd->security_ctx;
7415 
7416   if (!sctx->user) // replication
7417     return true;
7418 
7419   ACL_USER *grantee= find_user_exact(sctx->priv_host, sctx->priv_user);
7420   if (!grantee)
7421     return false;
7422 
7423   return traverse_role_graph_down(grantee, role, NULL,
7424                                   can_grant_role_callback) == -1;
7425 }
7426 
7427 
mysql_grant_role(THD * thd,List<LEX_USER> & list,bool revoke)7428 bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
7429 {
7430   DBUG_ENTER("mysql_grant_role");
7431   /*
7432      The first entry in the list is the granted role. Need at least two
7433      entries for the command to be valid
7434    */
7435   DBUG_ASSERT(list.elements >= 2);
7436   int result;
7437   bool create_new_user, no_auto_create_user;
7438   String wrong_users;
7439   LEX_USER *user, *granted_role;
7440   LEX_CSTRING rolename;
7441   LEX_CSTRING username;
7442   LEX_CSTRING hostname;
7443   ACL_ROLE *role, *role_as_user;
7444 
7445   List_iterator <LEX_USER> user_list(list);
7446   granted_role= user_list++;
7447   if (!(granted_role= get_current_user(thd, granted_role)))
7448     DBUG_RETURN(TRUE);
7449 
7450   DBUG_ASSERT(granted_role->is_role());
7451   rolename= granted_role->user;
7452 
7453   create_new_user= test_if_create_new_users(thd);
7454   no_auto_create_user= MY_TEST(thd->variables.sql_mode &
7455                                MODE_NO_AUTO_CREATE_USER);
7456 
7457   Grant_tables tables;
7458   if ((result= tables.open_and_lock(thd, Table_user | Table_roles_mapping, TL_WRITE)))
7459     DBUG_RETURN(result != 1);
7460 
7461   mysql_rwlock_wrlock(&LOCK_grant);
7462   mysql_mutex_lock(&acl_cache->lock);
7463   if (!(role= find_acl_role(rolename.str)))
7464   {
7465     mysql_mutex_unlock(&acl_cache->lock);
7466     mysql_rwlock_unlock(&LOCK_grant);
7467     my_error(ER_INVALID_ROLE, MYF(0), rolename.str);
7468     DBUG_RETURN(TRUE);
7469   }
7470 
7471   if (!can_grant_role(thd, role))
7472   {
7473     mysql_mutex_unlock(&acl_cache->lock);
7474     mysql_rwlock_unlock(&LOCK_grant);
7475     my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
7476              thd->security_ctx->priv_user, thd->security_ctx->priv_host);
7477     DBUG_RETURN(TRUE);
7478   }
7479 
7480   while ((user= user_list++))
7481   {
7482     role_as_user= NULL;
7483     /* current_role is treated slightly different */
7484     if (user->user.str == current_role.str)
7485     {
7486       /* current_role is NONE */
7487       if (!thd->security_ctx->priv_role[0])
7488       {
7489         my_error(ER_INVALID_ROLE, MYF(0), "NONE");
7490         append_str(&wrong_users, STRING_WITH_LEN("NONE"));
7491         result= 1;
7492         continue;
7493       }
7494       if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role)))
7495       {
7496         LEX_CSTRING ls= { thd->security_ctx->priv_role,
7497                           strlen(thd->security_ctx->priv_role) };
7498         append_user(thd, &wrong_users, &ls, &empty_clex_str);
7499         result= 1;
7500         continue;
7501       }
7502 
7503       /* can not grant current_role to current_role */
7504       if (granted_role->user.str == current_role.str)
7505       {
7506         append_user(thd, &wrong_users, &role_as_user->user, &empty_clex_str);
7507         result= 1;
7508         continue;
7509       }
7510       username.str= thd->security_ctx->priv_role;
7511       username.length= strlen(username.str);
7512       hostname= empty_clex_str;
7513     }
7514     else if (user->user.str == current_user.str)
7515     {
7516       username.str= thd->security_ctx->priv_user;
7517       username.length= strlen(username.str);
7518       hostname.str= thd->security_ctx->priv_host;
7519       hostname.length= strlen(hostname.str);
7520     }
7521     else
7522     {
7523       username= user->user;
7524       if (user->host.str)
7525         hostname= user->host;
7526       else
7527       if ((role_as_user= find_acl_role(user->user.str)))
7528         hostname= empty_clex_str;
7529       else
7530       {
7531         if (is_invalid_role_name(username.str))
7532         {
7533           append_user(thd, &wrong_users, &username, &empty_clex_str);
7534           result= 1;
7535           continue;
7536         }
7537         hostname= host_not_specified;
7538       }
7539     }
7540 
7541     ROLE_GRANT_PAIR *hash_entry= find_role_grant_pair(&username, &hostname,
7542                                                       &rolename);
7543     ACL_USER_BASE *grantee= role_as_user;
7544 
7545     if (has_auth(user, thd->lex))
7546       DBUG_ASSERT(!grantee);
7547     else if (!grantee)
7548       grantee= find_user_exact(hostname.str, username.str);
7549 
7550     if (!grantee && !revoke)
7551     {
7552       LEX_USER user_combo = *user;
7553       user_combo.host = hostname;
7554       user_combo.user = username;
7555 
7556       if (copy_and_check_auth(&user_combo, &user_combo, thd) ||
7557           replace_user_table(thd, tables.user_table(), &user_combo, NO_ACL,
7558                              false, create_new_user,
7559                              no_auto_create_user))
7560       {
7561         append_user(thd, &wrong_users, &username, &hostname);
7562         result= 1;
7563         continue;
7564       }
7565       grantee= find_user_exact(hostname.str, username.str);
7566 
7567       /* either replace_user_table failed, or we've added the user */
7568       DBUG_ASSERT(grantee);
7569     }
7570 
7571     if (!grantee)
7572     {
7573       append_user(thd, &wrong_users, &username, &hostname);
7574       result= 1;
7575       continue;
7576     }
7577 
7578     if (!revoke)
7579     {
7580       if (hash_entry)
7581       {
7582         // perhaps, updating an existing grant, adding WITH ADMIN OPTION
7583       }
7584       else
7585       {
7586         add_role_user_mapping(grantee, role);
7587 
7588         /*
7589           Check if this grant would cause a cycle. It only needs to be run
7590           if we're granting a role to a role
7591         */
7592         if (role_as_user &&
7593             traverse_role_graph_down(role, 0, 0, 0) == ROLE_CYCLE_FOUND)
7594         {
7595           append_user(thd, &wrong_users, &username, &empty_clex_str);
7596           result= 1;
7597           undo_add_role_user_mapping(grantee, role);
7598           continue;
7599         }
7600       }
7601     }
7602     else
7603     {
7604       /* grant was already removed or never existed */
7605       if (!hash_entry)
7606       {
7607         append_user(thd, &wrong_users, &username, &hostname);
7608         result= 1;
7609         continue;
7610       }
7611       if (thd->lex->with_admin_option)
7612       {
7613         // only revoking an admin option, not the complete grant
7614       }
7615       else
7616       {
7617         /* revoke a role grant */
7618         remove_role_user_mapping(grantee, role);
7619       }
7620     }
7621 
7622     /* write into the roles_mapping table */
7623     /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
7624        Roles_mapping_table instead of TABLE directly. */
7625     if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
7626                                     &username, &hostname, &rolename,
7627                                     thd->lex->with_admin_option,
7628                                     hash_entry, revoke))
7629     {
7630       append_user(thd, &wrong_users, &username, &empty_clex_str);
7631       result= 1;
7632       if (!revoke)
7633       {
7634         /* need to remove the mapping added previously */
7635         undo_add_role_user_mapping(grantee, role);
7636       }
7637       else
7638       {
7639         /* need to restore the mapping deleted previously */
7640         add_role_user_mapping(grantee, role);
7641       }
7642       continue;
7643     }
7644     update_role_mapping(&username, &hostname, &rolename,
7645                         thd->lex->with_admin_option, hash_entry, revoke);
7646 
7647     /*
7648        Only need to propagate grants when granting/revoking a role to/from
7649        a role
7650     */
7651     if (role_as_user && merge_one_role_privileges(role_as_user) == 0)
7652       propagate_role_grants(role_as_user, PRIVS_TO_MERGE::ALL);
7653   }
7654 
7655   mysql_mutex_unlock(&acl_cache->lock);
7656 
7657   if (result)
7658     my_error(revoke ? ER_CANNOT_REVOKE_ROLE : ER_CANNOT_GRANT_ROLE, MYF(0),
7659              rolename.str, wrong_users.c_ptr_safe());
7660   else
7661     result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7662 
7663   mysql_rwlock_unlock(&LOCK_grant);
7664 
7665   DBUG_RETURN(result);
7666 }
7667 
7668 
mysql_grant(THD * thd,const char * db,List<LEX_USER> & list,privilege_t rights,bool revoke_grant,bool is_proxy)7669 bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
7670                  privilege_t rights, bool revoke_grant, bool is_proxy)
7671 {
7672   List_iterator <LEX_USER> str_list (list);
7673   LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
7674   char tmp_db[SAFE_NAME_LEN+1];
7675   bool create_new_users=0;
7676   int result;
7677   DBUG_ENTER("mysql_grant");
7678 
7679   if (lower_case_table_names && db)
7680   {
7681     char *end= strnmov(tmp_db,db, sizeof(tmp_db));
7682     if (end >= tmp_db + sizeof(tmp_db))
7683     {
7684       my_error(ER_WRONG_DB_NAME ,MYF(0), db);
7685       DBUG_RETURN(TRUE);
7686     }
7687     my_casedn_str(files_charset_info, tmp_db);
7688     db=tmp_db;
7689   }
7690 
7691   if (is_proxy)
7692   {
7693     DBUG_ASSERT(!db);
7694     proxied_user= str_list++;
7695   }
7696 
7697   const uint tables_to_open= Table_user | (is_proxy ? Table_proxies_priv : Table_db);
7698   Grant_tables tables;
7699   if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
7700     DBUG_RETURN(result != 1);
7701 
7702   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7703 
7704   if (!revoke_grant)
7705     create_new_users= test_if_create_new_users(thd);
7706 
7707   /* go through users in user_list */
7708   mysql_rwlock_wrlock(&LOCK_grant);
7709   mysql_mutex_lock(&acl_cache->lock);
7710   grant_version++;
7711 
7712   if (proxied_user)
7713   {
7714     if (!(proxied_user= get_current_user(thd, proxied_user, false)))
7715       DBUG_RETURN(TRUE);
7716     DBUG_ASSERT(proxied_user->host.length); // not a Role
7717   }
7718 
7719   while ((tmp_Str = str_list++))
7720   {
7721     if (!(Str= get_current_user(thd, tmp_Str, false)))
7722     {
7723       result= true;
7724       continue;
7725     }
7726 
7727     if (copy_and_check_auth(Str, tmp_Str, thd) ||
7728         replace_user_table(thd, tables.user_table(), Str,
7729                            (!db ? rights : NO_ACL),
7730                            revoke_grant, create_new_users,
7731                            MY_TEST(thd->variables.sql_mode &
7732                                    MODE_NO_AUTO_CREATE_USER)))
7733       result= true;
7734     else if (db)
7735     {
7736       privilege_t db_rights(rights & DB_ACLS);
7737       if (db_rights  == rights)
7738       {
7739 	if (replace_db_table(tables.db_table().table(), db, *Str, db_rights,
7740 			     revoke_grant))
7741 	  result= true;
7742       }
7743       else
7744       {
7745 	my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
7746 	result= true;
7747       }
7748     }
7749     else if (is_proxy)
7750     {
7751       if (replace_proxies_priv_table(thd, tables.proxies_priv_table().table(),
7752             Str, proxied_user, rights & GRANT_ACL ? TRUE : FALSE, revoke_grant))
7753         result= true;
7754     }
7755     if (Str->is_role())
7756       propagate_role_grants(find_acl_role(Str->user.str),
7757                             db ? PRIVS_TO_MERGE::DB : PRIVS_TO_MERGE::GLOBAL,
7758                             db);
7759   }
7760   mysql_mutex_unlock(&acl_cache->lock);
7761 
7762   if (!result)
7763   {
7764     result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
7765   }
7766 
7767   mysql_rwlock_unlock(&LOCK_grant);
7768 
7769   if (!result)
7770     my_ok(thd);
7771 
7772   DBUG_RETURN(result);
7773 }
7774 
7775 
7776 /* Free grant array if possible */
7777 
grant_free(void)7778 void  grant_free(void)
7779 {
7780   DBUG_ENTER("grant_free");
7781   my_hash_free(&column_priv_hash);
7782   my_hash_free(&proc_priv_hash);
7783   my_hash_free(&func_priv_hash);
7784   my_hash_free(&package_spec_priv_hash);
7785   my_hash_free(&package_body_priv_hash);
7786   free_root(&grant_memroot,MYF(0));
7787   DBUG_VOID_RETURN;
7788 }
7789 
7790 
7791 /**
7792   @brief Initialize structures responsible for table/column-level privilege
7793    checking and load information for them from tables in the 'mysql' database.
7794 
7795   @return Error status
7796     @retval 0 OK
7797     @retval 1 Could not initialize grant subsystem.
7798 */
7799 
grant_init()7800 bool grant_init()
7801 {
7802   THD  *thd;
7803   bool return_val;
7804   DBUG_ENTER("grant_init");
7805 
7806   if (!(thd= new THD(0)))
7807     DBUG_RETURN(1);				/* purecov: deadcode */
7808   thd->thread_stack= (char*) &thd;
7809   thd->store_globals();
7810   return_val=  grant_reload(thd);
7811   delete thd;
7812   DBUG_RETURN(return_val);
7813 }
7814 
7815 
7816 /**
7817   @brief Initialize structures responsible for table/column-level privilege
7818     checking and load information about grants from open privilege tables.
7819 
7820   @param thd Current thread
7821   @param tables List containing open "mysql.tables_priv" and
7822     "mysql.columns_priv" tables.
7823 
7824   @see grant_reload
7825 
7826   @return Error state
7827     @retval FALSE Success
7828     @retval TRUE Error
7829 */
7830 
grant_load(THD * thd,const Tables_priv_table & tables_priv,const Columns_priv_table & columns_priv,const Procs_priv_table & procs_priv)7831 static bool grant_load(THD *thd,
7832                        const Tables_priv_table& tables_priv,
7833                        const Columns_priv_table& columns_priv,
7834                        const Procs_priv_table& procs_priv)
7835 {
7836   bool return_val= 1;
7837   TABLE *t_table, *c_table, *p_table;
7838   bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
7839   MEM_ROOT *save_mem_root= thd->mem_root;
7840   DBUG_ENTER("grant_load");
7841 
7842   Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);
7843 
7844   (void) my_hash_init(key_memory_acl_memex, &column_priv_hash,
7845                       &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
7846                       get_grant_table, (my_hash_free_key) free_grant_table, 0);
7847   (void) my_hash_init(key_memory_acl_memex, &proc_priv_hash,
7848                       &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
7849                       get_grant_table, 0,0);
7850   (void) my_hash_init(key_memory_acl_memex, &func_priv_hash,
7851                       &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
7852                       get_grant_table, 0,0);
7853   (void) my_hash_init(key_memory_acl_memex, &package_spec_priv_hash,
7854                       &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
7855                       get_grant_table, 0,0);
7856   (void) my_hash_init(key_memory_acl_memex, &package_body_priv_hash,
7857                       &my_charset_utf8mb3_bin, 0,0,0, (my_hash_get_key)
7858                       get_grant_table, 0,0);
7859   init_sql_alloc(key_memory_acl_mem, &grant_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
7860 
7861   t_table= tables_priv.table();
7862   c_table= columns_priv.table();
7863   p_table= procs_priv.table(); // this can be NULL
7864 
7865   if (t_table->file->ha_index_init(0, 1))
7866     goto end_index_init;
7867 
7868   t_table->use_all_columns();
7869   c_table->use_all_columns();
7870 
7871   thd->mem_root= &grant_memroot;
7872 
7873   if (!t_table->file->ha_index_first(t_table->record[0]))
7874   {
7875     do
7876     {
7877       GRANT_TABLE *mem_check;
7878       /* TODO(cvicentiu) convert this to use tables_priv and columns_priv. */
7879       if (!(mem_check= new (&grant_memroot) GRANT_TABLE(t_table, c_table)))
7880       {
7881 	/* This could only happen if we are out memory */
7882 	goto end_unlock;
7883       }
7884 
7885       if (check_no_resolve)
7886       {
7887 	if (hostname_requires_resolving(mem_check->host.hostname))
7888 	{
7889           sql_print_warning("'tables_priv' entry '%s %s@%s' "
7890                             "ignored in --skip-name-resolve mode.",
7891                             mem_check->tname, mem_check->user,
7892                             safe_str(mem_check->host.hostname));
7893 	  continue;
7894 	}
7895       }
7896 
7897       if (! mem_check->ok())
7898 	delete mem_check;
7899       else if (column_priv_insert(mem_check))
7900       {
7901 	delete mem_check;
7902 	goto end_unlock;
7903       }
7904     }
7905     while (!t_table->file->ha_index_next(t_table->record[0]));
7906   }
7907 
7908   return_val= 0;
7909 
7910   if (p_table)
7911   {
7912     if (p_table->file->ha_index_init(0, 1))
7913       goto end_unlock;
7914 
7915     p_table->use_all_columns();
7916 
7917     if (!p_table->file->ha_index_first(p_table->record[0]))
7918     {
7919       do
7920       {
7921         GRANT_NAME *mem_check;
7922         HASH *hash;
7923         if (!(mem_check= new (&grant_memroot) GRANT_NAME(p_table, TRUE)))
7924         {
7925           /* This could only happen if we are out memory */
7926           goto end_unlock_p;
7927         }
7928 
7929         if (check_no_resolve)
7930         {
7931           if (hostname_requires_resolving(mem_check->host.hostname))
7932           {
7933             sql_print_warning("'procs_priv' entry '%s %s@%s' "
7934                               "ignored in --skip-name-resolve mode.",
7935                               mem_check->tname, mem_check->user,
7936                               safe_str(mem_check->host.hostname));
7937             continue;
7938           }
7939         }
7940         enum_sp_type type= (enum_sp_type)procs_priv.routine_type()->val_int();
7941         const Sp_handler *sph= Sp_handler::handler(type);
7942         if (!sph || !(hash= sph->get_priv_hash()))
7943         {
7944           sql_print_warning("'procs_priv' entry '%s' "
7945                             "ignored, bad routine type",
7946                             mem_check->tname);
7947           continue;
7948         }
7949 
7950         mem_check->privs= fix_rights_for_procedure(mem_check->privs);
7951         mem_check->init_privs= mem_check->privs;
7952         if (! mem_check->ok())
7953           delete mem_check;
7954         else if (my_hash_insert(hash, (uchar*) mem_check))
7955         {
7956           delete mem_check;
7957           goto end_unlock_p;
7958         }
7959       }
7960       while (!p_table->file->ha_index_next(p_table->record[0]));
7961     }
7962   }
7963 
7964 end_unlock_p:
7965   if (p_table)
7966     p_table->file->ha_index_end();
7967 end_unlock:
7968   t_table->file->ha_index_end();
7969   thd->mem_root= save_mem_root;
7970 end_index_init:
7971   DBUG_RETURN(return_val);
7972 }
7973 
propagate_role_grants_action(void * role_ptr,void * ptr)7974 static my_bool propagate_role_grants_action(void *role_ptr,
7975                                             void *ptr __attribute__((unused)))
7976 {
7977   ACL_ROLE *role= static_cast<ACL_ROLE *>(role_ptr);
7978   if (role->counter)
7979     return 0;
7980 
7981   mysql_mutex_assert_owner(&acl_cache->lock);
7982   PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
7983   traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
7984   return 0;
7985 }
7986 
7987 
7988 /**
7989   @brief Reload information about table and column level privileges if possible
7990 
7991   @param thd Current thread
7992 
7993   Locked tables are checked by acl_reload() and doesn't have to be checked
7994   in this call.
7995   This function is also used for initialization of structures responsible
7996   for table/column-level privilege checking.
7997 
7998   @return Error state
7999     @retval FALSE Success
8000     @retval TRUE  Error
8001 */
8002 
grant_reload(THD * thd)8003 bool grant_reload(THD *thd)
8004 {
8005   HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
8006   HASH old_package_spec_priv_hash, old_package_body_priv_hash;
8007   MEM_ROOT old_mem;
8008   int result;
8009   DBUG_ENTER("grant_reload");
8010 
8011   /*
8012     To avoid deadlocks we should obtain table locks before
8013     obtaining LOCK_grant rwlock.
8014   */
8015 
8016   Grant_tables tables;
8017   const uint tables_to_open= Table_tables_priv | Table_columns_priv| Table_procs_priv;
8018   if ((result= tables.open_and_lock(thd, tables_to_open, TL_READ)))
8019     DBUG_RETURN(result != 1);
8020 
8021   mysql_rwlock_wrlock(&LOCK_grant);
8022   grant_version++;
8023   old_column_priv_hash= column_priv_hash;
8024   old_proc_priv_hash= proc_priv_hash;
8025   old_func_priv_hash= func_priv_hash;
8026   old_package_spec_priv_hash= package_spec_priv_hash;
8027   old_package_body_priv_hash= package_body_priv_hash;
8028 
8029   /*
8030     Create a new memory pool but save the current memory pool to make an undo
8031     opertion possible in case of failure.
8032   */
8033   old_mem= grant_memroot;
8034 
8035   if ((result= grant_load(thd,
8036                           tables.tables_priv_table(),
8037                           tables.columns_priv_table(),
8038                           tables.procs_priv_table())))
8039   {						// Error. Revert to old hash
8040     DBUG_PRINT("error",("Reverting to old privileges"));
8041     grant_free();				/* purecov: deadcode */
8042     column_priv_hash= old_column_priv_hash;	/* purecov: deadcode */
8043     proc_priv_hash= old_proc_priv_hash;
8044     func_priv_hash= old_func_priv_hash;
8045     package_spec_priv_hash= old_package_spec_priv_hash;
8046     package_body_priv_hash= old_package_body_priv_hash;
8047     grant_memroot= old_mem;                     /* purecov: deadcode */
8048   }
8049   else
8050   {
8051     my_hash_free(&old_column_priv_hash);
8052     my_hash_free(&old_proc_priv_hash);
8053     my_hash_free(&old_func_priv_hash);
8054     my_hash_free(&old_package_spec_priv_hash);
8055     my_hash_free(&old_package_body_priv_hash);
8056     free_root(&old_mem,MYF(0));
8057   }
8058 
8059   mysql_mutex_lock(&acl_cache->lock);
8060   my_hash_iterate(&acl_roles, propagate_role_grants_action, NULL);
8061   mysql_mutex_unlock(&acl_cache->lock);
8062 
8063   mysql_rwlock_unlock(&LOCK_grant);
8064 
8065   close_mysql_tables(thd);
8066 
8067   DBUG_RETURN(result);
8068 }
8069 
8070 
8071 /**
8072   @brief Check table level grants
8073 
8074   @param thd          Thread handler
8075   @param want_access  Bits of privileges user needs to have.
8076   @param tables       List of tables to check. The user should have
8077                       'want_access' to all tables in list.
8078   @param any_combination_will_do TRUE if it's enough to have any privilege for
8079     any combination of the table columns.
8080   @param number       Check at most this number of tables.
8081   @param no_errors    TRUE if no error should be sent directly to the client.
8082 
8083   If table->grant.want_privilege != 0 then the requested privileges where
8084   in the set of COL_ACLS but access was not granted on the table level. As
8085   a consequence an extra check of column privileges is required.
8086 
8087   Specifically if this function returns FALSE the user has some kind of
8088   privilege on a combination of columns in each table.
8089 
8090   This function is usually preceeded by check_access which establish the
8091   User-, Db- and Host access rights.
8092 
8093   @see check_access
8094   @see check_table_access
8095 
8096   @note
8097      This functions assumes that either number of tables to be inspected
8098      by it is limited explicitly (i.e. is is not UINT_MAX) or table list
8099      used and thd->lex->query_tables_own_last value correspond to each
8100      other (the latter should be either 0 or point to next_global member
8101      of one of elements of this table list).
8102 
8103      We delay locking of LOCK_grant until we really need it as we assume that
8104      most privileges be resolved with user or db level accesses.
8105 
8106    @return Access status
8107      @retval FALSE Access granted; But column privileges might need to be
8108       checked.
8109      @retval TRUE The user did not have the requested privileges on any of the
8110       tables.
8111 
8112 */
8113 
check_grant(THD * thd,privilege_t want_access,TABLE_LIST * tables,bool any_combination_will_do,uint number,bool no_errors)8114 bool check_grant(THD *thd, privilege_t want_access, TABLE_LIST *tables,
8115                  bool any_combination_will_do, uint number, bool no_errors)
8116 {
8117   TABLE_LIST *tl;
8118   TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
8119   Security_context *sctx= thd->security_ctx;
8120   uint i;
8121   privilege_t original_want_access(want_access);
8122   bool locked= 0;
8123   GRANT_TABLE *grant_table;
8124   GRANT_TABLE *grant_table_role= NULL;
8125   DBUG_ENTER("check_grant");
8126   DBUG_ASSERT(number > 0);
8127 
8128   /*
8129     Walk through the list of tables that belong to the query and save the
8130     requested access (orig_want_privilege) to be able to use it when
8131     checking access rights to the underlying tables of a view. Our grant
8132     system gradually eliminates checked bits from want_privilege and thus
8133     after all checks are done we can no longer use it.
8134     The check that first_not_own_table is not reached is for the case when
8135     the given table list refers to the list for prelocking (contains tables
8136     of other queries). For simple queries first_not_own_table is 0.
8137   */
8138   for (i= 0, tl= tables;
8139        i < number  && tl != first_not_own_table;
8140        tl= tl->next_global, i++)
8141   {
8142     /*
8143       Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
8144       It will be checked during making view.
8145     */
8146     tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
8147   }
8148   number= i;
8149 
8150   for (tl= tables; number-- ; tl= tl->next_global)
8151   {
8152     TABLE_LIST *const t_ref=
8153       tl->correspondent_table ? tl->correspondent_table : tl;
8154     sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx;
8155     privilege_t orig_want_access(original_want_access);
8156 
8157     /*
8158       If sequence is used as part of NEXT VALUE, PREVIOUS VALUE or SELECT,
8159       we need to modify the requested access rights depending on how the
8160       sequence is used.
8161     */
8162     if (t_ref->sequence &&
8163         !(want_access & ~(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL)))
8164     {
8165       /*
8166         We want to have either SELECT or INSERT rights to sequences depending
8167         on how they are accessed
8168       */
8169       orig_want_access= ((t_ref->lock_type == TL_WRITE_ALLOW_WRITE) ?
8170                          INSERT_ACL : SELECT_ACL);
8171     }
8172 
8173     if (tl->with || !tl->db.str ||
8174         (tl->select_lex &&
8175          (tl->with= tl->select_lex->find_table_def_in_with_clauses(tl))))
8176       continue;
8177 
8178     const ACL_internal_table_access *access=
8179       get_cached_table_access(&t_ref->grant.m_internal,
8180                               t_ref->get_db_name(),
8181                               t_ref->get_table_name());
8182 
8183     if (access)
8184     {
8185       switch(access->check(orig_want_access, &t_ref->grant.privilege))
8186       {
8187       case ACL_INTERNAL_ACCESS_GRANTED:
8188         t_ref->grant.privilege|= orig_want_access;
8189         t_ref->grant.want_privilege= NO_ACL;
8190         continue;
8191       case ACL_INTERNAL_ACCESS_DENIED:
8192         goto err;
8193       case ACL_INTERNAL_ACCESS_CHECK_GRANT:
8194         break;
8195       }
8196     }
8197 
8198     want_access= orig_want_access;
8199     want_access&= ~sctx->master_access;
8200     if (!want_access)
8201       continue;                                 // ok
8202 
8203     if (!(~t_ref->grant.privilege & want_access) ||
8204         t_ref->is_anonymous_derived_table() || t_ref->schema_table)
8205     {
8206       /*
8207         It is subquery in the FROM clause. VIEW set t_ref->derived after
8208         table opening, but this function always called before table opening.
8209 
8210         NOTE: is_derived() can't be used here because subquery in this case
8211         the FROM clase (derived tables) can be not be marked yet.
8212       */
8213       if (t_ref->is_anonymous_derived_table() || t_ref->schema_table)
8214       {
8215         /*
8216           If it's a temporary table created for a subquery in the FROM
8217           clause, or an INFORMATION_SCHEMA table, drop the request for
8218           a privilege.
8219         */
8220         t_ref->grant.want_privilege= NO_ACL;
8221       }
8222       continue;
8223     }
8224 
8225     if (is_temporary_table(t_ref))
8226     {
8227       /*
8228         If this table list element corresponds to a pre-opened temporary
8229         table skip checking of all relevant table-level privileges for it.
8230         Note that during creation of temporary table we still need to check
8231         if user has CREATE_TMP_ACL.
8232       */
8233       t_ref->grant.privilege|= TMP_TABLE_ACLS;
8234       t_ref->grant.want_privilege= NO_ACL;
8235       continue;
8236     }
8237 
8238     if (!locked)
8239     {
8240       locked= 1;
8241       mysql_rwlock_rdlock(&LOCK_grant);
8242     }
8243 
8244     grant_table= table_hash_search(sctx->host, sctx->ip,
8245                                    t_ref->get_db_name(),
8246                                    sctx->priv_user,
8247                                    t_ref->get_table_name(),
8248                                    FALSE);
8249     if (sctx->priv_role[0])
8250       grant_table_role= table_hash_search("", NULL, t_ref->get_db_name(),
8251                                           sctx->priv_role,
8252                                           t_ref->get_table_name(),
8253                                           TRUE);
8254 
8255     if (!grant_table && !grant_table_role)
8256     {
8257       want_access&= ~t_ref->grant.privilege;
8258       goto err;					// No grants
8259     }
8260 
8261     /*
8262       For SHOW COLUMNS, SHOW INDEX it is enough to have some
8263       privileges on any column combination on the table.
8264     */
8265     if (any_combination_will_do)
8266       continue;
8267 
8268     t_ref->grant.grant_table_user= grant_table; // Remember for column test
8269     t_ref->grant.grant_table_role= grant_table_role;
8270     t_ref->grant.version= grant_version;
8271     t_ref->grant.privilege|= grant_table ? grant_table->privs : NO_ACL;
8272     t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : NO_ACL;
8273     t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege);
8274 
8275     if (!(~t_ref->grant.privilege & want_access))
8276       continue;
8277 
8278     if ((want_access&= ~((grant_table ? grant_table->cols : NO_ACL) |
8279                         (grant_table_role ? grant_table_role->cols : NO_ACL) |
8280                         t_ref->grant.privilege)))
8281     {
8282       goto err;                                 // impossible
8283     }
8284   }
8285   if (locked)
8286     mysql_rwlock_unlock(&LOCK_grant);
8287   DBUG_RETURN(FALSE);
8288 
8289 err:
8290   if (locked)
8291     mysql_rwlock_unlock(&LOCK_grant);
8292   if (!no_errors)				// Not a silent skip of table
8293   {
8294     char command[128];
8295     get_privilege_desc(command, sizeof(command), want_access);
8296     status_var_increment(thd->status_var.access_denied_errors);
8297 
8298     my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
8299              command,
8300              sctx->priv_user,
8301              sctx->host_or_ip,
8302              tl ? tl->get_table_name() : "unknown");
8303   }
8304   DBUG_RETURN(TRUE);
8305 }
8306 
8307 
check_grant_column_int(GRANT_TABLE * grant_table,const char * name,uint length,privilege_t * want_access)8308 static void check_grant_column_int(GRANT_TABLE *grant_table, const char *name,
8309                                    uint length, privilege_t *want_access)
8310 {
8311   if (grant_table)
8312   {
8313     *want_access&= ~grant_table->privs;
8314     if (*want_access & grant_table->cols)
8315     {
8316       GRANT_COLUMN *grant_column= column_hash_search(grant_table, name, length);
8317       if (grant_column)
8318         *want_access&= ~grant_column->rights;
8319     }
8320   }
8321 }
8322 
8323 /*
8324   Check column rights in given security context
8325 
8326   SYNOPSIS
8327     check_grant_column()
8328     thd                  thread handler
8329     grant                grant information structure
8330     db_name              db name
8331     table_name           table  name
8332     name                 column name
8333     length               column name length
8334     sctx                 security context
8335 
8336   RETURN
8337     FALSE OK
8338     TRUE  access denied
8339 */
8340 
check_grant_column(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * name,size_t length,Security_context * sctx)8341 bool check_grant_column(THD *thd, GRANT_INFO *grant,
8342 			const char *db_name, const char *table_name,
8343 			const char *name, size_t length,  Security_context *sctx)
8344 {
8345   privilege_t want_access(grant->want_privilege & ~grant->privilege);
8346   DBUG_ENTER("check_grant_column");
8347   DBUG_PRINT("enter", ("table: %s  want_access: %llx",
8348                        table_name, (longlong) want_access));
8349 
8350   if (!want_access)
8351     DBUG_RETURN(0);				// Already checked
8352 
8353   mysql_rwlock_rdlock(&LOCK_grant);
8354 
8355   /* reload table if someone has modified any grants */
8356 
8357   if (grant->version != grant_version)
8358   {
8359     grant->grant_table_user=
8360       table_hash_search(sctx->host, sctx->ip, db_name,
8361 			sctx->priv_user,
8362 			table_name, 0);         /* purecov: inspected */
8363     grant->grant_table_role=
8364       sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
8365                                              sctx->priv_role,
8366                                              table_name, TRUE) : NULL;
8367     grant->version= grant_version;		/* purecov: inspected */
8368   }
8369 
8370   check_grant_column_int(grant->grant_table_user, name, (uint)length,
8371                          &want_access);
8372   check_grant_column_int(grant->grant_table_role, name, (uint)length,
8373                          &want_access);
8374 
8375   mysql_rwlock_unlock(&LOCK_grant);
8376   if (!want_access)
8377     DBUG_RETURN(0);
8378 
8379   char command[128];
8380   get_privilege_desc(command, sizeof(command), want_access);
8381   /* TODO perhaps error should print current rolename aswell */
8382   my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), command, sctx->priv_user,
8383            sctx->host_or_ip, name, table_name);
8384   DBUG_RETURN(1);
8385 }
8386 
8387 
8388 /*
8389   Check the access right to a column depending on the type of table.
8390 
8391   SYNOPSIS
8392     check_column_grant_in_table_ref()
8393     thd              thread handler
8394     table_ref        table reference where to check the field
8395     name             name of field to check
8396     length           length of name
8397     fld              use fld object to check invisibility when it is
8398                      not 0, not_found_field, view_ref_found
8399 
8400   DESCRIPTION
8401     Check the access rights to a column depending on the type of table
8402     reference where the column is checked. The function provides a
8403     generic interface to check column access rights that hides the
8404     heterogeneity of the column representation - whether it is a view
8405     or a stored table colum.
8406 
8407   RETURN
8408     FALSE OK
8409     TRUE  access denied
8410 */
8411 
check_column_grant_in_table_ref(THD * thd,TABLE_LIST * table_ref,const char * name,size_t length,Field * fld)8412 bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
8413                                      const char *name, size_t length,
8414                                      Field *fld)
8415 {
8416   GRANT_INFO *grant;
8417   const char *db_name;
8418   const char *table_name;
8419   Security_context *sctx= table_ref->security_ctx ?
8420                           table_ref->security_ctx : thd->security_ctx;
8421   if (fld && fld != not_found_field && fld != view_ref_found
8422           && fld->invisible >= INVISIBLE_SYSTEM)
8423       return false;
8424 
8425   if (table_ref->view || table_ref->field_translation)
8426   {
8427     /* View or derived information schema table. */
8428     privilege_t view_privs(NO_ACL);
8429     grant= &(table_ref->grant);
8430     db_name= table_ref->view_db.str;
8431     table_name= table_ref->view_name.str;
8432     if (table_ref->belong_to_view &&
8433         thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
8434     {
8435       view_privs= get_column_grant(thd, grant, db_name, table_name, name);
8436       if (view_privs & VIEW_ANY_ACL)
8437       {
8438         table_ref->belong_to_view->allowed_show= TRUE;
8439         return FALSE;
8440       }
8441       table_ref->belong_to_view->allowed_show= FALSE;
8442       my_message(ER_VIEW_NO_EXPLAIN, ER_THD(thd, ER_VIEW_NO_EXPLAIN), MYF(0));
8443       return TRUE;
8444     }
8445   }
8446   else
8447   {
8448     /* Normal or temporary table. */
8449     TABLE *table= table_ref->table;
8450     grant= &(table->grant);
8451     db_name= table->s->db.str;
8452     table_name= table->s->table_name.str;
8453   }
8454 
8455   if (grant->want_privilege)
8456     return check_grant_column(thd, grant, db_name, table_name, name,
8457                               length, sctx);
8458   else
8459     return FALSE;
8460 
8461 }
8462 
8463 
8464 /**
8465   @brief check if a query can access a set of columns
8466 
8467   @param  thd  the current thread
8468   @param  want_access_arg  the privileges requested
8469   @param  fields an iterator over the fields of a table reference.
8470   @return Operation status
8471     @retval 0 Success
8472     @retval 1 Falure
8473   @details This function walks over the columns of a table reference
8474    The columns may originate from different tables, depending on the kind of
8475    table reference, e.g. join, view.
8476    For each table it will retrieve the grant information and will use it
8477    to check the required access privileges for the fields requested from it.
8478 */
check_grant_all_columns(THD * thd,privilege_t want_access_arg,Field_iterator_table_ref * fields)8479 bool check_grant_all_columns(THD *thd, privilege_t want_access_arg,
8480                              Field_iterator_table_ref *fields)
8481 {
8482   Security_context *sctx= thd->security_ctx;
8483   privilege_t want_access(NO_ACL);
8484   const char *table_name= NULL;
8485   const char* db_name;
8486   GRANT_INFO *grant;
8487   GRANT_TABLE *UNINIT_VAR(grant_table);
8488   GRANT_TABLE *UNINIT_VAR(grant_table_role);
8489   /*
8490      Flag that gets set if privilege checking has to be performed on column
8491      level.
8492   */
8493   bool using_column_privileges= FALSE;
8494 
8495   mysql_rwlock_rdlock(&LOCK_grant);
8496 
8497   for (; !fields->end_of_fields(); fields->next())
8498   {
8499     if (fields->field() &&
8500         fields->field()->invisible >= INVISIBLE_SYSTEM)
8501       continue;
8502     LEX_CSTRING *field_name= fields->name();
8503 
8504     if (table_name != fields->get_table_name())
8505     {
8506       table_name= fields->get_table_name();
8507       db_name= fields->get_db_name();
8508       grant= fields->grant();
8509       /* get a fresh one for each table */
8510       want_access= want_access_arg & ~grant->privilege;
8511       if (want_access)
8512       {
8513         /* reload table if someone has modified any grants */
8514         if (grant->version != grant_version)
8515         {
8516           grant->grant_table_user=
8517             table_hash_search(sctx->host, sctx->ip, db_name,
8518                               sctx->priv_user,
8519                               table_name, 0);	/* purecov: inspected */
8520           grant->grant_table_role=
8521             sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
8522                                                    sctx->priv_role,
8523                                                    table_name, TRUE) : NULL;
8524           grant->version= grant_version;	/* purecov: inspected */
8525         }
8526 
8527         grant_table= grant->grant_table_user;
8528         grant_table_role= grant->grant_table_role;
8529         if (!grant_table && !grant_table_role)
8530           goto err;
8531       }
8532     }
8533 
8534     if (want_access)
8535     {
8536       privilege_t have_access(NO_ACL);
8537       if (grant_table)
8538       {
8539         GRANT_COLUMN *grant_column=
8540           column_hash_search(grant_table, field_name->str, field_name->length);
8541         if (grant_column)
8542           have_access= grant_column->rights;
8543       }
8544       if (grant_table_role)
8545       {
8546         GRANT_COLUMN *grant_column=
8547           column_hash_search(grant_table_role, field_name->str,
8548                              field_name->length);
8549         if (grant_column)
8550           have_access|= grant_column->rights;
8551       }
8552 
8553       if (have_access)
8554         using_column_privileges= TRUE;
8555       if (want_access & ~have_access)
8556         goto err;
8557     }
8558   }
8559   mysql_rwlock_unlock(&LOCK_grant);
8560   return 0;
8561 
8562 err:
8563   mysql_rwlock_unlock(&LOCK_grant);
8564 
8565   char command[128];
8566   get_privilege_desc(command, sizeof(command), want_access);
8567   /*
8568     Do not give an error message listing a column name unless the user has
8569     privilege to see all columns.
8570   */
8571   if (using_column_privileges)
8572     my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
8573              command, sctx->priv_user,
8574              sctx->host_or_ip, table_name);
8575   else
8576     my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
8577              command,
8578              sctx->priv_user,
8579              sctx->host_or_ip,
8580              fields->name()->str,
8581              table_name);
8582   return 1;
8583 }
8584 
8585 
check_grant_db_routine(THD * thd,const char * db,HASH * hash)8586 static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
8587 {
8588   Security_context *sctx= thd->security_ctx;
8589 
8590   for (uint idx= 0; idx < hash->records; ++idx)
8591   {
8592     GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
8593 
8594     if (strcmp(item->user, sctx->priv_user) == 0 &&
8595         strcmp(item->db, db) == 0 &&
8596         compare_hostname(&item->host, sctx->host, sctx->ip))
8597     {
8598       return FALSE;
8599     }
8600     if (sctx->priv_role[0] && strcmp(item->user, sctx->priv_role) == 0 &&
8601         strcmp(item->db, db) == 0 &&
8602         (!item->host.hostname || !item->host.hostname[0]))
8603     {
8604       return FALSE; /* Found current role match */
8605     }
8606   }
8607 
8608   return TRUE;
8609 }
8610 
8611 
8612 /*
8613   Check if a user has the right to access a database
8614   Access is accepted if he has a grant for any table/routine in the database
8615   Return 1 if access is denied
8616 */
8617 
check_grant_db(THD * thd,const char * db)8618 bool check_grant_db(THD *thd, const char *db)
8619 {
8620   Security_context *sctx= thd->security_ctx;
8621   char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end;
8622   char helping2 [SAFE_NAME_LEN + USERNAME_LENGTH+2], *tmp_db;
8623   uint len, UNINIT_VAR(len2);
8624   bool error= TRUE;
8625 
8626   tmp_db= strmov(helping, sctx->priv_user) + 1;
8627   end= strnmov(tmp_db, db, helping + sizeof(helping) - tmp_db);
8628 
8629   if (end >= helping + sizeof(helping)) // db name was truncated
8630     return 1;                           // no privileges for an invalid db name
8631 
8632   if (lower_case_table_names)
8633   {
8634     end = tmp_db + my_casedn_str(files_charset_info, tmp_db);
8635     db=tmp_db;
8636   }
8637 
8638   len= (uint) (end - helping) + 1;
8639 
8640   /*
8641      If a role is set, we need to check for privileges here as well.
8642   */
8643   if (sctx->priv_role[0])
8644   {
8645     end= strmov(helping2, sctx->priv_role) + 1;
8646     end= strnmov(end, db, helping2 + sizeof(helping2) - end);
8647     len2= (uint) (end - helping2) + 1;
8648   }
8649 
8650 
8651   mysql_rwlock_rdlock(&LOCK_grant);
8652 
8653   for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
8654   {
8655     GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
8656                                                              idx);
8657     if (len < grant_table->key_length &&
8658         !memcmp(grant_table->hash_key, helping, len) &&
8659         compare_hostname(&grant_table->host, sctx->host, sctx->ip))
8660     {
8661       error= FALSE; /* Found match. */
8662       break;
8663     }
8664     if (sctx->priv_role[0] &&
8665         len2 < grant_table->key_length &&
8666         !memcmp(grant_table->hash_key, helping2, len2) &&
8667         (!grant_table->host.hostname || !grant_table->host.hostname[0]))
8668     {
8669       error= FALSE; /* Found role match */
8670       break;
8671     }
8672   }
8673 
8674   if (error)
8675     error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
8676            check_grant_db_routine(thd, db, &func_priv_hash) &&
8677            check_grant_db_routine(thd, db, &package_spec_priv_hash) &&
8678            check_grant_db_routine(thd, db, &package_body_priv_hash);
8679 
8680   mysql_rwlock_unlock(&LOCK_grant);
8681 
8682   return error;
8683 }
8684 
8685 
8686 /****************************************************************************
8687   Check routine level grants
8688 
8689   SYNPOSIS
8690    bool check_grant_routine()
8691    thd		Thread handler
8692    want_access  Bits of privileges user needs to have
8693    procs	List of routines to check. The user should have 'want_access'
8694    sph          SP handler
8695    no_errors	If 0 then we write an error. The error is sent directly to
8696 		the client
8697 
8698    RETURN
8699      0  ok
8700      1  Error: User did not have the requested privielges
8701 ****************************************************************************/
8702 
check_grant_routine(THD * thd,privilege_t want_access,TABLE_LIST * procs,const Sp_handler * sph,bool no_errors)8703 bool check_grant_routine(THD *thd, privilege_t want_access,
8704 			 TABLE_LIST *procs, const Sp_handler *sph,
8705 			 bool no_errors)
8706 {
8707   TABLE_LIST *table;
8708   Security_context *sctx= thd->security_ctx;
8709   char *user= sctx->priv_user;
8710   char *host= sctx->priv_host;
8711   char *role= sctx->priv_role;
8712   DBUG_ENTER("check_grant_routine");
8713 
8714   want_access&= ~sctx->master_access;
8715   if (!want_access)
8716     DBUG_RETURN(0);                             // ok
8717 
8718   mysql_rwlock_rdlock(&LOCK_grant);
8719   for (table= procs; table; table= table->next_global)
8720   {
8721     GRANT_NAME *grant_proc;
8722     if ((grant_proc= routine_hash_search(host, sctx->ip, table->db.str, user,
8723                                          table->table_name.str, sph, 0)))
8724       table->grant.privilege|= grant_proc->privs;
8725     if (role[0]) /* current role set check */
8726     {
8727       if ((grant_proc= routine_hash_search("", NULL, table->db.str, role,
8728                                            table->table_name.str, sph, 0)))
8729       table->grant.privilege|= grant_proc->privs;
8730     }
8731 
8732     if (want_access & ~table->grant.privilege)
8733     {
8734       want_access &= ~table->grant.privilege;
8735       goto err;
8736     }
8737   }
8738   mysql_rwlock_unlock(&LOCK_grant);
8739   DBUG_RETURN(0);
8740 err:
8741   mysql_rwlock_unlock(&LOCK_grant);
8742   if (!no_errors)
8743   {
8744     char buff[1024];
8745     const char *command="";
8746     if (table)
8747       strxmov(buff, table->db.str, ".", table->table_name.str, NullS);
8748     if (want_access & EXECUTE_ACL)
8749       command= "execute";
8750     else if (want_access & ALTER_PROC_ACL)
8751       command= "alter routine";
8752     else if (want_access & GRANT_ACL)
8753       command= "grant";
8754     my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
8755              command, user, host, table ? buff : "unknown");
8756   }
8757   DBUG_RETURN(1);
8758 }
8759 
8760 
8761 /*
8762   Check if routine has any of the
8763   routine level grants
8764 
8765   SYNPOSIS
8766    bool    check_routine_level_acl()
8767    thd	        Thread handler
8768    db           Database name
8769    name         Routine name
8770 
8771   RETURN
8772    0            Ok
8773    1            error
8774 */
8775 
check_routine_level_acl(THD * thd,const char * db,const char * name,const Sp_handler * sph)8776 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
8777                              const Sp_handler *sph)
8778 {
8779   bool no_routine_acl= 1;
8780   GRANT_NAME *grant_proc;
8781   Security_context *sctx= thd->security_ctx;
8782   mysql_rwlock_rdlock(&LOCK_grant);
8783   if ((grant_proc= routine_hash_search(sctx->priv_host,
8784                                        sctx->ip, db,
8785                                        sctx->priv_user,
8786                                        name, sph, 0)))
8787     no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
8788 
8789   if (no_routine_acl && sctx->priv_role[0]) /* current set role check */
8790   {
8791     if ((grant_proc= routine_hash_search("",
8792                                          NULL, db,
8793                                          sctx->priv_role,
8794                                          name, sph, 0)))
8795       no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
8796   }
8797   mysql_rwlock_unlock(&LOCK_grant);
8798   return no_routine_acl;
8799 }
8800 
8801 
8802 /*****************************************************************************
8803   Functions to retrieve the grant for a table/column  (for SHOW functions)
8804 *****************************************************************************/
8805 
get_table_grant(THD * thd,TABLE_LIST * table)8806 privilege_t get_table_grant(THD *thd, TABLE_LIST *table)
8807 {
8808   Security_context *sctx= thd->security_ctx;
8809   const char *db = table->db.str ? table->db.str : thd->db.str;
8810   GRANT_TABLE *grant_table;
8811   GRANT_TABLE *grant_table_role= NULL;
8812 
8813   mysql_rwlock_rdlock(&LOCK_grant);
8814 #ifdef EMBEDDED_LIBRARY
8815   grant_table= NULL;
8816   grant_table_role= NULL;
8817 #else
8818   grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
8819 				 table->table_name.str, 0);
8820   if (sctx->priv_role[0])
8821     grant_table_role= table_hash_search("", "", db, sctx->priv_role,
8822                                         table->table_name.str, 0);
8823 #endif
8824   table->grant.grant_table_user= grant_table; // Remember for column test
8825   table->grant.grant_table_role= grant_table_role;
8826   table->grant.version=grant_version;
8827   if (grant_table)
8828     table->grant.privilege|= grant_table->privs;
8829   if (grant_table_role)
8830     table->grant.privilege|= grant_table_role->privs;
8831   privilege_t privilege(table->grant.privilege);
8832   mysql_rwlock_unlock(&LOCK_grant);
8833   return privilege;
8834 }
8835 
8836 
8837 /*
8838   Determine the access priviliges for a field.
8839 
8840   SYNOPSIS
8841     get_column_grant()
8842     thd         thread handler
8843     grant       grants table descriptor
8844     db_name     name of database that the field belongs to
8845     table_name  name of table that the field belongs to
8846     field_name  name of field
8847 
8848   DESCRIPTION
8849     The procedure may also modify: grant->grant_table and grant->version.
8850 
8851   RETURN
8852     The access priviliges for the field db_name.table_name.field_name
8853 */
8854 
get_column_grant(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * field_name)8855 privilege_t get_column_grant(THD *thd, GRANT_INFO *grant,
8856                         const char *db_name, const char *table_name,
8857                         const char *field_name)
8858 {
8859   GRANT_TABLE *grant_table;
8860   GRANT_TABLE *grant_table_role;
8861   GRANT_COLUMN *grant_column;
8862   privilege_t priv(NO_ACL);
8863 
8864   mysql_rwlock_rdlock(&LOCK_grant);
8865   /* reload table if someone has modified any grants */
8866   if (grant->version != grant_version)
8867   {
8868     Security_context *sctx= thd->security_ctx;
8869     grant->grant_table_user=
8870       table_hash_search(sctx->host, sctx->ip,
8871                         db_name, sctx->priv_user,
8872                         table_name, 0);         /* purecov: inspected */
8873     grant->grant_table_role=
8874       sctx->priv_role[0] ? table_hash_search("", "", db_name,
8875                                              sctx->priv_role,
8876                                              table_name, TRUE) : NULL;
8877     grant->version= grant_version;              /* purecov: inspected */
8878   }
8879 
8880   grant_table= grant->grant_table_user;
8881   grant_table_role= grant->grant_table_role;
8882 
8883   if (!grant_table && !grant_table_role)
8884     priv= grant->privilege;
8885   else
8886   {
8887     if (grant_table)
8888     {
8889       grant_column= column_hash_search(grant_table, field_name,
8890                                        (uint) strlen(field_name));
8891       if (!grant_column)
8892         priv= (grant->privilege | grant_table->privs);
8893       else
8894         priv= (grant->privilege | grant_table->privs | grant_column->rights);
8895     }
8896 
8897     if (grant_table_role)
8898     {
8899       grant_column= column_hash_search(grant_table_role, field_name,
8900                                        (uint) strlen(field_name));
8901       if (!grant_column)
8902         priv|= (grant->privilege | grant_table_role->privs);
8903       else
8904         priv|= (grant->privilege | grant_table_role->privs |
8905                 grant_column->rights);
8906     }
8907   }
8908   mysql_rwlock_unlock(&LOCK_grant);
8909   return priv;
8910 }
8911 
8912 
8913 /* Help function for mysql_show_grants */
8914 
add_user_option(String * grant,long value,const char * name,bool is_signed)8915 static void add_user_option(String *grant, long value, const char *name,
8916                             bool is_signed)
8917 {
8918   if (value)
8919   {
8920     char buff[22], *p; // just as in int2str
8921     grant->append(' ');
8922     grant->append(name, strlen(name));
8923     grant->append(' ');
8924     p=int10_to_str(value, buff, is_signed ? -10 : 10);
8925     grant->append(buff,p-buff);
8926   }
8927 }
8928 
8929 
add_user_option(String * grant,double value,const char * name)8930 static void add_user_option(String *grant, double value, const char *name)
8931 {
8932   if (value != 0.0 )
8933   {
8934     char buff[FLOATING_POINT_BUFFER];
8935     size_t len;
8936     grant->append(' ');
8937     grant->append(name, strlen(name));
8938     grant->append(' ');
8939     len= my_fcvt(value, 6, buff, NULL);
8940     grant->append(buff, len);
8941   }
8942 }
8943 
add_user_parameters(THD * thd,String * result,ACL_USER * acl_user,bool with_grant)8944 static void add_user_parameters(THD *thd, String *result, ACL_USER* acl_user,
8945                                 bool with_grant)
8946 {
8947   result->append('@');
8948   append_identifier(thd, result, acl_user->host.hostname,
8949                     acl_user->hostname_length);
8950 
8951   if (acl_user->nauth == 1 &&
8952       (acl_user->auth->plugin.str == native_password_plugin_name.str ||
8953        acl_user->auth->plugin.str == old_password_plugin_name.str))
8954   {
8955     if (acl_user->auth->auth_string.length)
8956     {
8957       result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
8958       result->append(&acl_user->auth->auth_string);
8959       result->append('\'');
8960     }
8961   }
8962   else
8963   {
8964     result->append(STRING_WITH_LEN(" IDENTIFIED VIA "));
8965     for (uint i=0; i < acl_user->nauth; i++)
8966     {
8967       if (i)
8968         result->append(STRING_WITH_LEN(" OR "));
8969       result->append(&acl_user->auth[i].plugin);
8970       if (acl_user->auth[i].auth_string.length)
8971       {
8972         result->append(STRING_WITH_LEN(" USING '"));
8973         result->append(&acl_user->auth[i].auth_string);
8974         result->append('\'');
8975       }
8976     }
8977   }
8978   /* "show grants" SSL related stuff */
8979   if (acl_user->ssl_type == SSL_TYPE_ANY)
8980     result->append(STRING_WITH_LEN(" REQUIRE SSL"));
8981   else if (acl_user->ssl_type == SSL_TYPE_X509)
8982     result->append(STRING_WITH_LEN(" REQUIRE X509"));
8983   else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
8984   {
8985     int ssl_options = 0;
8986     result->append(STRING_WITH_LEN(" REQUIRE "));
8987     if (acl_user->x509_issuer[0])
8988     {
8989       ssl_options++;
8990       result->append(STRING_WITH_LEN("ISSUER \'"));
8991       result->append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
8992       result->append('\'');
8993     }
8994     if (acl_user->x509_subject[0])
8995     {
8996       if (ssl_options++)
8997         result->append(' ');
8998       result->append(STRING_WITH_LEN("SUBJECT \'"));
8999       result->append(acl_user->x509_subject,strlen(acl_user->x509_subject),
9000                     system_charset_info);
9001       result->append('\'');
9002     }
9003     if (acl_user->ssl_cipher)
9004     {
9005       if (ssl_options++)
9006         result->append(' ');
9007       result->append(STRING_WITH_LEN("CIPHER '"));
9008       result->append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
9009                     system_charset_info);
9010       result->append('\'');
9011     }
9012   }
9013   if (with_grant ||
9014       (acl_user->user_resource.questions ||
9015        acl_user->user_resource.updates ||
9016        acl_user->user_resource.conn_per_hour ||
9017        acl_user->user_resource.user_conn ||
9018        acl_user->user_resource.max_statement_time != 0.0))
9019   {
9020     result->append(STRING_WITH_LEN(" WITH"));
9021     if (with_grant)
9022       result->append(STRING_WITH_LEN(" GRANT OPTION"));
9023     add_user_option(result, acl_user->user_resource.questions,
9024                     "MAX_QUERIES_PER_HOUR", false);
9025     add_user_option(result, acl_user->user_resource.updates,
9026                     "MAX_UPDATES_PER_HOUR", false);
9027     add_user_option(result, acl_user->user_resource.conn_per_hour,
9028                     "MAX_CONNECTIONS_PER_HOUR", false);
9029     add_user_option(result, acl_user->user_resource.user_conn,
9030                     "MAX_USER_CONNECTIONS", true);
9031     add_user_option(result, acl_user->user_resource.max_statement_time,
9032                     "MAX_STATEMENT_TIME");
9033   }
9034 }
9035 
9036 static const char *command_array[]=
9037 {
9038   "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
9039   "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
9040   "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
9041   "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "BINLOG MONITOR",
9042   "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
9043   "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE", "DELETE HISTORY",
9044   "SET USER", "FEDERATED ADMIN", "CONNECTION ADMIN", "READ_ONLY ADMIN",
9045   "REPLICATION SLAVE ADMIN", "REPLICATION MASTER ADMIN", "BINLOG ADMIN",
9046   "BINLOG REPLAY", "SLAVE MONITOR"
9047 };
9048 
9049 static uint command_lengths[]=
9050 {
9051   6, 6, 6, 6, 6, 4, 6,
9052   8, 7, 4, 5, 10, 5,
9053   5, 14, 5, 23,
9054   11, 7, 17, 14,
9055   11, 9, 14, 13,
9056   11, 5, 7, 17, 14,
9057   8, 15, 16, 15,
9058   23, 24, 12,
9059   13, 13
9060 };
9061 
9062 
9063 static_assert(array_elements(command_array) == PRIVILEGE_T_MAX_BIT + 1,
9064               "The definition of command_array does not match privilege_t");
9065 static_assert(array_elements(command_lengths) == PRIVILEGE_T_MAX_BIT + 1,
9066               "The definition of command_lengths does not match privilege_t");
9067 
9068 
print_grants_for_role(THD * thd,ACL_ROLE * role)9069 static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
9070 {
9071   char buff[1024];
9072 
9073   if (show_role_grants(thd, "", role, buff, sizeof(buff)))
9074     return TRUE;
9075 
9076   if (show_global_privileges(thd, role, TRUE, buff, sizeof(buff)))
9077     return TRUE;
9078 
9079   if (show_database_privileges(thd, role->user.str, "", buff, sizeof(buff)))
9080     return TRUE;
9081 
9082   if (show_table_and_column_privileges(thd, role->user.str, "", buff, sizeof(buff)))
9083     return TRUE;
9084 
9085   if (show_routine_grants(thd, role->user.str, "", &sp_handler_procedure,
9086                           buff, sizeof(buff)))
9087     return TRUE;
9088 
9089   if (show_routine_grants(thd, role->user.str, "", &sp_handler_function,
9090                           buff, sizeof(buff)))
9091     return TRUE;
9092 
9093   if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_spec,
9094                           buff, sizeof(buff)))
9095     return TRUE;
9096 
9097   if (show_routine_grants(thd, role->user.str, "", &sp_handler_package_body,
9098                           buff, sizeof(buff)))
9099     return TRUE;
9100 
9101   return FALSE;
9102 
9103 }
9104 
append_auto_expiration_policy(ACL_USER * acl_user,String * r)9105 static void append_auto_expiration_policy(ACL_USER *acl_user, String *r) {
9106     if (!acl_user->password_lifetime)
9107       r->append(STRING_WITH_LEN(" PASSWORD EXPIRE NEVER"));
9108     else if (acl_user->password_lifetime > 0)
9109     {
9110       r->append(STRING_WITH_LEN(" PASSWORD EXPIRE INTERVAL "));
9111       r->append_longlong(acl_user->password_lifetime);
9112       r->append(STRING_WITH_LEN(" DAY"));
9113     }
9114 }
9115 
mysql_show_create_user(THD * thd,LEX_USER * lex_user)9116 bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
9117 {
9118   const char *username= NULL, *hostname= NULL;
9119   char buff[1024]; //Show create user should not take more than 1024 bytes.
9120   Protocol *protocol= thd->protocol;
9121   bool error= false;
9122   ACL_USER *acl_user;
9123   uint head_length;
9124   DBUG_ENTER("mysql_show_create_user");
9125 
9126   if (!initialized)
9127   {
9128     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
9129     DBUG_RETURN(TRUE);
9130   }
9131   if (get_show_user(thd, lex_user, &username, &hostname, NULL))
9132     DBUG_RETURN(TRUE);
9133 
9134   List<Item> field_list;
9135   head_length= (uint) (strxmov(buff, "CREATE USER for ", username, "@",
9136                                hostname, NullS) - buff);
9137   Item_string *field = new (thd->mem_root) Item_string_ascii(thd, "", 0);
9138   if (!field)
9139     DBUG_RETURN(true);                          // Error given my my_alloc()
9140 
9141   field->name.str= buff;
9142   field->name.length= head_length;
9143   field->max_length= sizeof(buff);
9144   field_list.push_back(field, thd->mem_root);
9145   if (protocol->send_result_set_metadata(&field_list,
9146                                          Protocol::SEND_NUM_ROWS |
9147                                          Protocol::SEND_EOF))
9148     DBUG_RETURN(true);
9149 
9150   String result(buff, sizeof(buff), system_charset_info);
9151   result.length(0);
9152   mysql_rwlock_rdlock(&LOCK_grant);
9153   mysql_mutex_lock(&acl_cache->lock);
9154 
9155   acl_user= find_user_exact(hostname, username);
9156 
9157   // User not found in the internal data structures.
9158   if (!acl_user)
9159   {
9160     my_error(ER_PASSWORD_NO_MATCH, MYF(0));
9161     error= true;
9162     goto end;
9163   }
9164 
9165   result.append("CREATE USER ");
9166   append_identifier(thd, &result, username, strlen(username));
9167   add_user_parameters(thd, &result, acl_user, false);
9168 
9169   if (acl_user->account_locked)
9170     result.append(STRING_WITH_LEN(" ACCOUNT LOCK"));
9171 
9172   if (acl_user->password_expired)
9173     result.append(STRING_WITH_LEN(" PASSWORD EXPIRE"));
9174   else
9175     append_auto_expiration_policy(acl_user, &result);
9176 
9177   protocol->prepare_for_resend();
9178   protocol->store(result.ptr(), result.length(), result.charset());
9179   if (protocol->write())
9180   {
9181     error= true;
9182   }
9183 
9184   /* MDEV-24114 - PASSWORD EXPIRE and PASSWORD EXPIRE [NEVER | INTERVAL X DAY]
9185    are two different mechanisms. To make sure a tool can restore the state
9186    of a user account, including both the manual expiration state of the
9187    account and the automatic expiration policy attached to it, we should
9188    print two statements here, a CREATE USER (printed above) and an ALTER USER */
9189   if (acl_user->password_expired && acl_user->password_lifetime > -1) {
9190     result.length(0);
9191     result.append("ALTER USER ");
9192     append_identifier(thd, &result, username, strlen(username));
9193     result.append('@');
9194     append_identifier(thd, &result, acl_user->host.hostname,
9195                       acl_user->hostname_length);
9196     append_auto_expiration_policy(acl_user, &result);
9197     protocol->prepare_for_resend();
9198     protocol->store(result.ptr(), result.length(), result.charset());
9199     if (protocol->write())
9200     {
9201       error= true;
9202     }
9203   }
9204 
9205   my_eof(thd);
9206 
9207 end:
9208   mysql_rwlock_unlock(&LOCK_grant);
9209   mysql_mutex_unlock(&acl_cache->lock);
9210 
9211   DBUG_RETURN(error);
9212 }
9213 
9214 
show_grants_callback(ACL_USER_BASE * role,void * data)9215 static int show_grants_callback(ACL_USER_BASE *role, void *data)
9216 {
9217   THD *thd= (THD *)data;
9218   DBUG_ASSERT(role->flags & IS_ROLE);
9219   if (print_grants_for_role(thd, (ACL_ROLE *)role))
9220     return -1;
9221   return 0;
9222 }
9223 
mysql_show_grants_get_fields(THD * thd,List<Item> * fields,const char * name,size_t length)9224 void mysql_show_grants_get_fields(THD *thd, List<Item> *fields,
9225                                   const char *name, size_t length)
9226 {
9227   Item_string *field=new (thd->mem_root) Item_string_ascii(thd, "", 0);
9228   /* Set name explicit to avoid character set conversions */
9229   field->name.str= name;
9230   field->name.length= length;
9231   field->max_length=1024;
9232   fields->push_back(field, thd->mem_root);
9233 }
9234 
9235 /** checks privileges for SHOW GRANTS and SHOW CREATE USER
9236 
9237   @note that in case of SHOW CREATE USER the parser guarantees
9238   that a role can never happen here, so *rolename will never
9239   be assigned to
9240 */
get_show_user(THD * thd,LEX_USER * lex_user,const char ** username,const char ** hostname,const char ** rolename)9241 bool get_show_user(THD *thd, LEX_USER *lex_user, const char **username,
9242                    const char **hostname, const char **rolename)
9243 {
9244   if (lex_user->user.str == current_user.str)
9245   {
9246     *username= thd->security_ctx->priv_user;
9247     *hostname= thd->security_ctx->priv_host;
9248     return 0;
9249   }
9250   if (lex_user->user.str == current_role.str)
9251   {
9252     *rolename= thd->security_ctx->priv_role;
9253     return 0;
9254   }
9255   if (lex_user->user.str == current_user_and_current_role.str)
9256   {
9257     *username= thd->security_ctx->priv_user;
9258     *hostname= thd->security_ctx->priv_host;
9259     *rolename= thd->security_ctx->priv_role;
9260     return 0;
9261   }
9262 
9263   Security_context *sctx= thd->security_ctx;
9264   bool do_check_access;
9265 
9266   if (!(lex_user= get_current_user(thd, lex_user)))
9267     return 1;
9268 
9269   if (lex_user->is_role())
9270   {
9271     *rolename= lex_user->user.str;
9272     do_check_access= strcmp(*rolename, sctx->priv_role);
9273   }
9274   else
9275   {
9276     *username= lex_user->user.str;
9277     *hostname= lex_user->host.str;
9278     do_check_access= strcmp(*username, sctx->priv_user) ||
9279                      strcmp(*hostname, sctx->priv_host);
9280   }
9281 
9282   if (do_check_access && check_access(thd, SELECT_ACL, "mysql", 0, 0, 1, 0))
9283     return 1;
9284   return 0;
9285 }
9286 
9287 /*
9288   SHOW GRANTS;  Send grants for a user to the client
9289 
9290   IMPLEMENTATION
9291    Send to client grant-like strings depicting user@host privileges
9292 */
9293 
mysql_show_grants(THD * thd,LEX_USER * lex_user)9294 bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
9295 {
9296   int  error = -1;
9297   ACL_USER *UNINIT_VAR(acl_user);
9298   ACL_ROLE *acl_role= NULL;
9299   char buff[1024];
9300   Protocol *protocol= thd->protocol;
9301   const char *username= NULL, *hostname= NULL, *rolename= NULL, *end;
9302   DBUG_ENTER("mysql_show_grants");
9303 
9304   if (!initialized)
9305   {
9306     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
9307     DBUG_RETURN(TRUE);
9308   }
9309 
9310   if (get_show_user(thd, lex_user, &username, &hostname, &rolename))
9311     DBUG_RETURN(TRUE);
9312 
9313   DBUG_ASSERT(rolename || username);
9314 
9315   List<Item> field_list;
9316   if (username)
9317     end= strxmov(buff,"Grants for ",username,"@",hostname, NullS);
9318   else
9319     end= strxmov(buff,"Grants for ",rolename, NullS);
9320 
9321   mysql_show_grants_get_fields(thd, &field_list, buff, (uint) (end-buff));
9322 
9323   if (protocol->send_result_set_metadata(&field_list,
9324                                Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
9325     DBUG_RETURN(TRUE);
9326 
9327   mysql_rwlock_rdlock(&LOCK_grant);
9328   mysql_mutex_lock(&acl_cache->lock);
9329 
9330   if (username)
9331   {
9332     acl_user= find_user_exact(hostname, username);
9333     if (!acl_user)
9334     {
9335       mysql_mutex_unlock(&acl_cache->lock);
9336       mysql_rwlock_unlock(&LOCK_grant);
9337 
9338       my_error(ER_NONEXISTING_GRANT, MYF(0),
9339                username, hostname);
9340       DBUG_RETURN(TRUE);
9341     }
9342 
9343     /* Show granted roles to acl_user */
9344     if (show_role_grants(thd, hostname, acl_user, buff, sizeof(buff)))
9345       goto end;
9346 
9347     /* Add first global access grants */
9348     if (show_global_privileges(thd, acl_user, FALSE, buff, sizeof(buff)))
9349       goto end;
9350 
9351     /* Add database access */
9352     if (show_database_privileges(thd, username, hostname, buff, sizeof(buff)))
9353       goto end;
9354 
9355     /* Add table & column access */
9356     if (show_table_and_column_privileges(thd, username, hostname, buff, sizeof(buff)))
9357       goto end;
9358 
9359     if (show_routine_grants(thd, username, hostname, &sp_handler_procedure,
9360                             buff, sizeof(buff)))
9361       goto end;
9362 
9363     if (show_routine_grants(thd, username, hostname, &sp_handler_function,
9364                             buff, sizeof(buff)))
9365       goto end;
9366 
9367     if (show_routine_grants(thd, username, hostname, &sp_handler_package_spec,
9368                             buff, sizeof(buff)))
9369       goto end;
9370 
9371     if (show_routine_grants(thd, username, hostname, &sp_handler_package_body,
9372                             buff, sizeof(buff)))
9373       goto end;
9374 
9375     if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff)))
9376       goto end;
9377   }
9378 
9379   if (rolename)
9380   {
9381     acl_role= find_acl_role(rolename);
9382     if (acl_role)
9383     {
9384       /* get a list of all inherited roles */
9385       traverse_role_graph_down(acl_role, thd, show_grants_callback, NULL);
9386     }
9387     else
9388     {
9389       if (lex_user->user.str == current_role.str)
9390       {
9391         mysql_mutex_unlock(&acl_cache->lock);
9392         mysql_rwlock_unlock(&LOCK_grant);
9393         my_error(ER_NONEXISTING_GRANT, MYF(0),
9394                  thd->security_ctx->priv_user,
9395                  thd->security_ctx->priv_host);
9396         DBUG_RETURN(TRUE);
9397       }
9398     }
9399   }
9400 
9401   if (username)
9402   {
9403     /* Show default role to acl_user */
9404     if (show_default_role(thd, acl_user, buff, sizeof(buff)))
9405       goto end;
9406   }
9407 
9408 
9409   error= 0;
9410 end:
9411   mysql_mutex_unlock(&acl_cache->lock);
9412   mysql_rwlock_unlock(&LOCK_grant);
9413 
9414   my_eof(thd);
9415   DBUG_RETURN(error);
9416 }
9417 
find_role_grant_pair(const LEX_CSTRING * u,const LEX_CSTRING * h,const LEX_CSTRING * r)9418 static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u,
9419                                              const LEX_CSTRING *h,
9420                                              const LEX_CSTRING *r)
9421 {
9422   char buf[1024];
9423   String pair_key(buf, sizeof(buf), &my_charset_bin);
9424 
9425   size_t key_length= u->length + h->length + r->length + 3;
9426   pair_key.alloc(key_length);
9427 
9428   strmov(strmov(strmov(const_cast<char*>(pair_key.ptr()),
9429                        safe_str(u->str)) + 1, h->str) + 1, r->str);
9430 
9431   return (ROLE_GRANT_PAIR *)
9432     my_hash_search(&acl_roles_mappings, (uchar*)pair_key.ptr(), key_length);
9433 }
9434 
show_default_role(THD * thd,ACL_USER * acl_entry,char * buff,size_t buffsize)9435 static bool show_default_role(THD *thd, ACL_USER *acl_entry,
9436                               char *buff, size_t buffsize)
9437 {
9438   Protocol *protocol= thd->protocol;
9439   LEX_CSTRING def_rolename= acl_entry->default_rolename;
9440 
9441   if (def_rolename.length)
9442   {
9443     String def_str(buff, buffsize, system_charset_info);
9444     def_str.length(0);
9445     def_str.append(STRING_WITH_LEN("SET DEFAULT ROLE "));
9446     append_identifier(thd, &def_str, def_rolename.str, def_rolename.length);
9447     def_str.append(" FOR ");
9448     append_identifier(thd, &def_str, acl_entry->user.str, acl_entry->user.length);
9449     DBUG_ASSERT(!(acl_entry->flags & IS_ROLE));
9450     def_str.append('@');
9451     append_identifier(thd, &def_str, acl_entry->host.hostname,
9452                       acl_entry->hostname_length);
9453     protocol->prepare_for_resend();
9454     protocol->store(def_str.ptr(),def_str.length(),def_str.charset());
9455     if (protocol->write())
9456     {
9457       return TRUE;
9458     }
9459   }
9460   return FALSE;
9461 }
9462 
show_role_grants(THD * thd,const char * hostname,ACL_USER_BASE * acl_entry,char * buff,size_t buffsize)9463 static bool show_role_grants(THD *thd, const char *hostname,
9464                              ACL_USER_BASE *acl_entry,
9465                              char *buff, size_t buffsize)
9466 {
9467   uint counter;
9468   Protocol *protocol= thd->protocol;
9469   LEX_CSTRING host= {const_cast<char*>(hostname), strlen(hostname)};
9470 
9471   String grant(buff, buffsize, system_charset_info);
9472   for (counter= 0; counter < acl_entry->role_grants.elements; counter++)
9473   {
9474     grant.length(0);
9475     grant.append(STRING_WITH_LEN("GRANT "));
9476     ACL_ROLE *acl_role= *(dynamic_element(&acl_entry->role_grants, counter,
9477                                           ACL_ROLE**));
9478     append_identifier(thd, &grant, acl_role->user.str, acl_role->user.length);
9479     grant.append(STRING_WITH_LEN(" TO "));
9480     append_identifier(thd, &grant, acl_entry->user.str, acl_entry->user.length);
9481     if (!(acl_entry->flags & IS_ROLE))
9482     {
9483       grant.append('@');
9484       append_identifier(thd, &grant, host.str, host.length);
9485     }
9486 
9487     ROLE_GRANT_PAIR *pair=
9488       find_role_grant_pair(&acl_entry->user, &host, &acl_role->user);
9489     DBUG_ASSERT(pair);
9490 
9491     if (pair->with_admin)
9492       grant.append(STRING_WITH_LEN(" WITH ADMIN OPTION"));
9493 
9494     protocol->prepare_for_resend();
9495     protocol->store(grant.ptr(),grant.length(),grant.charset());
9496     if (protocol->write())
9497     {
9498       return TRUE;
9499     }
9500   }
9501   return FALSE;
9502 }
9503 
show_global_privileges(THD * thd,ACL_USER_BASE * acl_entry,bool handle_as_role,char * buff,size_t buffsize)9504 static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
9505                                    bool handle_as_role,
9506                                    char *buff, size_t buffsize)
9507 {
9508   uint counter;
9509   privilege_t want_access(NO_ACL);
9510   Protocol *protocol= thd->protocol;
9511 
9512   String global(buff, buffsize, system_charset_info);
9513   global.length(0);
9514   global.append(STRING_WITH_LEN("GRANT "));
9515 
9516   if (handle_as_role)
9517     want_access= ((ACL_ROLE *)acl_entry)->initial_role_access;
9518   else
9519     want_access= acl_entry->access;
9520   if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
9521     global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
9522   else if (!(want_access & ~GRANT_ACL))
9523     global.append(STRING_WITH_LEN("USAGE"));
9524   else
9525   {
9526     bool found=0;
9527     ulonglong j;
9528     privilege_t test_access(want_access & ~GRANT_ACL);
9529     for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
9530     {
9531       if (test_access & j)
9532       {
9533         if (found)
9534           global.append(STRING_WITH_LEN(", "));
9535         found=1;
9536         global.append(command_array[counter],command_lengths[counter]);
9537       }
9538     }
9539   }
9540   global.append (STRING_WITH_LEN(" ON *.* TO "));
9541   append_identifier(thd, &global, acl_entry->user.str, acl_entry->user.length);
9542 
9543   if (!handle_as_role)
9544     add_user_parameters(thd, &global, (ACL_USER *)acl_entry,
9545                         (want_access & GRANT_ACL));
9546 
9547   else if (want_access & GRANT_ACL)
9548     global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9549   protocol->prepare_for_resend();
9550   protocol->store(global.ptr(),global.length(),global.charset());
9551   if (protocol->write())
9552     return TRUE;
9553 
9554   return FALSE;
9555 
9556 }
9557 
9558 
add_to_user(THD * thd,String * result,const char * user,bool is_user,const char * host)9559 static void add_to_user(THD *thd, String *result, const char *user,
9560                         bool is_user, const char *host)
9561 {
9562   result->append(STRING_WITH_LEN(" TO "));
9563   append_identifier(thd, result, user, strlen(user));
9564   if (is_user)
9565   {
9566     result->append('@');
9567     // host and lex_user->host are equal except for case
9568     append_identifier(thd, result, host, strlen(host));
9569   }
9570 }
9571 
9572 
show_database_privileges(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)9573 static bool show_database_privileges(THD *thd, const char *username,
9574                                      const char *hostname,
9575                                      char *buff, size_t buffsize)
9576 {
9577   privilege_t want_access(NO_ACL);
9578   Protocol *protocol= thd->protocol;
9579 
9580   for (uint i=0 ; i < acl_dbs.elements() ; i++)
9581   {
9582     const char *user, *host;
9583 
9584     ACL_DB *acl_db= &acl_dbs.at(i);
9585     user= acl_db->user;
9586     host=acl_db->host.hostname;
9587 
9588     /*
9589       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
9590       but make it case-insensitive because that's the way they are
9591       actually applied, and showing fewer privileges than are applied
9592       would be wrong from a security point of view.
9593     */
9594 
9595     if (!strcmp(username, user) &&
9596         !my_strcasecmp(system_charset_info, hostname, host))
9597     {
9598       /*
9599         do not print inherited access bits for roles,
9600         the role bits present in the table are what matters
9601       */
9602       if (*hostname) // User
9603         want_access=acl_db->access;
9604       else // Role
9605         want_access=acl_db->initial_access;
9606       if (want_access)
9607       {
9608         String db(buff, buffsize, system_charset_info);
9609         db.length(0);
9610         db.append(STRING_WITH_LEN("GRANT "));
9611 
9612         if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
9613           db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
9614         else if (!(want_access & ~GRANT_ACL))
9615           db.append(STRING_WITH_LEN("USAGE"));
9616         else
9617         {
9618           int found=0, cnt;
9619           ulonglong j;
9620           privilege_t test_access(want_access & ~GRANT_ACL);
9621           for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
9622           {
9623             if (test_access & j)
9624             {
9625               if (found)
9626                 db.append(STRING_WITH_LEN(", "));
9627               found = 1;
9628               db.append(command_array[cnt],command_lengths[cnt]);
9629             }
9630           }
9631         }
9632         db.append (STRING_WITH_LEN(" ON "));
9633         append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
9634         db.append (STRING_WITH_LEN(".*"));
9635         add_to_user(thd, &db, username, (*hostname), host);
9636         if (want_access & GRANT_ACL)
9637           db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9638         protocol->prepare_for_resend();
9639         protocol->store(db.ptr(),db.length(),db.charset());
9640         if (protocol->write())
9641         {
9642           return TRUE;
9643         }
9644       }
9645     }
9646   }
9647   return FALSE;
9648 
9649 }
9650 
show_table_and_column_privileges(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)9651 static bool show_table_and_column_privileges(THD *thd, const char *username,
9652                                              const char *hostname,
9653                                              char *buff, size_t buffsize)
9654 {
9655   uint counter, index;
9656   Protocol *protocol= thd->protocol;
9657 
9658   for (index=0 ; index < column_priv_hash.records ; index++)
9659   {
9660     const char *user, *host;
9661     GRANT_TABLE *grant_table= (GRANT_TABLE*)
9662       my_hash_element(&column_priv_hash, index);
9663 
9664     user= grant_table->user;
9665     host= grant_table->host.hostname;
9666 
9667     /*
9668       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
9669       but make it case-insensitive because that's the way they are
9670       actually applied, and showing fewer privileges than are applied
9671       would be wrong from a security point of view.
9672     */
9673 
9674     if (!strcmp(username,user) &&
9675         !my_strcasecmp(system_charset_info, hostname, host))
9676     {
9677       privilege_t table_access(NO_ACL);
9678       privilege_t cols_access(NO_ACL);
9679       if (*hostname) // User
9680       {
9681         table_access= grant_table->privs;
9682         cols_access= grant_table->cols;
9683       }
9684       else // Role
9685       {
9686         table_access= grant_table->init_privs;
9687         cols_access= grant_table->init_cols;
9688       }
9689 
9690       if ((table_access | cols_access) != NO_ACL)
9691       {
9692         String global(buff, sizeof(buff), system_charset_info);
9693         privilege_t test_access= (table_access | cols_access) & ~GRANT_ACL;
9694 
9695         global.length(0);
9696         global.append(STRING_WITH_LEN("GRANT "));
9697 
9698         if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
9699           global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
9700         else if (!test_access)
9701           global.append(STRING_WITH_LEN("USAGE"));
9702         else
9703         {
9704           /* Add specific column access */
9705           int found= 0;
9706           ulonglong j;
9707 
9708           for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
9709           {
9710             if (test_access & j)
9711             {
9712               if (found)
9713                 global.append(STRING_WITH_LEN(", "));
9714               found= 1;
9715               global.append(command_array[counter],command_lengths[counter]);
9716 
9717               if (grant_table->cols)
9718               {
9719                 uint found_col= 0;
9720                 HASH *hash_columns;
9721                 hash_columns= &grant_table->hash_columns;
9722 
9723                 for (uint col_index=0 ;
9724                      col_index < hash_columns->records ;
9725                      col_index++)
9726                 {
9727                   GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
9728                     my_hash_element(hash_columns,col_index);
9729                   if (j & (*hostname ? grant_column->rights         // User
9730                                      : grant_column->init_rights))  // Role
9731                   {
9732                     if (!found_col)
9733                     {
9734                       found_col= 1;
9735                       /*
9736                         If we have a duplicated table level privilege, we
9737                         must write the access privilege name again.
9738                       */
9739                       if (table_access & j)
9740                       {
9741                         global.append(STRING_WITH_LEN(", "));
9742                         global.append(command_array[counter],
9743                                       command_lengths[counter]);
9744                       }
9745                       global.append(STRING_WITH_LEN(" ("));
9746                     }
9747                     else
9748                       global.append(STRING_WITH_LEN(", "));
9749                     global.append(grant_column->column,
9750                                   grant_column->key_length,
9751                                   system_charset_info);
9752                   }
9753                 }
9754                 if (found_col)
9755                   global.append(')');
9756               }
9757             }
9758           }
9759         }
9760         global.append(STRING_WITH_LEN(" ON "));
9761         append_identifier(thd, &global, grant_table->db,
9762                           strlen(grant_table->db));
9763         global.append('.');
9764         append_identifier(thd, &global, grant_table->tname,
9765                           strlen(grant_table->tname));
9766         add_to_user(thd, &global, username, (*hostname), host);
9767         if (table_access & GRANT_ACL)
9768           global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9769         protocol->prepare_for_resend();
9770         protocol->store(global.ptr(),global.length(),global.charset());
9771         if (protocol->write())
9772         {
9773           return TRUE;
9774         }
9775       }
9776     }
9777   }
9778   return FALSE;
9779 
9780 }
9781 
show_routine_grants(THD * thd,const char * username,const char * hostname,const Sp_handler * sph,char * buff,int buffsize)9782 static int show_routine_grants(THD* thd,
9783                                const char *username, const char *hostname,
9784                                const Sp_handler *sph,
9785                                char *buff, int buffsize)
9786 {
9787   uint counter, index;
9788   int error= 0;
9789   Protocol *protocol= thd->protocol;
9790   HASH *hash= sph->get_priv_hash();
9791   /* Add routine access */
9792   for (index=0 ; index < hash->records ; index++)
9793   {
9794     const char *user, *host;
9795     GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
9796 
9797     user= grant_proc->user;
9798     host= grant_proc->host.hostname;
9799 
9800     /*
9801       We do not make SHOW GRANTS case-sensitive here (like REVOKE),
9802       but make it case-insensitive because that's the way they are
9803       actually applied, and showing fewer privileges than are applied
9804       would be wrong from a security point of view.
9805     */
9806 
9807     if (!strcmp(username, user) &&
9808         !my_strcasecmp(system_charset_info, hostname, host))
9809     {
9810       privilege_t proc_access(NO_ACL);
9811       if (*hostname) // User
9812         proc_access= grant_proc->privs;
9813       else // Role
9814         proc_access= grant_proc->init_privs;
9815 
9816       if (proc_access != NO_ACL)
9817       {
9818 	String global(buff, buffsize, system_charset_info);
9819 	privilege_t test_access(proc_access & ~GRANT_ACL);
9820 
9821 	global.length(0);
9822 	global.append(STRING_WITH_LEN("GRANT "));
9823 
9824 	if (!test_access)
9825  	  global.append(STRING_WITH_LEN("USAGE"));
9826 	else
9827 	{
9828           /* Add specific procedure access */
9829 	  int found= 0;
9830 	  ulonglong j;
9831 
9832 	  for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
9833 	  {
9834 	    if (test_access & j)
9835 	    {
9836 	      if (found)
9837 		global.append(STRING_WITH_LEN(", "));
9838 	      found= 1;
9839 	      global.append(command_array[counter],command_lengths[counter]);
9840 	    }
9841 	  }
9842 	}
9843 	global.append(STRING_WITH_LEN(" ON "));
9844         LEX_CSTRING tmp= sph->type_lex_cstring();
9845         global.append(&tmp);
9846         global.append(' ');
9847 	append_identifier(thd, &global, grant_proc->db,
9848 			  strlen(grant_proc->db));
9849 	global.append('.');
9850 	append_identifier(thd, &global, grant_proc->tname,
9851 			  strlen(grant_proc->tname));
9852         add_to_user(thd, &global, username, (*hostname), host);
9853 	if (proc_access & GRANT_ACL)
9854 	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
9855 	protocol->prepare_for_resend();
9856 	protocol->store(global.ptr(),global.length(),global.charset());
9857 	if (protocol->write())
9858 	{
9859 	  error= -1;
9860 	  break;
9861 	}
9862       }
9863     }
9864   }
9865   return error;
9866 }
9867 
9868 
9869 /*
9870   Make a clear-text version of the requested privilege.
9871 */
9872 
get_privilege_desc(char * to,uint max_length,privilege_t access_arg)9873 void get_privilege_desc(char *to, uint max_length, privilege_t access_arg)
9874 {
9875   uint pos;
9876   char *start=to;
9877   DBUG_ASSERT(max_length >= 30);                // For end ', ' removal
9878 
9879   if (ulonglong access= access_arg)
9880   {
9881     max_length--;				// Reserve place for end-zero
9882     for (pos=0 ; access ; pos++, access>>=1)
9883     {
9884       if ((access & 1) &&
9885 	  command_lengths[pos] + (uint) (to-start) < max_length)
9886       {
9887 	to= strmov(to, command_array[pos]);
9888         *to++= ',';
9889         *to++= ' ';
9890       }
9891     }
9892     to--;                                       // Remove end ' '
9893     to--;					// Remove end ','
9894   }
9895   *to=0;
9896 }
9897 
9898 
get_mqh(const char * user,const char * host,USER_CONN * uc)9899 void get_mqh(const char *user, const char *host, USER_CONN *uc)
9900 {
9901   ACL_USER *acl_user;
9902 
9903   mysql_mutex_lock(&acl_cache->lock);
9904 
9905   if (initialized && (acl_user= find_user_wild(host,user)))
9906     uc->user_resources= acl_user->user_resource;
9907   else
9908     bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
9909 
9910   mysql_mutex_unlock(&acl_cache->lock);
9911 }
9912 
9913 /*
9914   Modify a privilege table.
9915 
9916   SYNOPSIS
9917     modify_grant_table()
9918     table                       The table to modify.
9919     host_field                  The host name field.
9920     user_field                  The user name field.
9921     user_to                     The new name for the user if to be renamed,
9922                                 NULL otherwise.
9923 
9924   DESCRIPTION
9925   Update user/host in the current record if user_to is not NULL.
9926   Delete the current record if user_to is NULL.
9927 
9928   RETURN
9929     0           OK.
9930     != 0        Error.
9931 */
9932 
modify_grant_table(TABLE * table,Field * host_field,Field * user_field,LEX_USER * user_to)9933 static int modify_grant_table(TABLE *table, Field *host_field,
9934                               Field *user_field, LEX_USER *user_to)
9935 {
9936   int error;
9937   DBUG_ENTER("modify_grant_table");
9938 
9939   if (user_to)
9940   {
9941     /* rename */
9942     store_record(table, record[1]);
9943     host_field->store(user_to->host.str, user_to->host.length,
9944                       system_charset_info);
9945     user_field->store(user_to->user.str, user_to->user.length,
9946                       system_charset_info);
9947     if (unlikely(error= table->file->ha_update_row(table->record[1],
9948                                                    table->record[0])) &&
9949         error != HA_ERR_RECORD_IS_THE_SAME)
9950       table->file->print_error(error, MYF(0));
9951     else
9952       error= 0;
9953   }
9954   else
9955   {
9956     /* delete */
9957     if (unlikely((error=table->file->ha_delete_row(table->record[0]))))
9958       table->file->print_error(error, MYF(0));
9959   }
9960 
9961   DBUG_RETURN(error);
9962 }
9963 
9964 /*
9965   Handle the roles_mapping privilege table
9966 */
handle_roles_mappings_table(TABLE * table,bool drop,LEX_USER * user_from,LEX_USER * user_to)9967 static int handle_roles_mappings_table(TABLE *table, bool drop,
9968                                        LEX_USER *user_from, LEX_USER *user_to)
9969 {
9970   /*
9971     All entries (Host, User) that match user_from will be renamed,
9972     as well as all Role entries that match if user_from.host.str == ""
9973 
9974     Otherwise, only matching (Host, User) will be renamed.
9975   */
9976   DBUG_ENTER("handle_roles_mappings_table");
9977 
9978   int error;
9979   int result= 0;
9980   THD *thd= table->in_use;
9981   const char *host, *user, *role;
9982   Field *host_field= table->field[0];
9983   Field *user_field= table->field[1];
9984   Field *role_field= table->field[2];
9985 
9986   DBUG_PRINT("info", ("Rewriting entry in roles_mapping table: %s@%s",
9987                       user_from->user.str, user_from->host.str));
9988   table->use_all_columns();
9989 
9990   if (unlikely(table->file->ha_rnd_init_with_error(1)))
9991     result= -1;
9992   else
9993   {
9994     while((error= table->file->ha_rnd_next(table->record[0])) !=
9995           HA_ERR_END_OF_FILE)
9996     {
9997       if (error)
9998       {
9999         DBUG_PRINT("info", ("scan error: %d", error));
10000         continue;
10001       }
10002 
10003       host= safe_str(get_field(thd->mem_root, host_field));
10004       user= safe_str(get_field(thd->mem_root, user_field));
10005 
10006       if (!(strcmp(user_from->user.str, user) ||
10007             my_strcasecmp(system_charset_info, user_from->host.str, host)))
10008         result= ((drop || user_to) &&
10009                  modify_grant_table(table, host_field, user_field, user_to)) ?
10010           -1 : result ? result : 1; /* Error or keep result or found. */
10011       else
10012       {
10013         role= safe_str(get_field(thd->mem_root, role_field));
10014 
10015         if (!user_from->is_role() || strcmp(user_from->user.str, role))
10016           continue;
10017 
10018         error= 0;
10019 
10020         if (drop) /* drop if requested */
10021         {
10022           if (unlikely((error= table->file->ha_delete_row(table->record[0]))))
10023             table->file->print_error(error, MYF(0));
10024         }
10025         else if (user_to)
10026         {
10027           store_record(table, record[1]);
10028           role_field->store(user_to->user.str, user_to->user.length,
10029                             system_charset_info);
10030           if (unlikely(error= table->file->ha_update_row(table->record[1],
10031                                                          table->record[0])) &&
10032               error != HA_ERR_RECORD_IS_THE_SAME)
10033             table->file->print_error(error, MYF(0));
10034         }
10035 
10036         /* Error or keep result or found. */
10037         result= error ? -1 : result ? result : 1;
10038       }
10039     }
10040     table->file->ha_rnd_end();
10041   }
10042   DBUG_RETURN(result);
10043 }
10044 
10045 /*
10046   Handle a privilege table.
10047 
10048   SYNOPSIS
10049     handle_grant_table()
10050     grant_table                 An open grant table handle.
10051     which_table                 Which grant table to handle.
10052     drop                        If user_from is to be dropped.
10053     user_from                   The the user to be searched/dropped/renamed.
10054     user_to                     The new name for the user if to be renamed,
10055                                 NULL otherwise.
10056 
10057   DESCRIPTION
10058     Scan through all records in a grant table and apply the requested
10059     operation. For the "user" table, a single index access is sufficient,
10060     since there is an unique index on (host, user).
10061     Delete from grant table if drop is true.
10062     Update in grant table if drop is false and user_to is not NULL.
10063     Search in grant table if drop is false and user_to is NULL.
10064 
10065   RETURN
10066     > 0         At least one record matched.
10067     0           OK, but no record matched.
10068     < 0         Error.
10069 
10070    TODO(cvicentiu) refactor handle_grant_table to use
10071    Grant_table_base instead of TABLE directly.
10072 */
10073 
handle_grant_table(THD * thd,const Grant_table_base & grant_table,enum enum_acl_tables which_table,bool drop,LEX_USER * user_from,LEX_USER * user_to)10074 static int handle_grant_table(THD *thd, const Grant_table_base& grant_table,
10075                               enum enum_acl_tables which_table, bool drop,
10076                               LEX_USER *user_from, LEX_USER *user_to)
10077 {
10078   int result= 0;
10079   int error;
10080   TABLE *table= grant_table.table();
10081   Field *host_field= table->field[0];
10082   Field *user_field= table->field[which_table == USER_TABLE ||
10083                                   which_table == PROXIES_PRIV_TABLE ? 1 : 2];
10084   const char *host_str= user_from->host.str;
10085   const char *user_str= user_from->user.str;
10086   const char *host;
10087   const char *user;
10088   uchar user_key[MAX_KEY_LENGTH];
10089   uint key_prefix_length;
10090   DBUG_ENTER("handle_grant_table");
10091 
10092   if (which_table == ROLES_MAPPING_TABLE)
10093   {
10094     result= handle_roles_mappings_table(table, drop, user_from, user_to);
10095     DBUG_RETURN(result);
10096   }
10097 
10098   table->use_all_columns();
10099   if (which_table == USER_TABLE) // mysql.user table
10100   {
10101     /*
10102       The 'user' table has an unique index on (host, user).
10103       Thus, we can handle everything with a single index access.
10104       The host- and user fields are consecutive in the user table records.
10105       So we set host- and user fields of table->record[0] and use the
10106       pointer to the host field as key.
10107       index_read_idx() will replace table->record[0] (its first argument)
10108       by the searched record, if it exists.
10109     */
10110     DBUG_PRINT("info",("read table: '%s'  search: '%s'@'%s'",
10111                        table->s->table_name.str, user_str, host_str));
10112     host_field->store(host_str, user_from->host.length, system_charset_info);
10113     user_field->store(user_str, user_from->user.length, system_charset_info);
10114 
10115     key_prefix_length= (table->key_info->key_part[0].store_length +
10116                         table->key_info->key_part[1].store_length);
10117     key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
10118 
10119     error= table->file->ha_index_read_idx_map(table->record[0], 0,
10120                                               user_key, (key_part_map)3,
10121                                               HA_READ_KEY_EXACT);
10122     if (!unlikely(error) && !*host_str)
10123     {
10124       // verify that we got a role or a user, as needed
10125       if (static_cast<const User_table&>(grant_table).get_is_role() !=
10126           user_from->is_role())
10127         error= HA_ERR_KEY_NOT_FOUND;
10128     }
10129     if (unlikely(error))
10130     {
10131       if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
10132       {
10133         table->file->print_error(error, MYF(0));
10134         result= -1;
10135       }
10136     }
10137     else
10138     {
10139       /* If requested, delete or update the record. */
10140       result= ((drop || user_to) &&
10141                modify_grant_table(table, host_field, user_field, user_to)) ?
10142         -1 : 1; /* Error or found. */
10143     }
10144     DBUG_PRINT("info",("read result: %d", result));
10145   }
10146   else
10147   {
10148     /*
10149       The non-'user' table do not have indexes on (host, user).
10150       And their host- and user fields are not consecutive.
10151       Thus, we need to do a table scan to find all matching records.
10152     */
10153     if (unlikely(table->file->ha_rnd_init_with_error(1)))
10154       result= -1;
10155     else
10156     {
10157 #ifdef EXTRA_DEBUG
10158       DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
10159                          table->s->table_name.str, user_str, host_str));
10160 #endif
10161       while ((error= table->file->ha_rnd_next(table->record[0])) !=
10162              HA_ERR_END_OF_FILE)
10163       {
10164         if (error)
10165         {
10166           /* Most probable 'deleted record'. */
10167           DBUG_PRINT("info",("scan error: %d", error));
10168           continue;
10169         }
10170         host= safe_str(get_field(thd->mem_root, host_field));
10171         user= safe_str(get_field(thd->mem_root, user_field));
10172 
10173 #ifdef EXTRA_DEBUG
10174         if (which_table != PROXIES_PRIV_TABLE)
10175         {
10176           DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
10177                              user, host,
10178                              get_field(thd->mem_root, table->field[1]) /*db*/,
10179                              get_field(thd->mem_root, table->field[3]) /*table*/,
10180                              get_field(thd->mem_root,
10181                                        table->field[4]) /*column*/));
10182         }
10183 #endif
10184         if (strcmp(user_str, user) ||
10185             my_strcasecmp(system_charset_info, host_str, host))
10186           continue;
10187 
10188         /* If requested, delete or update the record. */
10189         result= ((drop || user_to) &&
10190                  modify_grant_table(table, host_field, user_field, user_to)) ?
10191           -1 : result ? result : 1; /* Error or keep result or found. */
10192         /* If search is requested, we do not need to search further. */
10193         if (! drop && ! user_to)
10194           break ;
10195       }
10196       (void) table->file->ha_rnd_end();
10197       DBUG_PRINT("info",("scan result: %d", result));
10198     }
10199   }
10200 
10201   DBUG_RETURN(result);
10202 }
10203 
10204 
10205 /**
10206   Handle an in-memory privilege structure.
10207 
10208   @param struct_no  The number of the structure to handle (0..6).
10209   @param drop       If user_from is to be dropped.
10210   @param user_from  The the user to be searched/dropped/renamed.
10211   @param user_to    The new name for the user if to be renamed, NULL otherwise.
10212 
10213   @note
10214     Scan through all elements in an in-memory grant structure and apply
10215     the requested operation.
10216     Delete from grant structure if drop is true.
10217     Update in grant structure if drop is false and user_to is not NULL.
10218     Search in grant structure if drop is false and user_to is NULL.
10219 
10220   @retval > 0  At least one element matched.
10221   @retval 0    OK, but no element matched.
10222 */
10223 
handle_grant_struct(enum enum_acl_lists struct_no,bool drop,LEX_USER * user_from,LEX_USER * user_to)10224 static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
10225                                LEX_USER *user_from, LEX_USER *user_to)
10226 {
10227   int result= 0;
10228   int elements;
10229   bool restart;
10230   const char *UNINIT_VAR(user);
10231   const char *UNINIT_VAR(host);
10232   ACL_USER *acl_user= NULL;
10233   ACL_ROLE *acl_role= NULL;
10234   ACL_DB *acl_db= NULL;
10235   ACL_PROXY_USER *acl_proxy_user= NULL;
10236   GRANT_NAME *grant_name= NULL;
10237   ROLE_GRANT_PAIR *UNINIT_VAR(role_grant_pair);
10238   HASH *grant_name_hash= NULL;
10239   HASH *roles_mappings_hash= NULL;
10240   DBUG_ENTER("handle_grant_struct");
10241   DBUG_PRINT("info",("scan struct: %u  search: '%s'@'%s'",
10242                      struct_no, user_from->user.str, user_from->host.str));
10243 
10244   mysql_mutex_assert_owner(&acl_cache->lock);
10245 
10246   /* No point in querying ROLE ACL if user_from is not a role */
10247   if (struct_no == ROLE_ACL && user_from->host.length)
10248     DBUG_RETURN(0);
10249 
10250   /* same. no roles in PROXY_USERS_ACL */
10251   if (struct_no == PROXY_USERS_ACL && user_from->is_role())
10252     DBUG_RETURN(0);
10253 
10254   if (struct_no == ROLE_ACL) //no need to scan the structures in this case
10255   {
10256     acl_role= find_acl_role(user_from->user.str);
10257     if (!acl_role)
10258       DBUG_RETURN(0);
10259 
10260     if (!drop && !user_to) //role was found
10261       DBUG_RETURN(1);
10262 
10263     /* this calls for a role update */
10264     const char *old_key= acl_role->user.str;
10265     size_t old_key_length= acl_role->user.length;
10266     if (drop)
10267     {
10268       /* all grants must be revoked from this role by now. propagate this */
10269       propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL);
10270 
10271       // delete the role from cross-reference arrays
10272       for (uint i=0; i < acl_role->role_grants.elements; i++)
10273       {
10274         ACL_ROLE *grant= *dynamic_element(&acl_role->role_grants,
10275                                           i, ACL_ROLE**);
10276         remove_ptr_from_dynarray(&grant->parent_grantee, acl_role);
10277       }
10278 
10279       for (uint i=0; i < acl_role->parent_grantee.elements; i++)
10280       {
10281         ACL_USER_BASE *grantee= *dynamic_element(&acl_role->parent_grantee,
10282                                                  i, ACL_USER_BASE**);
10283         remove_ptr_from_dynarray(&grantee->role_grants, acl_role);
10284       }
10285 
10286       my_hash_delete(&acl_roles, (uchar*) acl_role);
10287       DBUG_RETURN(1);
10288     }
10289     acl_role->user= safe_lexcstrdup_root(&acl_memroot, user_to->user);
10290 
10291     my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key,
10292                    old_key_length);
10293     DBUG_RETURN(1);
10294 
10295   }
10296 
10297   /* Get the number of elements in the in-memory structure. */
10298   switch (struct_no) {
10299   case USER_ACL:
10300     elements= acl_users.elements;
10301     break;
10302   case DB_ACL:
10303     elements= int(acl_dbs.elements());
10304     break;
10305   case COLUMN_PRIVILEGES_HASH:
10306     grant_name_hash= &column_priv_hash;
10307     elements= grant_name_hash->records;
10308     break;
10309   case PROC_PRIVILEGES_HASH:
10310     grant_name_hash= &proc_priv_hash;
10311     elements= grant_name_hash->records;
10312     break;
10313   case FUNC_PRIVILEGES_HASH:
10314     grant_name_hash= &func_priv_hash;
10315     elements= grant_name_hash->records;
10316     break;
10317   case PACKAGE_SPEC_PRIVILEGES_HASH:
10318     grant_name_hash= &package_spec_priv_hash;
10319     elements= grant_name_hash->records;
10320     break;
10321   case PACKAGE_BODY_PRIVILEGES_HASH:
10322     grant_name_hash= &package_body_priv_hash;
10323     elements= grant_name_hash->records;
10324     break;
10325   case PROXY_USERS_ACL:
10326     elements= acl_proxy_users.elements;
10327     break;
10328   case ROLES_MAPPINGS_HASH:
10329     roles_mappings_hash= &acl_roles_mappings;
10330     elements= roles_mappings_hash->records;
10331     break;
10332   default:
10333     DBUG_ASSERT(0);
10334     DBUG_RETURN(-1);
10335   }
10336 
10337 
10338 #ifdef EXTRA_DEBUG
10339     DBUG_PRINT("loop",("scan struct: %u  search    user: '%s'  host: '%s'",
10340                        struct_no, user_from->user.str, user_from->host.str));
10341 #endif
10342   /* Loop over elements backwards as it may reduce the number of mem-moves
10343      for dynamic arrays.
10344 
10345      We restart the loop, if we deleted or updated anything in a hash table
10346      because calling my_hash_delete or my_hash_update shuffles elements indices
10347      and we can miss some if we do only one scan.
10348   */
10349   do {
10350     restart= false;
10351     for (int idx= elements - 1; idx >= 0; idx--)
10352     {
10353       /*
10354         Get a pointer to the element.
10355       */
10356       switch (struct_no) {
10357       case USER_ACL:
10358         acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
10359         user= acl_user->user.str;
10360         host= acl_user->host.hostname;
10361       break;
10362 
10363       case DB_ACL:
10364         acl_db= &acl_dbs.at(idx);
10365         user= acl_db->user;
10366         host= acl_db->host.hostname;
10367         break;
10368 
10369       case COLUMN_PRIVILEGES_HASH:
10370       case PROC_PRIVILEGES_HASH:
10371       case FUNC_PRIVILEGES_HASH:
10372       case PACKAGE_SPEC_PRIVILEGES_HASH:
10373       case PACKAGE_BODY_PRIVILEGES_HASH:
10374         grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
10375         user= grant_name->user;
10376         host= grant_name->host.hostname;
10377         break;
10378 
10379       case PROXY_USERS_ACL:
10380         acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
10381         user= acl_proxy_user->get_user();
10382         host= acl_proxy_user->get_host();
10383         break;
10384 
10385       case ROLES_MAPPINGS_HASH:
10386         role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx);
10387         user= role_grant_pair->u_uname;
10388         host= role_grant_pair->u_hname;
10389         break;
10390 
10391       default:
10392         DBUG_ASSERT(0);
10393       }
10394       if (! host)
10395         host= "";
10396 
10397 #ifdef EXTRA_DEBUG
10398       DBUG_PRINT("loop",("scan struct: %u  index: %u  user: '%s'  host: '%s'",
10399                          struct_no, idx, user, host));
10400 #endif
10401 
10402       if (struct_no == ROLES_MAPPINGS_HASH)
10403       {
10404         const char* role= role_grant_pair->r_uname? role_grant_pair->r_uname: "";
10405         if (user_from->is_role())
10406         {
10407           /* When searching for roles within the ROLES_MAPPINGS_HASH, we have
10408              to check both the user field as well as the role field for a match.
10409 
10410              It is possible to have a role granted to a role. If we are going
10411              to modify the mapping entry, it needs to be done on either on the
10412              "user" end (here represented by a role) or the "role" end. At least
10413              one part must match.
10414 
10415              If the "user" end has a not-empty host string, it can never match
10416              as we are searching for a role here. A role always has an empty host
10417              string.
10418           */
10419           if ((*host || strcmp(user_from->user.str, user)) &&
10420               strcmp(user_from->user.str, role))
10421             continue;
10422         }
10423         else
10424         {
10425           if (strcmp(user_from->user.str, user) ||
10426               my_strcasecmp(system_charset_info, user_from->host.str, host))
10427             continue;
10428         }
10429       }
10430       else
10431       {
10432         if (strcmp(user_from->user.str, user) ||
10433             my_strcasecmp(system_charset_info, user_from->host.str, host))
10434           continue;
10435       }
10436 
10437       result= 1; /* At least one element found. */
10438       if ( drop )
10439       {
10440         elements--;
10441         switch ( struct_no ) {
10442         case USER_ACL:
10443           free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*));
10444           delete_dynamic_element(&acl_users, idx);
10445           break;
10446 
10447         case DB_ACL:
10448           acl_dbs.del(idx);
10449           break;
10450 
10451         case COLUMN_PRIVILEGES_HASH:
10452         case PROC_PRIVILEGES_HASH:
10453         case FUNC_PRIVILEGES_HASH:
10454         case PACKAGE_SPEC_PRIVILEGES_HASH:
10455         case PACKAGE_BODY_PRIVILEGES_HASH:
10456           my_hash_delete(grant_name_hash, (uchar*) grant_name);
10457           restart= true;
10458           break;
10459 
10460         case PROXY_USERS_ACL:
10461           delete_dynamic_element(&acl_proxy_users, idx);
10462           break;
10463 
10464         case ROLES_MAPPINGS_HASH:
10465           my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair);
10466           restart= true;
10467           break;
10468 
10469         default:
10470           DBUG_ASSERT(0);
10471           break;
10472         }
10473       }
10474       else if ( user_to )
10475       {
10476         switch ( struct_no ) {
10477         case USER_ACL:
10478           acl_user->user= safe_lexcstrdup_root(&acl_memroot, user_to->user);
10479           update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str));
10480           acl_user->hostname_length= strlen(acl_user->host.hostname);
10481           break;
10482 
10483         case DB_ACL:
10484           acl_db->user= strdup_root(&acl_memroot, user_to->user.str);
10485           update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str));
10486           break;
10487 
10488         case COLUMN_PRIVILEGES_HASH:
10489         case PROC_PRIVILEGES_HASH:
10490         case FUNC_PRIVILEGES_HASH:
10491         case PACKAGE_SPEC_PRIVILEGES_HASH:
10492         case PACKAGE_BODY_PRIVILEGES_HASH:
10493           {
10494             /*
10495               Save old hash key and its length to be able to properly update
10496               element position in hash.
10497             */
10498             char *old_key= grant_name->hash_key;
10499             size_t old_key_length= grant_name->key_length;
10500 
10501             /*
10502               Update the grant structure with the new user name and host name.
10503             */
10504             grant_name->set_user_details(user_to->host.str, grant_name->db,
10505                                          user_to->user.str, grant_name->tname,
10506                                          TRUE);
10507 
10508             /*
10509               Since username is part of the hash key, when the user name
10510               is renamed, the hash key is changed. Update the hash to
10511               ensure that the position matches the new hash key value
10512             */
10513             my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
10514                            old_key_length);
10515             restart= true;
10516             break;
10517           }
10518 
10519         case PROXY_USERS_ACL:
10520           acl_proxy_user->set_user (&acl_memroot, user_to->user.str);
10521           acl_proxy_user->set_host (&acl_memroot, user_to->host.str);
10522           break;
10523 
10524         case ROLES_MAPPINGS_HASH:
10525           {
10526             /*
10527               Save old hash key and its length to be able to properly update
10528               element position in hash.
10529             */
10530             char *old_key= role_grant_pair->hashkey.str;
10531             size_t old_key_length= role_grant_pair->hashkey.length;
10532             bool oom;
10533 
10534             if (user_to->is_role())
10535               oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname,
10536                                          role_grant_pair->u_hname,
10537                                          user_to->user.str, false);
10538             else
10539               oom= role_grant_pair->init(&acl_memroot, user_to->user.str,
10540                                          user_to->host.str,
10541                                          role_grant_pair->r_uname, false);
10542             if (oom)
10543               DBUG_RETURN(-1);
10544 
10545             my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair,
10546                            (uchar*) old_key, old_key_length);
10547             restart= true;
10548             break;
10549           }
10550 
10551         default:
10552           DBUG_ASSERT(0);
10553           break;
10554         }
10555 
10556       }
10557       else
10558       {
10559         /* If search is requested, we do not need to search further. */
10560         break;
10561       }
10562     }
10563   } while (restart);
10564 #ifdef EXTRA_DEBUG
10565   DBUG_PRINT("loop",("scan struct: %u  result %d", struct_no, result));
10566 #endif
10567 
10568   DBUG_RETURN(result);
10569 }
10570 
10571 
10572 /*
10573   Handle all privilege tables and in-memory privilege structures.
10574 
10575   SYNOPSIS
10576     handle_grant_data()
10577     tables                      The array with the four open tables.
10578     drop                        If user_from is to be dropped.
10579     user_from                   The the user to be searched/dropped/renamed.
10580     user_to                     The new name for the user if to be renamed,
10581                                 NULL otherwise.
10582 
10583   DESCRIPTION
10584     Go through all grant tables and in-memory grant structures and apply
10585     the requested operation.
10586     Delete from grant data if drop is true.
10587     Update in grant data if drop is false and user_to is not NULL.
10588     Search in grant data if drop is false and user_to is NULL.
10589 
10590   RETURN
10591     > 0         At least one element matched.
10592     0           OK, but no element matched.
10593     < 0         Error.
10594 */
10595 
handle_grant_data(THD * thd,Grant_tables & tables,bool drop,LEX_USER * user_from,LEX_USER * user_to)10596 static int handle_grant_data(THD *thd, Grant_tables& tables, bool drop,
10597                              LEX_USER *user_from, LEX_USER *user_to)
10598 {
10599   int result= 0;
10600   int found;
10601   bool handle_as_role= user_from->is_role();
10602   bool search_only= !drop && !user_to;
10603   DBUG_ENTER("handle_grant_data");
10604 
10605   if (user_to)
10606     DBUG_ASSERT(handle_as_role == user_to->is_role());
10607 
10608   if (search_only)
10609   {
10610     /* quickly search in-memory structures first */
10611     if (handle_as_role && find_acl_role(user_from->user.str))
10612       DBUG_RETURN(1); // found
10613 
10614     if (!handle_as_role && find_user_exact(user_from->host.str, user_from->user.str))
10615       DBUG_RETURN(1); // found
10616   }
10617 
10618   /* Handle db table. */
10619   if ((found= handle_grant_table(thd, tables.db_table(),
10620                                  DB_TABLE, drop, user_from,
10621                                  user_to)) < 0)
10622   {
10623     /* Handle of table failed, don't touch the in-memory array. */
10624     result= -1;
10625   }
10626   else
10627   {
10628     /* Handle db array. */
10629     if ((handle_grant_struct(DB_ACL, drop, user_from, user_to) || found)
10630         && ! result)
10631     {
10632       result= 1; /* At least one record/element found. */
10633       /* If search is requested, we do not need to search further. */
10634       if (search_only)
10635         goto end;
10636       acl_cache->clear(1);
10637     }
10638   }
10639 
10640   /* Handle stored routines table. */
10641   if ((found= handle_grant_table(thd, tables.procs_priv_table(),
10642                                  PROCS_PRIV_TABLE, drop,
10643                                  user_from, user_to)) < 0)
10644   {
10645     /* Handle of table failed, don't touch in-memory array. */
10646     result= -1;
10647   }
10648   else
10649   {
10650     /* Handle procs array. */
10651     if ((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10652         && ! result)
10653     {
10654       result= 1; /* At least one record/element found. */
10655       /* If search is requested, we do not need to search further. */
10656       if (search_only)
10657         goto end;
10658     }
10659     /* Handle funcs array. */
10660     if ((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10661         && ! result)
10662     {
10663       result= 1; /* At least one record/element found. */
10664       /* If search is requested, we do not need to search further. */
10665       if (search_only)
10666         goto end;
10667     }
10668     /* Handle package spec array. */
10669     if ((handle_grant_struct(PACKAGE_SPEC_PRIVILEGES_HASH,
10670                              drop, user_from, user_to) || found)
10671         && ! result)
10672     {
10673       result= 1; /* At least one record/element found. */
10674       /* If search is requested, we do not need to search further. */
10675       if (search_only)
10676         goto end;
10677     }
10678     /* Handle package body array. */
10679     if ((handle_grant_struct(PACKAGE_BODY_PRIVILEGES_HASH,
10680                              drop, user_from, user_to) || found)
10681         && ! result)
10682     {
10683       result= 1; /* At least one record/element found. */
10684       /* If search is requested, we do not need to search further. */
10685       if (search_only)
10686         goto end;
10687     }
10688   }
10689 
10690   /* Handle tables table. */
10691   if ((found= handle_grant_table(thd, tables.tables_priv_table(),
10692                                  TABLES_PRIV_TABLE, drop,
10693                                  user_from, user_to)) < 0)
10694   {
10695     /* Handle of table failed, don't touch columns and in-memory array. */
10696     result= -1;
10697   }
10698   else
10699   {
10700     if (found && ! result)
10701     {
10702       result= 1; /* At least one record found. */
10703       /* If search is requested, we do not need to search further. */
10704       if (search_only)
10705         goto end;
10706     }
10707 
10708     /* Handle columns table. */
10709     if ((found= handle_grant_table(thd, tables.columns_priv_table(),
10710                                    COLUMNS_PRIV_TABLE, drop,
10711                                    user_from, user_to)) < 0)
10712     {
10713       /* Handle of table failed, don't touch the in-memory array. */
10714       result= -1;
10715     }
10716     else
10717     {
10718       /* Handle columns hash. */
10719       if ((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) || found)
10720           && ! result)
10721         result= 1; /* At least one record/element found. */
10722       if (search_only)
10723         goto end;
10724     }
10725   }
10726 
10727   /* Handle proxies_priv table. */
10728   if (tables.proxies_priv_table().table_exists())
10729   {
10730     if ((found= handle_grant_table(thd, tables.proxies_priv_table(),
10731                                    PROXIES_PRIV_TABLE, drop,
10732                                    user_from, user_to)) < 0)
10733     {
10734       /* Handle of table failed, don't touch the in-memory array. */
10735       result= -1;
10736     }
10737     else
10738     {
10739       /* Handle proxies_priv array. */
10740       if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) || found)
10741           && ! result)
10742         result= 1; /* At least one record/element found. */
10743       if (search_only)
10744         goto end;
10745     }
10746   }
10747 
10748   /* Handle roles_mapping table. */
10749   if (tables.roles_mapping_table().table_exists())
10750   {
10751     if ((found= handle_grant_table(thd, tables.roles_mapping_table(),
10752                                    ROLES_MAPPING_TABLE, drop,
10753                                    user_from, user_to)) < 0)
10754     {
10755       /* Handle of table failed, don't touch the in-memory array. */
10756       result= -1;
10757     }
10758     else
10759     {
10760       /* Handle acl_roles_mappings array */
10761       if ((handle_grant_struct(ROLES_MAPPINGS_HASH, drop, user_from, user_to) || found)
10762           && ! result)
10763         result= 1; /* At least one record/element found */
10764       if (search_only)
10765         goto end;
10766     }
10767   }
10768 
10769   /* Handle user table. */
10770   if ((found= handle_grant_table(thd, tables.user_table(), USER_TABLE,
10771                                  drop, user_from, user_to)) < 0)
10772   {
10773     /* Handle of table failed, don't touch the in-memory array. */
10774     result= -1;
10775   }
10776   else
10777   {
10778     enum enum_acl_lists what= handle_as_role ? ROLE_ACL : USER_ACL;
10779     if (((handle_grant_struct(what, drop, user_from, user_to)) || found) && !result)
10780     {
10781       result= 1; /* At least one record/element found. */
10782       DBUG_ASSERT(! search_only);
10783     }
10784   }
10785 
10786 end:
10787   DBUG_RETURN(result);
10788 }
10789 
10790 /*
10791   Create a list of users.
10792 
10793   SYNOPSIS
10794     mysql_create_user()
10795     thd                         The current thread.
10796     list                        The users to create.
10797     handle_as_role              Handle the user list as roles if true
10798 
10799   RETURN
10800     FALSE       OK.
10801     TRUE        Error.
10802 */
10803 
mysql_create_user(THD * thd,List<LEX_USER> & list,bool handle_as_role)10804 bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
10805 {
10806   int result;
10807   String wrong_users;
10808   LEX_USER *user_name;
10809   List_iterator <LEX_USER> user_list(list);
10810   bool binlog= false;
10811   bool some_users_dropped= false;
10812   DBUG_ENTER("mysql_create_user");
10813   DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
10814 
10815   if (handle_as_role && sp_process_definer(thd))
10816     DBUG_RETURN(TRUE);
10817 
10818   /* CREATE USER may be skipped on replication client. */
10819   Grant_tables tables;
10820   const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
10821                              Table_columns_priv | Table_procs_priv |
10822                              Table_proxies_priv | Table_roles_mapping;
10823   if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
10824     DBUG_RETURN(result != 1);
10825 
10826   mysql_rwlock_wrlock(&LOCK_grant);
10827   mysql_mutex_lock(&acl_cache->lock);
10828 
10829   while ((user_name= user_list++))
10830   {
10831     if (user_name->user.str == current_user.str)
10832     {
10833       append_str(&wrong_users, STRING_WITH_LEN("CURRENT_USER"));
10834       result= TRUE;
10835       continue;
10836     }
10837 
10838     if (user_name->user.str == current_role.str)
10839     {
10840       append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
10841       result= TRUE;
10842       continue;
10843     }
10844 
10845     if (handle_as_role && is_invalid_role_name(user_name->user.str))
10846     {
10847       append_user(thd, &wrong_users, user_name);
10848       result= TRUE;
10849       continue;
10850     }
10851 
10852     if (!user_name->host.str)
10853       user_name->host= host_not_specified;
10854 
10855     /*
10856       Search all in-memory structures and grant tables
10857       for a mention of the new user/role name.
10858     */
10859     if (handle_grant_data(thd, tables, 0, user_name, NULL))
10860     {
10861       if (thd->lex->create_info.or_replace())
10862       {
10863         // Drop the existing user
10864         if (handle_grant_data(thd, tables, 1, user_name, NULL) <= 0)
10865         {
10866           // DROP failed
10867           append_user(thd, &wrong_users, user_name);
10868           result= true;
10869           continue;
10870         }
10871         else
10872           some_users_dropped= true;
10873         // Proceed with the creation
10874       }
10875       else if (thd->lex->create_info.if_not_exists())
10876       {
10877         binlog= true;
10878         if (handle_as_role)
10879           push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10880                               ER_ROLE_CREATE_EXISTS,
10881                               ER_THD(thd, ER_ROLE_CREATE_EXISTS),
10882                               user_name->user.str);
10883         else
10884           push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
10885                               ER_USER_CREATE_EXISTS,
10886                               ER_THD(thd, ER_USER_CREATE_EXISTS),
10887                               user_name->user.str, user_name->host.str);
10888         continue;
10889       }
10890       else
10891       {
10892         // "CREATE USER user1" for an existing user
10893         append_user(thd, &wrong_users, user_name);
10894         result= true;
10895         continue;
10896       }
10897     }
10898 
10899     if (replace_user_table(thd, tables.user_table(), user_name,
10900                            NO_ACL, 0, 1, 0))
10901     {
10902       append_user(thd, &wrong_users, user_name);
10903       result= TRUE;
10904       continue;
10905     }
10906     binlog= true;
10907 
10908     // every created role is automatically granted to its creator-admin
10909     if (handle_as_role)
10910     {
10911       ACL_USER_BASE *grantee= find_acl_user_base(thd->lex->definer->user.str,
10912                                                  thd->lex->definer->host.str);
10913       ACL_ROLE *role= find_acl_role(user_name->user.str);
10914 
10915       /*
10916         just like with routines, views, triggers, and events we allow
10917         non-existant definers here with a warning (see sp_process_definer())
10918       */
10919       if (grantee)
10920         add_role_user_mapping(grantee, role);
10921 
10922       /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
10923          Roles_mapping_table instead of TABLE directly. */
10924       if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
10925                                       &thd->lex->definer->user,
10926                                       &thd->lex->definer->host,
10927                                       &user_name->user, true,
10928                                       NULL, false))
10929       {
10930         append_user(thd, &wrong_users, user_name);
10931         if (grantee)
10932           undo_add_role_user_mapping(grantee, role);
10933         result= TRUE;
10934       }
10935       else if (grantee)
10936              update_role_mapping(&thd->lex->definer->user,
10937                                  &thd->lex->definer->host,
10938                                  &user_name->user, true, NULL, false);
10939     }
10940   }
10941 
10942   if (result && some_users_dropped && !handle_as_role)
10943   {
10944     /* Rebuild in-memory structs, since 'acl_users' has been modified */
10945     rebuild_check_host();
10946     rebuild_role_grants();
10947   }
10948 
10949   mysql_mutex_unlock(&acl_cache->lock);
10950 
10951   if (result)
10952   {
10953     my_error(ER_CANNOT_USER, MYF(0),
10954              (handle_as_role) ? "CREATE ROLE" : "CREATE USER",
10955              wrong_users.c_ptr_safe());
10956   }
10957 
10958   if (binlog)
10959     result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
10960 
10961   mysql_rwlock_unlock(&LOCK_grant);
10962   DBUG_RETURN(result);
10963 }
10964 
10965 /*
10966   Drop a list of users and all their privileges.
10967 
10968   SYNOPSIS
10969     mysql_drop_user()
10970     thd                         The current thread.
10971     list                        The users to drop.
10972 
10973   RETURN
10974     FALSE       OK.
10975     TRUE        Error.
10976 */
10977 
mysql_drop_user(THD * thd,List<LEX_USER> & list,bool handle_as_role)10978 bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
10979 {
10980   int result;
10981   String wrong_users;
10982   LEX_USER *user_name, *tmp_user_name;
10983   List_iterator <LEX_USER> user_list(list);
10984   bool binlog= false;
10985   DBUG_ENTER("mysql_drop_user");
10986   DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
10987 
10988   /* DROP USER may be skipped on replication client. */
10989   Grant_tables tables;
10990   const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
10991                              Table_columns_priv | Table_procs_priv |
10992                              Table_proxies_priv | Table_roles_mapping;
10993   if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
10994     DBUG_RETURN(result != 1);
10995 
10996   Sql_mode_instant_remove sms(thd, MODE_PAD_CHAR_TO_FULL_LENGTH);
10997 
10998   mysql_rwlock_wrlock(&LOCK_grant);
10999   mysql_mutex_lock(&acl_cache->lock);
11000 
11001   while ((tmp_user_name= user_list++))
11002   {
11003     int rc;
11004     user_name= get_current_user(thd, tmp_user_name, false);
11005     if (!user_name)
11006     {
11007       thd->clear_error();
11008       append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
11009       result= TRUE;
11010       continue;
11011     }
11012 
11013     if (handle_as_role != user_name->is_role())
11014     {
11015       append_user(thd, &wrong_users, user_name);
11016       result= TRUE;
11017       continue;
11018     }
11019 
11020     if ((rc= handle_grant_data(thd, tables, 1, user_name, NULL)) > 0)
11021     {
11022       // The user or role was successfully deleted
11023       binlog= true;
11024       continue;
11025     }
11026 
11027     if (rc == 0 && thd->lex->if_exists())
11028     {
11029       // "DROP USER IF EXISTS user1" for a non-existing user or role
11030       if (handle_as_role)
11031         push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
11032                             ER_ROLE_DROP_EXISTS,
11033                             ER_THD(thd, ER_ROLE_DROP_EXISTS),
11034                             user_name->user.str);
11035       else
11036         push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
11037                             ER_USER_DROP_EXISTS,
11038                             ER_THD(thd, ER_USER_DROP_EXISTS),
11039                             user_name->user.str, user_name->host.str);
11040       binlog= true;
11041       continue;
11042     }
11043     // Internal error, or "DROP USER user1" for a non-existing user
11044     append_user(thd, &wrong_users, user_name);
11045     result= TRUE;
11046   }
11047 
11048   if (!handle_as_role)
11049   {
11050     /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
11051     rebuild_check_host();
11052 
11053     /*
11054       Rebuild every user's role_grants since 'acl_users' has been sorted
11055       and old pointers to ACL_USER elements are no longer valid
11056     */
11057     rebuild_role_grants();
11058   }
11059 
11060   mysql_mutex_unlock(&acl_cache->lock);
11061 
11062   if (result)
11063     my_error(ER_CANNOT_USER, MYF(0),
11064              (handle_as_role) ? "DROP ROLE" : "DROP USER",
11065              wrong_users.c_ptr_safe());
11066 
11067   if (binlog)
11068     result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
11069 
11070   mysql_rwlock_unlock(&LOCK_grant);
11071   DBUG_RETURN(result);
11072 }
11073 
11074 /*
11075   Rename a user.
11076 
11077   SYNOPSIS
11078     mysql_rename_user()
11079     thd                         The current thread.
11080     list                        The user name pairs: (from, to).
11081 
11082   RETURN
11083     FALSE       OK.
11084     TRUE        Error.
11085 */
11086 
mysql_rename_user(THD * thd,List<LEX_USER> & list)11087 bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
11088 {
11089   int result;
11090   String wrong_users;
11091   LEX_USER *user_from, *tmp_user_from;
11092   LEX_USER *user_to, *tmp_user_to;
11093   List_iterator <LEX_USER> user_list(list);
11094   bool some_users_renamed= FALSE;
11095   DBUG_ENTER("mysql_rename_user");
11096 
11097   /* RENAME USER may be skipped on replication client. */
11098   Grant_tables tables;
11099   const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
11100                              Table_columns_priv | Table_procs_priv |
11101                              Table_proxies_priv | Table_roles_mapping;
11102   if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
11103     DBUG_RETURN(result != 1);
11104 
11105   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
11106 
11107   mysql_rwlock_wrlock(&LOCK_grant);
11108   mysql_mutex_lock(&acl_cache->lock);
11109 
11110   while ((tmp_user_from= user_list++))
11111   {
11112     tmp_user_to= user_list++;
11113     if (!(user_from= get_current_user(thd, tmp_user_from, false)))
11114     {
11115       append_user(thd, &wrong_users, user_from);
11116       result= TRUE;
11117       continue;
11118     }
11119     if (!(user_to= get_current_user(thd, tmp_user_to, false)))
11120     {
11121       append_user(thd, &wrong_users, user_to);
11122       result= TRUE;
11123       continue;
11124     }
11125     DBUG_ASSERT(!user_from->is_role());
11126     DBUG_ASSERT(!user_to->is_role());
11127 
11128     /*
11129       Search all in-memory structures and grant tables
11130       for a mention of the new user name.
11131     */
11132     if (handle_grant_data(thd, tables, 0, user_to, NULL) ||
11133         handle_grant_data(thd, tables, 0, user_from, user_to) <= 0)
11134     {
11135       /* NOTE TODO renaming roles is not yet implemented */
11136       append_user(thd, &wrong_users, user_from);
11137       result= TRUE;
11138       continue;
11139     }
11140     some_users_renamed= TRUE;
11141     rebuild_acl_users();
11142   }
11143 
11144   /* Rebuild 'acl_dbs' since 'acl_users' has been modified */
11145   rebuild_acl_dbs();
11146 
11147   /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
11148   rebuild_check_host();
11149 
11150   /*
11151     Rebuild every user's role_grants since 'acl_users' has been sorted
11152     and old pointers to ACL_USER elements are no longer valid
11153   */
11154   rebuild_role_grants();
11155 
11156   mysql_mutex_unlock(&acl_cache->lock);
11157 
11158   if (result)
11159     my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
11160 
11161   if (some_users_renamed && mysql_bin_log.is_open())
11162     result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
11163 
11164   mysql_rwlock_unlock(&LOCK_grant);
11165   DBUG_RETURN(result);
11166 }
11167 
11168 /*
11169   Alter a user's connection and resource settings.
11170 
11171   SYNOPSIS
11172     mysql_alter_user()
11173     thd                         The current thread.
11174     list                        The users to alter.
11175 
11176   RETURN
11177     > 0         Error. Error message already sent.
11178     0           OK.
11179 */
mysql_alter_user(THD * thd,List<LEX_USER> & users_list)11180 int mysql_alter_user(THD* thd, List<LEX_USER> &users_list)
11181 {
11182   DBUG_ENTER("mysql_alter_user");
11183   int result= 0;
11184   String wrong_users;
11185   bool some_users_altered= false;
11186 
11187   /* The only table we're altering is the user table. */
11188   Grant_tables tables;
11189   if ((result= tables.open_and_lock(thd, Table_user, TL_WRITE)))
11190     DBUG_RETURN(result != 1);
11191 
11192   /* Lock ACL data structures until we finish altering all users. */
11193   mysql_rwlock_wrlock(&LOCK_grant);
11194   mysql_mutex_lock(&acl_cache->lock);
11195 
11196   LEX_USER *tmp_lex_user;
11197   List_iterator<LEX_USER> users_list_iterator(users_list);
11198 
11199   while ((tmp_lex_user= users_list_iterator++))
11200   {
11201     LEX_USER* lex_user= get_current_user(thd, tmp_lex_user, false);
11202     if (!lex_user ||
11203         replace_user_table(thd, tables.user_table(), lex_user, NO_ACL,
11204                            false, false, true))
11205     {
11206       thd->clear_error();
11207       append_user(thd, &wrong_users, tmp_lex_user);
11208       result= TRUE;
11209       continue;
11210     }
11211     some_users_altered= true;
11212   }
11213 
11214   /* Unlock ACL data structures. */
11215   mysql_mutex_unlock(&acl_cache->lock);
11216   mysql_rwlock_unlock(&LOCK_grant);
11217 
11218   if (result)
11219   {
11220     /* 'if exists' flag leads to warnings instead of errors. */
11221     if (thd->lex->create_info.if_exists())
11222     {
11223       push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
11224                           ER_CANNOT_USER,
11225                           ER_THD(thd, ER_CANNOT_USER),
11226                           "ALTER USER", wrong_users.c_ptr_safe());
11227       result= FALSE;
11228     }
11229     else
11230     {
11231       my_error(ER_CANNOT_USER, MYF(0),
11232                "ALTER USER",
11233                wrong_users.c_ptr_safe());
11234     }
11235   }
11236 
11237   if (some_users_altered)
11238     result|= write_bin_log(thd, FALSE, thd->query(),
11239                                      thd->query_length());
11240   DBUG_RETURN(result);
11241 }
11242 
11243 
11244 static bool
mysql_revoke_sp_privs(THD * thd,Grant_tables * tables,const Sp_handler * sph,const LEX_USER * lex_user)11245 mysql_revoke_sp_privs(THD *thd, Grant_tables *tables, const Sp_handler *sph,
11246                       const LEX_USER *lex_user)
11247 {
11248   bool rc= false;
11249   uint counter, revoked;
11250   do {
11251     HASH *hash= sph->get_priv_hash();
11252     for (counter= 0, revoked= 0 ; counter < hash->records ; )
11253     {
11254       const char *user,*host;
11255       GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
11256       user= grant_proc->user;
11257       host= safe_str(grant_proc->host.hostname);
11258 
11259       if (!strcmp(lex_user->user.str, user) &&
11260           !strcmp(lex_user->host.str, host))
11261       {
11262         if (replace_routine_table(thd, grant_proc,
11263                                   tables->procs_priv_table().table(),
11264                                   *lex_user,
11265                                   grant_proc->db, grant_proc->tname,
11266                                   sph, ALL_KNOWN_ACL, 1) == 0)
11267         {
11268           revoked= 1;
11269           continue;
11270         }
11271         rc= true;  // Something went wrong
11272       }
11273       counter++;
11274     }
11275   } while (revoked);
11276   return rc;
11277 }
11278 
11279 
11280 /*
11281   Revoke all privileges from a list of users.
11282 
11283   SYNOPSIS
11284     mysql_revoke_all()
11285     thd                         The current thread.
11286     list                        The users to revoke all privileges from.
11287 
11288   RETURN
11289     > 0         Error. Error message already sent.
11290     0           OK.
11291     < 0         Error. Error message not yet sent.
11292 */
11293 
mysql_revoke_all(THD * thd,List<LEX_USER> & list)11294 bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
11295 {
11296   uint counter, revoked;
11297   int result;
11298   ACL_DB *acl_db;
11299   DBUG_ENTER("mysql_revoke_all");
11300 
11301   Grant_tables tables;
11302   const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
11303                              Table_columns_priv | Table_procs_priv |
11304                              Table_proxies_priv | Table_roles_mapping;
11305   if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
11306     DBUG_RETURN(result != 1);
11307 
11308   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
11309 
11310   mysql_rwlock_wrlock(&LOCK_grant);
11311   mysql_mutex_lock(&acl_cache->lock);
11312 
11313   LEX_USER *lex_user, *tmp_lex_user;
11314   List_iterator <LEX_USER> user_list(list);
11315   while ((tmp_lex_user= user_list++))
11316   {
11317     if (!(lex_user= get_current_user(thd, tmp_lex_user, false)))
11318     {
11319       result= -1;
11320       continue;
11321     }
11322 
11323     /* This is not a role and the user could not be found */
11324     if (!lex_user->is_role() &&
11325         !find_user_exact(lex_user->host.str, lex_user->user.str))
11326     {
11327       result= -1;
11328       continue;
11329     }
11330 
11331     if (replace_user_table(thd, tables.user_table(), lex_user,
11332                            ALL_KNOWN_ACL, 1, 0, 0))
11333     {
11334       result= -1;
11335       continue;
11336     }
11337 
11338     /* Remove db access privileges */
11339     /*
11340       Because acl_dbs and column_priv_hash shrink and may re-order
11341       as privileges are removed, removal occurs in a repeated loop
11342       until no more privileges are revoked.
11343      */
11344     do
11345     {
11346       for (counter= 0, revoked= 0 ; counter < acl_dbs.elements() ; )
11347       {
11348         const char *user, *host;
11349 
11350         acl_db= &acl_dbs.at(counter);
11351 
11352         user= acl_db->user;
11353         host= safe_str(acl_db->host.hostname);
11354 
11355 	if (!strcmp(lex_user->user.str, user) &&
11356             !strcmp(lex_user->host.str, host))
11357 	{
11358       /* TODO(cvicentiu) refactor replace_db_table to use
11359          Db_table instead of TABLE directly. */
11360 	  if (!replace_db_table(tables.db_table().table(), acl_db->db, *lex_user,
11361                                 ALL_KNOWN_ACL, 1))
11362 	  {
11363 	    /*
11364 	      Don't increment counter as replace_db_table deleted the
11365 	      current element in acl_dbs.
11366 	     */
11367 	    revoked= 1;
11368 	    continue;
11369 	  }
11370 	  result= -1; // Something went wrong
11371 	}
11372 	counter++;
11373       }
11374     } while (revoked);
11375 
11376     /* Remove column access */
11377     do
11378     {
11379       for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
11380       {
11381         const char *user,*host;
11382         GRANT_TABLE *grant_table=
11383           (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
11384         user= grant_table->user;
11385         host= safe_str(grant_table->host.hostname);
11386 
11387         if (!strcmp(lex_user->user.str,user) &&
11388             !strcmp(lex_user->host.str, host))
11389         {
11390           List<LEX_COLUMN> columns;
11391           /* TODO(cvicentiu) refactor to use
11392              Db_table instead of TABLE directly. */
11393           if (replace_column_table(grant_table,
11394                                    tables.columns_priv_table().table(),
11395                                    *lex_user, columns,
11396                                    grant_table->db, grant_table->tname,
11397                                    ALL_KNOWN_ACL, 1))
11398             result= -1;
11399           if (int res= replace_table_table(thd, grant_table,
11400                                            tables.tables_priv_table().table(),
11401                                            *lex_user,
11402                                            grant_table->db, grant_table->tname,
11403                                            ALL_KNOWN_ACL, NO_ACL, 1))
11404           {
11405             if (res > 0)
11406               result= -1;
11407             else
11408             {
11409               /*
11410                 Entry was deleted. We have to retry the loop as the
11411                 hash table has probably been reorganized.
11412               */
11413               revoked= 1;
11414               continue;
11415             }
11416           }
11417         }
11418         counter++;
11419       }
11420     } while (revoked);
11421 
11422     /* Remove procedure access */
11423     if (mysql_revoke_sp_privs(thd, &tables, &sp_handler_function, lex_user) ||
11424         mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user) ||
11425         mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_spec, lex_user) ||
11426         mysql_revoke_sp_privs(thd, &tables, &sp_handler_package_body, lex_user))
11427       result= -1;
11428 
11429     ACL_USER_BASE *user_or_role;
11430     /* remove role grants */
11431     if (lex_user->is_role())
11432     {
11433       /* this can not fail due to get_current_user already having searched for it */
11434       user_or_role= find_acl_role(lex_user->user.str);
11435     }
11436     else
11437     {
11438       user_or_role= find_user_exact(lex_user->host.str, lex_user->user.str);
11439     }
11440     /*
11441       Find every role grant pair matching the role_grants array and remove it,
11442       both from the acl_roles_mappings and the roles_mapping table
11443     */
11444     for (counter= 0; counter < user_or_role->role_grants.elements; counter++)
11445     {
11446       ACL_ROLE *role_grant= *dynamic_element(&user_or_role->role_grants,
11447                                              counter, ACL_ROLE**);
11448       ROLE_GRANT_PAIR *pair = find_role_grant_pair(&lex_user->user,
11449                                                    &lex_user->host,
11450                                                    &role_grant->user);
11451       /* TODO(cvicentiu) refactor replace_roles_mapping_table to use
11452          Roles_mapping_table instead of TABLE directly. */
11453       if (replace_roles_mapping_table(tables.roles_mapping_table().table(),
11454                                       &lex_user->user, &lex_user->host,
11455                                       &role_grant->user, false, pair, true))
11456       {
11457         result= -1; //Something went wrong
11458       }
11459       update_role_mapping(&lex_user->user, &lex_user->host,
11460                           &role_grant->user, false, pair, true);
11461       /*
11462         Delete from the parent_grantee array of the roles granted,
11463         the entry pointing to this user_or_role
11464       */
11465       remove_ptr_from_dynarray(&role_grant->parent_grantee, user_or_role);
11466     }
11467     /* TODO
11468        How to handle an error in the replace_roles_mapping_table, in
11469        regards to the privileges held in memory
11470     */
11471 
11472     /* Finally, clear the role_grants array */
11473     if (counter == user_or_role->role_grants.elements)
11474     {
11475       reset_dynamic(&user_or_role->role_grants);
11476     }
11477     /*
11478       If we are revoking from a role, we need to update all the parent grantees
11479     */
11480     if (lex_user->is_role())
11481     {
11482       propagate_role_grants((ACL_ROLE *)user_or_role, PRIVS_TO_MERGE::ALL);
11483     }
11484   }
11485 
11486   mysql_mutex_unlock(&acl_cache->lock);
11487 
11488   if (result)
11489     my_message(ER_REVOKE_GRANTS, ER_THD(thd, ER_REVOKE_GRANTS), MYF(0));
11490 
11491   result= result |
11492     write_bin_log(thd, FALSE, thd->query(), thd->query_length());
11493 
11494   mysql_rwlock_unlock(&LOCK_grant);
11495 
11496   DBUG_RETURN(result);
11497 }
11498 
11499 
11500 
11501 
11502 /**
11503   If the defining user for a routine does not exist, then the ACL lookup
11504   code should raise two errors which we should intercept.  We convert the more
11505   descriptive error into a warning, and consume the other.
11506 
11507   If any other errors are raised, then we set a flag that should indicate
11508   that there was some failure we should complain at a higher level.
11509 */
11510 class Silence_routine_definer_errors : public Internal_error_handler
11511 {
11512 public:
Silence_routine_definer_errors()11513   Silence_routine_definer_errors()
11514     : is_grave(FALSE)
11515   {}
11516 
~Silence_routine_definer_errors()11517   virtual ~Silence_routine_definer_errors()
11518   {}
11519 
11520   virtual bool handle_condition(THD *thd,
11521                                 uint sql_errno,
11522                                 const char* sqlstate,
11523                                 Sql_condition::enum_warning_level *level,
11524                                 const char* msg,
11525                                 Sql_condition ** cond_hdl);
11526 
has_errors()11527   bool has_errors() { return is_grave; }
11528 
11529 private:
11530   bool is_grave;
11531 };
11532 
11533 bool
handle_condition(THD * thd,uint sql_errno,const char *,Sql_condition::enum_warning_level * level,const char * msg,Sql_condition ** cond_hdl)11534 Silence_routine_definer_errors::handle_condition(
11535   THD *thd,
11536   uint sql_errno,
11537   const char*,
11538   Sql_condition::enum_warning_level *level,
11539   const char* msg,
11540   Sql_condition ** cond_hdl)
11541 {
11542   *cond_hdl= NULL;
11543   if (*level == Sql_condition::WARN_LEVEL_ERROR)
11544   {
11545     switch (sql_errno)
11546     {
11547       case ER_NONEXISTING_PROC_GRANT:
11548         /* Convert the error into a warning. */
11549         push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
11550                      sql_errno, msg);
11551         return TRUE;
11552       default:
11553         is_grave= TRUE;
11554     }
11555   }
11556 
11557   return FALSE;
11558 }
11559 
11560 
11561 /**
11562   Revoke privileges for all users on a stored procedure.  Use an error handler
11563   that converts errors about missing grants into warnings.
11564 
11565   @param
11566     thd                         The current thread.
11567   @param
11568     db				DB of the stored procedure
11569   @param
11570     name			Name of the stored procedure
11571 
11572   @retval
11573     0           OK.
11574   @retval
11575     < 0         Error. Error message not yet sent.
11576 */
11577 
sp_revoke_privileges(THD * thd,const char * sp_db,const char * sp_name,const Sp_handler * sph)11578 bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
11579                           const Sp_handler *sph)
11580 {
11581   uint counter, revoked;
11582   int result;
11583   HASH *hash= sph->get_priv_hash();
11584   Silence_routine_definer_errors error_handler;
11585   DBUG_ENTER("sp_revoke_privileges");
11586 
11587   Grant_tables tables;
11588   const uint tables_to_open= Table_user | Table_db | Table_tables_priv |
11589                              Table_columns_priv | Table_procs_priv |
11590                              Table_proxies_priv | Table_roles_mapping;
11591   if ((result= tables.open_and_lock(thd, tables_to_open, TL_WRITE)))
11592     DBUG_RETURN(result != 1);
11593 
11594   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
11595 
11596   /* Be sure to pop this before exiting this scope! */
11597   thd->push_internal_handler(&error_handler);
11598 
11599   mysql_rwlock_wrlock(&LOCK_grant);
11600   mysql_mutex_lock(&acl_cache->lock);
11601 
11602   /* Remove procedure access */
11603   do
11604   {
11605     for (counter= 0, revoked= 0 ; counter < hash->records ; )
11606     {
11607       GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
11608       if (!my_strcasecmp(&my_charset_utf8mb3_bin, grant_proc->db, sp_db) &&
11609 	  !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
11610       {
11611         LEX_USER lex_user;
11612 	lex_user.user.str= grant_proc->user;
11613 	lex_user.user.length= strlen(grant_proc->user);
11614         lex_user.host.str= safe_str(grant_proc->host.hostname);
11615         lex_user.host.length= strlen(lex_user.host.str);
11616         if (replace_routine_table(thd, grant_proc,
11617                                   tables.procs_priv_table().table(), lex_user,
11618                                   grant_proc->db, grant_proc->tname,
11619                                   sph, ALL_KNOWN_ACL, 1) == 0)
11620 	{
11621 	  revoked= 1;
11622 	  continue;
11623 	}
11624       }
11625       counter++;
11626     }
11627   } while (revoked);
11628 
11629   mysql_mutex_unlock(&acl_cache->lock);
11630   mysql_rwlock_unlock(&LOCK_grant);
11631 
11632   thd->pop_internal_handler();
11633 
11634   DBUG_RETURN(error_handler.has_errors());
11635 }
11636 
11637 
11638 /**
11639   Grant EXECUTE,ALTER privilege for a stored procedure
11640 
11641   @param thd The current thread.
11642   @param sp_db
11643   @param sp_name
11644   @param sph
11645 
11646   @return
11647     @retval FALSE Success
11648     @retval TRUE An error occurred. Error message not yet sent.
11649 */
11650 
sp_grant_privileges(THD * thd,const char * sp_db,const char * sp_name,const Sp_handler * sph)11651 bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
11652                          const Sp_handler *sph)
11653 {
11654   Security_context *sctx= thd->security_ctx;
11655   LEX_USER *combo;
11656   TABLE_LIST tables[1];
11657   List<LEX_USER> user_list;
11658   bool result;
11659   ACL_USER *au;
11660   Dummy_error_handler error_handler;
11661   DBUG_ENTER("sp_grant_privileges");
11662 
11663   if (!(combo=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
11664     DBUG_RETURN(TRUE);
11665 
11666   combo->user.str= (char *) sctx->priv_user;
11667 
11668   mysql_mutex_lock(&acl_cache->lock);
11669   if ((au= find_user_exact(combo->host.str= (char *) sctx->priv_host,
11670                            combo->user.str)))
11671     goto found_acl;
11672 
11673   mysql_mutex_unlock(&acl_cache->lock);
11674   DBUG_RETURN(TRUE);
11675 
11676  found_acl:
11677   mysql_mutex_unlock(&acl_cache->lock);
11678 
11679   bzero((char*)tables, sizeof(TABLE_LIST));
11680   user_list.empty();
11681 
11682   tables->db.str= sp_db;
11683   tables->db.length= sp_db ? strlen(sp_db) : 0;
11684   tables->table_name.str= tables->alias.str= sp_name;
11685   tables->table_name.length= tables->alias.length= sp_name ? strlen(sp_name) : 0;
11686 
11687   thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str));
11688   thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str));
11689 
11690   combo->auth= NULL;
11691 
11692   if (user_list.push_back(combo, thd->mem_root))
11693     DBUG_RETURN(TRUE);
11694 
11695   thd->lex->account_options.reset();
11696 
11697   /*
11698     Only care about whether the operation failed or succeeded
11699     as all errors will be handled later.
11700   */
11701   thd->push_internal_handler(&error_handler);
11702   result= mysql_routine_grant(thd, tables, sph, user_list,
11703                               DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
11704   thd->pop_internal_handler();
11705   DBUG_RETURN(result);
11706 }
11707 
11708 
11709 /**
11710   Validate if a user can proxy as another user
11711 
11712   @thd                     current thread
11713   @param user              the logged in user (proxy user)
11714   @param authenticated_as  the effective user a plugin is trying to
11715                            impersonate as (proxied user)
11716   @return                  proxy user definition
11717     @retval NULL           proxy user definition not found or not applicable
11718     @retval non-null       the proxy user data
11719 */
11720 
11721 static ACL_PROXY_USER *
acl_find_proxy_user(const char * user,const char * host,const char * ip,const char * authenticated_as,bool * proxy_used)11722 acl_find_proxy_user(const char *user, const char *host, const char *ip,
11723                     const char *authenticated_as, bool *proxy_used)
11724 {
11725   uint i;
11726   /* if the proxied and proxy user are the same return OK */
11727   DBUG_ENTER("acl_find_proxy_user");
11728   DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
11729                       user, host, ip, authenticated_as));
11730 
11731   if (!strcmp(authenticated_as, user))
11732   {
11733     DBUG_PRINT ("info", ("user is the same as authenticated_as"));
11734     DBUG_RETURN (NULL);
11735   }
11736 
11737   *proxy_used= TRUE;
11738   for (i=0; i < acl_proxy_users.elements; i++)
11739   {
11740     ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11741                                            ACL_PROXY_USER *);
11742     if (proxy->matches(host, user, ip, authenticated_as))
11743       DBUG_RETURN(proxy);
11744   }
11745 
11746   DBUG_RETURN(NULL);
11747 }
11748 
11749 
11750 bool
acl_check_proxy_grant_access(THD * thd,const char * host,const char * user,bool with_grant)11751 acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
11752                              bool with_grant)
11753 {
11754   DBUG_ENTER("acl_check_proxy_grant_access");
11755   DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
11756                       (int) with_grant));
11757   if (!initialized)
11758   {
11759     my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
11760     DBUG_RETURN(1);
11761   }
11762 
11763   /* replication slave thread can do anything */
11764   if (thd->slave_thread)
11765   {
11766     DBUG_PRINT("info", ("replication slave"));
11767     DBUG_RETURN(FALSE);
11768   }
11769 
11770   /*
11771     one can grant proxy for self to others.
11772     Security context in THD contains two pairs of (user,host):
11773     1. (user,host) pair referring to inbound connection.
11774     2. (priv_user,priv_host) pair obtained from mysql.user table after doing
11775         authentication of incoming connection.
11776     Privileges should be checked wrt (priv_user, priv_host) tuple, because
11777     (user,host) pair obtained from inbound connection may have different
11778     values than what is actually stored in mysql.user table and while granting
11779     or revoking proxy privilege, user is expected to provide entries mentioned
11780     in mysql.user table.
11781   */
11782   if (thd->security_ctx->is_priv_user(user, host))
11783   {
11784     DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
11785                         thd->security_ctx->priv_user, user,
11786                         host, thd->security_ctx->priv_host));
11787     DBUG_RETURN(FALSE);
11788   }
11789 
11790   mysql_mutex_lock(&acl_cache->lock);
11791 
11792   /* check for matching WITH PROXY rights */
11793   for (uint i=0; i < acl_proxy_users.elements; i++)
11794   {
11795     ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11796                                            ACL_PROXY_USER *);
11797     if (proxy->matches(thd->security_ctx->host,
11798                        thd->security_ctx->user,
11799                        thd->security_ctx->ip,
11800                        user) &&
11801         proxy->get_with_grant())
11802     {
11803       DBUG_PRINT("info", ("found"));
11804       mysql_mutex_unlock(&acl_cache->lock);
11805       DBUG_RETURN(FALSE);
11806     }
11807   }
11808 
11809   mysql_mutex_unlock(&acl_cache->lock);
11810   my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
11811            thd->security_ctx->user,
11812            thd->security_ctx->host_or_ip);
11813   DBUG_RETURN(TRUE);
11814 }
11815 
11816 
11817 static bool
show_proxy_grants(THD * thd,const char * username,const char * hostname,char * buff,size_t buffsize)11818 show_proxy_grants(THD *thd, const char *username, const char *hostname,
11819                   char *buff, size_t buffsize)
11820 {
11821   Protocol *protocol= thd->protocol;
11822   int error= 0;
11823 
11824   for (uint i=0; i < acl_proxy_users.elements; i++)
11825   {
11826     ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
11827                                            ACL_PROXY_USER *);
11828     if (proxy->granted_on(hostname, username))
11829     {
11830       String global(buff, buffsize, system_charset_info);
11831       global.length(0);
11832       proxy->print_grant(&global);
11833       protocol->prepare_for_resend();
11834       protocol->store(global.ptr(), global.length(), global.charset());
11835       if (protocol->write())
11836       {
11837         error= -1;
11838         break;
11839       }
11840     }
11841   }
11842   return error;
11843 }
11844 
enabled_roles_insert(ACL_USER_BASE * role,void * context_data)11845 static int enabled_roles_insert(ACL_USER_BASE *role, void *context_data)
11846 {
11847   TABLE *table= (TABLE*) context_data;
11848   DBUG_ASSERT(role->flags & IS_ROLE);
11849 
11850   restore_record(table, s->default_values);
11851   table->field[0]->set_notnull();
11852   table->field[0]->store(role->user.str, role->user.length,
11853                          system_charset_info);
11854   if (schema_table_store_record(table->in_use, table))
11855     return -1;
11856   return 0;
11857 }
11858 
11859 struct APPLICABLE_ROLES_DATA
11860 {
11861   TABLE *table;
11862   const LEX_CSTRING host;
11863   const LEX_CSTRING user_and_host;
11864   ACL_USER *user;
11865 };
11866 
11867 static int
applicable_roles_insert(ACL_USER_BASE * grantee,ACL_ROLE * role,void * ptr)11868 applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr)
11869 {
11870   APPLICABLE_ROLES_DATA *data= (APPLICABLE_ROLES_DATA *)ptr;
11871   CHARSET_INFO *cs= system_charset_info;
11872   TABLE *table= data->table;
11873   bool is_role= grantee != data->user;
11874   const LEX_CSTRING *user_and_host= is_role ? &grantee->user
11875                                            : &data->user_and_host;
11876   const LEX_CSTRING *host= is_role ? &empty_clex_str : &data->host;
11877 
11878   restore_record(table, s->default_values);
11879   table->field[0]->store(user_and_host->str, user_and_host->length, cs);
11880   table->field[1]->store(role->user.str, role->user.length, cs);
11881 
11882   ROLE_GRANT_PAIR *pair=
11883     find_role_grant_pair(&grantee->user, host, &role->user);
11884   DBUG_ASSERT(pair);
11885 
11886   if (pair->with_admin)
11887     table->field[2]->store(STRING_WITH_LEN("YES"), cs);
11888   else
11889     table->field[2]->store(STRING_WITH_LEN("NO"), cs);
11890 
11891   /* Default role is only valid when looking at a role granted to a user. */
11892   if (!is_role)
11893   {
11894     if (data->user->default_rolename.length &&
11895         lex_string_eq(&data->user->default_rolename, &role->user))
11896       table->field[3]->store(STRING_WITH_LEN("YES"), cs);
11897     else
11898       table->field[3]->store(STRING_WITH_LEN("NO"), cs);
11899     table->field[3]->set_notnull();
11900   }
11901 
11902   if (schema_table_store_record(table->in_use, table))
11903     return -1;
11904   return 0;
11905 }
11906 
11907 /**
11908   Hash iterate function to count the number of total column privileges granted.
11909 */
count_column_grants(void * grant_table,void * current_count)11910 static my_bool count_column_grants(void *grant_table,
11911                                        void *current_count)
11912 {
11913   HASH hash_columns = ((GRANT_TABLE *)grant_table)->hash_columns;
11914   *(ulong *)current_count+= hash_columns.records;
11915   return 0;
11916 }
11917 
11918 /**
11919   SHOW function that computes the number of column grants.
11920 
11921   This must be performed under the mutex in order to make sure the
11922   iteration does not fail.
11923 */
show_column_grants(THD * thd,SHOW_VAR * var,char * buff,enum enum_var_type scope)11924 static int show_column_grants(THD *thd, SHOW_VAR *var, char *buff,
11925                               enum enum_var_type scope)
11926 {
11927   var->type= SHOW_ULONG;
11928   var->value= buff;
11929   *(ulong *)buff= 0;
11930   if (initialized)
11931   {
11932     mysql_rwlock_rdlock(&LOCK_grant);
11933     mysql_mutex_lock(&acl_cache->lock);
11934     my_hash_iterate(&column_priv_hash, count_column_grants, buff);
11935     mysql_mutex_unlock(&acl_cache->lock);
11936     mysql_rwlock_unlock(&LOCK_grant);
11937   }
11938   return 0;
11939 }
11940 
show_database_grants(THD * thd,SHOW_VAR * var,char * buff,enum enum_var_type scope)11941 static int show_database_grants(THD *thd, SHOW_VAR *var, char *buff,
11942                                 enum enum_var_type scope)
11943 {
11944   var->type= SHOW_UINT;
11945   var->value= buff;
11946   *(uint *)buff= uint(acl_dbs.elements());
11947   return 0;
11948 }
11949 
11950 #else
set_user_salt_if_needed(ACL_USER *,int,plugin_ref)11951 static bool set_user_salt_if_needed(ACL_USER *, int, plugin_ref)
11952 { return 0; }
check_grant(THD *,privilege_t,TABLE_LIST *,bool,uint,bool)11953 bool check_grant(THD *, privilege_t, TABLE_LIST *, bool, uint, bool)
11954 { return 0; }
11955 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
11956 
11957 
11958 #ifdef NO_EMBEDDED_ACCESS_CHECKS
11959 
execute(THD * thd)11960 bool Sql_cmd_grant_proxy::execute(THD *thd)
11961 {
11962   my_ok(thd);
11963   return false;
11964 }
11965 
execute(THD * thd)11966 bool Sql_cmd_grant_table::execute(THD *thd)
11967 {
11968   my_ok(thd);
11969   return false;
11970 }
11971 
11972 
execute(THD * thd)11973 bool Sql_cmd_grant_sp::execute(THD *thd)
11974 {
11975   my_ok(thd);
11976   return false;
11977 }
11978 
11979 #else // not NO_EMBEDDED_ACCESS_CHECKS
11980 
11981 
warn_hostname_requires_resolving(THD * thd,List<LEX_USER> & users)11982 void Sql_cmd_grant::warn_hostname_requires_resolving(THD *thd,
11983                                                      List<LEX_USER> &users)
11984 {
11985   LEX_USER *user;
11986   List_iterator <LEX_USER> it(users);
11987   while ((user= it++))
11988   {
11989     if (specialflag & SPECIAL_NO_RESOLVE &&
11990         hostname_requires_resolving(user->host.str))
11991       push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
11992                           ER_WARN_HOSTNAME_WONT_WORK,
11993                           ER_THD(thd, ER_WARN_HOSTNAME_WONT_WORK));
11994   }
11995 }
11996 
11997 
grant_stage0(THD * thd)11998 void Sql_cmd_grant::grant_stage0(THD *thd)
11999 {
12000   thd->binlog_invoker(false);   // Replicate current user as grantor
12001   if (thd->security_ctx->user)  // If not replication
12002     warn_hostname_requires_resolving(thd, thd->lex->users_list);
12003 }
12004 
12005 
user_list_reset_mqh(THD * thd,List<LEX_USER> & users)12006 bool Sql_cmd_grant::user_list_reset_mqh(THD *thd, List<LEX_USER> &users)
12007 {
12008   List_iterator <LEX_USER> it(users);
12009   LEX_USER *user, *tmp_user;
12010   while ((tmp_user= it++))
12011   {
12012     if (!(user= get_current_user(thd, tmp_user)))
12013       return true;
12014     reset_mqh(user, 0);
12015   }
12016   return false;
12017 }
12018 
12019 
check_access_proxy(THD * thd,List<LEX_USER> & users)12020 bool Sql_cmd_grant_proxy::check_access_proxy(THD *thd, List<LEX_USER> &users)
12021 {
12022   LEX_USER *user;
12023   List_iterator <LEX_USER> it(users);
12024   if ((user= it++))
12025   {
12026     // GRANT/REVOKE PROXY has the target user as a first entry in the list
12027     if (!(user= get_current_user(thd, user)) || !user->host.str)
12028       return true;
12029     if (acl_check_proxy_grant_access(thd, user->host.str, user->user.str,
12030                                      m_grant_option & GRANT_ACL))
12031       return true;
12032   }
12033   return false;
12034 }
12035 
12036 
execute(THD * thd)12037 bool Sql_cmd_grant_proxy::execute(THD *thd)
12038 {
12039   LEX  *lex= thd->lex;
12040 
12041   DBUG_ASSERT(lex->first_select_lex()->table_list.first == NULL);
12042   DBUG_ASSERT((m_grant_option & ~GRANT_ACL) == NO_ACL); // only WITH GRANT OPTION
12043 
12044   grant_stage0(thd);
12045 
12046   if (thd->security_ctx->user /* If not replication */ &&
12047       check_access_proxy(thd, lex->users_list))
12048     return true;
12049 
12050   WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
12051   /* Conditionally writes to binlog */
12052   if (mysql_grant(thd, NULL/*db*/, lex->users_list, m_grant_option,
12053                   is_revoke(), true/*proxy*/))
12054     return true;
12055 
12056   return !is_revoke() && user_list_reset_mqh(thd, lex->users_list);
12057 
12058 #ifdef WITH_WSREP
12059 wsrep_error_label:
12060   return true;
12061 #endif // WITH_WSREP
12062 }
12063 
12064 
grant_stage0_exact_object(THD * thd,TABLE_LIST * table)12065 bool Sql_cmd_grant_object::grant_stage0_exact_object(THD *thd,
12066                                                      TABLE_LIST *table)
12067 {
12068   privilege_t priv= m_object_privilege | m_column_privilege_total | GRANT_ACL;
12069   if (check_access(thd, priv, table->db.str,
12070                    &table->grant.privilege, &table->grant.m_internal,
12071                    0, 0))
12072     return true;
12073   grant_stage0(thd);
12074   return false;
12075 }
12076 
12077 
execute_exact_table(THD * thd,TABLE_LIST * table)12078 bool Sql_cmd_grant_table::execute_exact_table(THD *thd, TABLE_LIST *table)
12079 {
12080   LEX  *lex= thd->lex;
12081   if (grant_stage0_exact_object(thd, table) ||
12082       check_grant(thd, m_object_privilege | m_column_privilege_total | GRANT_ACL,
12083                   lex->query_tables, FALSE, UINT_MAX, FALSE))
12084     return true;
12085   /* Conditionally writes to binlog */
12086   WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
12087   return mysql_table_grant(thd, lex->query_tables, lex->users_list,
12088                            m_columns, m_object_privilege,
12089                            is_revoke());
12090 #ifdef WITH_WSREP
12091 wsrep_error_label:
12092   return true;
12093 #endif // WITH_WSREP
12094 }
12095 
12096 
execute(THD * thd)12097 bool Sql_cmd_grant_sp::execute(THD *thd)
12098 {
12099   DBUG_ASSERT(!m_columns.elements);
12100   DBUG_ASSERT(!m_column_privilege_total);
12101   LEX  *lex= thd->lex;
12102   TABLE_LIST *table= lex->first_select_lex()->table_list.first;
12103   privilege_t grants= m_all_privileges
12104                ? (PROC_ACLS & ~GRANT_ACL) | (m_object_privilege & GRANT_ACL)
12105                : m_object_privilege;
12106 
12107   if (!table) // e.g: GRANT EXECUTE ON PROCEDURE *.*
12108   {
12109     my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
12110                MYF(0));
12111     return true;
12112   }
12113 
12114   if (grant_stage0_exact_object(thd, table) ||
12115       check_grant_routine(thd, grants|GRANT_ACL, lex->query_tables, &m_sph, 0))
12116     return true;
12117 
12118   /* Conditionally writes to binlog */
12119   WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
12120   if (mysql_routine_grant(thd, lex->query_tables, &m_sph,
12121                           lex->users_list, grants,
12122                           is_revoke(), true))
12123     return true;
12124   my_ok(thd);
12125   return false;
12126 #ifdef WITH_WSREP
12127 wsrep_error_label:
12128   return true;
12129 #endif // WITH_WSREP
12130 }
12131 
12132 
execute_table_mask(THD * thd)12133 bool Sql_cmd_grant_table::execute_table_mask(THD *thd)
12134 {
12135   LEX  *lex= thd->lex;
12136   DBUG_ASSERT(lex->first_select_lex()->table_list.first == NULL);
12137 
12138   if (check_access(thd, m_object_privilege | m_column_privilege_total | GRANT_ACL,
12139                    m_db.str, NULL, NULL, 1, 0))
12140     return true;
12141 
12142   grant_stage0(thd);
12143 
12144   if (m_columns.elements) // e.g. GRANT SELECT (a) ON *.*
12145   {
12146     my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER_THD(thd, ER_ILLEGAL_GRANT_FOR_TABLE),
12147                MYF(0));
12148     return true;
12149   }
12150 
12151   WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
12152   /* Conditionally writes to binlog */
12153   if (mysql_grant(thd, m_db.str, lex->users_list, m_object_privilege,
12154                   is_revoke(), false/*not proxy*/))
12155     return true;
12156 
12157   return !is_revoke() && user_list_reset_mqh(thd, lex->users_list);
12158 
12159 #ifdef WITH_WSREP
12160 wsrep_error_label:
12161   return true;
12162 #endif // WITH_WSREP
12163 }
12164 
12165 
execute(THD * thd)12166 bool Sql_cmd_grant_table::execute(THD *thd)
12167 {
12168   TABLE_LIST *table= thd->lex->first_select_lex()->table_list.first;
12169   return table ? execute_exact_table(thd, table) :
12170                  execute_table_mask(thd);
12171 }
12172 
12173 
12174 #endif // NO_EMBEDDED_ACCESS_CHECKS
12175 
12176 
12177 
12178 SHOW_VAR acl_statistics[] = {
12179 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12180   {"column_grants",    (char*)show_column_grants,          SHOW_SIMPLE_FUNC},
12181   {"database_grants",  (char*)show_database_grants,        SHOW_SIMPLE_FUNC},
12182   {"function_grants",  (char*)&func_priv_hash.records,     SHOW_ULONG},
12183   {"procedure_grants", (char*)&proc_priv_hash.records,     SHOW_ULONG},
12184   {"package_spec_grants", (char*)&package_spec_priv_hash.records, SHOW_ULONG},
12185   {"package_body_grants", (char*)&package_body_priv_hash.records, SHOW_ULONG},
12186   {"proxy_users",      (char*)&acl_proxy_users.elements,   SHOW_UINT},
12187   {"role_grants",      (char*)&acl_roles_mappings.records, SHOW_ULONG},
12188   {"roles",            (char*)&acl_roles.records,          SHOW_ULONG},
12189   {"table_grants",     (char*)&column_priv_hash.records,   SHOW_ULONG},
12190   {"users",            (char*)&acl_users.elements,         SHOW_UINT},
12191 #endif
12192   {NullS, NullS, SHOW_LONG},
12193 };
12194 
12195 /* Check if a role is granted to a user/role. We traverse the role graph
12196    and return true if we find a match.
12197 
12198    hostname == NULL means we are looking for a role as a starting point,
12199    otherwise a user.
12200 */
check_role_is_granted(const char * username,const char * hostname,const char * rolename)12201 bool check_role_is_granted(const char *username,
12202                            const char *hostname,
12203                            const char *rolename)
12204 {
12205   DBUG_ENTER("check_role_is_granted");
12206   bool result= false;
12207 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12208   ACL_USER_BASE *root;
12209   mysql_mutex_lock(&acl_cache->lock);
12210   if (hostname)
12211     root= find_user_exact(hostname, username);
12212   else
12213     root= find_acl_role(username);
12214 
12215   LEX_CSTRING role_lex;
12216   role_lex.str= rolename;
12217   role_lex.length= strlen(rolename);
12218 
12219   if (root && /* No grantee, nothing to search. */
12220       traverse_role_graph_down(root, &role_lex, check_role_is_granted_callback,
12221                                NULL) == -1)
12222   {
12223     /* We have found the role during our search. */
12224     result= true;
12225   }
12226 
12227   /* We haven't found the role or we had no initial grantee to start from. */
12228   mysql_mutex_unlock(&acl_cache->lock);
12229 #endif
12230   DBUG_RETURN(result);
12231 }
12232 
fill_schema_enabled_roles(THD * thd,TABLE_LIST * tables,COND * cond)12233 int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond)
12234 {
12235   TABLE *table= tables->table;
12236 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12237   if (thd->security_ctx->priv_role[0])
12238   {
12239     mysql_rwlock_rdlock(&LOCK_grant);
12240     mysql_mutex_lock(&acl_cache->lock);
12241     ACL_ROLE *acl_role= find_acl_role(thd->security_ctx->priv_role);
12242     if (acl_role)
12243       traverse_role_graph_down(acl_role, table, enabled_roles_insert, NULL);
12244     mysql_mutex_unlock(&acl_cache->lock);
12245     mysql_rwlock_unlock(&LOCK_grant);
12246     if (acl_role)
12247       return 0;
12248   }
12249 #endif
12250 
12251   restore_record(table, s->default_values);
12252   table->field[0]->set_null();
12253   return schema_table_store_record(table->in_use, table);
12254 }
12255 
12256 
12257 /*
12258   This shows all roles granted to current user
12259   and recursively all roles granted to those roles
12260 */
fill_schema_applicable_roles(THD * thd,TABLE_LIST * tables,COND * cond)12261 int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond)
12262 {
12263   int res= 0;
12264 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12265   if (initialized)
12266   {
12267     TABLE *table= tables->table;
12268     Security_context *sctx= thd->security_ctx;
12269     mysql_rwlock_rdlock(&LOCK_grant);
12270     mysql_mutex_lock(&acl_cache->lock);
12271     ACL_USER *user= find_user_exact(sctx->priv_host, sctx->priv_user);
12272     if (user)
12273     {
12274       char buff[USER_HOST_BUFF_SIZE+10];
12275       DBUG_ASSERT(user->user.length + user->hostname_length +2 < sizeof(buff));
12276       char *end= strxmov(buff, user->user.str, "@", user->host.hostname, NULL);
12277       APPLICABLE_ROLES_DATA data= { table,
12278         { user->host.hostname, user->hostname_length },
12279         { buff, (size_t)(end - buff) }, user
12280       };
12281 
12282       res= traverse_role_graph_down(user, &data, 0, applicable_roles_insert);
12283     }
12284 
12285     mysql_mutex_unlock(&acl_cache->lock);
12286     mysql_rwlock_unlock(&LOCK_grant);
12287   }
12288 #endif
12289 
12290   return res;
12291 }
12292 
12293 
wild_case_compare(CHARSET_INFO * cs,const char * str,const char * wildstr)12294 int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
12295 {
12296   int flag;
12297   DBUG_ENTER("wild_case_compare");
12298   DBUG_PRINT("enter",("str: '%s'  wildstr: '%s'",str,wildstr));
12299   while (*wildstr)
12300   {
12301     while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
12302     {
12303       if (*wildstr == wild_prefix && wildstr[1])
12304 	wildstr++;
12305       if (my_toupper(cs, *wildstr++) !=
12306           my_toupper(cs, *str++)) DBUG_RETURN(1);
12307     }
12308     if (! *wildstr ) DBUG_RETURN (*str != 0);
12309     if (*wildstr++ == wild_one)
12310     {
12311       if (! *str++) DBUG_RETURN (1);	/* One char; skip */
12312     }
12313     else
12314     {						/* Found '*' */
12315       if (!*wildstr) DBUG_RETURN(0);		/* '*' as last char: OK */
12316       flag=(*wildstr != wild_many && *wildstr != wild_one);
12317       do
12318       {
12319 	if (flag)
12320 	{
12321 	  char cmp;
12322 	  if ((cmp= *wildstr) == wild_prefix && wildstr[1])
12323 	    cmp=wildstr[1];
12324 	  cmp=my_toupper(cs, cmp);
12325 	  while (*str && my_toupper(cs, *str) != cmp)
12326 	    str++;
12327 	  if (!*str) DBUG_RETURN (1);
12328 	}
12329 	if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
12330       } while (*str++);
12331       DBUG_RETURN(1);
12332     }
12333   }
12334   DBUG_RETURN (*str != '\0');
12335 }
12336 
12337 
12338 #ifndef NO_EMBEDDED_ACCESS_CHECKS
update_schema_privilege(THD * thd,TABLE * table,const char * buff,const char * db,const char * t_name,const char * column,uint col_length,const char * priv,uint priv_length,const char * is_grantable)12339 static bool update_schema_privilege(THD *thd, TABLE *table, const char *buff,
12340                                     const char* db, const char* t_name,
12341                                     const char* column, uint col_length,
12342                                     const char *priv, uint priv_length,
12343                                     const char* is_grantable)
12344 {
12345   int i= 2;
12346   CHARSET_INFO *cs= system_charset_info;
12347   restore_record(table, s->default_values);
12348   table->field[0]->store(buff, (uint) strlen(buff), cs);
12349   table->field[1]->store(STRING_WITH_LEN("def"), cs);
12350   if (db)
12351     table->field[i++]->store(db, (uint) strlen(db), cs);
12352   if (t_name)
12353     table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
12354   if (column)
12355     table->field[i++]->store(column, col_length, cs);
12356   table->field[i++]->store(priv, priv_length, cs);
12357   table->field[i]->store(is_grantable, strlen(is_grantable), cs);
12358   return schema_table_store_record(thd, table);
12359 }
12360 #endif
12361 
12362 
12363 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12364 class Grantee_str
12365 {
12366   char m_buff[USER_HOST_BUFF_SIZE + 6 /* 4 quotes, @, '\0' */];
12367 public:
Grantee_str(const char * user,const char * host)12368   Grantee_str(const char *user, const char *host)
12369   {
12370     DBUG_ASSERT(strlen(user) + strlen(host) + 6 < sizeof(m_buff));
12371     strxmov(m_buff, "'", user, "'@'", host, "'", NullS);
12372   }
operator const char*() const12373   operator const char *() const { return m_buff; }
12374 };
12375 #endif
12376 
12377 
fill_schema_user_privileges(THD * thd,TABLE_LIST * tables,COND * cond)12378 int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
12379 {
12380 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12381   int error= 0;
12382   uint counter;
12383   ACL_USER *acl_user;
12384   TABLE *table= tables->table;
12385   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
12386                                       NULL, NULL, 1, 1);
12387   DBUG_ENTER("fill_schema_user_privileges");
12388 
12389   if (!initialized)
12390     DBUG_RETURN(0);
12391   mysql_mutex_lock(&acl_cache->lock);
12392 
12393   for (counter=0 ; counter < acl_users.elements ; counter++)
12394   {
12395     const char *user,*host, *is_grantable="YES";
12396     acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
12397     user= acl_user->user.str;
12398     host= safe_str(acl_user->host.hostname);
12399 
12400     if (no_global_access &&
12401         !thd->security_ctx->is_priv_user(user, host))
12402       continue;
12403 
12404     privilege_t want_access(acl_user->access);
12405     if (!(want_access & GRANT_ACL))
12406       is_grantable= "NO";
12407 
12408     Grantee_str grantee(user, host);
12409     if (!(want_access & ~GRANT_ACL))
12410     {
12411       if (update_schema_privilege(thd, table, grantee, 0, 0, 0, 0,
12412                                   STRING_WITH_LEN("USAGE"), is_grantable))
12413       {
12414         error= 1;
12415         goto err;
12416       }
12417     }
12418     else
12419     {
12420       uint priv_id;
12421       ulonglong j;
12422       privilege_t test_access(want_access & ~GRANT_ACL);
12423       for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
12424       {
12425         if (test_access & j)
12426         {
12427           if (update_schema_privilege(thd, table, grantee, 0, 0, 0, 0,
12428                                       command_array[priv_id],
12429                                       command_lengths[priv_id], is_grantable))
12430           {
12431             error= 1;
12432             goto err;
12433           }
12434         }
12435       }
12436     }
12437   }
12438 err:
12439   mysql_mutex_unlock(&acl_cache->lock);
12440 
12441   DBUG_RETURN(error);
12442 #else
12443   return(0);
12444 #endif
12445 }
12446 
12447 
fill_schema_schema_privileges(THD * thd,TABLE_LIST * tables,COND * cond)12448 int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
12449 {
12450 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12451   int error= 0;
12452   uint counter;
12453   ACL_DB *acl_db;
12454   TABLE *table= tables->table;
12455   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
12456                                       NULL, NULL, 1, 1);
12457   DBUG_ENTER("fill_schema_schema_privileges");
12458 
12459   if (!initialized)
12460     DBUG_RETURN(0);
12461   mysql_mutex_lock(&acl_cache->lock);
12462 
12463   for (counter=0 ; counter < acl_dbs.elements() ; counter++)
12464   {
12465     const char *user, *host, *is_grantable="YES";
12466 
12467     acl_db=&acl_dbs.at(counter);
12468     user= acl_db->user;
12469     host= safe_str(acl_db->host.hostname);
12470 
12471     if (no_global_access &&
12472         !thd->security_ctx->is_priv_user(user, host))
12473       continue;
12474 
12475     privilege_t want_access(acl_db->access);
12476     if (want_access)
12477     {
12478       if (!(want_access & GRANT_ACL))
12479       {
12480         is_grantable= "NO";
12481       }
12482       Grantee_str grantee(user, host);
12483       if (!(want_access & ~GRANT_ACL))
12484       {
12485         if (update_schema_privilege(thd, table, grantee, acl_db->db, 0, 0,
12486                                     0, STRING_WITH_LEN("USAGE"), is_grantable))
12487         {
12488           error= 1;
12489           goto err;
12490         }
12491       }
12492       else
12493       {
12494         int cnt;
12495         ulonglong j;
12496         privilege_t test_access(want_access & ~GRANT_ACL);
12497         for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
12498           if (test_access & j)
12499           {
12500             if (update_schema_privilege(thd, table,
12501                                         grantee, acl_db->db, 0, 0, 0,
12502                                         command_array[cnt], command_lengths[cnt],
12503                                         is_grantable))
12504             {
12505               error= 1;
12506               goto err;
12507             }
12508           }
12509       }
12510     }
12511   }
12512 err:
12513   mysql_mutex_unlock(&acl_cache->lock);
12514 
12515   DBUG_RETURN(error);
12516 #else
12517   return (0);
12518 #endif
12519 }
12520 
12521 
fill_schema_table_privileges(THD * thd,TABLE_LIST * tables,COND * cond)12522 int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
12523 {
12524 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12525   int error= 0;
12526   uint index;
12527   TABLE *table= tables->table;
12528   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
12529                                       NULL, NULL, 1, 1);
12530   DBUG_ENTER("fill_schema_table_privileges");
12531 
12532   mysql_rwlock_rdlock(&LOCK_grant);
12533 
12534   for (index=0 ; index < column_priv_hash.records ; index++)
12535   {
12536     const char *user, *host, *is_grantable= "YES";
12537     GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
12538                                                              index);
12539     user= grant_table->user;
12540     host= safe_str(grant_table->host.hostname);
12541 
12542     if (no_global_access &&
12543         !thd->security_ctx->is_priv_user(user, host))
12544       continue;
12545 
12546     privilege_t table_access(grant_table->privs);
12547     if (table_access)
12548     {
12549       privilege_t test_access(table_access & ~GRANT_ACL);
12550       /*
12551         We should skip 'usage' privilege on table if
12552         we have any privileges on column(s) of this table
12553       */
12554       if (!test_access && grant_table->cols)
12555         continue;
12556       if (!(table_access & GRANT_ACL))
12557         is_grantable= "NO";
12558 
12559       Grantee_str grantee(user, host);
12560       if (!test_access)
12561       {
12562         if (update_schema_privilege(thd, table,
12563                                     grantee, grant_table->db,
12564                                     grant_table->tname, 0, 0,
12565                                     STRING_WITH_LEN("USAGE"), is_grantable))
12566         {
12567           error= 1;
12568           goto err;
12569         }
12570       }
12571       else
12572       {
12573         ulonglong j;
12574         int cnt;
12575         for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
12576         {
12577           if (test_access & j)
12578           {
12579             if (update_schema_privilege(thd, table,
12580                                         grantee, grant_table->db,
12581                                         grant_table->tname, 0, 0,
12582                                         command_array[cnt],
12583                                         command_lengths[cnt], is_grantable))
12584             {
12585               error= 1;
12586               goto err;
12587             }
12588           }
12589         }
12590       }
12591     }
12592   }
12593 err:
12594   mysql_rwlock_unlock(&LOCK_grant);
12595 
12596   DBUG_RETURN(error);
12597 #else
12598   return (0);
12599 #endif
12600 }
12601 
12602 
fill_schema_column_privileges(THD * thd,TABLE_LIST * tables,COND * cond)12603 int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
12604 {
12605 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12606   int error= 0;
12607   uint index;
12608   TABLE *table= tables->table;
12609   bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
12610                                       NULL, NULL, 1, 1);
12611   DBUG_ENTER("fill_schema_table_privileges");
12612 
12613   mysql_rwlock_rdlock(&LOCK_grant);
12614 
12615   for (index=0 ; index < column_priv_hash.records ; index++)
12616   {
12617     const char *user, *host, *is_grantable= "YES";
12618     GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
12619                                                           index);
12620     user= grant_table->user;
12621     host= safe_str(grant_table->host.hostname);
12622 
12623     if (no_global_access &&
12624         !thd->security_ctx->is_priv_user(user, host))
12625       continue;
12626 
12627     privilege_t table_access(grant_table->cols);
12628     if (table_access != NO_ACL)
12629     {
12630       if (!(grant_table->privs & GRANT_ACL))
12631         is_grantable= "NO";
12632 
12633       privilege_t test_access(table_access & ~GRANT_ACL);
12634       Grantee_str grantee(user, host);
12635       if (!test_access)
12636         continue;
12637       else
12638       {
12639         ulonglong j;
12640         int cnt;
12641         for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
12642         {
12643           if (test_access & j)
12644           {
12645             for (uint col_index=0 ;
12646                  col_index < grant_table->hash_columns.records ;
12647                  col_index++)
12648             {
12649               GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
12650                 my_hash_element(&grant_table->hash_columns,col_index);
12651               if ((grant_column->rights & j) && (table_access & j))
12652               {
12653                 if (update_schema_privilege(thd, table,
12654                                             grantee,
12655                                             grant_table->db,
12656                                             grant_table->tname,
12657                                             grant_column->column,
12658                                             grant_column->key_length,
12659                                             command_array[cnt],
12660                                             command_lengths[cnt], is_grantable))
12661                 {
12662                   error= 1;
12663                   goto err;
12664                 }
12665               }
12666             }
12667           }
12668         }
12669       }
12670     }
12671   }
12672 err:
12673   mysql_rwlock_unlock(&LOCK_grant);
12674 
12675   DBUG_RETURN(error);
12676 #else
12677   return (0);
12678 #endif
12679 }
12680 
12681 
12682 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12683 /*
12684   fill effective privileges for table
12685 
12686   SYNOPSIS
12687     fill_effective_table_privileges()
12688     thd     thread handler
12689     grant   grants table descriptor
12690     db      db name
12691     table   table name
12692 */
12693 
fill_effective_table_privileges(THD * thd,GRANT_INFO * grant,const char * db,const char * table)12694 void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
12695                                      const char *db, const char *table)
12696 {
12697   Security_context *sctx= thd->security_ctx;
12698   DBUG_ENTER("fill_effective_table_privileges");
12699   DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
12700                        sctx->priv_host, sctx->ip, sctx->priv_user, db, table));
12701   /* --skip-grants */
12702   if (!initialized)
12703   {
12704     DBUG_PRINT("info", ("skip grants"));
12705     grant->privilege= ALL_KNOWN_ACL;             // everything is allowed
12706     DBUG_PRINT("info", ("privilege 0x%llx", (longlong) grant->privilege));
12707     DBUG_VOID_RETURN;
12708   }
12709 
12710   /* global privileges */
12711   grant->privilege= sctx->master_access;
12712 
12713   if (!thd->db.str || strcmp(db, thd->db.str))
12714   {
12715     /* db privileges */
12716     grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
12717     /* db privileges for role */
12718     if (sctx->priv_role[0])
12719       grant->privilege|= acl_get("", "", sctx->priv_role, db, 0);
12720   }
12721   else
12722   {
12723     grant->privilege|= sctx->db_access;
12724   }
12725 
12726   /* table privileges */
12727   mysql_rwlock_rdlock(&LOCK_grant);
12728   if (grant->version != grant_version)
12729   {
12730     grant->grant_table_user=
12731       table_hash_search(sctx->host, sctx->ip, db,
12732                         sctx->priv_user,
12733                         table, 0);              /* purecov: inspected */
12734     grant->grant_table_role=
12735       sctx->priv_role[0] ? table_hash_search("", "", db,
12736                                              sctx->priv_role,
12737                                              table, TRUE) : NULL;
12738     grant->version= grant_version;              /* purecov: inspected */
12739   }
12740   if (grant->grant_table_user != 0)
12741   {
12742     grant->privilege|= grant->grant_table_user->privs;
12743   }
12744   if (grant->grant_table_role != 0)
12745   {
12746     grant->privilege|= grant->grant_table_role->privs;
12747   }
12748   mysql_rwlock_unlock(&LOCK_grant);
12749 
12750   DBUG_PRINT("info", ("privilege 0x%llx", (longlong) grant->privilege));
12751   DBUG_VOID_RETURN;
12752 }
12753 
12754 #else /* NO_EMBEDDED_ACCESS_CHECKS */
12755 
12756 /****************************************************************************
12757  Dummy wrappers when we don't have any access checks
12758 ****************************************************************************/
12759 
check_routine_level_acl(THD * thd,const char * db,const char * name,const Sp_handler * sph)12760 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
12761                              const Sp_handler *sph)
12762 {
12763   return FALSE;
12764 }
12765 
12766 #endif
12767 
12768 /**
12769   Return information about user or current user.
12770 
12771   @param[in] thd          thread handler
12772   @param[in] user         user
12773   @param[in] lock         whether &acl_cache->lock mutex needs to be locked
12774 
12775   @return
12776     - On success, return a valid pointer to initialized
12777     LEX_USER, which contains user information.
12778     - On error, return 0.
12779 */
12780 
get_current_user(THD * thd,LEX_USER * user,bool lock)12781 LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock)
12782 {
12783   if (user->user.str == current_user.str)  // current_user
12784     return create_default_definer(thd, false);
12785 
12786   if (user->user.str == current_role.str)  // current_role
12787     return create_default_definer(thd, true);
12788 
12789   if (user->host.str == NULL) // Possibly a role
12790   {
12791     // to be reexecution friendly we have to make a copy
12792     LEX_USER *dup= (LEX_USER*) thd->memdup(user, sizeof(*user));
12793     if (!dup)
12794       return 0;
12795 
12796 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12797     if (has_auth(user, thd->lex))
12798     {
12799       dup->host= host_not_specified;
12800       return dup;
12801     }
12802 
12803     if (is_invalid_role_name(user->user.str))
12804       return 0;
12805 
12806     if (lock)
12807       mysql_mutex_lock(&acl_cache->lock);
12808     if (find_acl_role(dup->user.str))
12809       dup->host= empty_clex_str;
12810     else
12811       dup->host= host_not_specified;
12812     if (lock)
12813       mysql_mutex_unlock(&acl_cache->lock);
12814 #endif
12815 
12816     return dup;
12817   }
12818 
12819   return user;
12820 }
12821 
12822 struct ACL_internal_schema_registry_entry
12823 {
12824   const LEX_CSTRING *m_name;
12825   const ACL_internal_schema_access *m_access;
12826 };
12827 
12828 /**
12829   Internal schema registered.
12830   Currently, this is only:
12831   - performance_schema
12832   - information_schema,
12833   This can be reused later for:
12834   - mysql
12835 */
12836 static ACL_internal_schema_registry_entry registry_array[2];
12837 static uint m_registry_array_size= 0;
12838 
12839 /**
12840   Add an internal schema to the registry.
12841   @param name the schema name
12842   @param access the schema ACL specific rules
12843 */
register_schema(const LEX_CSTRING * name,const ACL_internal_schema_access * access)12844 void ACL_internal_schema_registry::register_schema
12845   (const LEX_CSTRING *name, const ACL_internal_schema_access *access)
12846 {
12847   DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
12848 
12849   /* Not thread safe, and does not need to be. */
12850   registry_array[m_registry_array_size].m_name= name;
12851   registry_array[m_registry_array_size].m_access= access;
12852   m_registry_array_size++;
12853 }
12854 
12855 /**
12856   Search per internal schema ACL by name.
12857   @param name a schema name
12858   @return per schema rules, or NULL
12859 */
12860 const ACL_internal_schema_access *
lookup(const char * name)12861 ACL_internal_schema_registry::lookup(const char *name)
12862 {
12863   DBUG_ASSERT(name != NULL);
12864 
12865   uint i;
12866 
12867   for (i= 0; i<m_registry_array_size; i++)
12868   {
12869     if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
12870                       name) == 0)
12871       return registry_array[i].m_access;
12872   }
12873   return NULL;
12874 }
12875 
12876 /**
12877   Get a cached internal schema access.
12878   @param grant_internal_info the cache
12879   @param schema_name the name of the internal schema
12880 */
12881 const ACL_internal_schema_access *
get_cached_schema_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name)12882 get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
12883                          const char *schema_name)
12884 {
12885   if (grant_internal_info)
12886   {
12887     if (! grant_internal_info->m_schema_lookup_done)
12888     {
12889       grant_internal_info->m_schema_access=
12890         ACL_internal_schema_registry::lookup(schema_name);
12891       grant_internal_info->m_schema_lookup_done= TRUE;
12892     }
12893     return grant_internal_info->m_schema_access;
12894   }
12895   return ACL_internal_schema_registry::lookup(schema_name);
12896 }
12897 
12898 /**
12899   Get a cached internal table access.
12900   @param grant_internal_info the cache
12901   @param schema_name the name of the internal schema
12902   @param table_name the name of the internal table
12903 */
12904 const ACL_internal_table_access *
get_cached_table_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name,const char * table_name)12905 get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
12906                         const char *schema_name,
12907                         const char *table_name)
12908 {
12909   DBUG_ASSERT(grant_internal_info);
12910   if (! grant_internal_info->m_table_lookup_done)
12911   {
12912     const ACL_internal_schema_access *schema_access;
12913     schema_access= get_cached_schema_access(grant_internal_info, schema_name);
12914     if (schema_access)
12915       grant_internal_info->m_table_access= schema_access->lookup(table_name);
12916     grant_internal_info->m_table_lookup_done= TRUE;
12917   }
12918   return grant_internal_info->m_table_access;
12919 }
12920 
12921 
12922 /****************************************************************************
12923    AUTHENTICATION CODE
12924    including initial connect handshake, invoking appropriate plugins,
12925    client-server plugin negotiation, COM_CHANGE_USER, and native
12926    MySQL authentication plugins.
12927 ****************************************************************************/
12928 
12929 /* few defines to have less ifdef's in the code below */
12930 #ifdef EMBEDDED_LIBRARY
12931 #undef HAVE_OPENSSL
12932 #ifdef NO_EMBEDDED_ACCESS_CHECKS
12933 #define initialized 0
12934 #define check_for_max_user_connections(X,Y)   0
12935 #define get_or_create_user_conn(A,B,C,D) 0
12936 #endif
12937 #endif
12938 #ifndef HAVE_OPENSSL
12939 #define ssl_acceptor_fd 0
12940 #define sslaccept(A,B,C,D) 1
12941 #endif
12942 
12943 /**
12944   The internal version of what plugins know as MYSQL_PLUGIN_VIO,
12945   basically the context of the authentication session
12946 */
12947 struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
12948 {
12949   MYSQL_SERVER_AUTH_INFO auth_info;
12950   ACL_USER *acl_user;       ///< a copy, independent from acl_users array
12951   plugin_ref plugin;        ///< what plugin we're under
12952   LEX_CSTRING db;           ///< db name from the handshake packet
12953   /** when restarting a plugin this caches the last client reply */
12954   struct {
12955     const char *plugin;
12956     char *pkt;              ///< pointer into NET::buff
12957     uint pkt_len;
12958   } cached_client_reply;
12959   /** this caches the first plugin packet for restart request on the client */
12960   struct {
12961     char *pkt;
12962     uint pkt_len;
12963   } cached_server_packet;
12964   uint curr_auth;                    ///< an index in acl_user->auth[]
12965   int packets_read, packets_written; ///< counters for send/received packets
12966   bool make_it_fail;
12967   /** when plugin returns a failure this tells us what really happened */
12968   enum { SUCCESS, FAILURE, RESTART } status;
12969 };
12970 
12971 /**
12972   a helper function to report an access denied error in most proper places
12973 */
login_failed_error(THD * thd)12974 static void login_failed_error(THD *thd)
12975 {
12976   my_error(access_denied_error_code(thd->password), MYF(0),
12977            thd->main_security_ctx.user,
12978            thd->main_security_ctx.host_or_ip,
12979            thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12980   general_log_print(thd, COM_CONNECT,
12981                     ER_THD(thd, access_denied_error_code(thd->password)),
12982                     thd->main_security_ctx.user,
12983                     thd->main_security_ctx.host_or_ip,
12984                     thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12985   status_var_increment(thd->status_var.access_denied_errors);
12986   /*
12987     Log access denied messages to the error log when log-warnings = 2
12988     so that the overhead of the general query log is not required to track
12989     failed connections.
12990   */
12991   if (global_system_variables.log_warnings > 1)
12992   {
12993     sql_print_warning(ER_THD(thd, access_denied_error_code(thd->password)),
12994                       thd->main_security_ctx.user,
12995                       thd->main_security_ctx.host_or_ip,
12996                       thd->password ? ER_THD(thd, ER_YES) : ER_THD(thd, ER_NO));
12997   }
12998 }
12999 
13000 /**
13001   sends a server handshake initialization packet, the very first packet
13002   after the connection was established
13003 
13004   Packet format:
13005 
13006     Bytes       Content
13007     -----       ----
13008     1           protocol version (always 10)
13009     n           server version string, \0-terminated
13010     4           thread id
13011     8           first 8 bytes of the plugin provided data (scramble)
13012     1           \0 byte, terminating the first part of a scramble
13013     2           server capabilities (two lower bytes)
13014     1           server character set
13015     2           server status
13016     2           server capabilities (two upper bytes)
13017     1           length of the scramble
13018     10          reserved, always 0
13019     n           rest of the plugin provided data (at least 12 bytes)
13020     1           \0 byte, terminating the second part of a scramble
13021 
13022   @retval 0 ok
13023   @retval 1 error
13024 */
send_server_handshake_packet(MPVIO_EXT * mpvio,const char * data,uint data_len)13025 static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
13026                                          const char *data, uint data_len)
13027 {
13028   DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
13029   DBUG_ASSERT(data_len <= 255);
13030 
13031   THD *thd= mpvio->auth_info.thd;
13032   char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + 1 + data_len + 64);
13033   char scramble_buf[SCRAMBLE_LENGTH];
13034   char *end= buff;
13035   DBUG_ENTER("send_server_handshake_packet");
13036 
13037   *end++= protocol_version;
13038 
13039   thd->client_capabilities= CLIENT_BASIC_FLAGS;
13040 
13041   if (opt_using_transactions)
13042     thd->client_capabilities|= CLIENT_TRANSACTIONS;
13043 
13044   thd->client_capabilities|= CAN_CLIENT_COMPRESS;
13045 
13046   if (ssl_acceptor_fd)
13047   {
13048     thd->client_capabilities |= CLIENT_SSL;
13049     thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
13050   }
13051 
13052   if (data_len)
13053   {
13054     mpvio->cached_server_packet.pkt= (char*)thd->memdup(data, data_len);
13055     mpvio->cached_server_packet.pkt_len= data_len;
13056   }
13057 
13058   if (data_len < SCRAMBLE_LENGTH)
13059   {
13060     if (data_len)
13061     {
13062       /*
13063         the first packet *must* have at least 20 bytes of a scramble.
13064         if a plugin provided less, we pad it to 20 with zeros
13065       */
13066       memcpy(scramble_buf, data, data_len);
13067       bzero(scramble_buf + data_len, SCRAMBLE_LENGTH - data_len);
13068       data= scramble_buf;
13069     }
13070     else
13071     {
13072       /*
13073         if the default plugin does not provide the data for the scramble at
13074         all, we generate a scramble internally anyway, just in case the
13075         user account (that will be known only later) uses a
13076         native_password_plugin (which needs a scramble). If we don't send a
13077         scramble now - wasting 20 bytes in the packet -
13078         native_password_plugin will have to send it in a separate packet,
13079         adding one more round trip.
13080       */
13081       thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
13082       data= thd->scramble;
13083     }
13084     data_len= SCRAMBLE_LENGTH;
13085   }
13086 
13087   /* When server version is specified in config file, don't include
13088      the replication hack prefix. */
13089   if (using_custom_server_version)
13090     end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
13091   else
13092     end= strxnmov(end, SERVER_VERSION_LENGTH, RPL_VERSION_HACK, server_version, NullS) + 1;
13093 
13094   int4store((uchar*) end, mpvio->auth_info.thd->thread_id);
13095   end+= 4;
13096 
13097   /*
13098     Old clients does not understand long scrambles, but can ignore packet
13099     tail: that's why first part of the scramble is placed here, and second
13100     part at the end of packet.
13101   */
13102   end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
13103   end+= SCRAMBLE_LENGTH_323;
13104   *end++= 0;
13105 
13106   int2store(end, thd->client_capabilities);
13107   /* write server characteristics: up to 16 bytes allowed */
13108   end[2]= (char) default_charset_info->number;
13109   int2store(end+3, mpvio->auth_info.thd->server_status);
13110   int2store(end+5, thd->client_capabilities >> 16);
13111   end[7]= data_len;
13112   DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
13113   DBUG_EXECUTE_IF("increase_srv_handshake_scramble_len", end[7]= 50;);
13114   bzero(end + 8, 6);
13115   int4store(end + 14, thd->client_capabilities >> 32);
13116   end+= 18;
13117   /* write scramble tail */
13118   end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
13119                       data_len - SCRAMBLE_LENGTH_323);
13120   end+= data_len - SCRAMBLE_LENGTH_323;
13121   end= strmake(end, plugin_name(mpvio->plugin)->str,
13122                     plugin_name(mpvio->plugin)->length);
13123 
13124   int res= my_net_write(&mpvio->auth_info.thd->net, (uchar*) buff,
13125                         (size_t) (end - buff + 1)) ||
13126            net_flush(&mpvio->auth_info.thd->net);
13127   my_afree(buff);
13128   DBUG_RETURN (res);
13129 }
13130 
secure_auth(THD * thd)13131 static bool secure_auth(THD *thd)
13132 {
13133   if (!opt_secure_auth)
13134     return 0;
13135 
13136   /*
13137     If the server is running in secure auth mode, short scrambles are
13138     forbidden. Extra juggling to report the same error as the old code.
13139   */
13140   if (thd->client_capabilities & CLIENT_PROTOCOL_41)
13141   {
13142     my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
13143              thd->security_ctx->user,
13144              thd->security_ctx->host_or_ip);
13145     general_log_print(thd, COM_CONNECT,
13146                       ER_THD(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE),
13147                       thd->security_ctx->user,
13148                       thd->security_ctx->host_or_ip);
13149   }
13150   else
13151   {
13152     my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
13153     general_log_print(thd, COM_CONNECT,
13154                       ER_THD(thd, ER_NOT_SUPPORTED_AUTH_MODE));
13155   }
13156   return 1;
13157 }
13158 
13159 /**
13160   sends a "change plugin" packet, requesting a client to restart authentication
13161   using a different authentication plugin
13162 
13163   Packet format:
13164 
13165     Bytes       Content
13166     -----       ----
13167     1           byte with the value 254
13168     n           client plugin to use, \0-terminated
13169     n           plugin provided data
13170 
13171   In a special case of switching from native_password_plugin to
13172   old_password_plugin, the packet contains only one - the first - byte,
13173   plugin name is omitted, plugin data aren't needed as the scramble was
13174   already sent. This one-byte packet is identical to the "use the short
13175   scramble" packet in the protocol before plugins were introduced.
13176 
13177   @retval 0 ok
13178   @retval 1 error
13179 */
send_plugin_request_packet(MPVIO_EXT * mpvio,const uchar * data,uint data_len)13180 static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
13181                                        const uchar *data, uint data_len)
13182 {
13183   NET *net= &mpvio->auth_info.thd->net;
13184   static uchar switch_plugin_request_buf[]= { 254 };
13185   DBUG_ENTER("send_plugin_request_packet");
13186 
13187   const char *client_auth_plugin=
13188     ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
13189 
13190   DBUG_EXECUTE_IF("auth_disconnect", { DBUG_RETURN(1); });
13191   DBUG_EXECUTE_IF("auth_invalid_plugin", client_auth_plugin="foo/bar"; );
13192   DBUG_ASSERT(client_auth_plugin);
13193 
13194   /*
13195     we send an old "short 4.0 scramble request", if we need to request a
13196     client to use 4.0 auth plugin (short scramble) and the scramble was
13197     already sent to the client
13198 
13199     below, cached_client_reply.plugin is the plugin name that client has used,
13200     client_auth_plugin is derived from mysql.user table, for the given
13201     user account, it's the plugin that the client need to use to login.
13202   */
13203   bool switch_from_long_to_short_scramble=
13204     client_auth_plugin == old_password_plugin_name.str &&
13205     my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
13206                   native_password_plugin_name.str) == 0;
13207 
13208   if (switch_from_long_to_short_scramble)
13209     DBUG_RETURN (secure_auth(mpvio->auth_info.thd) ||
13210                  my_net_write(net, switch_plugin_request_buf, 1) ||
13211                  net_flush(net));
13212 
13213   /*
13214     We never request a client to switch from a short to long scramble.
13215     Plugin-aware clients can do that, but traditionally it meant to
13216     ask an old 4.0 client to use the new 4.1 authentication protocol.
13217   */
13218   bool switch_from_short_to_long_scramble=
13219     client_auth_plugin == native_password_plugin_name.str &&
13220     my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
13221                   old_password_plugin_name.str) == 0;
13222 
13223   if (switch_from_short_to_long_scramble)
13224   {
13225     my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
13226     general_log_print(mpvio->auth_info.thd, COM_CONNECT,
13227                       ER_THD(mpvio->auth_info.thd, ER_NOT_SUPPORTED_AUTH_MODE));
13228     DBUG_RETURN (1);
13229   }
13230 
13231   DBUG_PRINT("info", ("requesting client to use the %s plugin",
13232                       client_auth_plugin));
13233   DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
13234                                 (uchar*) client_auth_plugin,
13235                                 strlen(client_auth_plugin) + 1,
13236                                 (uchar*) data, data_len));
13237 }
13238 
13239 #ifndef NO_EMBEDDED_ACCESS_CHECKS
13240 
13241 /**
13242   Safeguard to avoid blocking the root, when max_password_errors
13243   limit is reached.
13244 
13245   Currently, we allow password errors for superuser on localhost.
13246 
13247   @return true, if password errors should be ignored, and user should not be locked.
13248 */
ignore_max_password_errors(const ACL_USER * acl_user)13249 static bool ignore_max_password_errors(const ACL_USER *acl_user)
13250 {
13251  const char *host= acl_user->host.hostname;
13252  return (acl_user->access & PRIV_IGNORE_MAX_PASSWORD_ERRORS)
13253    && (!strcasecmp(host, "localhost") ||
13254        !strcmp(host, "127.0.0.1") ||
13255        !strcmp(host, "::1"));
13256 }
13257 /**
13258    Finds acl entry in user database for authentication purposes.
13259 
13260    Finds a user and copies it into mpvio. Creates a fake user
13261    if no matching user account is found.
13262 
13263    @retval 0    found
13264    @retval 1    error
13265 */
find_mpvio_user(MPVIO_EXT * mpvio)13266 static bool find_mpvio_user(MPVIO_EXT *mpvio)
13267 {
13268   Security_context *sctx= mpvio->auth_info.thd->security_ctx;
13269   DBUG_ENTER("find_mpvio_user");
13270   DBUG_ASSERT(mpvio->acl_user == 0);
13271 
13272   mysql_mutex_lock(&acl_cache->lock);
13273 
13274   ACL_USER *user= find_user_or_anon(sctx->host, sctx->user, sctx->ip);
13275 
13276   if (user)
13277     mpvio->acl_user= user->copy(mpvio->auth_info.thd->mem_root);
13278 
13279   mysql_mutex_unlock(&acl_cache->lock);
13280 
13281   if (!mpvio->acl_user)
13282   {
13283     /*
13284       A matching user was not found. Fake it. Take any user, make the
13285       authentication fail later.
13286       This way we get a realistically looking failure, with occasional
13287       "change auth plugin" requests even for nonexistent users. The ratio
13288       of "change auth plugin" request will be the same for real and
13289       nonexistent users.
13290       Note, that we cannot pick any user at random, it must always be
13291       the same user account for the incoming sctx->user name.
13292     */
13293     ulong nr1=1, nr2=4;
13294     CHARSET_INFO *cs= &my_charset_latin1;
13295     cs->hash_sort((uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2);
13296 
13297     mysql_mutex_lock(&acl_cache->lock);
13298     if (!acl_users.elements)
13299     {
13300       mysql_mutex_unlock(&acl_cache->lock);
13301       login_failed_error(mpvio->auth_info.thd);
13302       DBUG_RETURN(1);
13303     }
13304     uint i= nr1 % acl_users.elements;
13305     ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
13306     mpvio->acl_user= acl_user_tmp->copy(mpvio->auth_info.thd->mem_root);
13307     mysql_mutex_unlock(&acl_cache->lock);
13308 
13309     mpvio->make_it_fail= true;
13310   }
13311 
13312   if (mpvio->acl_user->password_errors >= max_password_errors &&
13313       !ignore_max_password_errors(mpvio->acl_user))
13314   {
13315     my_error(ER_USER_IS_BLOCKED, MYF(0));
13316     general_log_print(mpvio->auth_info.thd, COM_CONNECT,
13317       ER_THD(mpvio->auth_info.thd, ER_USER_IS_BLOCKED));
13318     DBUG_RETURN(1);
13319   }
13320 
13321   /* user account requires non-default plugin and the client is too old */
13322   if (mpvio->acl_user->auth->plugin.str != native_password_plugin_name.str &&
13323       mpvio->acl_user->auth->plugin.str != old_password_plugin_name.str &&
13324       !(mpvio->auth_info.thd->client_capabilities & CLIENT_PLUGIN_AUTH))
13325   {
13326     DBUG_ASSERT(my_strcasecmp(system_charset_info,
13327       mpvio->acl_user->auth->plugin.str, native_password_plugin_name.str));
13328     DBUG_ASSERT(my_strcasecmp(system_charset_info,
13329       mpvio->acl_user->auth->plugin.str, old_password_plugin_name.str));
13330     my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
13331     general_log_print(mpvio->auth_info.thd, COM_CONNECT,
13332                       ER_THD(mpvio->auth_info.thd, ER_NOT_SUPPORTED_AUTH_MODE));
13333     DBUG_RETURN (1);
13334   }
13335   DBUG_RETURN(0);
13336 }
13337 
13338 static bool
read_client_connect_attrs(char ** ptr,char * end,CHARSET_INFO * from_cs)13339 read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs)
13340 {
13341   ulonglong length;
13342   char *ptr_save= *ptr;
13343 
13344   /* not enough bytes to hold the length */
13345   if (ptr_save >= end)
13346     return true;
13347 
13348   length= safe_net_field_length_ll((uchar **) ptr, end - ptr_save);
13349 
13350   /* cannot even read the length */
13351   if (*ptr == NULL)
13352     return true;
13353 
13354   /* length says there're more data than can fit into the packet */
13355   if (*ptr + length > end)
13356     return true;
13357 
13358   /* impose an artificial length limit of 64k */
13359   if (length > 65535)
13360     return true;
13361 
13362   if (PSI_CALL_set_thread_connect_attrs(*ptr, (uint)length, from_cs) &&
13363       current_thd->variables.log_warnings)
13364     sql_print_warning("Connection attributes of length %llu were truncated",
13365                       length);
13366   return false;
13367 }
13368 
13369 #endif
13370 
13371 /* the packet format is described in send_change_user_packet() */
parse_com_change_user_packet(MPVIO_EXT * mpvio,uint packet_length)13372 static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
13373 {
13374   THD *thd= mpvio->auth_info.thd;
13375   NET *net= &thd->net;
13376   Security_context *sctx= thd->security_ctx;
13377 
13378   char *user= (char*) net->read_pos;
13379   char *end= user + packet_length;
13380   /* Safe because there is always a trailing \0 at the end of the packet */
13381   char *passwd= strend(user) + 1;
13382   uint user_len= (uint)(passwd - user - 1);
13383   char *db= passwd;
13384   char db_buff[SAFE_NAME_LEN + 1];            // buffer to store db in utf8
13385   char user_buff[USERNAME_LENGTH + 1];	      // buffer to store user in utf8
13386   uint dummy_errors;
13387   DBUG_ENTER ("parse_com_change_user_packet");
13388 
13389   if (passwd >= end)
13390   {
13391     my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
13392                MYF(0));
13393     DBUG_RETURN (1);
13394   }
13395 
13396   /*
13397     Old clients send null-terminated string as password; new clients send
13398     the size (1 byte) + string (not null-terminated). Hence in case of empty
13399     password both send '\0'.
13400 
13401     This strlen() can't be easily deleted without changing protocol.
13402 
13403     Cast *passwd to an unsigned char, so that it doesn't extend the sign for
13404     *passwd > 127 and become 2**32-127+ after casting to uint.
13405   */
13406   uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
13407                     (uchar) (*passwd++) : (uint)strlen(passwd));
13408 
13409   db+= passwd_len + 1;
13410   /*
13411     Database name is always NUL-terminated, so in case of empty database
13412     the packet must contain at least the trailing '\0'.
13413   */
13414   if (db >= end)
13415   {
13416     my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
13417                MYF(0));
13418     DBUG_RETURN (1);
13419   }
13420 
13421   size_t db_len= strlen(db);
13422 
13423   char *next_field= db + db_len + 1;
13424 
13425   if (next_field + 1 < end)
13426   {
13427     if (thd_init_client_charset(thd, uint2korr(next_field)))
13428       DBUG_RETURN(1);
13429     next_field+= 2;
13430   }
13431 
13432   /* Convert database and user names to utf8 */
13433   db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
13434                            db, db_len, thd->charset(), &dummy_errors);
13435 
13436   user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
13437                              system_charset_info, user, user_len,
13438                              thd->charset(), &dummy_errors);
13439 
13440   if (!(sctx->user= my_strndup(key_memory_MPVIO_EXT_auth_info, user_buff,
13441                                user_len, MYF(MY_WME))))
13442     DBUG_RETURN(1);
13443 
13444   /* Clear variables that are allocated */
13445   thd->user_connect= 0;
13446   strmake_buf(sctx->priv_user, sctx->user);
13447 
13448   if (thd->make_lex_string(&mpvio->db, db_buff, db_len) == 0)
13449     DBUG_RETURN(1); /* The error is set by make_lex_string(). */
13450 
13451   /*
13452     Clear thd->db as it points to something, that will be freed when
13453     connection is closed. We don't want to accidentally free a wrong
13454     pointer if connect failed.
13455   */
13456   thd->reset_db(&null_clex_str);
13457 
13458   if (!initialized)
13459   {
13460     // if mysqld's been started with --skip-grant-tables option
13461     mpvio->status= MPVIO_EXT::SUCCESS;
13462     DBUG_RETURN(0);
13463   }
13464 
13465 #ifndef NO_EMBEDDED_ACCESS_CHECKS
13466   thd->password= passwd_len > 0;
13467   if (find_mpvio_user(mpvio))
13468     DBUG_RETURN(1);
13469 
13470   const char *client_plugin;
13471   if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
13472   {
13473     if (next_field >= end)
13474     {
13475       my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
13476                  MYF(0));
13477       DBUG_RETURN(1);
13478     }
13479     client_plugin= next_field;
13480     next_field+= strlen(next_field) + 1;
13481   }
13482   else
13483   {
13484     if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
13485       client_plugin= native_password_plugin_name.str;
13486     else
13487     {
13488       /*
13489         Normally old clients use old_password_plugin, but for
13490         a passwordless accounts we use native_password_plugin.
13491         See guess_auth_plugin().
13492       */
13493       client_plugin= passwd_len ? old_password_plugin_name.str
13494                                 : native_password_plugin_name.str;
13495     }
13496   }
13497 
13498   if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
13499       read_client_connect_attrs(&next_field, end, thd->charset()))
13500   {
13501     my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR),
13502                MYF(0));
13503     DBUG_RETURN(1);
13504   }
13505 
13506   DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
13507   /*
13508     Remember the data part of the packet, to present it to plugin in
13509     read_packet()
13510   */
13511   mpvio->cached_client_reply.pkt= passwd;
13512   mpvio->cached_client_reply.pkt_len= passwd_len;
13513   mpvio->cached_client_reply.plugin= client_plugin;
13514   mpvio->status= MPVIO_EXT::RESTART;
13515 #endif
13516 
13517   DBUG_RETURN (0);
13518 }
13519 
13520 
13521 /* the packet format is described in send_client_reply_packet() */
parse_client_handshake_packet(MPVIO_EXT * mpvio,uchar ** buff,ulong pkt_len)13522 static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
13523                                            uchar **buff, ulong pkt_len)
13524 {
13525 #ifndef EMBEDDED_LIBRARY
13526   THD *thd= mpvio->auth_info.thd;
13527   NET *net= &thd->net;
13528   char *end;
13529   DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
13530 
13531   if (pkt_len < MIN_HANDSHAKE_SIZE)
13532     return packet_error;
13533 
13534   /*
13535     Protocol buffer is guaranteed to always end with \0. (see my_net_read())
13536     As the code below depends on this, lets check that.
13537   */
13538   DBUG_ASSERT(net->read_pos[pkt_len] == 0);
13539 
13540   ulonglong client_capabilities= uint2korr(net->read_pos);
13541   compile_time_assert(sizeof(client_capabilities) >= 8);
13542   if (client_capabilities & CLIENT_PROTOCOL_41)
13543   {
13544     if (pkt_len < 32)
13545       return packet_error;
13546     client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
13547     if (!(client_capabilities & CLIENT_MYSQL))
13548     {
13549       // it is client with mariadb extensions
13550       ulonglong ext_client_capabilities=
13551         (((ulonglong)uint4korr(net->read_pos + 28)) << 32);
13552       client_capabilities|= ext_client_capabilities;
13553     }
13554   }
13555 
13556   /* Disable those bits which are not supported by the client. */
13557   compile_time_assert(sizeof(thd->client_capabilities) >= 8);
13558   thd->client_capabilities&= client_capabilities;
13559 
13560   DBUG_PRINT("info", ("client capabilities: %llu", thd->client_capabilities));
13561   if (thd->client_capabilities & CLIENT_SSL)
13562   {
13563     unsigned long errptr __attribute__((unused));
13564 
13565     /* Do the SSL layering. */
13566     if (!ssl_acceptor_fd)
13567       return packet_error;
13568 
13569     DBUG_PRINT("info", ("IO layer change in progress..."));
13570     mysql_rwlock_rdlock(&LOCK_ssl_refresh);
13571     int ssl_ret = sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr);
13572     mysql_rwlock_unlock(&LOCK_ssl_refresh);
13573     ssl_acceptor_stats_update(ssl_ret);
13574 
13575     if(ssl_ret)
13576     {
13577       DBUG_PRINT("error", ("Failed to accept new SSL connection"));
13578       return packet_error;
13579     }
13580 
13581     DBUG_PRINT("info", ("Reading user information over SSL layer"));
13582     pkt_len= my_net_read(net);
13583     if (unlikely(pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE))
13584     {
13585       DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
13586 			   pkt_len));
13587       return packet_error;
13588     }
13589   }
13590 
13591   if (client_capabilities & CLIENT_PROTOCOL_41)
13592   {
13593     thd->max_client_packet_length= uint4korr(net->read_pos+4);
13594     DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
13595     if (thd_init_client_charset(thd, (uint) net->read_pos[8]))
13596       return packet_error;
13597     end= (char*) net->read_pos+32;
13598   }
13599   else
13600   {
13601     if (pkt_len < 5)
13602       return packet_error;
13603     thd->max_client_packet_length= uint3korr(net->read_pos+2);
13604     end= (char*) net->read_pos+5;
13605   }
13606 
13607   if (end >= (char*) net->read_pos+ pkt_len +2)
13608     return packet_error;
13609 
13610   if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
13611     thd->variables.sql_mode|= MODE_IGNORE_SPACE;
13612   if (thd->client_capabilities & CLIENT_INTERACTIVE)
13613     thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
13614 
13615   if (end >= (char*) net->read_pos+ pkt_len +2)
13616     return packet_error;
13617 
13618   if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
13619       opt_using_transactions)
13620     net->return_status= &thd->server_status;
13621 
13622   char *user= end;
13623   char *passwd= strend(user)+1;
13624   size_t user_len= (size_t)(passwd - user - 1), db_len;
13625   char *db= passwd;
13626   char user_buff[USERNAME_LENGTH + 1];	// buffer to store user in utf8
13627   uint dummy_errors;
13628 
13629   /*
13630     Old clients send null-terminated string as password; new clients send
13631     the size (1 byte) + string (not null-terminated). Hence in case of empty
13632     password both send '\0'.
13633 
13634     This strlen() can't be easily deleted without changing protocol.
13635 
13636     Cast *passwd to an unsigned char, so that it doesn't extend the sign for
13637     *passwd > 127 and become 2**32-127+ after casting to uint.
13638   */
13639   ulonglong len;
13640   size_t passwd_len;
13641 
13642   if (!(thd->client_capabilities & CLIENT_SECURE_CONNECTION))
13643     len= strlen(passwd);
13644   else if (!(thd->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA))
13645     len= (uchar)(*passwd++);
13646   else
13647   {
13648     len= safe_net_field_length_ll((uchar**)&passwd,
13649                                       net->read_pos + pkt_len - (uchar*)passwd);
13650     if (len > pkt_len)
13651       return packet_error;
13652   }
13653 
13654   passwd_len= (size_t)len;
13655   db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
13656     db + passwd_len + 1 : 0;
13657 
13658   if (passwd == NULL ||
13659       passwd + passwd_len + MY_TEST(db) > (char*) net->read_pos + pkt_len)
13660     return packet_error;
13661 
13662   /* strlen() can't be easily deleted without changing protocol */
13663   db_len= safe_strlen(db);
13664 
13665   char *next_field;
13666   const char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0);
13667 
13668   /*
13669     Since 4.1 all database names are stored in utf8
13670     The cast is ok as copy_with_error will create a new area for db
13671   */
13672   if (unlikely(thd->copy_with_error(system_charset_info,
13673                                     (LEX_STRING*) &mpvio->db,
13674                                     thd->charset(), db, db_len)))
13675     return packet_error;
13676 
13677   user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
13678                              system_charset_info, user, user_len,
13679                              thd->charset(), &dummy_errors);
13680   user= user_buff;
13681 
13682   /* If username starts and ends in "'", chop them off */
13683   if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
13684   {
13685     user++;
13686     user_len-= 2;
13687   }
13688 
13689   /*
13690     Clip username to allowed length in characters (not bytes).  This is
13691     mostly for backward compatibility (to truncate long usernames, as
13692     old 5.1 did)
13693   */
13694   user_len= Well_formed_prefix(system_charset_info, user, user_len,
13695                                username_char_length).length();
13696   user[user_len]= '\0';
13697 
13698   Security_context *sctx= thd->security_ctx;
13699 
13700   my_free(const_cast<char*>(sctx->user));
13701   if (!(sctx->user= my_strndup(key_memory_MPVIO_EXT_auth_info, user, user_len, MYF(MY_WME))))
13702     return packet_error; /* The error is set by my_strdup(). */
13703 
13704 
13705   /*
13706     Clear thd->db as it points to something, that will be freed when
13707     connection is closed. We don't want to accidentally free a wrong
13708     pointer if connect failed.
13709   */
13710   thd->reset_db(&null_clex_str);
13711 
13712   if (!initialized)
13713   {
13714     // if mysqld's been started with --skip-grant-tables option
13715     mpvio->status= MPVIO_EXT::SUCCESS;
13716     return packet_error;
13717   }
13718 
13719   thd->password= passwd_len > 0;
13720   if (find_mpvio_user(mpvio))
13721     return packet_error;
13722 
13723   if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) &&
13724       (client_plugin < (char *)net->read_pos + pkt_len))
13725   {
13726     next_field+= strlen(next_field) + 1;
13727   }
13728   else
13729   {
13730     /* Some clients lie. Sad, but true */
13731     thd->client_capabilities &= ~CLIENT_PLUGIN_AUTH;
13732 
13733     if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
13734       client_plugin= native_password_plugin_name.str;
13735     else
13736     {
13737       /*
13738         Normally old clients use old_password_plugin, but for
13739         a passwordless accounts we use native_password_plugin.
13740         See guess_auth_plugin().
13741       */
13742       client_plugin= passwd_len ? old_password_plugin_name.str
13743                                 : native_password_plugin_name.str;
13744     }
13745   }
13746 
13747   if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
13748       read_client_connect_attrs(&next_field, ((char *)net->read_pos) + pkt_len,
13749                                 mpvio->auth_info.thd->charset()))
13750     return packet_error;
13751 
13752   /*
13753     if the acl_user needs a different plugin to authenticate
13754     (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
13755     we need to restart the authentication in the server.
13756     But perhaps the client has already used the correct plugin -
13757     in that case the authentication on the client may not need to be
13758     restarted and a server auth plugin will read the data that the client
13759     has just send. Cache them to return in the next server_mpvio_read_packet().
13760   */
13761   if (!lex_string_eq(&mpvio->acl_user->auth->plugin, plugin_name(mpvio->plugin)))
13762   {
13763     mpvio->cached_client_reply.pkt= passwd;
13764     mpvio->cached_client_reply.pkt_len= (uint)passwd_len;
13765     mpvio->cached_client_reply.plugin= client_plugin;
13766     mpvio->status= MPVIO_EXT::RESTART;
13767     return packet_error;
13768   }
13769 
13770   /*
13771     ok, we don't need to restart the authentication on the server.
13772     but if the client used the wrong plugin, we need to restart
13773     the authentication on the client. Do it here, the server plugin
13774     doesn't need to know.
13775   */
13776   const char *client_auth_plugin=
13777     ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
13778 
13779   if (client_auth_plugin &&
13780       my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
13781   {
13782     mpvio->cached_client_reply.plugin= client_plugin;
13783     if (send_plugin_request_packet(mpvio,
13784                                    (uchar*) mpvio->cached_server_packet.pkt,
13785                                    mpvio->cached_server_packet.pkt_len))
13786       return packet_error;
13787 
13788     passwd_len= my_net_read(&thd->net);
13789     passwd= (char*)thd->net.read_pos;
13790   }
13791 
13792   *buff= (uchar*) passwd;
13793   return (ulong)passwd_len;
13794 #else
13795   return 0;
13796 #endif
13797 }
13798 
13799 
13800 /**
13801   vio->write_packet() callback method for server authentication plugins
13802 
13803   This function is called by a server authentication plugin, when it wants
13804   to send data to the client.
13805 
13806   It transparently wraps the data into a handshake packet,
13807   and handles plugin negotiation with the client. If necessary,
13808   it escapes the plugin data, if it starts with a mysql protocol packet byte.
13809 */
server_mpvio_write_packet(MYSQL_PLUGIN_VIO * param,const uchar * packet,int packet_len)13810 static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
13811                                    const uchar *packet, int packet_len)
13812 {
13813   MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
13814   int res;
13815   DBUG_ENTER("server_mpvio_write_packet");
13816 
13817   /* reset cached_client_reply */
13818   mpvio->cached_client_reply.pkt= 0;
13819 
13820   /* for the 1st packet we wrap plugin data into the handshake packet */
13821   if (mpvio->packets_written == 0)
13822     res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
13823   else if (mpvio->status == MPVIO_EXT::RESTART)
13824     res= send_plugin_request_packet(mpvio, packet, packet_len);
13825   else if (packet_len > 0 && (*packet == 1 || *packet == 255 || *packet == 254))
13826   {
13827     /*
13828       we cannot allow plugin data packet to start from 255 or 254 -
13829       as the client will treat it as an error or "change plugin" packet.
13830       We'll escape these bytes with \1. Consequently, we
13831       have to escape \1 byte too.
13832     */
13833     res= net_write_command(&mpvio->auth_info.thd->net, 1, (uchar*)"", 0,
13834                            packet, packet_len);
13835   }
13836   else
13837   {
13838     res= my_net_write(&mpvio->auth_info.thd->net, packet, packet_len) ||
13839          net_flush(&mpvio->auth_info.thd->net);
13840   }
13841   mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
13842   mpvio->packets_written++;
13843   DBUG_RETURN(res);
13844 }
13845 
13846 /**
13847   vio->read_packet() callback method for server authentication plugins
13848 
13849   This function is called by a server authentication plugin, when it wants
13850   to read data from the client.
13851 
13852   It transparently extracts the client plugin data, if embedded into
13853   a client authentication handshake packet, and handles plugin negotiation
13854   with the client, if necessary.
13855 */
server_mpvio_read_packet(MYSQL_PLUGIN_VIO * param,uchar ** buf)13856 static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
13857 {
13858   MPVIO_EXT * const mpvio= (MPVIO_EXT *) param;
13859   MYSQL_SERVER_AUTH_INFO * const ai= &mpvio->auth_info;
13860   ulong pkt_len;
13861   DBUG_ENTER("server_mpvio_read_packet");
13862   if (mpvio->status == MPVIO_EXT::RESTART)
13863   {
13864     const char *client_auth_plugin=
13865       ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
13866     if (client_auth_plugin == 0)
13867     {
13868       mpvio->status= MPVIO_EXT::FAILURE;
13869       pkt_len= 0;
13870       *buf= 0;
13871       goto done;
13872     }
13873 
13874     if (mpvio->cached_client_reply.pkt)
13875     {
13876       DBUG_ASSERT(mpvio->packets_read > 0);
13877       /*
13878         if the have the data cached from the last server_mpvio_read_packet
13879         (which can be the case if it's a restarted authentication)
13880         and a client has used the correct plugin, then we can return the
13881         cached data straight away and avoid one round trip.
13882       */
13883       if (my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
13884                         client_auth_plugin) == 0)
13885       {
13886         mpvio->status= MPVIO_EXT::FAILURE;
13887         pkt_len= mpvio->cached_client_reply.pkt_len;
13888         *buf= (uchar*) mpvio->cached_client_reply.pkt;
13889         mpvio->packets_read++;
13890         goto done;
13891       }
13892     }
13893 
13894     /*
13895       plugin wants to read the data without sending anything first.
13896       send an empty packet to force a server handshake packet to be sent
13897     */
13898     if (server_mpvio_write_packet(mpvio, 0, 0))
13899       pkt_len= packet_error;
13900     else
13901       pkt_len= my_net_read(&ai->thd->net);
13902   }
13903   else
13904     pkt_len= my_net_read(&ai->thd->net);
13905 
13906   if (unlikely(pkt_len == packet_error))
13907     goto err;
13908 
13909   mpvio->packets_read++;
13910 
13911   /*
13912     the 1st packet has the plugin data wrapped into the client authentication
13913     handshake packet
13914   */
13915   if (mpvio->packets_read == 1)
13916   {
13917     pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
13918     if (unlikely(pkt_len == packet_error))
13919       goto err;
13920   }
13921   else
13922     *buf= ai->thd->net.read_pos;
13923 
13924 done:
13925   if (set_user_salt_if_needed(mpvio->acl_user, mpvio->curr_auth, mpvio->plugin))
13926   {
13927     ai->thd->clear_error(); // authenticating user should not see these errors
13928     my_error(ER_ACCESS_DENIED_ERROR, MYF(0), ai->thd->security_ctx->user,
13929              ai->thd->security_ctx->host_or_ip, ER_THD(ai->thd, ER_YES));
13930     goto err;
13931   }
13932 
13933   ai->user_name= ai->thd->security_ctx->user;
13934   ai->user_name_length= (uint) strlen(ai->user_name);
13935   ai->auth_string= mpvio->acl_user->auth[mpvio->curr_auth].salt.str;
13936   ai->auth_string_length= (ulong) mpvio->acl_user->auth[mpvio->curr_auth].salt.length;
13937   strmake_buf(ai->authenticated_as, mpvio->acl_user->user.str);
13938 
13939   DBUG_RETURN((int)pkt_len);
13940 
13941 err:
13942   if (mpvio->status == MPVIO_EXT::FAILURE)
13943   {
13944     if (!ai->thd->is_error())
13945       my_error(ER_HANDSHAKE_ERROR, MYF(0));
13946   }
13947   DBUG_RETURN(-1);
13948 }
13949 
13950 /**
13951   fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
13952   connection
13953 */
server_mpvio_info(MYSQL_PLUGIN_VIO * vio,MYSQL_PLUGIN_VIO_INFO * info)13954 static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
13955                               MYSQL_PLUGIN_VIO_INFO *info)
13956 {
13957   MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
13958   mpvio_info(mpvio->auth_info.thd->net.vio, info);
13959 }
13960 
acl_check_ssl(THD * thd,const ACL_USER * acl_user)13961 static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
13962 {
13963   Vio *vio= thd->net.vio;
13964 #ifdef HAVE_OPENSSL
13965   SSL *ssl= (SSL *) vio->ssl_arg;
13966   X509 *cert;
13967 #endif
13968 
13969   /*
13970     At this point we know that user is allowed to connect
13971     from given host by given username/password pair. Now
13972     we check if SSL is required, if user is using SSL and
13973     if X509 certificate attributes are OK
13974   */
13975   switch (acl_user->ssl_type) {
13976   case SSL_TYPE_NOT_SPECIFIED:                  // Impossible
13977   case SSL_TYPE_NONE:                           // SSL is not required
13978     if (opt_require_secure_transport)
13979     {
13980       enum enum_vio_type type= vio_type(vio);
13981 #ifdef HAVE_OPENSSL
13982       return type != VIO_TYPE_SSL &&
13983 #ifndef _WIN32
13984              type != VIO_TYPE_SOCKET;
13985 #else
13986              type != VIO_TYPE_NAMEDPIPE;
13987 #endif
13988 #else
13989 #ifndef _WIN32
13990       return type != VIO_TYPE_SOCKET;
13991 #else
13992       return type != VIO_TYPE_NAMEDPIPE;
13993 #endif
13994 #endif
13995     }
13996     return 0;
13997 #ifdef HAVE_OPENSSL
13998   case SSL_TYPE_ANY:                            // Any kind of SSL is ok
13999     return vio_type(vio) != VIO_TYPE_SSL;
14000   case SSL_TYPE_X509: /* Client should have any valid certificate. */
14001     /*
14002       Connections with non-valid certificates are dropped already
14003       in sslaccept() anyway, so we do not check validity here.
14004 
14005       We need to check for absence of SSL because without SSL
14006       we should reject connection.
14007     */
14008     if (vio_type(vio) == VIO_TYPE_SSL &&
14009         SSL_get_verify_result(ssl) == X509_V_OK &&
14010         (cert= SSL_get_peer_certificate(ssl)))
14011     {
14012       X509_free(cert);
14013       return 0;
14014     }
14015     return 1;
14016   case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
14017     /* If a cipher name is specified, we compare it to actual cipher in use. */
14018     if (vio_type(vio) != VIO_TYPE_SSL ||
14019         SSL_get_verify_result(ssl) != X509_V_OK)
14020       return 1;
14021     if (acl_user->ssl_cipher)
14022     {
14023       const char *ssl_cipher= SSL_get_cipher(ssl);
14024       DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
14025                          acl_user->ssl_cipher, ssl_cipher));
14026       if (strcmp(acl_user->ssl_cipher, ssl_cipher))
14027       {
14028         if (global_system_variables.log_warnings)
14029           sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
14030                             acl_user->ssl_cipher, ssl_cipher);
14031         return 1;
14032       }
14033     }
14034     if (!acl_user->x509_issuer[0] && !acl_user->x509_subject[0])
14035       return 0; // all done
14036 
14037     /* Prepare certificate (if exists) */
14038     if (!(cert= SSL_get_peer_certificate(ssl)))
14039       return 1;
14040     /* If X509 issuer is specified, we check it... */
14041     if (acl_user->x509_issuer[0])
14042     {
14043       char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
14044       DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
14045                          acl_user->x509_issuer, ptr));
14046       if (strcmp(acl_user->x509_issuer, ptr))
14047       {
14048         if (global_system_variables.log_warnings)
14049           sql_print_information("X509 issuer mismatch: should be '%s' "
14050                             "but is '%s'", acl_user->x509_issuer, ptr);
14051         free(ptr);
14052         X509_free(cert);
14053         return 1;
14054       }
14055       free(ptr);
14056     }
14057     /* X509 subject is specified, we check it .. */
14058     if (acl_user->x509_subject[0])
14059     {
14060       char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
14061       DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
14062                          acl_user->x509_subject, ptr));
14063       if (strcmp(acl_user->x509_subject, ptr))
14064       {
14065         if (global_system_variables.log_warnings)
14066           sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
14067                           acl_user->x509_subject, ptr);
14068         free(ptr);
14069         X509_free(cert);
14070         return 1;
14071       }
14072       free(ptr);
14073     }
14074     X509_free(cert);
14075     return 0;
14076 #else  /* HAVE_OPENSSL */
14077   default:
14078     /*
14079       If we don't have SSL but SSL is required for this user the
14080       authentication should fail.
14081     */
14082     return 1;
14083 #endif /* HAVE_OPENSSL */
14084   }
14085   return 1;
14086 }
14087 
14088 
do_auth_once(THD * thd,const LEX_CSTRING * auth_plugin_name,MPVIO_EXT * mpvio)14089 static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name,
14090                         MPVIO_EXT *mpvio)
14091 {
14092   int res= CR_OK;
14093   bool unlock_plugin= false;
14094   plugin_ref plugin= get_auth_plugin(thd, *auth_plugin_name, &unlock_plugin);
14095 
14096   mpvio->plugin= plugin;
14097   mpvio->auth_info.user_name= NULL;
14098 
14099   if (plugin)
14100   {
14101     st_mysql_auth *info= (st_mysql_auth *) plugin_decl(plugin)->info;
14102     switch (info->interface_version >> 8) {
14103     case 0x02:
14104       res= info->authenticate_user(mpvio, &mpvio->auth_info);
14105       break;
14106     case 0x01:
14107       {
14108         MYSQL_SERVER_AUTH_INFO_0x0100 compat;
14109         compat.downgrade(&mpvio->auth_info);
14110         res= info->authenticate_user(mpvio, (MYSQL_SERVER_AUTH_INFO *)&compat);
14111         compat.upgrade(&mpvio->auth_info);
14112       }
14113       break;
14114     default: DBUG_ASSERT(0);
14115     }
14116 
14117     if (unlock_plugin)
14118       plugin_unlock(thd, plugin);
14119   }
14120   else
14121   {
14122     /* Server cannot load the required plugin. */
14123     Host_errors errors;
14124     errors.m_no_auth_plugin= 1;
14125     inc_host_errors(mpvio->auth_info.thd->security_ctx->ip, &errors);
14126     my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
14127     res= CR_ERROR;
14128   }
14129 
14130   return res;
14131 }
14132 
14133 enum PASSWD_ERROR_ACTION
14134 {
14135   PASSWD_ERROR_CLEAR,
14136   PASSWD_ERROR_INCREMENT
14137 };
14138 
14139 /* Increment, or clear password errors for a user. */
handle_password_errors(const char * user,const char * hostname,PASSWD_ERROR_ACTION action)14140 static void handle_password_errors(const char *user, const char *hostname, PASSWD_ERROR_ACTION action)
14141 {
14142 #ifndef NO_EMBEDDED_ACCESS_CHECKS
14143   mysql_mutex_assert_not_owner(&acl_cache->lock);
14144   mysql_mutex_lock(&acl_cache->lock);
14145   ACL_USER *u = find_user_exact(hostname, user);
14146   if (u)
14147   {
14148     switch(action)
14149     {
14150       case PASSWD_ERROR_INCREMENT:
14151         u->password_errors++;
14152         break;
14153       case PASSWD_ERROR_CLEAR:
14154         u->password_errors= 0;
14155         break;
14156       default:
14157         DBUG_ASSERT(0);
14158         break;
14159     }
14160   }
14161   mysql_mutex_unlock(&acl_cache->lock);
14162 #endif
14163 }
14164 
check_password_lifetime(THD * thd,const ACL_USER & acl_user)14165 static bool check_password_lifetime(THD *thd, const ACL_USER &acl_user)
14166 {
14167   /* the password should never expire */
14168   if (!acl_user.password_lifetime)
14169     return false;
14170 
14171   longlong interval= acl_user.password_lifetime;
14172   if (interval < 0)
14173   {
14174     interval= default_password_lifetime;
14175 
14176     /* default global policy applies, and that is password never expires */
14177     if (!interval)
14178       return false;
14179   }
14180 
14181   thd->set_time();
14182 
14183   if ((thd->query_start() - acl_user.password_last_changed)/3600/24 >= interval)
14184     return true;
14185 
14186   return false;
14187 }
14188 
14189 /**
14190   Perform the handshake, authorize the client and update thd sctx variables.
14191 
14192   @param thd                     thread handle
14193   @param com_change_user_pkt_len size of the COM_CHANGE_USER packet
14194                                  (without the first, command, byte) or 0
14195                                  if it's not a COM_CHANGE_USER (that is, if
14196                                  it's a new connection)
14197 
14198   @retval 0  success, thd is updated.
14199   @retval 1  error
14200 */
acl_authenticate(THD * thd,uint com_change_user_pkt_len)14201 bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
14202 {
14203   int res= CR_OK;
14204   MPVIO_EXT mpvio;
14205   enum  enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
14206                                                              : COM_CONNECT;
14207   DBUG_ENTER("acl_authenticate");
14208 
14209   bzero(&mpvio, sizeof(mpvio));
14210   mpvio.read_packet= server_mpvio_read_packet;
14211   mpvio.write_packet= server_mpvio_write_packet;
14212   mpvio.cached_client_reply.plugin= "";
14213   mpvio.info= server_mpvio_info;
14214   mpvio.status= MPVIO_EXT::RESTART;
14215   mpvio.auth_info.thd= thd;
14216   mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
14217   mpvio.auth_info.host_or_ip_length=
14218     (unsigned int) strlen(thd->security_ctx->host_or_ip);
14219 
14220   DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
14221 
14222   if (command == COM_CHANGE_USER)
14223   {
14224     mpvio.packets_written++; // pretend that a server handshake packet was sent
14225     mpvio.packets_read++;    // take COM_CHANGE_USER packet into account
14226 
14227     if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
14228       DBUG_RETURN(1);
14229 
14230     res= mpvio.status ==  MPVIO_EXT::SUCCESS ? CR_OK : CR_ERROR;
14231 
14232     DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
14233                 mpvio.status == MPVIO_EXT::SUCCESS);
14234   }
14235   else
14236   {
14237     /* mark the thd as having no scramble yet */
14238     thd->scramble[SCRAMBLE_LENGTH]= 1;
14239 
14240     /*
14241       perform the first authentication attempt, with the default plugin.
14242       This sends the server handshake packet, reads the client reply
14243       with a user name, and performs the authentication if everyone has used
14244       the correct plugin.
14245     */
14246 
14247     res= do_auth_once(thd, default_auth_plugin_name, &mpvio);
14248   }
14249 
14250   PSI_CALL_set_connection_type(vio_type(thd->net.vio));
14251 
14252   Security_context * const sctx= thd->security_ctx;
14253   const ACL_USER * acl_user= mpvio.acl_user;
14254   if (!acl_user)
14255     statistic_increment(aborted_connects_preauth, &LOCK_status);
14256 
14257   if (acl_user)
14258   {
14259     /*
14260       retry the authentication with curr_auth==0 if after receiving the user
14261       name we found that we need to switch to a non-default plugin
14262     */
14263     for (mpvio.curr_auth= mpvio.status != MPVIO_EXT::RESTART;
14264          res != CR_OK && mpvio.curr_auth < acl_user->nauth;
14265          mpvio.curr_auth++)
14266     {
14267       thd->clear_error();
14268       mpvio.status= MPVIO_EXT::RESTART;
14269       res= do_auth_once(thd, &acl_user->auth[mpvio.curr_auth].plugin, &mpvio);
14270     }
14271   }
14272 
14273   if (mpvio.make_it_fail && res == CR_OK)
14274   {
14275     mpvio.status= MPVIO_EXT::FAILURE;
14276     res= CR_ERROR;
14277   }
14278 
14279   thd->password= mpvio.auth_info.password_used;  // remember for error messages
14280 
14281   /*
14282     Log the command here so that the user can check the log
14283     for the tried logins and also to detect break-in attempts.
14284 
14285     if sctx->user is unset it's protocol failure, bad packet.
14286   */
14287   if (sctx->user)
14288   {
14289     general_log_print(thd, command, (char*) "%s@%s on %s using %s",
14290                       sctx->user, sctx->host_or_ip,
14291                       safe_str(mpvio.db.str), safe_vio_type_name(thd->net.vio));
14292   }
14293 
14294   if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
14295   {
14296     Host_errors errors;
14297     switch (res)
14298     {
14299     case CR_AUTH_PLUGIN_ERROR:
14300       errors.m_auth_plugin= 1;
14301       break;
14302     case CR_AUTH_HANDSHAKE:
14303       errors.m_handshake= 1;
14304       break;
14305     case CR_AUTH_USER_CREDENTIALS:
14306       errors.m_authentication= 1;
14307       if (thd->password && !mpvio.make_it_fail)
14308         handle_password_errors(acl_user->user.str, acl_user->host.hostname, PASSWD_ERROR_INCREMENT);
14309       break;
14310     case CR_ERROR:
14311     default:
14312       /* Unknown of unspecified auth plugin error. */
14313       errors.m_auth_plugin= 1;
14314       break;
14315     }
14316     inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
14317     if (!thd->is_error())
14318       login_failed_error(thd);
14319     DBUG_RETURN(1);
14320   }
14321 
14322   sctx->proxy_user[0]= 0;
14323   if (thd->password && acl_user->password_errors)
14324   {
14325     /* Login succeeded, clear password errors.*/
14326     handle_password_errors(acl_user->user.str, acl_user->host.hostname, PASSWD_ERROR_CLEAR);
14327   }
14328 
14329   if (initialized) // if not --skip-grant-tables
14330   {
14331     /*
14332       OK. Let's check the SSL. Historically it was checked after the password,
14333       as an additional layer, not instead of the password
14334       (in which case it would've been a plugin too).
14335     */
14336     if (acl_check_ssl(thd, acl_user))
14337     {
14338       Host_errors errors;
14339       errors.m_ssl= 1;
14340       inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
14341       login_failed_error(thd);
14342       DBUG_RETURN(1);
14343     }
14344 
14345     if (acl_user->account_locked) {
14346       status_var_increment(denied_connections);
14347       my_error(ER_ACCOUNT_HAS_BEEN_LOCKED, MYF(0));
14348       DBUG_RETURN(1);
14349     }
14350 
14351     bool client_can_handle_exp_pass= thd->client_capabilities &
14352                                      CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
14353     bool password_expired= thd->password != PASSWORD_USED_NO_MENTION
14354                            && (acl_user->password_expired ||
14355                                check_password_lifetime(thd, *acl_user));
14356 
14357     if (!client_can_handle_exp_pass && disconnect_on_expired_password &&
14358         password_expired)
14359     {
14360       status_var_increment(denied_connections);
14361       my_error(ER_MUST_CHANGE_PASSWORD_LOGIN, MYF(0));
14362       DBUG_RETURN(1);
14363     }
14364 
14365     sctx->password_expired= password_expired;
14366 
14367 #ifndef NO_EMBEDDED_ACCESS_CHECKS
14368     if (!password_expired)
14369     {
14370       bool is_proxy_user= FALSE;
14371       const char *auth_user = acl_user->user.str;
14372       ACL_PROXY_USER *proxy_user;
14373       /* check if the user is allowed to proxy as another user */
14374       proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
14375                                       mpvio.auth_info.authenticated_as,
14376                                             &is_proxy_user);
14377       if (is_proxy_user)
14378       {
14379         ACL_USER *acl_proxy_user;
14380 
14381         /* we need to find the proxy user, but there was none */
14382         if (!proxy_user)
14383         {
14384           Host_errors errors;
14385           errors.m_proxy_user= 1;
14386           inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
14387           if (!thd->is_error())
14388             login_failed_error(thd);
14389           DBUG_RETURN(1);
14390         }
14391 
14392         my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
14393                     "'%s'@'%s'", auth_user,
14394                     safe_str(acl_user->host.hostname));
14395 
14396         /* we're proxying : find the proxy user definition */
14397         mysql_mutex_lock(&acl_cache->lock);
14398         acl_proxy_user= find_user_exact(safe_str(proxy_user->get_proxied_host()),
14399                                        mpvio.auth_info.authenticated_as);
14400         if (!acl_proxy_user)
14401         {
14402           mysql_mutex_unlock(&acl_cache->lock);
14403 
14404           Host_errors errors;
14405           errors.m_proxy_user_acl= 1;
14406           inc_host_errors(mpvio.auth_info.thd->security_ctx->ip, &errors);
14407           if (!thd->is_error())
14408             login_failed_error(thd);
14409           DBUG_RETURN(1);
14410         }
14411         acl_user= acl_proxy_user->copy(thd->mem_root);
14412         mysql_mutex_unlock(&acl_cache->lock);
14413       }
14414     }
14415 #endif
14416 
14417     sctx->master_access= acl_user->access;
14418     strmake_buf(sctx->priv_user, acl_user->user.str);
14419 
14420     if (acl_user->host.hostname)
14421       strmake_buf(sctx->priv_host, acl_user->host.hostname);
14422     else
14423       *sctx->priv_host= 0;
14424 
14425 
14426     /*
14427       Don't allow the user to connect if he has done too many queries.
14428       As we are testing max_user_connections == 0 here, it means that we
14429       can't let the user change max_user_connections from 0 in the server
14430       without a restart as it would lead to wrong connect counting.
14431     */
14432     if ((acl_user->user_resource.questions ||
14433          acl_user->user_resource.updates ||
14434          acl_user->user_resource.conn_per_hour ||
14435          acl_user->user_resource.user_conn ||
14436          acl_user->user_resource.max_statement_time != 0.0 ||
14437          max_user_connections_checking) &&
14438          get_or_create_user_conn(thd,
14439            (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
14440            (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
14441            &acl_user->user_resource))
14442       DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
14443 
14444     if (acl_user->user_resource.max_statement_time != 0.0)
14445     {
14446       thd->variables.max_statement_time_double=
14447         acl_user->user_resource.max_statement_time;
14448       thd->variables.max_statement_time=
14449         (ulonglong) (thd->variables.max_statement_time_double * 1e6 + 0.1);
14450     }
14451   }
14452   else
14453     sctx->skip_grants();
14454 
14455   if (thd->user_connect &&
14456       (thd->user_connect->user_resources.conn_per_hour ||
14457        thd->user_connect->user_resources.user_conn ||
14458        max_user_connections_checking) &&
14459        check_for_max_user_connections(thd, thd->user_connect))
14460   {
14461     /* Ensure we don't decrement thd->user_connections->connections twice */
14462     thd->user_connect= 0;
14463     status_var_increment(denied_connections);
14464     DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
14465   }
14466 
14467   DBUG_PRINT("info",
14468              ("Capabilities: %llu  packet_length: %ld  Host: '%s'  "
14469               "Login user: '%s' Priv_user: '%s'  Using password: %s "
14470               "Access: %llx  db: '%s'",
14471               thd->client_capabilities, thd->max_client_packet_length,
14472               sctx->host_or_ip, sctx->user, sctx->priv_user,
14473               thd->password ? "yes": "no",
14474               (longlong) sctx->master_access, mpvio.db.str));
14475 
14476   if (command == COM_CONNECT &&
14477       !(thd->main_security_ctx.master_access & PRIV_IGNORE_MAX_CONNECTIONS))
14478   {
14479     if (*thd->scheduler->connection_count > *thd->scheduler->max_connections)
14480     {                                         // too many connections
14481       my_error(ER_CON_COUNT_ERROR, MYF(0));
14482       DBUG_RETURN(1);
14483     }
14484   }
14485 
14486   /*
14487     This is the default access rights for the current database.  It's
14488     set to 0 here because we don't have an active database yet (and we
14489     may not have an active database to set.
14490   */
14491   sctx->db_access= NO_ACL;
14492 
14493 #ifndef NO_EMBEDDED_ACCESS_CHECKS
14494   /*
14495     In case the user has a default role set, attempt to set that role
14496   */
14497   if (initialized && acl_user->default_rolename.length) {
14498     privilege_t access(NO_ACL);
14499     int result;
14500     result= acl_check_setrole(thd, acl_user->default_rolename.str, &access);
14501     if (!result)
14502       result= acl_setrole(thd, acl_user->default_rolename.str, access);
14503     if (result)
14504       thd->clear_error(); // even if the default role was not granted, do not
14505                           // close the connection
14506   }
14507 #endif
14508 
14509   /* Change a database if necessary */
14510   if (mpvio.db.length)
14511   {
14512     uint err = mysql_change_db(thd, &mpvio.db, FALSE);
14513     if(err)
14514     {
14515       if (err == ER_DBACCESS_DENIED_ERROR)
14516       {
14517         /*
14518           Got an "access denied" error, which must be handled
14519           other access denied errors (see login_failed_error()).
14520           mysql_change_db() already sent error to client, and
14521           wrote to general log, we only need to increment the counter
14522           and maybe write a warning to error log.
14523         */
14524         status_var_increment(thd->status_var.access_denied_errors);
14525         if (global_system_variables.log_warnings > 1)
14526         {
14527           Security_context* sctx = thd->security_ctx;
14528           sql_print_warning(ER_THD(thd, err),
14529             sctx->priv_user, sctx->priv_host, mpvio.db.str);
14530         }
14531       }
14532       DBUG_RETURN(1);
14533     }
14534   }
14535 
14536   thd->net.net_skip_rest_factor= 2;  // skip at most 2*max_packet_size
14537 
14538   if (mpvio.auth_info.external_user[0])
14539     sctx->external_user= my_strdup(key_memory_MPVIO_EXT_auth_info,
14540                                    mpvio.auth_info.external_user, MYF(0));
14541 
14542   if (res == CR_OK_HANDSHAKE_COMPLETE)
14543     thd->get_stmt_da()->disable_status();
14544   else
14545     my_ok(thd);
14546 
14547   PSI_CALL_set_thread_account
14548     (thd->main_security_ctx.user, static_cast<uint>(strlen(thd->main_security_ctx.user)),
14549     thd->main_security_ctx.host_or_ip, static_cast<uint>(strlen(thd->main_security_ctx.host_or_ip)));
14550 
14551   /* Ready to handle queries */
14552   DBUG_RETURN(0);
14553 }
14554 
14555 /**
14556   MySQL Server Password Authentication Plugin
14557 
14558   In the MySQL authentication protocol:
14559   1. the server sends the random scramble to the client
14560   2. client sends the encrypted password back to the server
14561   3. the server checks the password.
14562 */
native_password_authenticate(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)14563 static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
14564                                         MYSQL_SERVER_AUTH_INFO *info)
14565 {
14566   uchar *pkt;
14567   int pkt_len;
14568   MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
14569   THD *thd=info->thd;
14570   DBUG_ENTER("native_password_authenticate");
14571 
14572   /* generate the scramble, or reuse the old one */
14573   if (thd->scramble[SCRAMBLE_LENGTH])
14574     thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
14575 
14576   /* and send it to the client */
14577   if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
14578     DBUG_RETURN(CR_AUTH_HANDSHAKE);
14579 
14580   /* reply and authenticate */
14581 
14582   /*
14583     <digression>
14584       This is more complex than it looks.
14585 
14586       The plugin (we) may be called right after the client was connected -
14587       and will need to send a scramble, read reply, authenticate.
14588 
14589       Or the plugin may be called after another plugin has sent a scramble,
14590       and read the reply. If the client has used the correct client-plugin,
14591       we won't need to read anything here from the client, the client
14592       has already sent a reply with everything we need for authentication.
14593 
14594       Or the plugin may be called after another plugin has sent a scramble,
14595       and read the reply, but the client has used the wrong client-plugin.
14596       We'll need to sent a "switch to another plugin" packet to the
14597       client and read the reply. "Use the short scramble" packet is a special
14598       case of "switch to another plugin" packet.
14599 
14600       Or, perhaps, the plugin may be called after another plugin has
14601       done the handshake but did not send a useful scramble. We'll need
14602       to send a scramble (and perhaps a "switch to another plugin" packet)
14603       and read the reply.
14604 
14605       Besides, a client may be an old one, that doesn't understand plugins.
14606       Or doesn't even understand 4.0 scramble.
14607 
14608       And we want to keep the same protocol on the wire  unless non-native
14609       plugins are involved.
14610 
14611       Anyway, it still looks simple from a plugin point of view:
14612       "send the scramble, read the reply and authenticate".
14613       All the magic is transparently handled by the server.
14614     </digression>
14615   */
14616 
14617   /* read the reply with the encrypted password */
14618   if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
14619     DBUG_RETURN(CR_AUTH_HANDSHAKE);
14620   DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
14621 
14622 #ifdef NO_EMBEDDED_ACCESS_CHECKS
14623   DBUG_RETURN(CR_OK);
14624 #endif
14625 
14626   DBUG_EXECUTE_IF("native_password_bad_reply", { pkt_len= 12; });
14627 
14628   if (pkt_len == 0) /* no password */
14629     DBUG_RETURN(info->auth_string_length != 0
14630                 ? CR_AUTH_USER_CREDENTIALS : CR_OK);
14631 
14632   info->password_used= PASSWORD_USED_YES;
14633   if (pkt_len == SCRAMBLE_LENGTH)
14634   {
14635     if (info->auth_string_length != SCRAMBLE_LENGTH)
14636       DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
14637 
14638     if (check_scramble(pkt, thd->scramble, (uchar*)info->auth_string))
14639       DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
14640     else
14641       DBUG_RETURN(CR_OK);
14642   }
14643 
14644   my_error(ER_HANDSHAKE_ERROR, MYF(0));
14645   DBUG_RETURN(CR_AUTH_HANDSHAKE);
14646 }
14647 
native_password_make_scramble(const char * password,size_t password_length,char * hash,size_t * hash_length)14648 static int native_password_make_scramble(const char *password,
14649                       size_t password_length, char *hash, size_t *hash_length)
14650 {
14651   DBUG_ASSERT(*hash_length >= SCRAMBLED_PASSWORD_CHAR_LENGTH);
14652   if (password_length == 0)
14653     *hash_length= 0;
14654   else
14655   {
14656     *hash_length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
14657     my_make_scrambled_password(hash, password, password_length);
14658   }
14659   return 0;
14660 }
14661 
14662 /* As this contains is a string of not a valid SCRAMBLE_LENGTH */
14663 static const char invalid_password[] = "*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE";
14664 
native_password_get_salt(const char * hash,size_t hash_length,unsigned char * out,size_t * out_length)14665 static int native_password_get_salt(const char *hash, size_t hash_length,
14666                                     unsigned char *out, size_t *out_length)
14667 {
14668   DBUG_ASSERT(sizeof(invalid_password) > SCRAMBLE_LENGTH);
14669   DBUG_ASSERT(*out_length >= SCRAMBLE_LENGTH);
14670   DBUG_ASSERT(*out_length >= sizeof(invalid_password));
14671   if (hash_length == 0)
14672   {
14673     *out_length= 0;
14674     return 0;
14675   }
14676 
14677   if (hash_length != SCRAMBLED_PASSWORD_CHAR_LENGTH)
14678   {
14679     if (hash_length == 7 && strcmp(hash, "invalid") == 0)
14680     {
14681       memcpy(out, invalid_password, sizeof(invalid_password));
14682       *out_length= sizeof(invalid_password);
14683       return 0;
14684     }
14685     my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
14686     return 1;
14687   }
14688 
14689   for (const char *c= hash + 1; c < (hash + hash_length); c++)
14690   {
14691     /* If any non-hex characters are found, mark the password as invalid. */
14692     if (!(*c >= '0' && *c <= '9') &&
14693         !(*c >= 'A' && *c <= 'F') &&
14694         !(*c >= 'a' && *c <= 'f'))
14695     {
14696       memcpy(out, invalid_password, sizeof(invalid_password));
14697       *out_length= sizeof(invalid_password);
14698       return 0;
14699     }
14700   }
14701 
14702   *out_length= SCRAMBLE_LENGTH;
14703   get_salt_from_password(out, hash);
14704   return 0;
14705 }
14706 
old_password_authenticate(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)14707 static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
14708                                      MYSQL_SERVER_AUTH_INFO *info)
14709 {
14710   uchar *pkt;
14711   int pkt_len;
14712   MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
14713   THD *thd=info->thd;
14714 
14715   /* generate the scramble, or reuse the old one */
14716   if (thd->scramble[SCRAMBLE_LENGTH])
14717     thd_create_random_password(thd, thd->scramble, SCRAMBLE_LENGTH);
14718   /* and send it to the client */
14719   if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
14720     return CR_AUTH_HANDSHAKE;
14721 
14722   /* read the reply and authenticate */
14723   if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
14724     return CR_AUTH_HANDSHAKE;
14725 
14726 #ifdef NO_EMBEDDED_ACCESS_CHECKS
14727   return CR_OK;
14728 #endif
14729 
14730   /*
14731     legacy: if switch_from_long_to_short_scramble,
14732     the password is sent \0-terminated, the pkt_len is always 9 bytes.
14733     We need to figure out the correct scramble length here.
14734   */
14735   if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
14736     pkt_len= (int)strnlen((char*)pkt, pkt_len);
14737 
14738   if (pkt_len == 0) /* no password */
14739     return info->auth_string_length ? CR_AUTH_USER_CREDENTIALS : CR_OK;
14740 
14741   if (secure_auth(thd))
14742     return CR_AUTH_HANDSHAKE;
14743 
14744   info->password_used= PASSWORD_USED_YES;
14745 
14746   if (pkt_len == SCRAMBLE_LENGTH_323)
14747   {
14748     if (!info->auth_string_length)
14749       return CR_AUTH_USER_CREDENTIALS;
14750 
14751     return check_scramble_323(pkt, thd->scramble, (ulong *) info->auth_string)
14752              ? CR_AUTH_USER_CREDENTIALS : CR_OK;
14753   }
14754 
14755   my_error(ER_HANDSHAKE_ERROR, MYF(0));
14756   return CR_AUTH_HANDSHAKE;
14757 }
14758 
old_password_make_scramble(const char * password,size_t password_length,char * hash,size_t * hash_length)14759 static int old_password_make_scramble(const char *password,
14760                       size_t password_length, char *hash, size_t *hash_length)
14761 {
14762   DBUG_ASSERT(*hash_length >= SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
14763   if (password_length == 0)
14764     *hash_length= 0;
14765   else
14766   {
14767     *hash_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
14768     my_make_scrambled_password_323(hash, password, password_length);
14769   }
14770   return 0;
14771 }
14772 
14773 #define SALT_LENGTH_323 (sizeof(ulong)*2)
old_password_get_salt(const char * hash,size_t hash_length,unsigned char * out,size_t * out_length)14774 static int old_password_get_salt(const char *hash, size_t hash_length,
14775                                  unsigned char *out, size_t *out_length)
14776 {
14777   DBUG_ASSERT(*out_length >= SALT_LENGTH_323);
14778 
14779   if (hash_length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
14780   {
14781     my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
14782     return 1;
14783   }
14784 
14785   *out_length= SALT_LENGTH_323;
14786   get_salt_from_password_323((ulong*)out, hash);
14787   return 0;
14788 }
14789 
14790 static struct st_mysql_auth native_password_handler=
14791 {
14792   MYSQL_AUTHENTICATION_INTERFACE_VERSION,
14793   native_password_plugin_name.str,
14794   native_password_authenticate,
14795   native_password_make_scramble,
14796   native_password_get_salt
14797 };
14798 
14799 static struct st_mysql_auth old_password_handler=
14800 {
14801   MYSQL_AUTHENTICATION_INTERFACE_VERSION,
14802   old_password_plugin_name.str,
14803   old_password_authenticate,
14804   old_password_make_scramble,
14805   old_password_get_salt
14806 };
14807 
maria_declare_plugin(mysql_password)14808 maria_declare_plugin(mysql_password)
14809 {
14810   MYSQL_AUTHENTICATION_PLUGIN,                  /* type constant    */
14811   &native_password_handler,                     /* type descriptor  */
14812   native_password_plugin_name.str,              /* Name             */
14813   "R.J.Silk, Sergei Golubchik",                 /* Author           */
14814   "Native MySQL authentication",                /* Description      */
14815   PLUGIN_LICENSE_GPL,                           /* License          */
14816   NULL,                                         /* Init function    */
14817   NULL,                                         /* Deinit function  */
14818   0x0100,                                       /* Version (1.0)    */
14819   NULL,                                         /* status variables */
14820   NULL,                                         /* system variables */
14821   "1.0",                                        /* String version   */
14822   MariaDB_PLUGIN_MATURITY_STABLE                /* Maturity         */
14823 },
14824 {
14825   MYSQL_AUTHENTICATION_PLUGIN,                  /* type constant    */
14826   &old_password_handler,                        /* type descriptor  */
14827   old_password_plugin_name.str,                 /* Name             */
14828   "R.J.Silk, Sergei Golubchik",                 /* Author           */
14829   "Old MySQL-4.0 authentication",               /* Description      */
14830   PLUGIN_LICENSE_GPL,                           /* License          */
14831   NULL,                                         /* Init function    */
14832   NULL,                                         /* Deinit function  */
14833   0x0100,                                       /* Version (1.0)    */
14834   NULL,                                         /* status variables */
14835   NULL,                                         /* system variables */
14836   "1.0",                                        /* String version   */
14837   MariaDB_PLUGIN_MATURITY_STABLE                /* Maturity         */
14838 }
14839 maria_declare_plugin_end;
14840 
14841 
14842 /*
14843   Exporting functions that allow plugins to do server-style
14844   host/user matching. Used in server_audit2 plugin.
14845 */
maria_compare_hostname(const char * wild_host,long wild_ip,long ip_mask,const char * host,const char * ip)14846 extern "C" int maria_compare_hostname(
14847                   const char *wild_host, long wild_ip, long ip_mask,
14848                   const char *host, const char *ip)
14849 {
14850 #ifndef NO_EMBEDDED_ACCESS_CHECKS
14851   acl_host_and_ip h;
14852   h.hostname= (char *) wild_host;
14853   h.ip= wild_ip;
14854   h.ip_mask= ip_mask;
14855 
14856   return compare_hostname(&h, host, ip);
14857 #else
14858   return 0;
14859 #endif
14860 }
14861 
14862 
maria_update_hostname(const char ** wild_host,long * wild_ip,long * ip_mask,const char * host)14863 extern "C" void maria_update_hostname(
14864                   const char **wild_host, long *wild_ip, long *ip_mask,
14865                   const char *host)
14866 {
14867 #ifndef NO_EMBEDDED_ACCESS_CHECKS
14868   acl_host_and_ip h;
14869   update_hostname(&h, host);
14870   *wild_host= h.hostname;
14871   *wild_ip= h.ip;
14872   *ip_mask= h.ip_mask;
14873 #endif
14874 }
14875 
14876 
14877