MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sql_acl.cc
1 /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 
17 /*
18  The privileges are saved in the following tables:
19  mysql/user ; super user who are allowed to do almost anything
20  mysql/host ; host privileges. This is used if host is empty in mysql/db.
21  mysql/db ; database privileges / user
22 
23  data in tables is sorted according to how many not-wild-cards there is
24  in the relevant fields. Empty strings comes last.
25 */
26 
27 
28 #include "my_global.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" // build_table_filename
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 <m_ctype.h>
42 #include <stdarg.h>
43 #include "sp_head.h"
44 #include "sp.h"
45 #include "transaction.h"
46 #include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
47 #include "records.h" // init_read_record, end_read_record
48 #include <sql_common.h>
49 #include <mysql/plugin_auth.h>
50 #include "sql_connect.h"
51 #include "hostname.h"
52 #include "sql_db.h"
53 #include <mysql/plugin_validate_password.h>
54 #include "password.h"
55 #include "crypt_genhash_impl.h"
56 
57 #if defined(HAVE_OPENSSL) && !defined(HAVE_YASSL)
58 #include <openssl/rsa.h>
59 #include <openssl/pem.h>
60 #include <openssl/err.h>
61 #endif
62 
63 using std::min;
64 using std::max;
65 
66 bool mysql_user_table_is_in_short_password_format= false;
67 my_bool disconnect_on_expired_password= TRUE;
68 bool auth_plugin_is_built_in(const char *plugin_name);
69 bool auth_plugin_supports_expiration(const char *plugin_name);
70 void optimize_plugin_compare_by_pointer(LEX_STRING *plugin_name);
71 
72 static const
73 TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
74  {
75  { C_STRING_WITH_LEN("Host") },
76  { C_STRING_WITH_LEN("char(60)") },
77  {NULL, 0}
78  },
79  {
80  { C_STRING_WITH_LEN("Db") },
81  { C_STRING_WITH_LEN("char(64)") },
82  {NULL, 0}
83  },
84  {
85  { C_STRING_WITH_LEN("User") },
86  { C_STRING_WITH_LEN("char(16)") },
87  {NULL, 0}
88  },
89  {
90  { C_STRING_WITH_LEN("Select_priv") },
91  { C_STRING_WITH_LEN("enum('N','Y')") },
92  { C_STRING_WITH_LEN("utf8") }
93  },
94  {
95  { C_STRING_WITH_LEN("Insert_priv") },
96  { C_STRING_WITH_LEN("enum('N','Y')") },
97  { C_STRING_WITH_LEN("utf8") }
98  },
99  {
100  { C_STRING_WITH_LEN("Update_priv") },
101  { C_STRING_WITH_LEN("enum('N','Y')") },
102  { C_STRING_WITH_LEN("utf8") }
103  },
104  {
105  { C_STRING_WITH_LEN("Delete_priv") },
106  { C_STRING_WITH_LEN("enum('N','Y')") },
107  { C_STRING_WITH_LEN("utf8") }
108  },
109  {
110  { C_STRING_WITH_LEN("Create_priv") },
111  { C_STRING_WITH_LEN("enum('N','Y')") },
112  { C_STRING_WITH_LEN("utf8") }
113  },
114  {
115  { C_STRING_WITH_LEN("Drop_priv") },
116  { C_STRING_WITH_LEN("enum('N','Y')") },
117  { C_STRING_WITH_LEN("utf8") }
118  },
119  {
120  { C_STRING_WITH_LEN("Grant_priv") },
121  { C_STRING_WITH_LEN("enum('N','Y')") },
122  { C_STRING_WITH_LEN("utf8") }
123  },
124  {
125  { C_STRING_WITH_LEN("References_priv") },
126  { C_STRING_WITH_LEN("enum('N','Y')") },
127  { C_STRING_WITH_LEN("utf8") }
128  },
129  {
130  { C_STRING_WITH_LEN("Index_priv") },
131  { C_STRING_WITH_LEN("enum('N','Y')") },
132  { C_STRING_WITH_LEN("utf8") }
133  },
134  {
135  { C_STRING_WITH_LEN("Alter_priv") },
136  { C_STRING_WITH_LEN("enum('N','Y')") },
137  { C_STRING_WITH_LEN("utf8") }
138  },
139  {
140  { C_STRING_WITH_LEN("Create_tmp_table_priv") },
141  { C_STRING_WITH_LEN("enum('N','Y')") },
142  { C_STRING_WITH_LEN("utf8") }
143  },
144  {
145  { C_STRING_WITH_LEN("Lock_tables_priv") },
146  { C_STRING_WITH_LEN("enum('N','Y')") },
147  { C_STRING_WITH_LEN("utf8") }
148  },
149  {
150  { C_STRING_WITH_LEN("Create_view_priv") },
151  { C_STRING_WITH_LEN("enum('N','Y')") },
152  { C_STRING_WITH_LEN("utf8") }
153  },
154  {
155  { C_STRING_WITH_LEN("Show_view_priv") },
156  { C_STRING_WITH_LEN("enum('N','Y')") },
157  { C_STRING_WITH_LEN("utf8") }
158  },
159  {
160  { C_STRING_WITH_LEN("Create_routine_priv") },
161  { C_STRING_WITH_LEN("enum('N','Y')") },
162  { C_STRING_WITH_LEN("utf8") }
163  },
164  {
165  { C_STRING_WITH_LEN("Alter_routine_priv") },
166  { C_STRING_WITH_LEN("enum('N','Y')") },
167  { C_STRING_WITH_LEN("utf8") }
168  },
169  {
170  { C_STRING_WITH_LEN("Execute_priv") },
171  { C_STRING_WITH_LEN("enum('N','Y')") },
172  { C_STRING_WITH_LEN("utf8") }
173  },
174  {
175  { C_STRING_WITH_LEN("Event_priv") },
176  { C_STRING_WITH_LEN("enum('N','Y')") },
177  { C_STRING_WITH_LEN("utf8") }
178  },
179  {
180  { C_STRING_WITH_LEN("Trigger_priv") },
181  { C_STRING_WITH_LEN("enum('N','Y')") },
182  { C_STRING_WITH_LEN("utf8") }
183  }
184 };
185 
186 static const
187 TABLE_FIELD_TYPE mysql_user_table_fields[MYSQL_USER_FIELD_COUNT] = {
188  {
189  { C_STRING_WITH_LEN("Host") },
190  { C_STRING_WITH_LEN("char(60)") },
191  { NULL, 0 }
192  },
193  {
194  { C_STRING_WITH_LEN("User") },
195  { C_STRING_WITH_LEN("char(16)") },
196  { NULL, 0 }
197  },
198  {
199  { C_STRING_WITH_LEN("Password") },
200  { C_STRING_WITH_LEN("char(41)") },
201  { C_STRING_WITH_LEN("latin1") }
202  },
203  {
204  { C_STRING_WITH_LEN("Select_priv") },
205  { C_STRING_WITH_LEN("enum('N','Y')") },
206  { C_STRING_WITH_LEN("utf8") }
207  },
208  {
209  { C_STRING_WITH_LEN("Insert_priv") },
210  { C_STRING_WITH_LEN("enum('N','Y')") },
211  { C_STRING_WITH_LEN("utf8") }
212  },
213  {
214  { C_STRING_WITH_LEN("Update_priv") },
215  { C_STRING_WITH_LEN("enum('N','Y')") },
216  { C_STRING_WITH_LEN("utf8") }
217  },
218  {
219  { C_STRING_WITH_LEN("Delete_priv") },
220  { C_STRING_WITH_LEN("enum('N','Y')") },
221  { C_STRING_WITH_LEN("utf8") }
222  },
223  {
224  { C_STRING_WITH_LEN("Create_priv") },
225  { C_STRING_WITH_LEN("enum('N','Y')") },
226  { C_STRING_WITH_LEN("utf8") }
227  },
228  {
229  { C_STRING_WITH_LEN("Drop_priv") },
230  { C_STRING_WITH_LEN("enum('N','Y')") },
231  { C_STRING_WITH_LEN("utf8") }
232  },
233  {
234  { C_STRING_WITH_LEN("Reload_priv") },
235  { C_STRING_WITH_LEN("enum('N','Y')") },
236  { C_STRING_WITH_LEN("utf8") }
237  },
238  {
239  { C_STRING_WITH_LEN("Shutdown_priv") },
240  { C_STRING_WITH_LEN("enum('N','Y')") },
241  { C_STRING_WITH_LEN("utf8") }
242  },
243  {
244  { C_STRING_WITH_LEN("Process_priv") },
245  { C_STRING_WITH_LEN("enum('N','Y')") },
246  { C_STRING_WITH_LEN("utf8") }
247  },
248  {
249  { C_STRING_WITH_LEN("File_priv") },
250  { C_STRING_WITH_LEN("enum('N','Y')") },
251  { C_STRING_WITH_LEN("utf8") }
252  },
253  {
254  { C_STRING_WITH_LEN("Grant_priv") },
255  { C_STRING_WITH_LEN("enum('N','Y')") },
256  { C_STRING_WITH_LEN("utf8") }
257  },
258  {
259  { C_STRING_WITH_LEN("References_priv") },
260  { C_STRING_WITH_LEN("enum('N','Y')") },
261  { C_STRING_WITH_LEN("utf8") }
262  },
263  {
264  { C_STRING_WITH_LEN("Index_priv") },
265  { C_STRING_WITH_LEN("enum('N','Y')") },
266  { C_STRING_WITH_LEN("utf8") }
267  },
268  {
269  { C_STRING_WITH_LEN("Alter_priv") },
270  { C_STRING_WITH_LEN("enum('N','Y')") },
271  { C_STRING_WITH_LEN("utf8") }
272  },
273  {
274  { C_STRING_WITH_LEN("Show_db_priv") },
275  { C_STRING_WITH_LEN("enum('N','Y')") },
276  { C_STRING_WITH_LEN("utf8") }
277  },
278  {
279  { C_STRING_WITH_LEN("Super_priv") },
280  { C_STRING_WITH_LEN("enum('N','Y')") },
281  { C_STRING_WITH_LEN("utf8") }
282  },
283  {
284  { C_STRING_WITH_LEN("Create_tmp_table_priv") },
285  { C_STRING_WITH_LEN("enum('N','Y')") },
286  { C_STRING_WITH_LEN("utf8") }
287  },
288  {
289  { C_STRING_WITH_LEN("Lock_tables_priv") },
290  { C_STRING_WITH_LEN("enum('N','Y')") },
291  { C_STRING_WITH_LEN("utf8") }
292  },
293  {
294  { C_STRING_WITH_LEN("Execute_priv") },
295  { C_STRING_WITH_LEN("enum('N','Y')") },
296  { C_STRING_WITH_LEN("utf8") }
297  },
298  {
299  { C_STRING_WITH_LEN("Repl_slave_priv") },
300  { C_STRING_WITH_LEN("enum('N','Y')") },
301  { C_STRING_WITH_LEN("utf8") }
302  },
303  {
304  { C_STRING_WITH_LEN("Repl_client_priv") },
305  { C_STRING_WITH_LEN("enum('N','Y')") },
306  { C_STRING_WITH_LEN("utf8") }
307  },
308  {
309  { C_STRING_WITH_LEN("Create_view_priv") },
310  { C_STRING_WITH_LEN("enum('N','Y')") },
311  { C_STRING_WITH_LEN("utf8") }
312  },
313  {
314  { C_STRING_WITH_LEN("Show_view_priv") },
315  { C_STRING_WITH_LEN("enum('N','Y')") },
316  { C_STRING_WITH_LEN("utf8") }
317  },
318  {
319  { C_STRING_WITH_LEN("Create_routine_priv") },
320  { C_STRING_WITH_LEN("enum('N','Y')") },
321  { C_STRING_WITH_LEN("utf8") }
322  },
323  {
324  { C_STRING_WITH_LEN("Alter_routine_priv") },
325  { C_STRING_WITH_LEN("enum('N','Y')") },
326  { C_STRING_WITH_LEN("utf8") }
327  },
328  {
329  { C_STRING_WITH_LEN("Create_user_priv") },
330  { C_STRING_WITH_LEN("enum('N','Y')") },
331  { C_STRING_WITH_LEN("utf8") }
332  },
333  {
334  { C_STRING_WITH_LEN("Event_priv") },
335  { C_STRING_WITH_LEN("enum('N','Y')") },
336  { C_STRING_WITH_LEN("utf8") }
337  },
338  {
339  { C_STRING_WITH_LEN("Trigger_priv") },
340  { C_STRING_WITH_LEN("enum('N','Y')") },
341  { C_STRING_WITH_LEN("utf8") }
342  },
343  {
344  { C_STRING_WITH_LEN("Create_tablespace_priv") },
345  { C_STRING_WITH_LEN("enum('N','Y')") },
346  { C_STRING_WITH_LEN("utf8") }
347  },
348  {
349  { C_STRING_WITH_LEN("ssl_type") },
350  { C_STRING_WITH_LEN("enum('','ANY','X509','SPECIFIED')") },
351  { C_STRING_WITH_LEN("utf8") }
352  },
353  {
354  { C_STRING_WITH_LEN("ssl_cipher") },
355  { C_STRING_WITH_LEN("blob") },
356  { NULL, 0 }
357  },
358  {
359  { C_STRING_WITH_LEN("x509_issuer") },
360  { C_STRING_WITH_LEN("blob") },
361  { NULL, 0 }
362  },
363  {
364  { C_STRING_WITH_LEN("x509_subject") },
365  { C_STRING_WITH_LEN("blob") },
366  { NULL, 0 }
367  },
368  {
369  { C_STRING_WITH_LEN("max_questions") },
370  { C_STRING_WITH_LEN("int(11)") },
371  { NULL, 0 }
372  },
373  {
374  { C_STRING_WITH_LEN("max_updates") },
375  { C_STRING_WITH_LEN("int(11)") },
376  { NULL, 0 }
377  },
378  {
379  { C_STRING_WITH_LEN("max_connections") },
380  { C_STRING_WITH_LEN("int(11)") },
381  { NULL, 0 }
382  },
383  {
384  { C_STRING_WITH_LEN("plugin") },
385  { C_STRING_WITH_LEN("char(64)") },
386  { NULL, 0 }
387  },
388  {
389  { C_STRING_WITH_LEN("authentication_string") },
390  { C_STRING_WITH_LEN("text") },
391  { NULL, 0 }
392  }
393 };
394 
395 const TABLE_FIELD_DEF
396  mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields};
397 
398 const TABLE_FIELD_DEF
399  mysql_user_table_def= {MYSQL_USER_FIELD_COUNT, mysql_user_table_fields};
400 
401 static LEX_STRING native_password_plugin_name= {
402  C_STRING_WITH_LEN("mysql_native_password")
403 };
404 
405 static LEX_STRING old_password_plugin_name= {
406  C_STRING_WITH_LEN("mysql_old_password")
407 };
408 
409 LEX_STRING sha256_password_plugin_name= {
410  C_STRING_WITH_LEN("sha256_password")
411 };
412 
413 static LEX_STRING validate_password_plugin_name= {
414  C_STRING_WITH_LEN("validate_password")
415 };
416 
417 LEX_STRING default_auth_plugin_name;
418 
419 #ifndef NO_EMBEDDED_ACCESS_CHECKS
420 static plugin_ref old_password_plugin;
421 #endif
422 static plugin_ref native_password_plugin;
423 
424 #define WARN_DEPRECATED_41_PWD_HASH(thd) \
425  WARN_DEPRECATED(thd, "pre-4.1 password hash", "post-4.1 password hash")
426 
427 /* Classes */
428 
430 {
431  char *hostname;
432  uint hostname_length;
433  long ip, ip_mask; // Used with masked ip:s
434 
435  const char *calc_ip(const char *ip_arg, long *val, char end)
436  {
437  long ip_val,tmp;
438  if (!(ip_arg=str2int(ip_arg,10,0,255,&ip_val)) || *ip_arg != '.')
439  return 0;
440  ip_val<<=24;
441  if (!(ip_arg=str2int(ip_arg+1,10,0,255,&tmp)) || *ip_arg != '.')
442  return 0;
443  ip_val+=tmp<<16;
444  if (!(ip_arg=str2int(ip_arg+1,10,0,255,&tmp)) || *ip_arg != '.')
445  return 0;
446  ip_val+=tmp<<8;
447  if (!(ip_arg=str2int(ip_arg+1,10,0,255,&tmp)) || *ip_arg != end)
448  return 0;
449  *val=ip_val+tmp;
450  return ip_arg;
451  }
452 
453 public:
454  const char *get_host() const { return hostname; }
455  int get_host_len() { return hostname_length; }
456 
457  bool has_wildcard()
458  {
459  return (strchr(hostname,wild_many) ||
460  strchr(hostname,wild_one) || ip_mask );
461  }
462 
463  bool check_allow_all_hosts()
464  {
465  return (!hostname ||
466  (hostname[0] == wild_many && !hostname[1]));
467  }
468 
474  void update_hostname(const char *host_arg)
475  {
476  hostname=(char*) host_arg; // This will not be modified!
477  hostname_length= hostname ? strlen( hostname ) : 0;
478  if (!host_arg ||
479  (!(host_arg=(char*) calc_ip(host_arg,&ip,'/')) ||
480  !(host_arg=(char*) calc_ip(host_arg+1,&ip_mask,'\0'))))
481  {
482  ip= ip_mask=0; // Not a masked ip
483  }
484  }
485 
486  /*
487  @brief Comparing of hostnames
488 
489  @param host_arg Hostname to be compared with
490  @param ip_arg IP address to be compared with
491 
492  @notes
493  A hostname may be of type:
494  1) hostname (May include wildcards); monty.pp.sci.fi
495  2) ip (May include wildcards); 192.168.0.0
496  3) ip/netmask 192.168.0.0/255.255.255.0
497  A net mask of 0.0.0.0 is not allowed.
498 
499  @return
500  true if matched
501  false if not matched
502  */
503 
504  bool compare_hostname(const char *host_arg, const char *ip_arg)
505  {
506  long tmp;
507  if (ip_mask && ip_arg && calc_ip(ip_arg,&tmp,'\0'))
508  {
509  return (tmp & ip_mask) == ip;
510  }
511  return (!hostname ||
512  (host_arg &&
513  !wild_case_compare(system_charset_info, host_arg, hostname)) ||
514  (ip_arg && !wild_compare(ip_arg, hostname, 0)));
515  }
516 
517 };
518 
519 class ACL_ACCESS {
520 public:
521  ACL_HOST_AND_IP host;
522  ulong sort;
523  ulong access;
524 };
525 
526 /* ACL_HOST is used if no host is specified */
527 
528 class ACL_HOST :public ACL_ACCESS
529 {
530 public:
531  char *db;
532 };
533 
534 class ACL_USER :public ACL_ACCESS
535 {
536 public:
537  USER_RESOURCES user_resource;
538  char *user;
543  uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form
548  uint8 salt_len;
549  enum SSL_type ssl_type;
550  const char *ssl_cipher, *x509_issuer, *x509_subject;
551  LEX_STRING plugin;
552  LEX_STRING auth_string;
553  bool password_expired;
554 
555  ACL_USER *copy(MEM_ROOT *root)
556  {
557  ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
558  if (!dst)
559  return 0;
560  *dst= *this;
561  dst->user= safe_strdup_root(root, user);
562  dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
563  dst->x509_issuer= safe_strdup_root(root, x509_issuer);
564  dst->x509_subject= safe_strdup_root(root, x509_subject);
565  /*
566  If the plugin is built in we don't need to reallocate the name of the
567  plugin.
568  */
569  if (auth_plugin_is_built_in(dst->plugin.str))
570  dst->plugin= plugin;
571  else
572  {
573  dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
574  dst->plugin.length= plugin.length;
575  }
576  dst->auth_string.str= safe_strdup_root(root, auth_string.str);
577  dst->host.update_hostname(safe_strdup_root(root, host.get_host()));
578  return dst;
579  }
580 };
581 
582 class ACL_DB :public ACL_ACCESS
583 {
584 public:
585  char *user,*db;
586 };
587 
588 
589 #ifndef NO_EMBEDDED_ACCESS_CHECKS
590 static void validate_user_plugin_records();
591 
592 static ulong get_sort(uint count,...);
593 static bool show_proxy_grants (THD *thd, LEX_USER *user,
594  char *buff, size_t buffsize);
595 
596 
598 {
599  const char *user;
600  ACL_HOST_AND_IP proxied_host;
601  const char *proxied_user;
602  bool with_grant;
603 
604  typedef enum {
605  MYSQL_PROXIES_PRIV_HOST,
606  MYSQL_PROXIES_PRIV_USER,
607  MYSQL_PROXIES_PRIV_PROXIED_HOST,
608  MYSQL_PROXIES_PRIV_PROXIED_USER,
609  MYSQL_PROXIES_PRIV_WITH_GRANT,
610  MYSQL_PROXIES_PRIV_GRANTOR,
611  MYSQL_PROXIES_PRIV_TIMESTAMP } old_acl_proxy_users;
612 public:
613  ACL_PROXY_USER () {};
614 
615  void init(const char *host_arg, const char *user_arg,
616  const char *proxied_host_arg, const char *proxied_user_arg,
617  bool with_grant_arg)
618  {
619  user= (user_arg && *user_arg) ? user_arg : NULL;
620  host.update_hostname ((host_arg && *host_arg) ? host_arg : NULL);
621  proxied_user= (proxied_user_arg && *proxied_user_arg) ?
622  proxied_user_arg : NULL;
623  proxied_host.update_hostname ((proxied_host_arg && *proxied_host_arg) ?
624  proxied_host_arg : NULL);
625  with_grant= with_grant_arg;
626  sort= get_sort(4, host.get_host(), user,
627  proxied_host.get_host(), proxied_user);
628  }
629 
630  void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
631  const char *proxied_host_arg, const char *proxied_user_arg,
632  bool with_grant_arg)
633  {
634  init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
635  (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
636  (proxied_host_arg && *proxied_host_arg) ?
637  strdup_root (mem, proxied_host_arg) : NULL,
638  (proxied_user_arg && *proxied_user_arg) ?
639  strdup_root (mem, proxied_user_arg) : NULL,
640  with_grant_arg);
641  }
642 
643  void init(TABLE *table, MEM_ROOT *mem)
644  {
645  init (get_field(mem, table->field[MYSQL_PROXIES_PRIV_HOST]),
646  get_field(mem, table->field[MYSQL_PROXIES_PRIV_USER]),
647  get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]),
648  get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]),
649  table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->val_int() != 0);
650  }
651 
652  bool get_with_grant() { return with_grant; }
653  const char *get_user() { return user; }
654  const char *get_proxied_user() { return proxied_user; }
655  const char *get_proxied_host() { return proxied_host.get_host(); }
656  void set_user(MEM_ROOT *mem, const char *user_arg)
657  {
658  user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
659  }
660 
661  bool check_validity(bool check_no_resolve)
662  {
663  if (check_no_resolve &&
664  (hostname_requires_resolving(host.get_host()) ||
665  hostname_requires_resolving(proxied_host.get_host())))
666  {
667  sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
668  "ignored in --skip-name-resolve mode.",
669  proxied_user ? proxied_user : "",
670  proxied_host.get_host() ? proxied_host.get_host() : "",
671  user ? user : "",
672  host.get_host() ? host.get_host() : "");
673  return TRUE;
674  }
675  return FALSE;
676  }
677 
678  bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
679  const char *proxied_user_arg)
680  {
681  DBUG_ENTER("ACL_PROXY_USER::matches");
682  DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
683  "compare_hostname(%s,%s,%s) &&"
684  "wild_compare (%s,%s) &&"
685  "wild_compare (%s,%s)",
686  host.get_host() ? host.get_host() : "<NULL>",
687  host_arg ? host_arg : "<NULL>",
688  ip_arg ? ip_arg : "<NULL>",
689  proxied_host.get_host() ? proxied_host.get_host() : "<NULL>",
690  host_arg ? host_arg : "<NULL>",
691  ip_arg ? ip_arg : "<NULL>",
692  user_arg ? user_arg : "<NULL>",
693  user ? user : "<NULL>",
694  proxied_user_arg ? proxied_user_arg : "<NULL>",
695  proxied_user ? proxied_user : "<NULL>"));
696  DBUG_RETURN(host.compare_hostname(host_arg, ip_arg) &&
697  proxied_host.compare_hostname(host_arg, ip_arg) &&
698  (!user ||
699  (user_arg && !wild_compare(user_arg, user, TRUE))) &&
700  (!proxied_user ||
701  (proxied_user && !wild_compare(proxied_user_arg,
702  proxied_user, TRUE))));
703  }
704 
705 
706  inline static bool auth_element_equals(const char *a, const char *b)
707  {
708  return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
709  }
710 
711 
712  bool pk_equals(ACL_PROXY_USER *grant)
713  {
714  DBUG_ENTER("pk_equals");
715  DBUG_PRINT("info", ("strcmp(%s,%s) &&"
716  "strcmp(%s,%s) &&"
717  "wild_compare (%s,%s) &&"
718  "wild_compare (%s,%s)",
719  user ? user : "<NULL>",
720  grant->user ? grant->user : "<NULL>",
721  proxied_user ? proxied_user : "<NULL>",
722  grant->proxied_user ? grant->proxied_user : "<NULL>",
723  host.get_host() ? host.get_host() : "<NULL>",
724  grant->host.get_host() ? grant->host.get_host() : "<NULL>",
725  proxied_host.get_host() ? proxied_host.get_host() : "<NULL>",
726  grant->proxied_host.get_host() ?
727  grant->proxied_host.get_host() : "<NULL>"));
728 
729  DBUG_RETURN(auth_element_equals(user, grant->user) &&
730  auth_element_equals(proxied_user, grant->proxied_user) &&
731  auth_element_equals(host.get_host(), grant->host.get_host()) &&
732  auth_element_equals(proxied_host.get_host(),
733  grant->proxied_host.get_host()));
734  }
735 
736 
737  bool granted_on(const char *host_arg, const char *user_arg)
738  {
739  return (((!user && (!user_arg || !user_arg[0])) ||
740  (user && user_arg && !strcmp(user, user_arg))) &&
741  ((!host.get_host() && (!host_arg || !host_arg[0])) ||
742  (host.get_host() && host_arg && !strcmp(host.get_host(), host_arg))));
743  }
744 
745 
746  void print_grant(String *str)
747  {
748  str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
749  if (proxied_user)
750  str->append(proxied_user, strlen(proxied_user));
751  str->append(STRING_WITH_LEN("'@'"));
752  if (proxied_host.get_host())
753  str->append(proxied_host.get_host(), strlen(proxied_host.get_host()));
754  str->append(STRING_WITH_LEN("' TO '"));
755  if (user)
756  str->append(user, strlen(user));
757  str->append(STRING_WITH_LEN("'@'"));
758  if (host.get_host())
759  str->append(host.get_host(), strlen(host.get_host()));
760  str->append(STRING_WITH_LEN("'"));
761  if (with_grant)
762  str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
763  }
764 
765  void set_data(ACL_PROXY_USER *grant)
766  {
767  with_grant= grant->with_grant;
768  }
769 
770  static int store_pk(TABLE *table,
771  const LEX_STRING *host,
772  const LEX_STRING *user,
773  const LEX_STRING *proxied_host,
774  const LEX_STRING *proxied_user)
775  {
776  DBUG_ENTER("ACL_PROXY_USER::store_pk");
777  DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
778  host->str ? host->str : "<NULL>",
779  user->str ? user->str : "<NULL>",
780  proxied_host->str ? proxied_host->str : "<NULL>",
781  proxied_user->str ? proxied_user->str : "<NULL>"));
782  if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
783  host->length,
784  system_charset_info))
785  DBUG_RETURN(TRUE);
786  if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
787  user->length,
788  system_charset_info))
789  DBUG_RETURN(TRUE);
790  if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str,
791  proxied_host->length,
792  system_charset_info))
793  DBUG_RETURN(TRUE);
794  if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str,
795  proxied_user->length,
796  system_charset_info))
797  DBUG_RETURN(TRUE);
798 
799  DBUG_RETURN(FALSE);
800  }
801 
802  static int store_data_record(TABLE *table,
803  const LEX_STRING *host,
804  const LEX_STRING *user,
805  const LEX_STRING *proxied_host,
806  const LEX_STRING *proxied_user,
807  bool with_grant,
808  const char *grantor)
809  {
810  DBUG_ENTER("ACL_PROXY_USER::store_pk");
811  if (store_pk(table, host, user, proxied_host, proxied_user))
812  DBUG_RETURN(TRUE);
813  DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
814  if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
815  TRUE))
816  DBUG_RETURN(TRUE);
817  if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
818  strlen(grantor),
819  system_charset_info))
820  DBUG_RETURN(TRUE);
821 
822  DBUG_RETURN(FALSE);
823  }
824 };
825 
826 #define FIRST_NON_YN_FIELD 26
827 
829 {
830 public:
831  ulong access;
832  uint16 length;
833  char key[1]; // Key will be stored here
834 };
835 
836 
837 static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
838  my_bool not_used __attribute__((unused)))
839 {
840  *length=(uint) entry->length;
841  return (uchar*) entry->key;
842 }
843 
844 #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
845 #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
846  1 + USERNAME_LENGTH + 1)
847 
849 #define AUTH_PACKET_HEADER_SIZE_PROTO_41 32
850 #define AUTH_PACKET_HEADER_SIZE_PROTO_40 5
851 
852 static DYNAMIC_ARRAY acl_users, acl_dbs, acl_proxy_users;
853 static MEM_ROOT global_acl_memory, memex;
854 static bool initialized=0;
855 static bool allow_all_hosts=1;
856 static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
857 static DYNAMIC_ARRAY acl_wild_hosts;
858 static hash_filo *acl_cache;
859 static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
860 static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
861 static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
862 static ulong get_sort(uint count,...);
863 static void init_check_host(void);
864 static void rebuild_check_host(void);
865 static ACL_USER *find_acl_user(const char *host, const char *user,
866  my_bool exact);
867 static bool update_user_table(THD *, TABLE *table, const char *host,
868  const char *user,
869  const char *new_password,
870  uint new_password_len,
871  enum mysql_user_table_field password_field,
872  bool password_expired, bool is_user_table_positioned);
873 static my_bool acl_load(THD *thd, TABLE_LIST *tables);
874 static my_bool grant_load(THD *thd, TABLE_LIST *tables);
875 static inline void get_grantor(THD *thd, char* grantor);
876 /*
877  Enumeration of various ACL's and Hashes used in handle_grant_struct()
878 */
879 enum enum_acl_lists
880 {
881  USER_ACL= 0,
882  DB_ACL,
883  COLUMN_PRIVILEGES_HASH,
884  PROC_PRIVILEGES_HASH,
885  FUNC_PRIVILEGES_HASH,
886  PROXY_USERS_ACL
887 };
888 
909 static
910 bool
911 set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
912 {
913  bool result= false;
914  /* Using old password protocol */
915  if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
916  {
917  get_salt_from_password(acl_user->salt, password);
918  acl_user->salt_len= SCRAMBLE_LENGTH;
919  }
920  else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
921  {
922  get_salt_from_password_323((ulong *) acl_user->salt, password);
923  acl_user->salt_len= SCRAMBLE_LENGTH_323;
924  }
925  else if (password_len == 0 || password == NULL)
926  {
927  /* This account doesn't use a password */
928  acl_user->salt_len= 0;
929  }
930  else if (acl_user->plugin.str == native_password_plugin_name.str ||
931  acl_user->plugin.str == old_password_plugin_name.str)
932  {
933  /* Unexpected format of the hash; login will probably be impossible */
934  result= true;
935  }
936 
937  /*
938  Since we're changing the password for the user we need to reset the
939  expiration flag.
940  */
941  acl_user->password_expired= false;
942 
943  return result;
944 }
945 
946 /*
947  Initialize structures responsible for user/db-level privilege checking and
948  load privilege information for them from tables in the 'mysql' database.
949 
950  SYNOPSIS
951  acl_init()
952  dont_read_acl_tables TRUE if we want to skip loading data from
953  privilege tables and disable privilege checking.
954 
955  NOTES
956  This function is mostly responsible for preparatory steps, main work
957  on initialization and grants loading is done in acl_reload().
958 
959  RETURN VALUES
960  0 ok
961  1 Could not initialize grant's
962 */
963 
964 my_bool acl_init(bool dont_read_acl_tables)
965 {
966  THD *thd;
967  my_bool return_val;
968  DBUG_ENTER("acl_init");
969 
970  acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
971  (my_hash_get_key) acl_entry_get_key,
972  (my_hash_free_key) free,
973  &my_charset_utf8_bin);
974 
975  /*
976  cache built-in native authentication plugins,
977  to avoid hash searches and a global mutex lock on every connect
978  */
979  native_password_plugin= my_plugin_lock_by_name(0,
980  &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
981  old_password_plugin= my_plugin_lock_by_name(0,
982  &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
983 
984  if (!native_password_plugin || !old_password_plugin)
985  DBUG_RETURN(1);
986 
987  if (dont_read_acl_tables)
988  {
989  DBUG_RETURN(0); /* purecov: tested */
990  }
991 
992  /*
993  To be able to run this from boot, we allocate a temporary THD
994  */
995  if (!(thd=new THD))
996  DBUG_RETURN(1); /* purecov: inspected */
997  thd->thread_stack= (char*) &thd;
998  thd->store_globals();
999  /*
1000  It is safe to call acl_reload() since acl_* arrays and hashes which
1001  will be freed there are global static objects and thus are initialized
1002  by zeros at startup.
1003  */
1004  return_val= acl_reload(thd);
1005  delete thd;
1006  /* Remember that we don't have a THD */
1007  my_pthread_setspecific_ptr(THR_THD, 0);
1008  DBUG_RETURN(return_val);
1009 }
1010 
1011 /*
1012  Initialize structures responsible for user/db-level privilege checking
1013  and load information about grants from open privilege tables.
1014 
1015  SYNOPSIS
1016  acl_load()
1017  thd Current thread
1018  tables List containing open "mysql.host", "mysql.user",
1019  "mysql.db" and "mysql.proxies_priv" tables in that order.
1020 
1021  RETURN VALUES
1022  FALSE Success
1023  TRUE Error
1024 */
1025 
1026 static my_bool acl_load(THD *thd, TABLE_LIST *tables)
1027 {
1028  TABLE *table;
1029  READ_RECORD read_record_info;
1030  my_bool return_val= TRUE;
1031  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
1032  char tmp_name[NAME_LEN+1];
1033  int password_length;
1034  char *password;
1035  uint password_len;
1036  sql_mode_t old_sql_mode= thd->variables.sql_mode;
1037  bool password_expired= false;
1038  DBUG_ENTER("acl_load");
1039 
1040  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
1041 
1042  grant_version++; /* Privileges updated */
1043 
1044 
1045  acl_cache->clear(1); // Clear locked hostname cache
1046 
1047  init_sql_alloc(&global_acl_memory, ACL_ALLOC_BLOCK_SIZE, 0);
1048  /*
1049  Prepare reading from the mysql.user table
1050  */
1051  if (init_read_record(&read_record_info, thd, table=tables[0].table,
1052  NULL, 1, 1, FALSE))
1053  goto end;
1054  table->use_all_columns();
1055  (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100);
1056 
1057  allow_all_hosts=0;
1058  while (!(read_record_info.read_record(&read_record_info)))
1059  {
1060  password_expired= false;
1061  /* Reading record from mysql.user */
1062  ACL_USER user;
1063  memset(&user, 0, sizeof(user));
1064  user.host.update_hostname(get_field(&global_acl_memory,
1065  table->field[MYSQL_USER_FIELD_HOST]));
1066  user.user= get_field(&global_acl_memory,
1067  table->field[MYSQL_USER_FIELD_USER]);
1068  if (check_no_resolve && hostname_requires_resolving(user.host.get_host()))
1069  {
1070  sql_print_warning("'user' entry '%s@%s' "
1071  "ignored in --skip-name-resolve mode.",
1072  user.user ? user.user : "",
1073  user.host.get_host() ? user.host.get_host() : "");
1074  continue;
1075  }
1076 
1077  /* Read legacy password */
1078  password= get_field(&global_acl_memory,
1079  table->field[MYSQL_USER_FIELD_PASSWORD]);
1080  password_len= password ? strlen(password) : 0;
1081  user.auth_string.str= password ? password : const_cast<char*>("");
1082  user.auth_string.length= password_len;
1083 
1084  {
1085  uint next_field;
1086  user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
1087  /*
1088  if it is pre 5.0.1 privilege table then map CREATE privilege on
1089  CREATE VIEW & SHOW VIEW privileges
1090  */
1091  if (table->s->fields <= 31 && (user.access & CREATE_ACL))
1092  user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
1093 
1094  /*
1095  if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
1096  CREATE PROCEDURE & ALTER PROCEDURE privileges
1097  */
1098  if (table->s->fields <= 33 && (user.access & CREATE_ACL))
1099  user.access|= CREATE_PROC_ACL;
1100  if (table->s->fields <= 33 && (user.access & ALTER_ACL))
1101  user.access|= ALTER_PROC_ACL;
1102 
1103  /*
1104  pre 5.0.3 did not have CREATE_USER_ACL
1105  */
1106  if (table->s->fields <= 36 && (user.access & GRANT_ACL))
1107  user.access|= CREATE_USER_ACL;
1108 
1109 
1110  /*
1111  if it is pre 5.1.6 privilege table then map CREATE privilege on
1112  CREATE|ALTER|DROP|EXECUTE EVENT
1113  */
1114  if (table->s->fields <= 37 && (user.access & SUPER_ACL))
1115  user.access|= EVENT_ACL;
1116 
1117  /*
1118  if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
1119  */
1120  if (table->s->fields <= 38 && (user.access & SUPER_ACL))
1121  user.access|= TRIGGER_ACL;
1122 
1123  user.sort= get_sort(2,user.host.get_host(),user.user);
1124 
1125  /* Starting from 4.0.2 we have more fields */
1126  if (table->s->fields >= 31)
1127  {
1128  char *ssl_type=
1129  get_field(thd->mem_root, table->field[MYSQL_USER_FIELD_SSL_TYPE]);
1130  if (!ssl_type)
1131  user.ssl_type=SSL_TYPE_NONE;
1132  else if (!strcmp(ssl_type, "ANY"))
1133  user.ssl_type=SSL_TYPE_ANY;
1134  else if (!strcmp(ssl_type, "X509"))
1135  user.ssl_type=SSL_TYPE_X509;
1136  else /* !strcmp(ssl_type, "SPECIFIED") */
1137  user.ssl_type=SSL_TYPE_SPECIFIED;
1138 
1139  user.ssl_cipher=
1140  get_field(&global_acl_memory, table->field[MYSQL_USER_FIELD_SSL_CIPHER]);
1141  user.x509_issuer=
1142  get_field(&global_acl_memory, table->field[MYSQL_USER_FIELD_X509_ISSUER]);
1143  user.x509_subject=
1144  get_field(&global_acl_memory, table->field[MYSQL_USER_FIELD_X509_SUBJECT]);
1145 
1146  char *ptr= get_field(thd->mem_root,
1147  table->field[MYSQL_USER_FIELD_MAX_QUESTIONS]);
1148  user.user_resource.questions=ptr ? atoi(ptr) : 0;
1149  ptr= get_field(thd->mem_root,
1150  table->field[MYSQL_USER_FIELD_MAX_UPDATES]);
1151  user.user_resource.updates=ptr ? atoi(ptr) : 0;
1152  ptr= get_field(thd->mem_root,
1153  table->field[MYSQL_USER_FIELD_MAX_CONNECTIONS]);
1154  user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
1155  if (user.user_resource.questions || user.user_resource.updates ||
1156  user.user_resource.conn_per_hour)
1157  mqh_used=1;
1158 
1159  if (table->s->fields > MYSQL_USER_FIELD_MAX_USER_CONNECTIONS)
1160  {
1161  /* Starting from 5.0.3 we have max_user_connections field */
1162  ptr= get_field(thd->mem_root,
1163  table->field[MYSQL_USER_FIELD_MAX_USER_CONNECTIONS]);
1164  user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
1165  }
1166 
1167  if (table->s->fields >= 41)
1168  {
1169  /* We may have plugin & auth_String fields */
1170  char *tmpstr= get_field(&global_acl_memory,
1171  table->field[MYSQL_USER_FIELD_PLUGIN]);
1172  if (tmpstr)
1173  {
1174  /*
1175  By comparing the plugin with the built in plugins it is possible
1176  to optimize the string allocation and comparision.
1177  */
1178  if (my_strcasecmp(system_charset_info, tmpstr,
1179  native_password_plugin_name.str) == 0)
1180  user.plugin= native_password_plugin_name;
1181  else
1182  if (my_strcasecmp(system_charset_info, tmpstr,
1183  old_password_plugin_name.str) == 0)
1184  user.plugin= old_password_plugin_name;
1185 #if defined(HAVE_OPENSSL)
1186  else
1187  if (my_strcasecmp(system_charset_info, tmpstr,
1188  sha256_password_plugin_name.str) == 0)
1189  user.plugin= sha256_password_plugin_name;
1190 #endif
1191  else
1192  {
1193  user.plugin.str= tmpstr;
1194  user.plugin.length= strlen(tmpstr);
1195  }
1196  if (user.auth_string.length &&
1197  user.plugin.str != native_password_plugin_name.str &&
1198  user.plugin.str != old_password_plugin_name.str)
1199  {
1200  sql_print_warning("'user' entry '%s@%s' has both a password "
1201  "and an authentication plugin specified. The "
1202  "password will be ignored.",
1203  user.user ? user.user : "",
1204  user.host.get_host() ? user.host.get_host() : "");
1205  }
1206  user.auth_string.str=
1207  get_field(&global_acl_memory,
1208  table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]);
1209  if (!user.auth_string.str)
1210  user.auth_string.str= const_cast<char*>("");
1211  user.auth_string.length= strlen(user.auth_string.str);
1212  }
1213  else /* skip auth_string if there's no plugin */
1214  next_field++;
1215  }
1216 
1217  if (table->s->fields > MYSQL_USER_FIELD_PASSWORD_EXPIRED)
1218  {
1219  char *tmpstr= get_field(&global_acl_memory,
1220  table->field[MYSQL_USER_FIELD_PASSWORD_EXPIRED]);
1221  if (tmpstr && (*tmpstr == 'Y' || *tmpstr == 'y'))
1222  {
1223  user.password_expired= true;
1224 
1225  if (!auth_plugin_supports_expiration(user.plugin.str))
1226  {
1227  sql_print_warning("'user' entry '%s@%s' has the password ignore "
1228  "flag raised, but its authentication plugin "
1229  "doesn't support password expiration. "
1230  "The user id will be ignored.",
1231  user.user ? user.user : "",
1232  user.host.get_host() ? user.host.get_host() : "");
1233  continue;
1234  }
1235  password_expired= true;
1236  }
1237  }
1238  } // end if (table->s->fields >= 31)
1239  else
1240  {
1241  user.ssl_type=SSL_TYPE_NONE;
1242 #ifndef TO_BE_REMOVED
1243  if (table->s->fields <= 13)
1244  { // Without grant
1245  if (user.access & CREATE_ACL)
1246  user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
1247  }
1248  /* Convert old privileges */
1249  user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
1250  if (user.access & FILE_ACL)
1251  user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
1252  if (user.access & PROCESS_ACL)
1253  user.access|= SUPER_ACL | EXECUTE_ACL;
1254 #endif
1255  }
1256  if (!user.plugin.length)
1257  {
1258  /*
1259  Set plugin deduced from password length.
1260  We can reach here in two cases:
1261  1. mysql.user doesn't have plugin field
1262  2. Plugin field is empty
1263  */
1264  user.plugin= native_password_plugin_name;
1265  if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1266  user.plugin= old_password_plugin_name;
1267 
1268  }
1269  /*
1270  Transform hex to octets and adjust the format.
1271  */
1272  if (set_user_salt(&user, password, password_len))
1273  {
1274  sql_print_warning("Found invalid password for user: '%s@%s'; "
1275  "Ignoring user", user.user ? user.user : "",
1276  user.host.get_host() ? user.host.get_host() : "");
1277  continue;
1278  }
1279 
1280  /* set_user_salt resets expiration flag so restore it */
1281  user.password_expired= password_expired;
1282 
1283  (void) push_dynamic(&acl_users,(uchar*) &user);
1284  if (user.host.check_allow_all_hosts())
1285  allow_all_hosts=1; // Anyone can connect
1286  }
1287  } // END while reading records from the mysql.user table
1288 
1289  my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
1290  sizeof(ACL_USER),(qsort_cmp) acl_compare);
1291  end_read_record(&read_record_info);
1292  freeze_size(&acl_users);
1293 
1294  /* Legacy password integrity checks ----------------------------------------*/
1295  {
1296  password_length= table->field[MYSQL_USER_FIELD_PASSWORD]->field_length /
1297  table->field[MYSQL_USER_FIELD_PASSWORD]->charset()->mbmaxlen;
1298  if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1299  {
1300  sql_print_error("Fatal error: mysql.user table is damaged or in "
1301  "unsupported 3.20 format.");
1302  goto end;
1303  }
1304 
1305  DBUG_PRINT("info",("user table fields: %d, password length: %d",
1306  table->s->fields, password_length));
1307 
1308  mysql_mutex_lock(&LOCK_global_system_variables);
1309  if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
1310  {
1311  if (opt_secure_auth)
1312  {
1313  mysql_mutex_unlock(&LOCK_global_system_variables);
1314  sql_print_error("Fatal error: mysql.user table is in old format, "
1315  "but server started with --secure-auth option.");
1316  goto end;
1317  }
1318  mysql_user_table_is_in_short_password_format= true;
1319  if (global_system_variables.old_passwords)
1320  mysql_mutex_unlock(&LOCK_global_system_variables);
1321  else
1322  {
1323  global_system_variables.old_passwords= 1;
1324  mysql_mutex_unlock(&LOCK_global_system_variables);
1325  sql_print_warning("mysql.user table is not updated to new password format; "
1326  "Disabling new password usage until "
1327  "mysql_fix_privilege_tables is run");
1328  }
1329  thd->variables.old_passwords= 1;
1330  }
1331  else
1332  {
1333  mysql_user_table_is_in_short_password_format= false;
1334  mysql_mutex_unlock(&LOCK_global_system_variables);
1335  }
1336  } /* End legacy password integrity checks ----------------------------------*/
1337 
1338  /*
1339  Prepare reading from the mysql.db table
1340  */
1341  if (init_read_record(&read_record_info, thd, table=tables[1].table,
1342  NULL, 1, 1, FALSE))
1343  goto end;
1344  table->use_all_columns();
1345  (void) my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100);
1346  while (!(read_record_info.read_record(&read_record_info)))
1347  {
1348  /* Reading record in mysql.db */
1349  ACL_DB db;
1350  db.host.update_hostname(get_field(&global_acl_memory,
1351  table->field[MYSQL_DB_FIELD_HOST]));
1352  db.db=get_field(&global_acl_memory, table->field[MYSQL_DB_FIELD_DB]);
1353  if (!db.db)
1354  {
1355  sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
1356  continue;
1357  }
1358  db.user=get_field(&global_acl_memory, table->field[MYSQL_DB_FIELD_USER]);
1359  if (check_no_resolve && hostname_requires_resolving(db.host.get_host()))
1360  {
1361  sql_print_warning("'db' entry '%s %s@%s' "
1362  "ignored in --skip-name-resolve mode.",
1363  db.db,
1364  db.user ? db.user : "",
1365  db.host.get_host() ? db.host.get_host() : "");
1366  continue;
1367  }
1368  db.access=get_access(table,3);
1369  db.access=fix_rights_for_db(db.access);
1370  if (lower_case_table_names)
1371  {
1372  /*
1373  convert db to lower case and give a warning if the db wasn't
1374  already in lower case
1375  */
1376  (void)strmov(tmp_name, db.db);
1377  my_casedn_str(files_charset_info, db.db);
1378  if (strcmp(db.db, tmp_name) != 0)
1379  {
1380  sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
1381  "case that has been forced to lowercase because "
1382  "lower_case_table_names is set. It will not be "
1383  "possible to remove this privilege using REVOKE.",
1384  db.db,
1385  db.user ? db.user : "",
1386  db.host.get_host() ? db.host.get_host() : "");
1387  }
1388  }
1389  db.sort=get_sort(3,db.host.get_host(),db.db,db.user);
1390 #ifndef TO_BE_REMOVED
1391  if (table->s->fields <= 9)
1392  { // Without grant
1393  if (db.access & CREATE_ACL)
1394  db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
1395  }
1396 #endif
1397  (void) push_dynamic(&acl_dbs,(uchar*) &db);
1398  } // END reading records from mysql.db tables
1399 
1400  my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
1401  sizeof(ACL_DB),(qsort_cmp) acl_compare);
1402  end_read_record(&read_record_info);
1403  freeze_size(&acl_dbs);
1404 
1405  /* Prepare to read records from the mysql.proxies_priv table */
1406  (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER),
1407  50, 100);
1408  if (tables[2].table)
1409  {
1410  if (init_read_record(&read_record_info, thd, table= tables[2].table,
1411  NULL, 1, 1, FALSE))
1412  goto end;
1413  table->use_all_columns();
1414  while (!(read_record_info.read_record(&read_record_info)))
1415  {
1416  /* Reading record in mysql.proxies_priv */
1417  ACL_PROXY_USER proxy;
1418  proxy.init(table, &global_acl_memory);
1419  if (proxy.check_validity(check_no_resolve))
1420  continue;
1421  if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
1422  {
1423  end_read_record(&read_record_info);
1424  goto end;
1425  }
1426  } // END reading records from the mysql.proxies_priv table
1427 
1428  my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
1429  acl_proxy_users.elements,
1430  sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
1431  end_read_record(&read_record_info);
1432  }
1433  else
1434  {
1435  sql_print_error("Missing system table mysql.proxies_priv; "
1436  "please run mysql_upgrade to create it");
1437  }
1438  freeze_size(&acl_proxy_users);
1439 
1440  validate_user_plugin_records();
1441  init_check_host();
1442 
1443  initialized=1;
1444  return_val= FALSE;
1445 
1446 end:
1447  thd->variables.sql_mode= old_sql_mode;
1448  DBUG_RETURN(return_val);
1449 }
1450 
1451 
1452 void acl_free(bool end)
1453 {
1454  free_root(&global_acl_memory,MYF(0));
1455  delete_dynamic(&acl_users);
1456  delete_dynamic(&acl_dbs);
1457  delete_dynamic(&acl_wild_hosts);
1458  delete_dynamic(&acl_proxy_users);
1459  my_hash_free(&acl_check_hosts);
1460  plugin_unlock(0, native_password_plugin);
1461  plugin_unlock(0, old_password_plugin);
1462  if (!end)
1463  acl_cache->clear(1); /* purecov: inspected */
1464  else
1465  {
1466  delete acl_cache;
1467  acl_cache=0;
1468  }
1469 }
1470 
1471 
1484 void close_acl_tables(THD *thd)
1485 {
1486  /* Transaction rollback request by SE is unlikely. Still we handle it. */
1487  if (thd->transaction_rollback_request)
1488  {
1489  trans_rollback_stmt(thd);
1490  trans_rollback_implicit(thd);
1491  }
1492  else
1493  {
1494 #ifndef DBUG_OFF
1495  bool res=
1496 #endif
1497  trans_commit_stmt(thd);
1498  DBUG_ASSERT(res == false);
1499  }
1500 
1501  close_mysql_tables(thd);
1502 }
1503 
1504 
1517 static bool acl_trans_commit_and_close_tables(THD *thd)
1518 {
1519  bool result;
1520  bool rollback= false;
1521 
1522  /*
1523  Try to commit a transaction even if we had some failures.
1524 
1525  Without this step changes to privilege tables will be rolled back at the
1526  end of mysql_execute_command() in the presence of error, leaving on-disk
1527  and in-memory descriptions of privileges out of sync and making behavior
1528  of ACL statements for transactional tables incompatible with legacy
1529  behavior.
1530 
1531  We need to commit both statement and normal transaction to make behavior
1532  consistent with both autocommit on and off.
1533 
1534  It is safe to do so since ACL statement always do implicit commit at the
1535  end of statement.
1536  */
1537  DBUG_ASSERT(stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END));
1538 
1539  if (thd->transaction_rollback_request)
1540  {
1541  /*
1542  Transaction rollback request by SE is unlikely. Still let us
1543  handle it and also do ACL reload if it happens.
1544  */
1545  result= trans_rollback_stmt(thd);
1546  result|= trans_rollback_implicit(thd);
1547  rollback= true;
1548  }
1549  else
1550  {
1551  result= trans_commit_stmt(thd);
1552  result|= trans_commit_implicit(thd);
1553  }
1554  close_thread_tables(thd);
1555  thd->mdl_context.release_transactional_locks();
1556 
1557  if (result || rollback)
1558  {
1559  /*
1560  Try to bring in-memory structures back in sync with on-disk data if we
1561  have failed to commit our changes.
1562  */
1563  (void) acl_reload(thd);
1564  (void) grant_reload(thd);
1565  }
1566 
1567  return result;
1568 }
1569 
1570 
1571 /*
1572  Forget current user/db-level privileges and read new privileges
1573  from the privilege tables.
1574 
1575  SYNOPSIS
1576  acl_reload()
1577  thd Current thread
1578 
1579  NOTE
1580  All tables of calling thread which were open and locked by LOCK TABLES
1581  statement will be unlocked and closed.
1582  This function is also used for initialization of structures responsible
1583  for user/db-level privilege checking.
1584 
1585  RETURN VALUE
1586  FALSE Success
1587  TRUE Failure
1588 */
1589 
1590 my_bool acl_reload(THD *thd)
1591 {
1592  TABLE_LIST tables[3];
1593  DYNAMIC_ARRAY old_acl_users, old_acl_dbs, old_acl_proxy_users;
1594  MEM_ROOT old_mem;
1595  bool old_initialized;
1596  my_bool return_val= TRUE;
1597  DBUG_ENTER("acl_reload");
1598 
1599  /*
1600  To avoid deadlocks we should obtain table locks before
1601  obtaining acl_cache->lock mutex.
1602  */
1603  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
1604  C_STRING_WITH_LEN("user"), "user", TL_READ);
1605  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
1606  C_STRING_WITH_LEN("db"), "db", TL_READ);
1607  tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
1608  C_STRING_WITH_LEN("proxies_priv"),
1609  "proxies_priv", TL_READ);
1610  tables[0].next_local= tables[0].next_global= tables + 1;
1611  tables[1].next_local= tables[1].next_global= tables + 2;
1612  tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
1613  tables[2].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
1614 
1615  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
1616  {
1617  /*
1618  Execution might have been interrupted; only print the error message
1619  if a user error condition has been raised.
1620  */
1621  if (thd->get_stmt_da()->is_error())
1622  {
1623  sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
1624  thd->get_stmt_da()->message());
1625  }
1626  goto end;
1627  }
1628 
1629  if ((old_initialized=initialized))
1630  mysql_mutex_lock(&acl_cache->lock);
1631 
1632  old_acl_users= acl_users;
1633  old_acl_proxy_users= acl_proxy_users;
1634  old_acl_dbs= acl_dbs;
1635  old_mem= global_acl_memory;
1636  delete_dynamic(&acl_wild_hosts);
1637  my_hash_free(&acl_check_hosts);
1638 
1639  if ((return_val= acl_load(thd, tables)))
1640  { // Error. Revert to old list
1641  DBUG_PRINT("error",("Reverting to old privileges"));
1642  acl_free(); /* purecov: inspected */
1643  acl_users= old_acl_users;
1644  acl_proxy_users= old_acl_proxy_users;
1645  acl_dbs= old_acl_dbs;
1646  global_acl_memory= old_mem;
1647  init_check_host();
1648  }
1649  else
1650  {
1651  free_root(&old_mem,MYF(0));
1652  delete_dynamic(&old_acl_users);
1653  delete_dynamic(&old_acl_proxy_users);
1654  delete_dynamic(&old_acl_dbs);
1655  }
1656  if (old_initialized)
1657  mysql_mutex_unlock(&acl_cache->lock);
1658 end:
1659  close_acl_tables(thd);
1660  DBUG_RETURN(return_val);
1661 }
1662 
1663 
1664 /*
1665  Get all access bits from table after fieldnr
1666 
1667  IMPLEMENTATION
1668  We know that the access privileges ends when there is no more fields
1669  or the field is not an enum with two elements.
1670 
1671  SYNOPSIS
1672  get_access()
1673  form an open table to read privileges from.
1674  The record should be already read in table->record[0]
1675  fieldnr number of the first privilege (that is ENUM('N','Y') field
1676  next_field on return - number of the field next to the last ENUM
1677  (unless next_field == 0)
1678 
1679  RETURN VALUE
1680  privilege mask
1681 */
1682 
1683 static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
1684 {
1685  ulong access_bits=0,bit;
1686  char buff[2];
1687  String res(buff,sizeof(buff),&my_charset_latin1);
1688  Field **pos;
1689 
1690  for (pos=form->field+fieldnr, bit=1;
1691  *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
1692  ((Field_enum*) (*pos))->typelib->count == 2 ;
1693  pos++, fieldnr++, bit<<=1)
1694  {
1695  (*pos)->val_str(&res);
1696  if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
1697  access_bits|= bit;
1698  }
1699  if (next_field)
1700  *next_field=fieldnr;
1701  return access_bits;
1702 }
1703 
1704 
1705 /*
1706  Return a number which, if sorted 'desc', puts strings in this order:
1707  no wildcards
1708  wildcards
1709  empty string
1710 */
1711 
1712 static ulong get_sort(uint count,...)
1713 {
1714  va_list args;
1715  va_start(args,count);
1716  ulong sort=0;
1717 
1718  /* Should not use this function with more than 4 arguments for compare. */
1719  DBUG_ASSERT(count <= 4);
1720 
1721  while (count--)
1722  {
1723  char *start, *str= va_arg(args,char*);
1724  uint chars= 0;
1725  uint wild_pos= 0; /* first wildcard position */
1726 
1727  if ((start= str))
1728  {
1729  for (; *str ; str++)
1730  {
1731  if (*str == wild_prefix && str[1])
1732  str++;
1733  else if (*str == wild_many || *str == wild_one)
1734  {
1735  wild_pos= (uint) (str - start) + 1;
1736  break;
1737  }
1738  chars= 128; // Marker that chars existed
1739  }
1740  }
1741  sort= (sort << 8) + (wild_pos ? min(wild_pos, 127U) : chars);
1742  }
1743  va_end(args);
1744  return sort;
1745 }
1746 
1747 
1748 static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
1749 {
1750  if (a->sort > b->sort)
1751  return -1;
1752  if (a->sort < b->sort)
1753  return 1;
1754  return 0;
1755 }
1756 
1757 
1758 /*
1759  Gets user credentials without authentication and resource limit checks.
1760 
1761  SYNOPSIS
1762  acl_getroot()
1763  sctx Context which should be initialized
1764  user user name
1765  host host name
1766  ip IP
1767  db current data base name
1768 
1769  RETURN
1770  FALSE OK
1771  TRUE Error
1772 */
1773 
1774 bool acl_getroot(Security_context *sctx, char *user, char *host,
1775  char *ip, char *db)
1776 {
1777  int res= 1;
1778  uint i;
1779  ACL_USER *acl_user= 0;
1780  DBUG_ENTER("acl_getroot");
1781 
1782  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
1783  (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
1784  user, (db ? db : "(NULL)")));
1785  sctx->user= user;
1786  sctx->set_host(host);
1787  sctx->set_ip(ip);
1788  sctx->host_or_ip= host ? host : (ip ? ip : "");
1789 
1790  if (!initialized)
1791  {
1792  /*
1793  here if mysqld's been started with --skip-grant-tables option.
1794  */
1795  sctx->skip_grants();
1796  DBUG_RETURN(FALSE);
1797  }
1798 
1799  mysql_mutex_lock(&acl_cache->lock);
1800 
1801  sctx->master_access= 0;
1802  sctx->db_access= 0;
1803  *sctx->priv_user= *sctx->priv_host= 0;
1804 
1805  /*
1806  Find acl entry in user database.
1807  This is specially tailored to suit the check we do for CALL of
1808  a stored procedure; user is set to what is actually a
1809  priv_user, which can be ''.
1810  */
1811  for (i=0 ; i < acl_users.elements ; i++)
1812  {
1813  ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
1814  if ((!acl_user_tmp->user && !user[0]) ||
1815  (acl_user_tmp->user && strcmp(user, acl_user_tmp->user) == 0))
1816  {
1817  if (acl_user_tmp->host.compare_hostname(host, ip))
1818  {
1819  acl_user= acl_user_tmp;
1820  res= 0;
1821  break;
1822  }
1823  }
1824  }
1825 
1826  if (acl_user)
1827  {
1828  for (i=0 ; i < acl_dbs.elements ; i++)
1829  {
1830  ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
1831  if (!acl_db->user ||
1832  (user && user[0] && !strcmp(user, acl_db->user)))
1833  {
1834  if (acl_db->host.compare_hostname(host, ip))
1835  {
1836  if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
1837  {
1838  sctx->db_access= acl_db->access;
1839  break;
1840  }
1841  }
1842  }
1843  }
1844  sctx->master_access= acl_user->access;
1845 
1846  if (acl_user->user)
1847  strmake(sctx->priv_user, user, USERNAME_LENGTH);
1848  else
1849  *sctx->priv_user= 0;
1850 
1851  if (acl_user->host.get_host())
1852  strmake(sctx->priv_host, acl_user->host.get_host(), MAX_HOSTNAME - 1);
1853  else
1854  *sctx->priv_host= 0;
1855 
1856  sctx->password_expired= acl_user->password_expired;
1857  }
1858  mysql_mutex_unlock(&acl_cache->lock);
1859  DBUG_RETURN(res);
1860 }
1861 
1862 static uchar* check_get_key(ACL_USER *buff, size_t *length,
1863  my_bool not_used __attribute__((unused)))
1864 {
1865  *length=buff->host.get_host_len();
1866  return (uchar*) buff->host.get_host();
1867 }
1868 
1869 
1870 static void acl_update_user(const char *user, const char *host,
1871  const char *password, uint password_len,
1872  enum SSL_type ssl_type,
1873  const char *ssl_cipher,
1874  const char *x509_issuer,
1875  const char *x509_subject,
1876  USER_RESOURCES *mqh,
1877  ulong privileges,
1878  const LEX_STRING *plugin,
1879  const LEX_STRING *auth)
1880 {
1881  DBUG_ENTER("acl_update_user");
1882  mysql_mutex_assert_owner(&acl_cache->lock);
1883  for (uint i=0 ; i < acl_users.elements ; i++)
1884  {
1885  ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
1886  if ((!acl_user->user && !user[0]) ||
1887  (acl_user->user && !strcmp(user,acl_user->user)))
1888  {
1889  if ((!acl_user->host.get_host() && !host[0]) ||
1890  (acl_user->host.get_host() &&
1891  !my_strcasecmp(system_charset_info, host, acl_user->host.get_host())))
1892  {
1893  if (plugin->length > 0)
1894  {
1895  acl_user->plugin= *plugin;
1896  optimize_plugin_compare_by_pointer(&acl_user->plugin);
1897  if (!auth_plugin_is_built_in(acl_user->plugin.str))
1898  acl_user->plugin.str= strmake_root(&global_acl_memory, plugin->str, plugin->length);
1899  acl_user->auth_string.str= auth->str ?
1900  strmake_root(&global_acl_memory, auth->str,
1901  auth->length) : const_cast<char*>("");
1902  acl_user->auth_string.length= auth->length;
1903  }
1904  acl_user->access=privileges;
1905  if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1906  acl_user->user_resource.questions=mqh->questions;
1907  if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1908  acl_user->user_resource.updates=mqh->updates;
1909  if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
1910  acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
1911  if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
1912  acl_user->user_resource.user_conn= mqh->user_conn;
1913  if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
1914  {
1915  acl_user->ssl_type= ssl_type;
1916  acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&global_acl_memory,
1917  ssl_cipher) : 0);
1918  acl_user->x509_issuer= (x509_issuer ? strdup_root(&global_acl_memory,
1919  x509_issuer) : 0);
1920  acl_user->x509_subject= (x509_subject ?
1921  strdup_root(&global_acl_memory, x509_subject) : 0);
1922  }
1923 
1924 
1925  if (password)
1926  {
1927  /*
1928  We just assert the hash is valid here since it's already
1929  checked in replace_user_table().
1930  */
1931  int hash_not_ok= set_user_salt(acl_user, password, password_len);
1932 
1933  DBUG_ASSERT(hash_not_ok == 0);
1934  /* dummy addition to fool the compiler */
1935  password_len+= hash_not_ok;
1936  }
1937  /* search complete: */
1938  break;
1939  }
1940  }
1941  }
1942  DBUG_VOID_RETURN;
1943 }
1944 
1945 
1946 static void acl_insert_user(const char *user, const char *host,
1947  const char *password, uint password_len,
1948  enum SSL_type ssl_type,
1949  const char *ssl_cipher,
1950  const char *x509_issuer,
1951  const char *x509_subject,
1952  USER_RESOURCES *mqh,
1953  ulong privileges,
1954  const LEX_STRING *plugin,
1955  const LEX_STRING *auth)
1956 {
1957  DBUG_ENTER("acl_insert_user");
1958  ACL_USER acl_user;
1959  int hash_not_ok;
1960 
1961  mysql_mutex_assert_owner(&acl_cache->lock);
1962 
1963  acl_user.user= *user ? strdup_root(&global_acl_memory,user) : 0;
1964  acl_user.host.update_hostname(*host ? strdup_root(&global_acl_memory, host) : 0);
1965  if (plugin->str[0])
1966  {
1967  acl_user.plugin= *plugin;
1968  optimize_plugin_compare_by_pointer(&acl_user.plugin);
1969  if (!auth_plugin_is_built_in(acl_user.plugin.str))
1970  acl_user.plugin.str= strmake_root(&global_acl_memory, plugin->str, plugin->length);
1971  acl_user.auth_string.str= auth->str ?
1972  strmake_root(&global_acl_memory, auth->str,
1973  auth->length) : const_cast<char*>("");
1974  acl_user.auth_string.length= auth->length;
1975 
1976  optimize_plugin_compare_by_pointer(&acl_user.plugin);
1977  }
1978  else
1979  {
1980  acl_user.plugin= password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 ?
1981  old_password_plugin_name : native_password_plugin_name;
1982  acl_user.auth_string.str= const_cast<char*>("");
1983  acl_user.auth_string.length= 0;
1984  }
1985 
1986  acl_user.access= privileges;
1987  acl_user.user_resource= *mqh;
1988  acl_user.sort= get_sort(2,acl_user.host.get_host(), acl_user.user);
1989  //acl_user.hostname_length=(uint) strlen(host);
1990  acl_user.ssl_type=
1991  (ssl_type != SSL_TYPE_NOT_SPECIFIED ? ssl_type : SSL_TYPE_NONE);
1992  acl_user.ssl_cipher=
1993  ssl_cipher ? strdup_root(&global_acl_memory, ssl_cipher) : 0;
1994  acl_user.x509_issuer=
1995  x509_issuer ? strdup_root(&global_acl_memory, x509_issuer) : 0;
1996  acl_user.x509_subject=
1997  x509_subject ? strdup_root(&global_acl_memory, x509_subject) : 0;
1998 
1999  hash_not_ok= set_user_salt(&acl_user, password, password_len);
2000  DBUG_ASSERT(hash_not_ok == 0);
2001  /* dummy addition to fool the compiler */
2002  password_len+= hash_not_ok;
2003 
2004 
2005  (void) push_dynamic(&acl_users,(uchar*) &acl_user);
2006  if (acl_user.host.check_allow_all_hosts())
2007  allow_all_hosts=1; // Anyone can connect /* purecov: tested */
2008  my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
2009  sizeof(ACL_USER),(qsort_cmp) acl_compare);
2010 
2011  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
2012  rebuild_check_host();
2013  DBUG_VOID_RETURN;
2014 }
2015 
2016 
2017 static void acl_update_db(const char *user, const char *host, const char *db,
2018  ulong privileges)
2019 {
2020  mysql_mutex_assert_owner(&acl_cache->lock);
2021 
2022  for (uint i=0 ; i < acl_dbs.elements ; i++)
2023  {
2024  ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
2025  if ((!acl_db->user && !user[0]) ||
2026  (acl_db->user &&
2027  !strcmp(user,acl_db->user)))
2028  {
2029  if ((!acl_db->host.get_host() && !host[0]) ||
2030  (acl_db->host.get_host() &&
2031  !strcmp(host, acl_db->host.get_host())))
2032  {
2033  if ((!acl_db->db && !db[0]) ||
2034  (acl_db->db && !strcmp(db,acl_db->db)))
2035  {
2036  if (privileges)
2037  acl_db->access=privileges;
2038  else
2039  delete_dynamic_element(&acl_dbs,i);
2040  }
2041  }
2042  }
2043  }
2044 }
2045 
2046 
2047 /*
2048  Insert a user/db/host combination into the global acl_cache
2049 
2050  SYNOPSIS
2051  acl_insert_db()
2052  user User name
2053  host Host name
2054  db Database name
2055  privileges Bitmap of privileges
2056 
2057  NOTES
2058  acl_cache->lock must be locked when calling this
2059 */
2060 
2061 static void acl_insert_db(const char *user, const char *host, const char *db,
2062  ulong privileges)
2063 {
2064  ACL_DB acl_db;
2065  mysql_mutex_assert_owner(&acl_cache->lock);
2066  acl_db.user= strdup_root(&global_acl_memory,user);
2067  acl_db.host.update_hostname(*host ? strdup_root(&global_acl_memory, host) : 0);
2068  acl_db.db= strdup_root(&global_acl_memory, db);
2069  acl_db.access= privileges;
2070  acl_db.sort= get_sort(3,acl_db.host.get_host(), acl_db.db, acl_db.user);
2071  (void) push_dynamic(&acl_dbs, (uchar*) &acl_db);
2072  my_qsort((uchar*) dynamic_element(&acl_dbs, 0, ACL_DB*), acl_dbs.elements,
2073  sizeof(ACL_DB),(qsort_cmp) acl_compare);
2074 }
2075 
2076 
2077 
2078 /*
2079  Get privilege for a host, user and db combination
2080 
2081  as db_is_pattern changes the semantics of comparison,
2082  acl_cache is not used if db_is_pattern is set.
2083 */
2084 
2085 ulong acl_get(const char *host, const char *ip,
2086  const char *user, const char *db, my_bool db_is_pattern)
2087 {
2088  ulong host_access= ~(ulong)0, db_access= 0;
2089  uint i;
2090  size_t key_length, copy_length;
2091  char key[ACL_KEY_LENGTH],*tmp_db,*end;
2092  acl_entry *entry;
2093  DBUG_ENTER("acl_get");
2094 
2095  copy_length= (size_t) (strlen(ip ? ip : "") +
2096  strlen(user ? user : "") +
2097  strlen(db ? db : ""));
2098  /*
2099  Make sure that strmov() operations do not result in buffer overflow.
2100  */
2101  if (copy_length >= ACL_KEY_LENGTH)
2102  DBUG_RETURN(0);
2103 
2104  mysql_mutex_lock(&acl_cache->lock);
2105  end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db);
2106  if (lower_case_table_names)
2107  {
2108  my_casedn_str(files_charset_info, tmp_db);
2109  db=tmp_db;
2110  }
2111  key_length= (size_t) (end-key);
2112  if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key,
2113  key_length)))
2114  {
2115  db_access=entry->access;
2116  mysql_mutex_unlock(&acl_cache->lock);
2117  DBUG_PRINT("exit", ("access: 0x%lx", db_access));
2118  DBUG_RETURN(db_access);
2119  }
2120 
2121  /*
2122  Check if there are some access rights for database and user
2123  */
2124  for (i=0 ; i < acl_dbs.elements ; i++)
2125  {
2126  ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
2127  if (!acl_db->user || !strcmp(user,acl_db->user))
2128  {
2129  if (acl_db->host.compare_hostname(host,ip))
2130  {
2131  if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
2132  {
2133  db_access=acl_db->access;
2134  if (acl_db->host.get_host())
2135  goto exit; // Fully specified. Take it
2136  break; /* purecov: tested */
2137  }
2138  }
2139  }
2140  }
2141  if (!db_access)
2142  goto exit; // Can't be better
2143 
2144 exit:
2145  /* Save entry in cache for quick retrieval */
2146  if (!db_is_pattern &&
2147  (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
2148  {
2149  entry->access=(db_access & host_access);
2150  entry->length=key_length;
2151  memcpy((uchar*) entry->key,key,key_length);
2152  acl_cache->add(entry);
2153  }
2154  mysql_mutex_unlock(&acl_cache->lock);
2155  DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
2156  DBUG_RETURN(db_access & host_access);
2157 }
2158 
2159 /*
2160  Check if there are any possible matching entries for this host
2161 
2162  NOTES
2163  All host names without wild cards are stored in a hash table,
2164  entries with wildcards are stored in a dynamic array
2165 */
2166 
2167 static void init_check_host(void)
2168 {
2169  DBUG_ENTER("init_check_host");
2170  (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(class ACL_HOST_AND_IP),
2171  acl_users.elements,1);
2172  (void) my_hash_init(&acl_check_hosts,system_charset_info,
2173  acl_users.elements, 0, 0,
2174  (my_hash_get_key) check_get_key, 0, 0);
2175  if (!allow_all_hosts)
2176  {
2177  for (uint i=0 ; i < acl_users.elements ; i++)
2178  {
2179  ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
2180  if (acl_user->host.has_wildcard())
2181  { // Has wildcard
2182  uint j;
2183  for (j=0 ; j < acl_wild_hosts.elements ; j++)
2184  { // Check if host already exists
2185  ACL_HOST_AND_IP *acl=dynamic_element(&acl_wild_hosts,j,
2186  ACL_HOST_AND_IP *);
2187  if (!my_strcasecmp(system_charset_info,
2188  acl_user->host.get_host(), acl->get_host()))
2189  break; // already stored
2190  }
2191  if (j == acl_wild_hosts.elements) // If new
2192  (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
2193  }
2194  else if (!my_hash_search(&acl_check_hosts,(uchar*)
2195  acl_user->host.get_host(),
2196  strlen(acl_user->host.get_host())))
2197  {
2198  if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
2199  { // End of memory
2200  allow_all_hosts=1; // Should never happen
2201  DBUG_VOID_RETURN;
2202  }
2203  }
2204  }
2205  }
2206  freeze_size(&acl_wild_hosts);
2207  freeze_size(&acl_check_hosts.array);
2208  DBUG_VOID_RETURN;
2209 }
2210 
2211 
2212 /*
2213  Rebuild lists used for checking of allowed hosts
2214 
2215  We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
2216  dropping or renaming user, since they contain pointers to elements of
2217  'acl_user' array, which are invalidated by drop operation, and use
2218  ACL_USER::host::hostname as a key, which is changed by rename.
2219 */
2220 void rebuild_check_host(void)
2221 {
2222  delete_dynamic(&acl_wild_hosts);
2223  my_hash_free(&acl_check_hosts);
2224  init_check_host();
2225 }
2226 
2227 
2228 /* Return true if there is no users that can match the given host */
2229 
2230 bool acl_check_host(const char *host, const char *ip)
2231 {
2232  if (allow_all_hosts)
2233  return 0;
2234  mysql_mutex_lock(&acl_cache->lock);
2235 
2236  if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
2237  (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
2238  {
2239  mysql_mutex_unlock(&acl_cache->lock);
2240  return 0; // Found host
2241  }
2242  for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
2243  {
2244  ACL_HOST_AND_IP *acl=dynamic_element(&acl_wild_hosts,i,ACL_HOST_AND_IP*);
2245  if (acl->compare_hostname(host, ip))
2246  {
2247  mysql_mutex_unlock(&acl_cache->lock);
2248  return 0; // Host ok
2249  }
2250  }
2251  mysql_mutex_unlock(&acl_cache->lock);
2252  if (ip != NULL)
2253  {
2254  /* Increment HOST_CACHE.COUNT_HOST_ACL_ERRORS. */
2256  errors.m_host_acl= 1;
2257  inc_host_errors(ip, &errors);
2258  }
2259  return 1; // Host is not allowed
2260 }
2261 
2262 
2278 int check_change_password(THD *thd, const char *host, const char *user,
2279  char *new_password, uint new_password_len)
2280 {
2281  if (!initialized)
2282  {
2283  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2284  return(1);
2285  }
2286  if (!thd->slave_thread &&
2287  (strcmp(thd->security_ctx->user, user) ||
2288  my_strcasecmp(system_charset_info, host,
2289  thd->security_ctx->priv_host)))
2290  {
2291  if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
2292  return(1);
2293  }
2294  if (!thd->slave_thread && !thd->security_ctx->user[0])
2295  {
2296  my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
2297  MYF(0));
2298  return(1);
2299  }
2300 
2301  return(0);
2302 }
2303 
2304 
2319 static bool
2320 update_sctx_cache(Security_context *sctx, ACL_USER *acl_user_ptr, bool expired)
2321 {
2322  const char *acl_host= acl_user_ptr->host.get_host();
2323  const char *acl_user= acl_user_ptr->user;
2324  const char *sctx_user= sctx->priv_user;
2325  const char *sctx_host= sctx->priv_host;
2326 
2327  if (!acl_host)
2328  acl_host= "";
2329  if(!acl_user)
2330  acl_user= "";
2331  if (!sctx_host)
2332  sctx_host= "";
2333  if (!sctx_user)
2334  sctx_user= "";
2335 
2336  if (!strcmp(acl_user, sctx_user) && !strcmp(acl_host, sctx_host))
2337  {
2338  sctx->password_expired= expired;
2339  return true;
2340  }
2341 
2342  return false;
2343 }
2344 
2345 
2346 
2368 bool change_password(THD *thd, const char *host, const char *user,
2369  char *new_password)
2370 {
2371  TABLE_LIST tables;
2372  TABLE *table;
2373  /* Buffer should be extended when password length is extended. */
2374  char buff[512];
2375  ulong query_length;
2376  bool save_binlog_row_based;
2377  uchar user_key[MAX_KEY_LENGTH];
2378  char *plugin_temp= NULL;
2379  bool plugin_empty;
2380  uint new_password_len= (uint) strlen(new_password);
2381  bool result= 1;
2382  enum mysql_user_table_field password_field= MYSQL_USER_FIELD_PASSWORD;
2383  DBUG_ENTER("change_password");
2384  DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
2385  host,user,new_password));
2386  DBUG_ASSERT(host != 0); // Ensured by parent
2387 
2388  if (check_change_password(thd, host, user, new_password, new_password_len))
2389  DBUG_RETURN(1);
2390 
2391  tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
2392 
2393 #ifdef HAVE_REPLICATION
2394  /*
2395  GRANT and REVOKE are applied the slave in/exclusion rules as they are
2396  some kind of updates to the mysql.% tables.
2397  */
2398  if (thd->slave_thread && rpl_filter->is_on())
2399  {
2400  /*
2401  The tables must be marked "updating" so that tables_ok() takes them into
2402  account in tests. It's ok to leave 'updating' set after tables_ok.
2403  */
2404  tables.updating= 1;
2405  /* Thanks to memset, tables.next==0 */
2406  if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, &tables)))
2407  DBUG_RETURN(0);
2408  }
2409 #endif
2410  if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
2411  DBUG_RETURN(1);
2412 
2413  if (!table->key_info)
2414  {
2415  my_error(ER_TABLE_CORRUPT, MYF(0), table->s->db.str,
2416  table->s->table_name.str);
2417  DBUG_RETURN(1);
2418  }
2419 
2420  /*
2421  This statement will be replicated as a statement, even when using
2422  row-based replication. The flag will be reset at the end of the
2423  statement.
2424  */
2425  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
2426  thd->clear_current_stmt_binlog_format_row();
2427 
2428  mysql_mutex_lock(&acl_cache->lock);
2429  ACL_USER *acl_user;
2430  if (!(acl_user= find_acl_user(host, user, TRUE)))
2431  {
2432  mysql_mutex_unlock(&acl_cache->lock);
2433  my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
2434  goto end;
2435  }
2436  mysql_mutex_assert_owner(&acl_cache->lock);
2437  table->use_all_columns();
2438  DBUG_ASSERT(host != '\0');
2439  table->field[MYSQL_USER_FIELD_HOST]->store(host, strlen(host),
2440  system_charset_info);
2441  table->field[MYSQL_USER_FIELD_USER]->store(user, strlen(user),
2442  system_charset_info);
2443 
2444  key_copy((uchar *) user_key, table->record[0], table->key_info,
2445  table->key_info->key_length);
2446  if (!table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
2447  HA_WHOLE_KEY,
2448  HA_READ_KEY_EXACT))
2449  plugin_temp= (table->s->fields > MYSQL_USER_FIELD_PLUGIN) ?
2450  get_field(&global_acl_memory, table->field[MYSQL_USER_FIELD_PLUGIN]) : NULL;
2451  else
2452  DBUG_ASSERT(FALSE);
2453 
2454  plugin_empty= plugin_temp ? false: true;
2455 
2456  if (acl_user->plugin.length == 0)
2457  {
2458  acl_user->plugin.length= default_auth_plugin_name.length;
2459  acl_user->plugin.str= default_auth_plugin_name.str;
2460  }
2461 
2462  if (new_password_len == 0)
2463  {
2464  String *password_str= new (thd->mem_root) String(new_password,
2465  thd->variables.
2466  character_set_client);
2467  if (check_password_policy(password_str))
2468  {
2469  result= 1;
2470  mysql_mutex_unlock(&acl_cache->lock);
2471  goto end;
2472  }
2473  }
2474 
2475 #if defined(HAVE_OPENSSL)
2476  /*
2477  update loaded acl entry:
2478  TODO Should password depend on @@old_variables here?
2479  - Probably not if the user exists and have a plugin set already.
2480  */
2481  if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
2482  sha256_password_plugin_name.str) == 0)
2483  {
2484  /*
2485  Accept empty passwords
2486  */
2487  if (new_password_len == 0)
2488  acl_user->auth_string= empty_lex_str;
2489  /*
2490  Check if password begins with correct magic number
2491  */
2492  else if (new_password[0] == '$' &&
2493  new_password[1] == '5' &&
2494  new_password[2] == '$')
2495  {
2496  password_field= MYSQL_USER_FIELD_AUTHENTICATION_STRING;
2497  if (new_password_len < CRYPT_MAX_PASSWORD_SIZE + 1)
2498  {
2499  /*
2500  Since we're changing the password for the user we need to reset the
2501  expiration flag.
2502  */
2503  if (!update_sctx_cache(thd->security_ctx, acl_user, false) &&
2504  thd->security_ctx->password_expired)
2505  {
2506  /* the current user is not the same as the user we operate on */
2507  my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
2508  result= 1;
2509  mysql_mutex_unlock(&acl_cache->lock);
2510  goto end;
2511  }
2512 
2513  acl_user->password_expired= false;
2514  /* copy string including \0 */
2515  acl_user->auth_string.str= (char *) memdup_root(&global_acl_memory,
2516  new_password,
2517  new_password_len + 1);
2518  acl_user->auth_string.length= new_password_len;
2519  }
2520  } else
2521  {
2522  /*
2523  Password format is unexpected. The user probably is using the wrong
2524  password algorithm with the PASSWORD() function.
2525  */
2526  my_error(ER_PASSWORD_FORMAT, MYF(0));
2527  result= 1;
2528  mysql_mutex_unlock(&acl_cache->lock);
2529  goto end;
2530  }
2531  }
2532  else
2533 #endif
2534  if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
2535  native_password_plugin_name.str) == 0 ||
2536  my_strcasecmp(system_charset_info, acl_user->plugin.str,
2537  old_password_plugin_name.str) == 0)
2538  {
2539  password_field= MYSQL_USER_FIELD_PASSWORD;
2540 
2541  /*
2542  Legacy code produced an error if the password hash didn't match the
2543  expectations.
2544  */
2545  if (new_password_len != 0)
2546  {
2547  if (plugin_empty)
2548  {
2549  if (new_password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
2550  acl_user->plugin= native_password_plugin_name;
2551  else if (new_password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
2552  acl_user->plugin= old_password_plugin_name;
2553  else
2554  {
2555  my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
2556  result= 1;
2557  mysql_mutex_unlock(&acl_cache->lock);
2558  goto end;
2559  }
2560  }
2561  else
2562  {
2563  if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
2564  native_password_plugin_name.str) == 0 &&
2565  new_password_len != SCRAMBLED_PASSWORD_CHAR_LENGTH)
2566  {
2567  my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
2568  result= 1;
2569  mysql_mutex_unlock(&acl_cache->lock);
2570  goto end;
2571  }
2572  else if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
2573  old_password_plugin_name.str) == 0 &&
2574  new_password_len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
2575  {
2576  my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
2577  result= 1;
2578  mysql_mutex_unlock(&acl_cache->lock);
2579  goto end;
2580  }
2581  }
2582  }
2583  else if (plugin_empty)
2584  acl_user->plugin= native_password_plugin_name;
2585 
2586  /*
2587  Update loaded acl entry in memory.
2588  set_user_salt() stores a binary (compact) representation of the password
2589  in memory (acl_user->salt and salt_len).
2590  set_user_plugin() sets the appropriate plugin based on password length and
2591  if the length doesn't match a warning is issued.
2592  */
2593  if (set_user_salt(acl_user, new_password, new_password_len))
2594  {
2595  my_error(ER_PASSWORD_FORMAT, MYF(0));
2596  result= 1;
2597  mysql_mutex_unlock(&acl_cache->lock);
2598  goto end;
2599  }
2600  if (!update_sctx_cache(thd->security_ctx, acl_user, false) &&
2601  thd->security_ctx->password_expired)
2602  {
2603  my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
2604  result= 1;
2605  mysql_mutex_unlock(&acl_cache->lock);
2606  goto end;
2607  }
2608  }
2609  else
2610  {
2611  push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
2612  ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
2613  /*
2614  An undefined password factory could very well mean that the password
2615  field is empty.
2616  */
2617  new_password_len= 0;
2618  }
2619 
2620  if (update_user_table(thd, table,
2621  acl_user->host.get_host() ? acl_user->host.get_host() : "",
2622  acl_user->user ? acl_user->user : "",
2623  new_password, new_password_len, password_field, false, true))
2624  {
2625  mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
2626  goto end;
2627  }
2628 
2629  acl_cache->clear(1); // Clear locked hostname cache
2630  mysql_mutex_unlock(&acl_cache->lock);
2631  result= 0;
2632  query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
2633  acl_user->user ? acl_user->user : "",
2634  acl_user->host.get_host() ? acl_user->host.get_host() : "",
2635  new_password);
2636  result= write_bin_log(thd, true, buff, query_length,
2637  table->file->has_transactions());
2638 end:
2639  result|= acl_trans_commit_and_close_tables(thd);
2640 
2641  /* Restore the state of binlog format */
2642  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
2643  if (save_binlog_row_based)
2644  thd->set_current_stmt_binlog_format_row();
2645 
2646  DBUG_RETURN(result);
2647 }
2648 
2649 
2650 /*
2651  Find user in ACL
2652 
2653  SYNOPSIS
2654  is_acl_user()
2655  host host name
2656  user user name
2657 
2658  RETURN
2659  FALSE user not fond
2660  TRUE there are such user
2661 */
2662 
2663 bool is_acl_user(const char *host, const char *user)
2664 {
2665  bool res;
2666 
2667  /* --skip-grants */
2668  if (!initialized)
2669  return TRUE;
2670 
2671  mysql_mutex_lock(&acl_cache->lock);
2672  res= find_acl_user(host, user, TRUE) != NULL;
2673  mysql_mutex_unlock(&acl_cache->lock);
2674  return res;
2675 }
2676 
2677 
2678 /*
2679  Find first entry that matches the current user
2680 */
2681 
2682 static ACL_USER *
2683 find_acl_user(const char *host, const char *user, my_bool exact)
2684 {
2685  DBUG_ENTER("find_acl_user");
2686  DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user));
2687 
2688  mysql_mutex_assert_owner(&acl_cache->lock);
2689 
2690  for (uint i=0 ; i < acl_users.elements ; i++)
2691  {
2692  ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
2693  DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
2694  user, acl_user->user ? acl_user->user : "",
2695  host,
2696  acl_user->host.get_host() ? acl_user->host.get_host() :
2697  ""));
2698  if ((!acl_user->user && !user[0]) ||
2699  (acl_user->user && !strcmp(user,acl_user->user)))
2700  {
2701  if (exact ? !my_strcasecmp(system_charset_info, host,
2702  acl_user->host.get_host() ?
2703  acl_user->host.get_host() : "") :
2704  acl_user->host.compare_hostname(host,host))
2705  {
2706  DBUG_RETURN(acl_user);
2707  }
2708  }
2709  }
2710  DBUG_RETURN(0);
2711 }
2712 
2739 bool hostname_requires_resolving(const char *hostname)
2740 {
2741  if (!hostname)
2742  return FALSE;
2743 
2744  /* Check if hostname is the localhost. */
2745 
2746  size_t hostname_len= strlen(hostname);
2747  size_t localhost_len= strlen(my_localhost);
2748 
2749  if (hostname == my_localhost ||
2750  (hostname_len == localhost_len &&
2751  !my_strnncoll(system_charset_info,
2752  (const uchar *) hostname, hostname_len,
2753  (const uchar *) my_localhost, strlen(my_localhost))))
2754  {
2755  return FALSE;
2756  }
2757 
2758  /*
2759  If the string contains any of {':', '%', '_', '/'}, it is definitely
2760  not a host name:
2761  - ':' means that the string is an IPv6 address;
2762  - '%' or '_' means that the string is a pattern;
2763  - '/' means that the string is an IPv4 network address;
2764  */
2765 
2766  for (const char *p= hostname; *p; ++p)
2767  {
2768  switch (*p) {
2769  case ':':
2770  case '%':
2771  case '_':
2772  case '/':
2773  return FALSE;
2774  }
2775  }
2776 
2777  /*
2778  Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
2779  (12.34.56.78). The assumption is that if the string contains only
2780  digits and dots, it is an IPv4 address. Otherwise -- a host name.
2781  */
2782 
2783  for (const char *p= hostname; *p; ++p)
2784  {
2785  if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
2786  return TRUE; /* a "letter" has been found. */
2787  }
2788 
2789  return FALSE; /* all characters are either dots or digits. */
2790 }
2791 
2792 
2808 static bool
2809 update_user_table(THD *thd, TABLE *table,
2810  const char *host, const char *user,
2811  const char *new_password, uint new_password_len,
2812  enum mysql_user_table_field password_field,
2813  bool password_expired, bool is_user_table_positioned)
2814 {
2815  char user_key[MAX_KEY_LENGTH];
2816  int error;
2817  DBUG_ENTER("update_user_table");
2818  DBUG_PRINT("enter",("user: %s host: %s",user,host));
2819 
2820  /* ALTER USER PASSWORD EXPIRE makes no sense on old system tables */
2821  if (table->s->fields <= MYSQL_USER_FIELD_PASSWORD_EXPIRED &&
2822  password_expired)
2823  {
2824  my_error(ER_BAD_FIELD_ERROR, MYF(0), "password_expired", "mysql.user");
2825  DBUG_RETURN(1);
2826  }
2827 
2828  /*
2829  If this function is reached through change_password, the user record is
2830  already available and hence need not be read again.
2831  */
2832 
2833  if (!is_user_table_positioned)
2834  {
2835  table->use_all_columns();
2836  DBUG_ASSERT(host != '\0');
2837  table->field[MYSQL_USER_FIELD_HOST]->store(host, (uint) strlen(host),
2838  system_charset_info);
2839  table->field[MYSQL_USER_FIELD_USER]->store(user, (uint) strlen(user),
2840  system_charset_info);
2841  key_copy((uchar *) user_key, table->record[0], table->key_info,
2842  table->key_info->key_length);
2843 
2844  if (table->file->ha_index_read_idx_map(table->record[0], 0,
2845  (uchar *) user_key, HA_WHOLE_KEY,
2846  HA_READ_KEY_EXACT))
2847  {
2848  my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
2849  MYF(0)); /* purecov: deadcode */
2850  DBUG_RETURN(1); /* purecov: deadcode */
2851  }
2852  }
2853  store_record(table,record[1]);
2854 
2855  /*
2856  When the flag is on we're inside ALTER TABLE ... PASSWORD EXPIRE and we
2857  have no password to update.
2858  */
2859  if (!password_expired)
2860  {
2861  table->field[(int) password_field]->store(new_password, new_password_len,
2862  system_charset_info);
2863  if (new_password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 &&
2864  password_field == MYSQL_USER_FIELD_PASSWORD)
2865  {
2866  WARN_DEPRECATED_41_PWD_HASH(thd);
2867  }
2868  }
2869 
2870  if (table->s->fields > MYSQL_USER_FIELD_PASSWORD_EXPIRED)
2871  {
2872  /* update password_expired if present */
2873  table->field[MYSQL_USER_FIELD_PASSWORD_EXPIRED]->store(password_expired ?
2874  "Y" : "N", 1,
2875  system_charset_info);
2876  }
2877 
2878  if ((error=table->file->ha_update_row(table->record[1],table->record[0])) &&
2879  error != HA_ERR_RECORD_IS_THE_SAME)
2880  {
2881  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
2882  DBUG_RETURN(1);
2883  }
2884 
2885  DBUG_RETURN(0);
2886 }
2887 
2888 
2889 /*
2890  Return 1 if we are allowed to create new users
2891  the logic here is: INSERT_ACL is sufficient.
2892  It's also a requirement in opt_safe_user_create,
2893  otherwise CREATE_USER_ACL is enough.
2894 */
2895 
2896 static bool test_if_create_new_users(THD *thd)
2897 {
2898  Security_context *sctx= thd->security_ctx;
2899  bool create_new_users= test(sctx->master_access & INSERT_ACL) ||
2900  (!opt_safe_user_create &&
2901  test(sctx->master_access & CREATE_USER_ACL));
2902  if (!create_new_users)
2903  {
2904  TABLE_LIST tl;
2905  ulong db_access;
2906  tl.init_one_table(C_STRING_WITH_LEN("mysql"),
2907  C_STRING_WITH_LEN("user"), "user", TL_WRITE);
2908  create_new_users= 1;
2909 
2910  db_access=acl_get(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
2911  sctx->priv_user, tl.db, 0);
2912  if (!(db_access & INSERT_ACL))
2913  {
2914  if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
2915  create_new_users=0;
2916  }
2917  }
2918  return create_new_users;
2919 }
2920 
2921 
2931 bool auth_plugin_supports_expiration(const char *plugin_name)
2932 {
2933  return (!plugin_name || !*plugin_name ||
2934  plugin_name == native_password_plugin_name.str ||
2935 #if defined(HAVE_OPENSSL)
2936  plugin_name == sha256_password_plugin_name.str ||
2937 #endif
2938  plugin_name == old_password_plugin_name.str);
2939 }
2940 
2941 
2942 /****************************************************************************
2943  Handle GRANT commands
2944 ****************************************************************************/
2945 
2946 static int replace_user_table(THD *thd, TABLE *table, LEX_USER *combo,
2947  ulong rights, bool revoke_grant,
2948  bool can_create_user, bool no_auto_create)
2949 {
2950  int error = -1;
2951  bool old_row_exists=0;
2952  char *password= empty_c_string;
2953  uint password_len= 0;
2954  char what= (revoke_grant) ? 'N' : 'Y';
2955  uchar user_key[MAX_KEY_LENGTH];
2956  LEX *lex= thd->lex;
2957  DBUG_ENTER("replace_user_table");
2958 
2959  mysql_mutex_assert_owner(&acl_cache->lock);
2960 
2961  if (!table->key_info)
2962  {
2963  my_error(ER_TABLE_CORRUPT, MYF(0), table->s->db.str,
2964  table->s->table_name.str);
2965  goto end;
2966  }
2967 
2968  table->use_all_columns();
2969  DBUG_ASSERT(combo->host.str != '\0');
2970  table->field[MYSQL_USER_FIELD_HOST]->store(combo->host.str,combo->host.length,
2971  system_charset_info);
2972  table->field[MYSQL_USER_FIELD_USER]->store(combo->user.str,combo->user.length,
2973  system_charset_info);
2974  key_copy(user_key, table->record[0], table->key_info,
2975  table->key_info->key_length);
2976 
2977  if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
2978  HA_WHOLE_KEY,
2979  HA_READ_KEY_EXACT))
2980  {
2981  /*
2982  The user record wasn't found; if the intention was to revoke privileges
2983  (indicated by what == 'N') then execution must fail now.
2984  */
2985  if (what == 'N')
2986  {
2987  my_error(ER_NONEXISTING_GRANT, MYF(0), combo->user.str, combo->host.str);
2988  goto end;
2989  }
2990 
2991  /* 1. Unresolved plugins become default plugin */
2992  if (!combo->uses_identified_with_clause)
2993  {
2994  combo->plugin.str= default_auth_plugin_name.str;
2995  combo->plugin.length= default_auth_plugin_name.length;
2996  }
2997  /* 2. Digest password if needed (plugin must have been resolved) */
2998  if (combo->uses_identified_by_clause)
2999  {
3000  if (digest_password(thd, combo))
3001  {
3002  my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), CRYPT_MAX_PASSWORD_SIZE);
3003  error= 1;
3004  goto end;
3005  }
3006  }
3007  password= combo->password.str;
3008  password_len= combo->password.length;
3009  /*
3010  There are four options which affect the process of creation of
3011  a new user (mysqld option --safe-create-user, 'insert' privilege
3012  on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
3013  SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
3014  how it should work.
3015  if (safe-user-create && ! INSERT_priv) => reject
3016  else if (identified_by) => create
3017  else if (no_auto_create_user) => reject
3018  else create
3019 
3020  see also test_if_create_new_users()
3021  */
3022  if (!password_len &&
3023  auth_plugin_is_built_in(combo->plugin.str) &&
3024  no_auto_create)
3025  {
3026  my_error(ER_PASSWORD_NO_MATCH, MYF(0), combo->user.str, combo->host.str);
3027  goto end;
3028  }
3029  else if (!can_create_user)
3030  {
3031  my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
3032  goto end;
3033  }
3034  else if (combo->plugin.str[0])
3035  {
3036  if (!plugin_is_ready(&combo->plugin, MYSQL_AUTHENTICATION_PLUGIN))
3037  {
3038  my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo->plugin.str);
3039  goto end;
3040  }
3041  }
3042 
3043  old_row_exists = 0;
3044  restore_record(table,s->default_values);
3045  DBUG_ASSERT(combo->host.str != '\0');
3046  table->field[MYSQL_USER_FIELD_HOST]->store(combo->host.str,combo->host.length,
3047  system_charset_info);
3048  table->field[MYSQL_USER_FIELD_USER]->store(combo->user.str,combo->user.length,
3049  system_charset_info);
3050 #if defined(HAVE_OPENSSL)
3051  if (combo->plugin.str == sha256_password_plugin_name.str)
3052  {
3053  /* Use the authentication_string field */
3054  combo->auth.str= password;
3055  combo->auth.length= password_len;
3056  if (password_len > 0)
3057  table->
3058  field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->
3059  store(password, password_len, &my_charset_utf8_bin);
3060  /* Assert that the proper plugin is set */
3061  table->
3062  field[MYSQL_USER_FIELD_PLUGIN]->
3063  store(sha256_password_plugin_name.str,
3064  sha256_password_plugin_name.length,
3065  system_charset_info);
3066 
3067  }
3068  else
3069 #endif
3070  {
3071  /* Use the legacy Password field */
3072  table->field[MYSQL_USER_FIELD_PASSWORD]->store(password, password_len,
3073  system_charset_info);
3074  table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->store("\0", 0,
3075  &my_charset_utf8_bin);
3076  }
3077  }
3078  else // if (table->file->ha_index_read_idx_map [..]
3079  {
3080  /*
3081  There is a matching user record ------------------------------------------
3082  */
3083 
3084  old_row_exists = 1;
3085  store_record(table,record[1]); // Save copy for update
3086 
3087  /*
3088  GRANT statement using IDENTIFIED WITH clause can be used only to create
3089  user and apply privileges to it. Hence it throws an error when used on
3090  existing users.
3091  */
3092  if (combo->uses_identified_with_clause)
3093  {
3094  error= 1;
3095  my_error(ER_GRANT_PLUGIN_USER_EXISTS, MYF(0), combo->user.length,
3096  combo->user.str);
3097  goto end;
3098  }
3099 
3100  /* 1. resolve plugins in the LEX_USER struct if needed */
3101  LEX_STRING old_plugin;
3102 
3103  /*
3104  Get old plugin value from storage.
3105  */
3106 
3107  old_plugin.str=
3108  get_field(thd->mem_root, table->field[MYSQL_USER_FIELD_PLUGIN]);
3109 
3110  /*
3111  It is important not to include the trailing '\0' in the string length
3112  because otherwise the plugin hash search will fail.
3113  */
3114  if (old_plugin.str)
3115  {
3116  old_plugin.length= strlen(old_plugin.str);
3117 
3118  /*
3119  Optimize for pointer comparision of built-in plugin name
3120  */
3121 
3122  optimize_plugin_compare_by_pointer(&old_plugin);
3123 
3124  /*
3125  Disable plugin change for existing rows with anything but
3126  the built in plugins.
3127  The idea is that all built in plugins support
3128  IDENTIFIED BY ... and none of the external ones currently do.
3129  */
3130  if ((combo->uses_identified_by_clause ||
3131  combo->uses_identified_by_password_clause) &&
3132  !auth_plugin_is_built_in(old_plugin.str))
3133  {
3134  const char *new_plugin= (combo->plugin.str && combo->plugin.str[0]) ?
3135  combo->plugin.str : default_auth_plugin_name.str;
3136 
3137  if (my_strcasecmp(system_charset_info, new_plugin, old_plugin.str))
3138  {
3139  push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
3140  ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
3141  }
3142  }
3143  }
3144  old_plugin.length= 0;
3145  combo->plugin= old_plugin;
3146 
3147  /*
3148  If the plugin value in user table is found to be null or an empty
3149  string, the following steps are followed:
3150 
3151  * If GRANT is used with IDENTIFIED BY PASSWORD clause, and the hash
3152  is found to be of mysql_native_password or mysql_old_password
3153  type, the statement passes without an error and the password field
3154  is updated accordingly.
3155  * If GRANT is used with IDENTIFIED BY clause and the password is
3156  provided as a plain string, hashing of the string is done according
3157  to the value of old_passwords variable in the following way.
3158 
3159  if old_passwords == 0, mysql_native hashing is used.
3160  if old_passwords == 1, mysql_old hashing is used.
3161  if old_passwords == 2, error.
3162  * An empty password is considered to be of mysql_native type.
3163  */
3164 
3165  if (combo->plugin.str == NULL || combo->plugin.str == '\0')
3166  {
3167  if (combo->uses_identified_by_password_clause)
3168  {
3169  if ((combo->password.length == SCRAMBLED_PASSWORD_CHAR_LENGTH) ||
3170  (combo->password.length == 0))
3171  {
3172  combo->plugin.str= native_password_plugin_name.str;
3173  combo->plugin.length= native_password_plugin_name.length;
3174  }
3175  else if (combo->password.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
3176  {
3177  combo->plugin.str= old_password_plugin_name.str;
3178  combo->plugin.length= old_password_plugin_name.length;
3179  }
3180  else
3181  {
3182  /*
3183  If hash length doesn't match either with mysql_native hash length or
3184  mysql_old hash length, throw an error.
3185  */
3186  my_error(ER_PASSWORD_FORMAT, MYF(0));
3187  error= 1;
3188  goto end;
3189  }
3190  }
3191  else
3192  {
3193  /*
3194  Handling of combo->plugin when IDENTIFIED BY PASSWORD clause is not
3195  used, i.e. when the password hash is not provided within the GRANT
3196  query.
3197  */
3198  if ((thd->variables.old_passwords == 1) && (combo->password.length != 0))
3199  {
3200  combo->plugin.str= old_password_plugin_name.str;
3201  combo->plugin.length= old_password_plugin_name.length;
3202  }
3203  else if ((thd->variables.old_passwords == 0) ||
3204  (combo->password.length == 0))
3205  {
3206  combo->plugin.str= native_password_plugin_name.str;
3207  combo->plugin.length= native_password_plugin_name.length;
3208  }
3209  else
3210  {
3211  /* If old_passwords variable is neither 0 nor 1, throw an error. */
3212  my_error(ER_PASSWORD_FORMAT, MYF(0));
3213  error= 1;
3214  goto end;
3215  }
3216  }
3217  }
3218 
3219  if (!combo->uses_authentication_string_clause)
3220  {
3221  combo->auth.str= get_field(thd->mem_root,
3222  table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]);
3223  if (combo->auth.str)
3224  combo->auth.length= strlen(combo->auth.str);
3225  else
3226  combo->auth.length= 0;
3227  }
3228 
3229  /* 2. Digest password if needed (plugin must have been resolved */
3230  if (combo->uses_identified_by_clause)
3231  {
3232  if (digest_password(thd, combo))
3233  {
3234  error= 1;
3235  goto end;
3236  }
3237  }
3238  password= combo->password.str;
3239  password_len= combo->password.length;
3240 
3241  if (password_len > 0)
3242  {
3243 #if defined(HAVE_OPENSSL)
3244  if (combo->plugin.str == sha256_password_plugin_name.str)
3245  {
3246  table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->
3247  store(password, password_len, &my_charset_utf8_bin);
3248  combo->auth.str= password;
3249  combo->auth.length= password_len;
3250  }
3251  else
3252 #endif
3253  {
3254  table->field[MYSQL_USER_FIELD_PASSWORD]->
3255  store(password, password_len, system_charset_info);
3256  table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->
3257  store("\0", 0, &my_charset_utf8_bin);
3258  }
3259  }
3260  else if (!rights && !revoke_grant &&
3261  lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
3262  !lex->mqh.specified_limits)
3263  {
3264 
3265  DBUG_PRINT("info", ("Proxy user exit path"));
3266  DBUG_RETURN(0);
3267  }
3268  }
3269 
3270  /* error checks on password */
3271  if (password_len > 0)
3272  {
3273  /*
3274  We need to check for hash validity here since later, when
3275  set_user_salt() is executed it will be too late to signal
3276  an error.
3277  */
3278  if ((combo->plugin.str == native_password_plugin_name.str &&
3279  password_len != SCRAMBLED_PASSWORD_CHAR_LENGTH) ||
3280  (combo->plugin.str == old_password_plugin_name.str &&
3281  password_len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323))
3282  {
3283  my_error(ER_PASSWORD_FORMAT, MYF(0));
3284  error= 1;
3285  goto end;
3286  }
3287  /* The legacy Password field is used */
3288  if (combo->plugin.str == old_password_plugin_name.str)
3289  WARN_DEPRECATED_41_PWD_HASH(thd);
3290  }
3291 
3292  /* Update table columns with new privileges */
3293 
3294  Field **tmp_field;
3295  ulong priv;
3296  uint next_field;
3297  for (tmp_field= table->field+3, priv = SELECT_ACL;
3298  *tmp_field && (*tmp_field)->real_type() == MYSQL_TYPE_ENUM &&
3299  ((Field_enum*) (*tmp_field))->typelib->count == 2 ;
3300  tmp_field++, priv <<= 1)
3301  {
3302  if (priv & rights) // set requested privileges
3303  (*tmp_field)->store(&what, 1, &my_charset_latin1);
3304  }
3305  rights= get_access(table, 3, &next_field);
3306  DBUG_PRINT("info",("table fields: %d",table->s->fields));
3307  if (table->s->fields >= 31) /* From 4.0.0 we have more fields */
3308  {
3309  /* We write down SSL related ACL stuff */
3310  switch (lex->ssl_type) {
3311  case SSL_TYPE_ANY:
3312  table->field[MYSQL_USER_FIELD_SSL_TYPE]->store(STRING_WITH_LEN("ANY"),
3313  &my_charset_latin1);
3314  table->field[MYSQL_USER_FIELD_SSL_CIPHER]->
3315  store("", 0, &my_charset_latin1);
3316  table->field[MYSQL_USER_FIELD_X509_ISSUER]->store("", 0, &my_charset_latin1);
3317  table->field[MYSQL_USER_FIELD_X509_SUBJECT]->store("", 0, &my_charset_latin1);
3318  break;
3319  case SSL_TYPE_X509:
3320  table->field[MYSQL_USER_FIELD_SSL_TYPE]->store(STRING_WITH_LEN("X509"),
3321  &my_charset_latin1);
3322  table->field[MYSQL_USER_FIELD_SSL_CIPHER]->
3323  store("", 0, &my_charset_latin1);
3324  table->field[MYSQL_USER_FIELD_X509_ISSUER]->store("", 0, &my_charset_latin1);
3325  table->field[MYSQL_USER_FIELD_X509_SUBJECT]->store("", 0, &my_charset_latin1);
3326  break;
3327  case SSL_TYPE_SPECIFIED:
3328  table->field[MYSQL_USER_FIELD_SSL_TYPE]->store(STRING_WITH_LEN("SPECIFIED"),
3329  &my_charset_latin1);
3330  table->field[MYSQL_USER_FIELD_SSL_CIPHER]->store("", 0, &my_charset_latin1);
3331  table->field[MYSQL_USER_FIELD_X509_ISSUER]->store("", 0, &my_charset_latin1);
3332  table->field[MYSQL_USER_FIELD_X509_SUBJECT]->store("", 0, &my_charset_latin1);
3333  if (lex->ssl_cipher)
3334  table->field[MYSQL_USER_FIELD_SSL_CIPHER]->store(lex->ssl_cipher,
3335  strlen(lex->ssl_cipher), system_charset_info);
3336  if (lex->x509_issuer)
3337  table->field[MYSQL_USER_FIELD_X509_ISSUER]->store(lex->x509_issuer,
3338  strlen(lex->x509_issuer), system_charset_info);
3339  if (lex->x509_subject)
3340  table->field[MYSQL_USER_FIELD_X509_SUBJECT]->store(lex->x509_subject,
3341  strlen(lex->x509_subject), system_charset_info);
3342  break;
3343  case SSL_TYPE_NOT_SPECIFIED:
3344  break;
3345  case SSL_TYPE_NONE:
3346  table->field[MYSQL_USER_FIELD_SSL_TYPE]->store("", 0, &my_charset_latin1);
3347  table->field[MYSQL_USER_FIELD_SSL_CIPHER]->store("", 0, &my_charset_latin1);
3348  table->field[MYSQL_USER_FIELD_X509_ISSUER]->store("", 0, &my_charset_latin1);
3349  table->field[MYSQL_USER_FIELD_X509_SUBJECT]->store("", 0, &my_charset_latin1);
3350  break;
3351  }
3352  next_field+=4;
3353 
3354  USER_RESOURCES mqh= lex->mqh;
3355  if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
3356  table->field[MYSQL_USER_FIELD_MAX_QUESTIONS]->
3357  store((longlong) mqh.questions, TRUE);
3358  if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
3359  table->field[MYSQL_USER_FIELD_MAX_UPDATES]->
3360  store((longlong) mqh.updates, TRUE);
3361  if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
3362  table->field[MYSQL_USER_FIELD_MAX_CONNECTIONS]->
3363  store((longlong) mqh.conn_per_hour, TRUE);
3364  if (table->s->fields >= 36 &&
3365  (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
3366  table->field[MYSQL_USER_FIELD_MAX_USER_CONNECTIONS]->
3367  store((longlong) mqh.user_conn, TRUE);
3368  mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
3369 
3370  next_field+= 4;
3371  if (combo->plugin.length > 0 && !old_row_exists)
3372  {
3373  if (table->s->fields >= 41)
3374  {
3375  table->field[MYSQL_USER_FIELD_PLUGIN]->
3376  store(combo->plugin.str, combo->plugin.length, system_charset_info);
3377  table->field[MYSQL_USER_FIELD_PLUGIN]->set_notnull();
3378  table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->
3379  store(combo->auth.str, combo->auth.length, &my_charset_utf8_bin);
3380  table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->set_notnull();
3381  }
3382  else
3383  {
3384  my_error(ER_BAD_FIELD_ERROR, MYF(0), "plugin", "mysql.user");
3385  goto end;
3386  }
3387  }
3388 
3389  /* if we have a password supplied we update the expiration field */
3390  if (table->s->fields > MYSQL_USER_FIELD_PASSWORD_EXPIRED &&
3391  password_len > 0)
3392  table->field[MYSQL_USER_FIELD_PASSWORD_EXPIRED]->store("N", 1,
3393  system_charset_info);
3394  }
3395 
3396  if (old_row_exists)
3397  {
3398  /*
3399  We should NEVER delete from the user table, as a uses can still
3400  use mysqld even if he doesn't have any privileges in the user table!
3401  */
3402  if (cmp_record(table,record[1]))
3403  {
3404  if ((error=
3405  table->file->ha_update_row(table->record[1],table->record[0])) &&
3406  error != HA_ERR_RECORD_IS_THE_SAME)
3407  { // This should never happen
3408  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
3409  error= -1; /* purecov: deadcode */
3410  goto end; /* purecov: deadcode */
3411  }
3412  else
3413  error= 0;
3414  }
3415  }
3416  else if ((error=table->file->ha_write_row(table->record[0]))) // insert
3417  { // This should never happen
3418  if (table->file->is_fatal_error(error, HA_CHECK_DUP))
3419  {
3420  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
3421  error= -1; /* purecov: deadcode */
3422  goto end; /* purecov: deadcode */
3423  }
3424  }
3425  error=0; // Privileges granted / revoked
3426 
3427 end:
3428  if (!error)
3429  {
3430  acl_cache->clear(1); // Clear privilege cache
3431  if (old_row_exists)
3432  acl_update_user(combo->user.str, combo->host.str,
3433  combo->password.str, password_len,
3434  lex->ssl_type,
3435  lex->ssl_cipher,
3436  lex->x509_issuer,
3437  lex->x509_subject,
3438  &lex->mqh,
3439  rights,
3440  &combo->plugin,
3441  &combo->auth);
3442  else
3443  acl_insert_user(combo->user.str, combo->host.str, password, password_len,
3444  lex->ssl_type,
3445  lex->ssl_cipher,
3446  lex->x509_issuer,
3447  lex->x509_subject,
3448  &lex->mqh,
3449  rights,
3450  &combo->plugin,
3451  &combo->auth);
3452  }
3453  DBUG_RETURN(error);
3454 }
3455 
3456 
3457 /*
3458  change grants in the mysql.db table
3459 */
3460 
3461 static int replace_db_table(TABLE *table, const char *db,
3462  const LEX_USER &combo,
3463  ulong rights, bool revoke_grant)
3464 {
3465  uint i;
3466  ulong priv,store_rights;
3467  bool old_row_exists=0;
3468  int error;
3469  char what= (revoke_grant) ? 'N' : 'Y';
3470  uchar user_key[MAX_KEY_LENGTH];
3471  DBUG_ENTER("replace_db_table");
3472 
3473  if (!initialized)
3474  {
3475  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3476  DBUG_RETURN(-1);
3477  }
3478 
3479  /* Check if there is such a user in user table in memory? */
3480  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
3481  {
3482  my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
3483  DBUG_RETURN(-1);
3484  }
3485 
3486  table->use_all_columns();
3487  table->field[0]->store(combo.host.str,combo.host.length,
3488  system_charset_info);
3489  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
3490  table->field[2]->store(combo.user.str,combo.user.length,
3491  system_charset_info);
3492  key_copy(user_key, table->record[0], table->key_info,
3493  table->key_info->key_length);
3494 
3495  if (table->file->ha_index_read_idx_map(table->record[0],0, user_key,
3496  HA_WHOLE_KEY,
3497  HA_READ_KEY_EXACT))
3498  {
3499  if (what == 'N')
3500  { // no row, no revoke
3501  my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
3502  goto abort;
3503  }
3504  old_row_exists = 0;
3505  restore_record(table, s->default_values);
3506  table->field[0]->store(combo.host.str,combo.host.length,
3507  system_charset_info);
3508  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
3509  table->field[2]->store(combo.user.str,combo.user.length,
3510  system_charset_info);
3511  }
3512  else
3513  {
3514  old_row_exists = 1;
3515  store_record(table,record[1]);
3516  }
3517 
3518  store_rights=get_rights_for_db(rights);
3519  for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
3520  {
3521  if (priv & store_rights) // do it if priv is chosen
3522  table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
3523  }
3524  rights=get_access(table,3);
3525  rights=fix_rights_for_db(rights);
3526 
3527  if (old_row_exists)
3528  {
3529  /* update old existing row */
3530  if (rights)
3531  {
3532  if ((error= table->file->ha_update_row(table->record[1],
3533  table->record[0])) &&
3534  error != HA_ERR_RECORD_IS_THE_SAME)
3535  goto table_error; /* purecov: deadcode */
3536  }
3537  else /* must have been a revoke of all privileges */
3538  {
3539  if ((error= table->file->ha_delete_row(table->record[1])))
3540  goto table_error; /* purecov: deadcode */
3541  }
3542  }
3543  else if (rights && (error= table->file->ha_write_row(table->record[0])))
3544  {
3545  if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
3546  goto table_error; /* purecov: deadcode */
3547  }
3548 
3549  acl_cache->clear(1); // Clear privilege cache
3550  if (old_row_exists)
3551  acl_update_db(combo.user.str,combo.host.str,db,rights);
3552  else
3553  if (rights)
3554  acl_insert_db(combo.user.str,combo.host.str,db,rights);
3555  DBUG_RETURN(0);
3556 
3557  /* This could only happen if the grant tables got corrupted */
3558 table_error:
3559  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
3560 
3561 abort:
3562  DBUG_RETURN(-1);
3563 }
3564 
3565 
3566 static void
3567 acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
3568 {
3569  mysql_mutex_assert_owner(&acl_cache->lock);
3570 
3571  DBUG_ENTER("acl_update_proxy_user");
3572  for (uint i= 0; i < acl_proxy_users.elements; i++)
3573  {
3574  ACL_PROXY_USER *acl_user=
3575  dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
3576 
3577  if (acl_user->pk_equals(new_value))
3578  {
3579  if (is_revoke)
3580  {
3581  DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
3582  delete_dynamic_element(&acl_proxy_users, i);
3583  }
3584  else
3585  {
3586  DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
3587  acl_user->set_data(new_value);
3588  }
3589  break;
3590  }
3591  }
3592  DBUG_VOID_RETURN;
3593 }
3594 
3595 
3596 static void
3597 acl_insert_proxy_user(ACL_PROXY_USER *new_value)
3598 {
3599  DBUG_ENTER("acl_insert_proxy_user");
3600  mysql_mutex_assert_owner(&acl_cache->lock);
3601  (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
3602  my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
3603  acl_proxy_users.elements,
3604  sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
3605  DBUG_VOID_RETURN;
3606 }
3607 
3608 
3609 static int
3610 replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
3611  const LEX_USER *proxied_user, bool with_grant_arg,
3612  bool revoke_grant)
3613 {
3614  bool old_row_exists= 0;
3615  int error;
3616  uchar user_key[MAX_KEY_LENGTH];
3617  ACL_PROXY_USER new_grant;
3618  char grantor[USER_HOST_BUFF_SIZE];
3619 
3620  DBUG_ENTER("replace_proxies_priv_table");
3621 
3622  if (!initialized)
3623  {
3624  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3625  DBUG_RETURN(-1);
3626  }
3627 
3628  /* Check if there is such a user in user table in memory? */
3629  if (!find_acl_user(user->host.str,user->user.str, FALSE))
3630  {
3631  my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
3632  DBUG_RETURN(-1);
3633  }
3634 
3635  table->use_all_columns();
3636  ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
3637  &proxied_user->host, &proxied_user->user);
3638 
3639  key_copy(user_key, table->record[0], table->key_info,
3640  table->key_info->key_length);
3641 
3642  get_grantor(thd, grantor);
3643 
3644  if ((error= table->file->ha_index_init(0, 1)))
3645  {
3646  table->file->print_error(error, MYF(0));
3647  DBUG_PRINT("info", ("ha_index_init error"));
3648  DBUG_RETURN(-1);
3649  }
3650 
3651  if (table->file->ha_index_read_map(table->record[0], user_key,
3652  HA_WHOLE_KEY,
3653  HA_READ_KEY_EXACT))
3654  {
3655  DBUG_PRINT ("info", ("Row not found"));
3656  if (revoke_grant)
3657  { // no row, no revoke
3658  my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
3659  goto abort;
3660  }
3661  old_row_exists= 0;
3662  restore_record(table, s->default_values);
3663  ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
3664  &proxied_user->host,
3665  &proxied_user->user,
3666  with_grant_arg,
3667  grantor);
3668  }
3669  else
3670  {
3671  DBUG_PRINT("info", ("Row found"));
3672  old_row_exists= 1;
3673  store_record(table, record[1]);
3674  }
3675 
3676  if (old_row_exists)
3677  {
3678  /* update old existing row */
3679  if (!revoke_grant)
3680  {
3681  if ((error= table->file->ha_update_row(table->record[1],
3682  table->record[0])) &&
3683  error != HA_ERR_RECORD_IS_THE_SAME)
3684  goto table_error; /* purecov: inspected */
3685  }
3686  else
3687  {
3688  if ((error= table->file->ha_delete_row(table->record[1])))
3689  goto table_error; /* purecov: inspected */
3690  }
3691  }
3692  else if ((error= table->file->ha_write_row(table->record[0])))
3693  {
3694  DBUG_PRINT("info", ("error inserting the row"));
3695  if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
3696  goto table_error; /* purecov: inspected */
3697  }
3698 
3699  acl_cache->clear(1); // Clear privilege cache
3700  if (old_row_exists)
3701  {
3702  new_grant.init(user->host.str, user->user.str,
3703  proxied_user->host.str, proxied_user->user.str,
3704  with_grant_arg);
3705  acl_update_proxy_user(&new_grant, revoke_grant);
3706  }
3707  else
3708  {
3709  new_grant.init(&global_acl_memory, user->host.str, user->user.str,
3710  proxied_user->host.str, proxied_user->user.str,
3711  with_grant_arg);
3712  acl_insert_proxy_user(&new_grant);
3713  }
3714 
3715  table->file->ha_index_end();
3716  DBUG_RETURN(0);
3717 
3718  /* This could only happen if the grant tables got corrupted */
3719 table_error:
3720  DBUG_PRINT("info", ("table error"));
3721  table->file->print_error(error, MYF(0)); /* purecov: inspected */
3722 
3723 abort:
3724  DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
3725  table->file->ha_index_end();
3726  DBUG_RETURN(-1);
3727 }
3728 
3729 
3731 {
3732 public:
3733  char *column;
3734  ulong rights;
3735  uint key_length;
3736  GRANT_COLUMN(String &c, ulong y) :rights (y)
3737  {
3738  column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length());
3739  }
3740 };
3741 
3742 
3743 static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
3744  my_bool not_used __attribute__((unused)))
3745 {
3746  *length=buff->key_length;
3747  return (uchar*) buff->column;
3748 }
3749 
3750 
3751 class GRANT_NAME :public Sql_alloc
3752 {
3753 public:
3754  ACL_HOST_AND_IP host;
3755  char *db, *user, *tname, *hash_key;
3756  ulong privs;
3757  ulong sort;
3758  size_t key_length;
3759  GRANT_NAME(const char *h, const char *d,const char *u,
3760  const char *t, ulong p, bool is_routine);
3761  GRANT_NAME (TABLE *form, bool is_routine);
3762  virtual ~GRANT_NAME() {};
3763  virtual bool ok() { return privs != 0; }
3764  void set_user_details(const char *h, const char *d,
3765  const char *u, const char *t,
3766  bool is_routine);
3767 };
3768 
3769 
3771 {
3772 public:
3773  ulong cols;
3774  HASH hash_columns;
3775 
3776  GRANT_TABLE(const char *h, const char *d,const char *u,
3777  const char *t, ulong p, ulong c);
3778  GRANT_TABLE (TABLE *form, TABLE *col_privs);
3779  ~GRANT_TABLE();
3780  bool ok() { return privs != 0 || cols != 0; }
3781 };
3782 
3783 
3784 void GRANT_NAME::set_user_details(const char *h, const char *d,
3785  const char *u, const char *t,
3786  bool is_routine)
3787 {
3788  /* Host given by user */
3789  host.update_hostname(strdup_root(&memex, h));
3790  if (db != d)
3791  {
3792  db= strdup_root(&memex, d);
3793  if (lower_case_table_names)
3794  my_casedn_str(files_charset_info, db);
3795  }
3796  user = strdup_root(&memex,u);
3797  sort= get_sort(3,host.get_host(),db,user);
3798  if (tname != t)
3799  {
3800  tname= strdup_root(&memex, t);
3801  if (lower_case_table_names || is_routine)
3802  my_casedn_str(files_charset_info, tname);
3803  }
3804  key_length= strlen(d) + strlen(u)+ strlen(t)+3;
3805  hash_key= (char*) alloc_root(&memex,key_length);
3806  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
3807 }
3808 
3809 GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
3810  const char *t, ulong p, bool is_routine)
3811  :db(0), tname(0), privs(p)
3812 {
3813  set_user_details(h, d, u, t, is_routine);
3814 }
3815 
3816 GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
3817  const char *t, ulong p, ulong c)
3818  :GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
3819 {
3820  (void) my_hash_init2(&hash_columns,4,system_charset_info,
3821  0,0,0, (my_hash_get_key) get_key_column,0,0);
3822 }
3823 
3824 
3825 GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
3826 {
3827  host.update_hostname(get_field(&memex, form->field[0]));
3828  db= get_field(&memex,form->field[1]);
3829  user= get_field(&memex,form->field[2]);
3830  if (!user)
3831  user= (char*) "";
3832  sort= get_sort(3, host.get_host(), db, user);
3833  tname= get_field(&memex,form->field[3]);
3834  if (!db || !tname) {
3835  /* Wrong table row; Ignore it */
3836  privs= 0;
3837  return; /* purecov: inspected */
3838  }
3839  if (lower_case_table_names)
3840  {
3841  my_casedn_str(files_charset_info, db);
3842  }
3843  if (lower_case_table_names || is_routine)
3844  {
3845  my_casedn_str(files_charset_info, tname);
3846  }
3847  key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
3848  hash_key= (char*) alloc_root(&memex, key_length);
3849  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
3850  privs = (ulong) form->field[6]->val_int();
3851  privs = fix_rights_for_table(privs);
3852 }
3853 
3854 
3855 GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
3856  :GRANT_NAME(form, FALSE)
3857 {
3858  uchar key[MAX_KEY_LENGTH];
3859 
3860  if (!db || !tname)
3861  {
3862  /* Wrong table row; Ignore it */
3863  my_hash_clear(&hash_columns); /* allow for destruction */
3864  cols= 0;
3865  return;
3866  }
3867  cols= (ulong) form->field[7]->val_int();
3868  cols = fix_rights_for_column(cols);
3869 
3870  (void) my_hash_init2(&hash_columns,4,system_charset_info,
3871  0,0,0, (my_hash_get_key) get_key_column,0,0);
3872  if (cols)
3873  {
3874  uint key_prefix_len;
3875  KEY_PART_INFO *key_part= col_privs->key_info->key_part;
3876  col_privs->field[0]->store(host.get_host(),
3877  host.get_host() ? (uint) host.get_host_len() : 0,
3878  system_charset_info);
3879  col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
3880  col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
3881  col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
3882 
3883  key_prefix_len= (key_part[0].store_length +
3884  key_part[1].store_length +
3885  key_part[2].store_length +
3886  key_part[3].store_length);
3887  key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
3888  col_privs->field[4]->store("",0, &my_charset_latin1);
3889 
3890  if (col_privs->file->ha_index_init(0, 1))
3891  {
3892  cols= 0;
3893  return;
3894  }
3895 
3896  if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
3897  (key_part_map)15, HA_READ_KEY_EXACT))
3898  {
3899  cols = 0; /* purecov: deadcode */
3900  col_privs->file->ha_index_end();
3901  return;
3902  }
3903  do
3904  {
3905  String *res,column_name;
3906  GRANT_COLUMN *mem_check;
3907  /* As column name is a string, we don't have to supply a buffer */
3908  res=col_privs->field[4]->val_str(&column_name);
3909  ulong priv= (ulong) col_privs->field[6]->val_int();
3910  if (!(mem_check = new GRANT_COLUMN(*res,
3911  fix_rights_for_column(priv))))
3912  {
3913  /* Don't use this entry */
3914  privs = cols = 0; /* purecov: deadcode */
3915  return; /* purecov: deadcode */
3916  }
3917  if (my_hash_insert(&hash_columns, (uchar *) mem_check))
3918  {
3919  /* Invalidate this entry */
3920  privs= cols= 0;
3921  return;
3922  }
3923  } while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
3924  !key_cmp_if_same(col_privs,key,0,key_prefix_len));
3925  col_privs->file->ha_index_end();
3926  }
3927 }
3928 
3929 
3930 GRANT_TABLE::~GRANT_TABLE()
3931 {
3932  my_hash_free(&hash_columns);
3933 }
3934 
3935 
3936 static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
3937  my_bool not_used __attribute__((unused)))
3938 {
3939  *length=buff->key_length;
3940  return (uchar*) buff->hash_key;
3941 }
3942 
3943 
3944 void free_grant_table(GRANT_TABLE *grant_table)
3945 {
3946  my_hash_free(&grant_table->hash_columns);
3947 }
3948 
3949 
3950 /* Search after a matching grant. Prefer exact grants before not exact ones */
3951 
3952 static GRANT_NAME *name_hash_search(HASH *name_hash,
3953  const char *host,const char* ip,
3954  const char *db,
3955  const char *user, const char *tname,
3956  bool exact, bool name_tolower)
3957 {
3958  char helping [NAME_LEN*2+USERNAME_LENGTH+3], *name_ptr;
3959  uint len;
3960  GRANT_NAME *grant_name,*found=0;
3961  HASH_SEARCH_STATE state;
3962 
3963  name_ptr= strmov(strmov(helping, user) + 1, db) + 1;
3964  len = (uint) (strmov(name_ptr, tname) - helping) + 1;
3965  if (name_tolower)
3966  my_casedn_str(files_charset_info, name_ptr);
3967  for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
3968  len, &state);
3969  grant_name ;
3970  grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
3971  len, &state))
3972  {
3973  if (exact)
3974  {
3975  if (!grant_name->host.get_host() ||
3976  (host &&
3977  !my_strcasecmp(system_charset_info, host,
3978  grant_name->host.get_host())) ||
3979  (ip && !strcmp(ip, grant_name->host.get_host())))
3980  return grant_name;
3981  }
3982  else
3983  {
3984  if (grant_name->host.compare_hostname(host, ip) &&
3985  (!found || found->sort < grant_name->sort))
3986  found=grant_name; // Host ok
3987  }
3988  }
3989  return found;
3990 }
3991 
3992 
3993 inline GRANT_NAME *
3994 routine_hash_search(const char *host, const char *ip, const char *db,
3995  const char *user, const char *tname, bool proc, bool exact)
3996 {
3997  return (GRANT_TABLE*)
3998  name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
3999  host, ip, db, user, tname, exact, TRUE);
4000 }
4001 
4002 
4003 inline GRANT_TABLE *
4004 table_hash_search(const char *host, const char *ip, const char *db,
4005  const char *user, const char *tname, bool exact)
4006 {
4007  return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
4008  user, tname, exact, FALSE);
4009 }
4010 
4011 
4012 inline GRANT_COLUMN *
4013 column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
4014 {
4015  return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
4016  (uchar*) cname, length);
4017 }
4018 
4019 
4020 static int replace_column_table(GRANT_TABLE *g_t,
4021  TABLE *table, const LEX_USER &combo,
4022  List <LEX_COLUMN> &columns,
4023  const char *db, const char *table_name,
4024  ulong rights, bool revoke_grant)
4025 {
4026  int result=0;
4027  uchar key[MAX_KEY_LENGTH];
4028  uint key_prefix_length;
4029  DBUG_ENTER("replace_column_table");
4030 
4031  if (!table->key_info)
4032  {
4033  my_error(ER_TABLE_CORRUPT, MYF(0), table->s->db.str,
4034  table->s->table_name.str);
4035  DBUG_RETURN(-1);
4036  }
4037 
4038  KEY_PART_INFO *key_part= table->key_info->key_part;
4039 
4040  table->use_all_columns();
4041  table->field[0]->store(combo.host.str,combo.host.length,
4042  system_charset_info);
4043  table->field[1]->store(db,(uint) strlen(db),
4044  system_charset_info);
4045  table->field[2]->store(combo.user.str,combo.user.length,
4046  system_charset_info);
4047  table->field[3]->store(table_name,(uint) strlen(table_name),
4048  system_charset_info);
4049 
4050  /* Get length of 4 first key parts */
4051  key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
4052  key_part[2].store_length + key_part[3].store_length);
4053  key_copy(key, table->record[0], table->key_info, key_prefix_length);
4054 
4055  rights&= COL_ACLS; // Only ACL for columns
4056 
4057  /* first fix privileges for all columns in column list */
4058 
4059  List_iterator <LEX_COLUMN> iter(columns);
4060  class LEX_COLUMN *column;
4061  int error= table->file->ha_index_init(0, 1);
4062  if (error)
4063  {
4064  table->file->print_error(error, MYF(0));
4065  DBUG_RETURN(-1);
4066  }
4067 
4068  while ((column= iter++))
4069  {
4070  ulong privileges= column->rights;
4071  bool old_row_exists=0;
4072  uchar user_key[MAX_KEY_LENGTH];
4073 
4074  key_restore(table->record[0],key,table->key_info,
4075  key_prefix_length);
4076  table->field[4]->store(column->column.ptr(), column->column.length(),
4077  system_charset_info);
4078  /* Get key for the first 4 columns */
4079  key_copy(user_key, table->record[0], table->key_info,
4080  table->key_info->key_length);
4081 
4082  if (table->file->ha_index_read_map(table->record[0], user_key, HA_WHOLE_KEY,
4083  HA_READ_KEY_EXACT))
4084  {
4085  if (revoke_grant)
4086  {
4087  my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
4088  combo.user.str, combo.host.str,
4089  table_name); /* purecov: inspected */
4090  result= -1; /* purecov: inspected */
4091  continue; /* purecov: inspected */
4092  }
4093  old_row_exists = 0;
4094  restore_record(table, s->default_values); // Get empty record
4095  key_restore(table->record[0],key,table->key_info,
4096  key_prefix_length);
4097  table->field[4]->store(column->column.ptr(),column->column.length(),
4098  system_charset_info);
4099  }
4100  else
4101  {
4102  ulong tmp= (ulong) table->field[6]->val_int();
4103  tmp=fix_rights_for_column(tmp);
4104 
4105  if (revoke_grant)
4106  privileges = tmp & ~(privileges | rights);
4107  else
4108  privileges |= tmp;
4109  old_row_exists = 1;
4110  store_record(table,record[1]); // copy original row
4111  }
4112 
4113  table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
4114 
4115  if (old_row_exists)
4116  {
4117  GRANT_COLUMN *grant_column;
4118  if (privileges)
4119  error=table->file->ha_update_row(table->record[1],table->record[0]);
4120  else
4121  error=table->file->ha_delete_row(table->record[1]);
4122  if (error && error != HA_ERR_RECORD_IS_THE_SAME)
4123  {
4124  table->file->print_error(error,MYF(0)); /* purecov: inspected */
4125  result= -1; /* purecov: inspected */
4126  goto end; /* purecov: inspected */
4127  }
4128  else
4129  error= 0;
4130  grant_column= column_hash_search(g_t, column->column.ptr(),
4131  column->column.length());
4132  if (grant_column) // Should always be true
4133  grant_column->rights= privileges; // Update hash
4134  }
4135  else // new grant
4136  {
4137  GRANT_COLUMN *grant_column;
4138  if ((error=table->file->ha_write_row(table->record[0])))
4139  {
4140  table->file->print_error(error,MYF(0)); /* purecov: inspected */
4141  result= -1; /* purecov: inspected */
4142  goto end; /* purecov: inspected */
4143  }
4144  grant_column= new GRANT_COLUMN(column->column,privileges);
4145  if (my_hash_insert(&g_t->hash_columns,(uchar*) grant_column))
4146  {
4147  result= -1;
4148  goto end;
4149  }
4150  }
4151  }
4152 
4153  /*
4154  If revoke of privileges on the table level, remove all such privileges
4155  for all columns
4156  */
4157 
4158  if (revoke_grant)
4159  {
4160  uchar user_key[MAX_KEY_LENGTH];
4161  key_copy(user_key, table->record[0], table->key_info,
4162  key_prefix_length);
4163 
4164  if (table->file->ha_index_read_map(table->record[0], user_key,
4165  (key_part_map)15,
4166  HA_READ_KEY_EXACT))
4167  goto end;
4168 
4169  /* Scan through all rows with the same host,db,user and table */
4170  do
4171  {
4172  ulong privileges = (ulong) table->field[6]->val_int();
4173  privileges=fix_rights_for_column(privileges);
4174  store_record(table,record[1]);
4175 
4176  if (privileges & rights) // is in this record the priv to be revoked ??
4177  {
4178  GRANT_COLUMN *grant_column = NULL;
4179  char colum_name_buf[HOSTNAME_LENGTH+1];
4180  String column_name(colum_name_buf,sizeof(colum_name_buf),
4181  system_charset_info);
4182 
4183  privileges&= ~rights;
4184  table->field[6]->store((longlong)
4185  get_rights_for_column(privileges), TRUE);
4186  table->field[4]->val_str(&column_name);
4187  grant_column = column_hash_search(g_t,
4188  column_name.ptr(),
4189  column_name.length());
4190  if (privileges)
4191  {
4192  int tmp_error;
4193  if ((tmp_error=table->file->ha_update_row(table->record[1],
4194  table->record[0])) &&
4195  tmp_error != HA_ERR_RECORD_IS_THE_SAME)
4196  { /* purecov: deadcode */
4197  table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
4198  result= -1; /* purecov: deadcode */
4199  goto end; /* purecov: deadcode */
4200  }
4201  if (grant_column)
4202  grant_column->rights = privileges; // Update hash
4203  }
4204  else
4205  {
4206  int tmp_error;
4207  if ((tmp_error = table->file->ha_delete_row(table->record[1])))
4208  { /* purecov: deadcode */
4209  table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
4210  result= -1; /* purecov: deadcode */
4211  goto end; /* purecov: deadcode */
4212  }
4213  if (grant_column)
4214  my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
4215  }
4216  }
4217  } while (!table->file->ha_index_next(table->record[0]) &&
4218  !key_cmp_if_same(table, key, 0, key_prefix_length));
4219  }
4220 
4221 end:
4222  table->file->ha_index_end();
4223  DBUG_RETURN(result);
4224 }
4225 
4226 static inline void get_grantor(THD *thd, char *grantor)
4227 {
4228  const char *user= thd->security_ctx->user;
4229  const char *host= thd->security_ctx->host_or_ip;
4230 
4231 #if defined(HAVE_REPLICATION)
4232  if (thd->slave_thread && thd->has_invoker())
4233  {
4234  user= thd->get_invoker_user().str;
4235  host= thd->get_invoker_host().str;
4236  }
4237 #endif
4238  strxmov(grantor, user, "@", host, NullS);
4239 }
4240 
4241 static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
4242  TABLE *table, const LEX_USER &combo,
4243  const char *db, const char *table_name,
4244  ulong rights, ulong col_rights,
4245  bool revoke_grant)
4246 {
4247  char grantor[USER_HOST_BUFF_SIZE];
4248  int old_row_exists = 1;
4249  int error=0;
4250  ulong store_table_rights, store_col_rights;
4251  uchar user_key[MAX_KEY_LENGTH];
4252  DBUG_ENTER("replace_table_table");
4253 
4254  get_grantor(thd, grantor);
4255  /*
4256  The following should always succeed as new users are created before
4257  this function is called!
4258  */
4259  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
4260  {
4261  my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
4262  MYF(0)); /* purecov: deadcode */
4263  DBUG_RETURN(-1); /* purecov: deadcode */
4264  }
4265 
4266  table->use_all_columns();
4267  restore_record(table, s->default_values); // Get empty record
4268  table->field[0]->store(combo.host.str,combo.host.length,
4269  system_charset_info);
4270  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4271  table->field[2]->store(combo.user.str,combo.user.length,
4272  system_charset_info);
4273  table->field[3]->store(table_name,(uint) strlen(table_name),
4274  system_charset_info);
4275  store_record(table,record[1]); // store at pos 1
4276  key_copy(user_key, table->record[0], table->key_info,
4277  table->key_info->key_length);
4278 
4279  if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
4280  HA_WHOLE_KEY,
4281  HA_READ_KEY_EXACT))
4282  {
4283  /*
4284  The following should never happen as we first check the in memory
4285  grant tables for the user. There is however always a small change that
4286  the user has modified the grant tables directly.
4287  */
4288  if (revoke_grant)
4289  { // no row, no revoke
4290  my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
4291  combo.user.str, combo.host.str,
4292  table_name); /* purecov: deadcode */
4293  DBUG_RETURN(-1); /* purecov: deadcode */
4294  }
4295  old_row_exists = 0;
4296  restore_record(table,record[1]); // Get saved record
4297  }
4298 
4299  store_table_rights= get_rights_for_table(rights);
4300  store_col_rights= get_rights_for_column(col_rights);
4301  if (old_row_exists)
4302  {
4303  ulong j,k;
4304  store_record(table,record[1]);
4305  j = (ulong) table->field[6]->val_int();
4306  k = (ulong) table->field[7]->val_int();
4307 
4308  if (revoke_grant)
4309  {
4310  /* column rights are already fixed in mysql_table_grant */
4311  store_table_rights=j & ~store_table_rights;
4312  }
4313  else
4314  {
4315  store_table_rights|= j;
4316  store_col_rights|= k;
4317  }
4318  }
4319 
4320  table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
4321  table->field[6]->store((longlong) store_table_rights, TRUE);
4322  table->field[7]->store((longlong) store_col_rights, TRUE);
4323  rights=fix_rights_for_table(store_table_rights);
4324  col_rights=fix_rights_for_column(store_col_rights);
4325 
4326  if (old_row_exists)
4327  {
4328  if (store_table_rights || store_col_rights)
4329  {
4330  if ((error=table->file->ha_update_row(table->record[1],
4331  table->record[0])) &&
4332  error != HA_ERR_RECORD_IS_THE_SAME)
4333  goto table_error; /* purecov: deadcode */
4334  }
4335  else if ((error = table->file->ha_delete_row(table->record[1])))
4336  goto table_error; /* purecov: deadcode */
4337  }
4338  else
4339  {
4340  error=table->file->ha_write_row(table->record[0]);
4341  if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4342  goto table_error; /* purecov: deadcode */
4343  }
4344 
4345  if (rights | col_rights)
4346  {
4347  grant_table->privs= rights;
4348  grant_table->cols= col_rights;
4349  }
4350  else
4351  {
4352  my_hash_delete(&column_priv_hash,(uchar*) grant_table);
4353  }
4354  DBUG_RETURN(0);
4355 
4356  /* This should never happen */
4357 table_error:
4358  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4359  DBUG_RETURN(-1); /* purecov: deadcode */
4360 }
4361 
4362 
4367 static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
4368  TABLE *table, const LEX_USER &combo,
4369  const char *db, const char *routine_name,
4370  bool is_proc, ulong rights, bool revoke_grant)
4371 {
4372  char grantor[USER_HOST_BUFF_SIZE];
4373  int old_row_exists= 1;
4374  int error=0;
4375  ulong store_proc_rights;
4376  DBUG_ENTER("replace_routine_table");
4377 
4378  if (!initialized)
4379  {
4380  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
4381  DBUG_RETURN(-1);
4382  }
4383 
4384  get_grantor(thd, grantor);
4385  /*
4386  New users are created before this function is called.
4387 
4388  There may be some cases where a routine's definer is removed but the
4389  routine remains.
4390  */
4391 
4392  table->use_all_columns();
4393  restore_record(table, s->default_values); // Get empty record
4394  table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
4395  table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
4396  table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
4397  table->field[3]->store(routine_name,(uint) strlen(routine_name),
4398  &my_charset_latin1);
4399  table->field[4]->store((longlong)(is_proc ?
4400  SP_TYPE_PROCEDURE : SP_TYPE_FUNCTION),
4401  TRUE);
4402  store_record(table,record[1]); // store at pos 1
4403 
4404  if (table->file->ha_index_read_idx_map(table->record[0], 0,
4405  (uchar*) table->field[0]->ptr,
4406  HA_WHOLE_KEY,
4407  HA_READ_KEY_EXACT))
4408  {
4409  /*
4410  The following should never happen as we first check the in memory
4411  grant tables for the user. There is however always a small change that
4412  the user has modified the grant tables directly.
4413  */
4414  if (revoke_grant)
4415  { // no row, no revoke
4416  my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
4417  combo.user.str, combo.host.str, routine_name);
4418  DBUG_RETURN(-1);
4419  }
4420  old_row_exists= 0;
4421  restore_record(table,record[1]); // Get saved record
4422  }
4423 
4424  store_proc_rights= get_rights_for_procedure(rights);
4425  if (old_row_exists)
4426  {
4427  ulong j;
4428  store_record(table,record[1]);
4429  j= (ulong) table->field[6]->val_int();
4430 
4431  if (revoke_grant)
4432  {
4433  /* column rights are already fixed in mysql_table_grant */
4434  store_proc_rights=j & ~store_proc_rights;
4435  }
4436  else
4437  {
4438  store_proc_rights|= j;
4439  }
4440  }
4441 
4442  table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
4443  table->field[6]->store((longlong) store_proc_rights, TRUE);
4444  rights=fix_rights_for_procedure(store_proc_rights);
4445 
4446  if (old_row_exists)
4447  {
4448  if (store_proc_rights)
4449  {
4450  if ((error=table->file->ha_update_row(table->record[1],
4451  table->record[0])) &&
4452  error != HA_ERR_RECORD_IS_THE_SAME)
4453  goto table_error;
4454  }
4455  else if ((error= table->file->ha_delete_row(table->record[1])))
4456  goto table_error;
4457  }
4458  else
4459  {
4460  error=table->file->ha_write_row(table->record[0]);
4461  if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4462  goto table_error;
4463  }
4464 
4465  if (rights)
4466  {
4467  grant_name->privs= rights;
4468  }
4469  else
4470  {
4471  my_hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*)
4472  grant_name);
4473  }
4474  DBUG_RETURN(0);
4475 
4476  /* This should never happen */
4477 table_error:
4478  table->file->print_error(error,MYF(0));
4479  DBUG_RETURN(-1);
4480 }
4481 
4482 
4483 /*
4484  Store table level and column level grants in the privilege tables
4485 
4486  SYNOPSIS
4487  mysql_table_grant()
4488  thd Thread handle
4489  table_list List of tables to give grant
4490  user_list List of users to give grant
4491  columns List of columns to give grant
4492  rights Table level grant
4493  revoke_grant Set to 1 if this is a REVOKE command
4494 
4495  RETURN
4496  FALSE ok
4497  TRUE error
4498 */
4499 
4500 int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
4501  List <LEX_USER> &user_list,
4502  List <LEX_COLUMN> &columns, ulong rights,
4503  bool revoke_grant)
4504 {
4505  ulong column_priv= 0;
4506  List_iterator <LEX_USER> str_list (user_list);
4507  LEX_USER *Str, *tmp_Str;
4508  TABLE_LIST tables[3];
4509  bool create_new_users=0;
4510  char *db_name, *table_name;
4511  bool save_binlog_row_based;
4512  bool transactional_tables;
4513  DBUG_ENTER("mysql_table_grant");
4514 
4515  if (!initialized)
4516  {
4517  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
4518  "--skip-grant-tables"); /* purecov: inspected */
4519  DBUG_RETURN(TRUE); /* purecov: inspected */
4520  }
4521  if (rights & ~TABLE_ACLS)
4522  {
4523  my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
4524  MYF(0));
4525  DBUG_RETURN(TRUE);
4526  }
4527 
4528  if (!revoke_grant)
4529  {
4530  if (columns.elements)
4531  {
4532  class LEX_COLUMN *column;
4533  List_iterator <LEX_COLUMN> column_iter(columns);
4534 
4535  if (open_normal_and_derived_tables(thd, table_list, 0))
4536  DBUG_RETURN(TRUE);
4537 
4538  while ((column = column_iter++))
4539  {
4540  uint unused_field_idx= NO_CACHED_FIELD_INDEX;
4541  TABLE_LIST *dummy;
4542  Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
4543  column->column.length(),
4544  column->column.ptr(), NULL, NULL,
4545  NULL, TRUE, FALSE,
4546  &unused_field_idx, FALSE, &dummy);
4547  if (f == (Field*)0)
4548  {
4549  my_error(ER_BAD_FIELD_ERROR, MYF(0),
4550  column->column.c_ptr(), table_list->alias);
4551  DBUG_RETURN(TRUE);
4552  }
4553  if (f == (Field *)-1)
4554  DBUG_RETURN(TRUE);
4555  column_priv|= column->rights;
4556  }
4557  close_mysql_tables(thd);
4558  }
4559  else
4560  {
4561  if (!(rights & CREATE_ACL))
4562  {
4563  char buf[FN_REFLEN + 1];
4564  build_table_filename(buf, sizeof(buf) - 1, table_list->db,
4565  table_list->table_name, reg_ext, 0);
4566  fn_format(buf, buf, "", "", MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS |
4567  MY_RETURN_REAL_PATH | MY_APPEND_EXT);
4568  if (access(buf,F_OK))
4569  {
4570  my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
4571  DBUG_RETURN(TRUE);
4572  }
4573  }
4574  if (table_list->grant.want_privilege)
4575  {
4576  char command[128];
4577  get_privilege_desc(command, sizeof(command),
4578  table_list->grant.want_privilege);
4579  my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
4580  command, thd->security_ctx->priv_user,
4581  thd->security_ctx->host_or_ip, table_list->alias);
4582  DBUG_RETURN(-1);
4583  }
4584  }
4585  }
4586 
4587  /* open the mysql.tables_priv and mysql.columns_priv tables */
4588 
4589  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
4590  C_STRING_WITH_LEN("user"), "user", TL_WRITE);
4591  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
4592  C_STRING_WITH_LEN("tables_priv"),
4593  "tables_priv", TL_WRITE);
4594  tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
4595  C_STRING_WITH_LEN("columns_priv"),
4596  "columns_priv", TL_WRITE);
4597  tables[0].next_local= tables[0].next_global= tables+1;
4598  /* Don't open column table if we don't need it ! */
4599  if (column_priv || (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
4600  tables[1].next_local= tables[1].next_global= tables+2;
4601 
4602  /*
4603  This statement will be replicated as a statement, even when using
4604  row-based replication. The flag will be reset at the end of the
4605  statement.
4606  */
4607  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
4608  thd->clear_current_stmt_binlog_format_row();
4609 
4610 #ifdef HAVE_REPLICATION
4611  /*
4612  GRANT and REVOKE are applied the slave in/exclusion rules as they are
4613  some kind of updates to the mysql.% tables.
4614  */
4615  if (thd->slave_thread && rpl_filter->is_on())
4616  {
4617  /*
4618  The tables must be marked "updating" so that tables_ok() takes them into
4619  account in tests.
4620  */
4621  tables[0].updating= tables[1].updating= tables[2].updating= 1;
4622  if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, tables)))
4623  {
4624  /* Restore the state of binlog format */
4625  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4626  if (save_binlog_row_based)
4627  thd->set_current_stmt_binlog_format_row();
4628  DBUG_RETURN(FALSE);
4629  }
4630  }
4631 #endif
4632 
4633  /*
4634  The lock api is depending on the thd->lex variable which needs to be
4635  re-initialized.
4636  */
4637  Query_tables_list backup;
4638  thd->lex->reset_n_backup_query_tables_list(&backup);
4639  /*
4640  Restore Query_tables_list::sql_command value, which was reset
4641  above, as the code writing query to the binary log assumes that
4642  this value corresponds to the statement being executed.
4643  */
4644  thd->lex->sql_command= backup.sql_command;
4645  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
4646  { // Should never happen
4647  /* Restore the state of binlog format */
4648  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4649  thd->lex->restore_backup_query_tables_list(&backup);
4650  if (save_binlog_row_based)
4651  thd->set_current_stmt_binlog_format_row();
4652  DBUG_RETURN(TRUE); /* purecov: deadcode */
4653  }
4654 
4655  transactional_tables= (tables[0].table->file->has_transactions() ||
4656  tables[1].table->file->has_transactions() ||
4657  (tables[2].table &&
4658  tables[2].table->file->has_transactions()));
4659 
4660  if (!revoke_grant)
4661  create_new_users= test_if_create_new_users(thd);
4662  bool result= FALSE;
4663  mysql_rwlock_wrlock(&LOCK_grant);
4664  mysql_mutex_lock(&acl_cache->lock);
4665  MEM_ROOT *old_root= thd->mem_root;
4666  thd->mem_root= &memex;
4667  grant_version++;
4668 
4669  while ((tmp_Str = str_list++))
4670  {
4671  int error;
4672  GRANT_TABLE *grant_table;
4673  if (!(Str= get_current_user(thd, tmp_Str)))
4674  {
4675  result= TRUE;
4676  continue;
4677  }
4678 
4679  /*
4680  No User, but a password?
4681  They did GRANT ... TO CURRENT_USER() IDENTIFIED BY ... !
4682  Get the current user, and shallow-copy the new password to them!
4683  */
4684  if (!tmp_Str->user.str && tmp_Str->password.str)
4685  Str->password= tmp_Str->password;
4686 
4687  /* Create user if needed */
4688  error=replace_user_table(thd, tables[0].table, Str,
4689  0, revoke_grant, create_new_users,
4690  test(thd->variables.sql_mode &
4691  MODE_NO_AUTO_CREATE_USER));
4692  if (error)
4693  {
4694  result= TRUE; // Remember error
4695  continue; // Add next user
4696  }
4697 
4698  db_name= table_list->get_db_name();
4699  thd->add_to_binlog_accessed_dbs(db_name); // collecting db:s for MTS
4700  table_name= table_list->get_table_name();
4701 
4702  /* Find/create cached table grant */
4703  grant_table= table_hash_search(Str->host.str, NullS, db_name,
4704  Str->user.str, table_name, 1);
4705  if (!grant_table)
4706  {
4707  if (revoke_grant)
4708  {
4709  my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
4710  Str->user.str, Str->host.str, table_list->table_name);
4711  result= TRUE;
4712  continue;
4713  }
4714  grant_table = new GRANT_TABLE (Str->host.str, db_name,
4715  Str->user.str, table_name,
4716  rights,
4717  column_priv);
4718  if (!grant_table ||
4719  my_hash_insert(&column_priv_hash,(uchar*) grant_table))
4720  {
4721  result= TRUE; /* purecov: deadcode */
4722  continue; /* purecov: deadcode */
4723  }
4724  }
4725 
4726  /* If revoke_grant, calculate the new column privilege for tables_priv */
4727  if (revoke_grant)
4728  {
4729  class LEX_COLUMN *column;
4730  List_iterator <LEX_COLUMN> column_iter(columns);
4731  GRANT_COLUMN *grant_column;
4732 
4733  /* Fix old grants */
4734  while ((column = column_iter++))
4735  {
4736  grant_column = column_hash_search(grant_table,
4737  column->column.ptr(),
4738  column->column.length());
4739  if (grant_column)
4740  grant_column->rights&= ~(column->rights | rights);
4741  }
4742  /* scan trough all columns to get new column grant */
4743  column_priv= 0;
4744  for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
4745  {
4746  grant_column= (GRANT_COLUMN*)
4747  my_hash_element(&grant_table->hash_columns, idx);
4748  grant_column->rights&= ~rights; // Fix other columns
4749  column_priv|= grant_column->rights;
4750  }
4751  }
4752  else
4753  {
4754  column_priv|= grant_table->cols;
4755  }
4756 
4757 
4758  /* update table and columns */
4759 
4760  if (replace_table_table(thd, grant_table, tables[1].table, *Str,
4761  db_name, table_name,
4762  rights, column_priv, revoke_grant))
4763  {
4764  /* Should only happen if table is crashed */
4765  result= TRUE; /* purecov: deadcode */
4766  }
4767  else if (tables[2].table)
4768  {
4769  if ((replace_column_table(grant_table, tables[2].table, *Str,
4770  columns,
4771  db_name, table_name,
4772  rights, revoke_grant)))
4773  {
4774  result= TRUE;
4775  }
4776  }
4777  }
4778  thd->mem_root= old_root;
4779  mysql_mutex_unlock(&acl_cache->lock);
4780 
4781  /*
4782  We only log "complete" successful commands, because partially
4783  failed REVOKE/GRANTS that fail because of insufficient privileges
4784  on the master, will succeed on the slave due to SQL thread SUPER
4785  privilege. Even though replication will stop (the error code from
4786  the master will mismatch the error code on the slave), the
4787  operation will already be executed (thence revoking or granting
4788  additional privileges on the slave).
4789  When some error happens, even partial, a incident event is logged
4790  instead stating that manual reconciliation is needed.
4791  */
4792  if (result)
4793  mysql_bin_log.write_incident(thd, true /* need_lock_log=true */);
4794  else
4795  result= result |
4796  write_bin_log(thd, FALSE, thd->query(), thd->query_length(),
4797  transactional_tables);
4798 
4799  mysql_rwlock_unlock(&LOCK_grant);
4800 
4801  result|= acl_trans_commit_and_close_tables(thd);
4802 
4803  if (!result) /* success */
4804  my_ok(thd);
4805 
4806  thd->lex->restore_backup_query_tables_list(&backup);
4807  /* Restore the state of binlog format */
4808  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4809  if (save_binlog_row_based)
4810  thd->set_current_stmt_binlog_format_row();
4811  DBUG_RETURN(result);
4812 }
4813 
4814 
4830 bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
4831  List <LEX_USER> &user_list, ulong rights,
4832  bool revoke_grant, bool write_to_binlog)
4833 {
4834  List_iterator <LEX_USER> str_list (user_list);
4835  LEX_USER *Str, *tmp_Str;
4836  TABLE_LIST tables[2];
4837  bool create_new_users=0, result=0;
4838  char *db_name, *table_name;
4839  bool save_binlog_row_based;
4840  bool transactional_tables;
4841  DBUG_ENTER("mysql_routine_grant");
4842 
4843  if (!initialized)
4844  {
4845  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
4846  "--skip-grant-tables");
4847  DBUG_RETURN(TRUE);
4848  }
4849  if (rights & ~PROC_ACLS)
4850  {
4851  my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
4852  MYF(0));
4853  DBUG_RETURN(TRUE);
4854  }
4855 
4856  if (!revoke_grant)
4857  {
4858  if (sp_exist_routines(thd, table_list, is_proc))
4859  DBUG_RETURN(TRUE);
4860  }
4861 
4862  /* open the mysql.user and mysql.procs_priv tables */
4863 
4864  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
4865  C_STRING_WITH_LEN("user"), "user", TL_WRITE);
4866  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
4867  C_STRING_WITH_LEN("procs_priv"), "procs_priv", TL_WRITE);
4868  tables[0].next_local= tables[0].next_global= tables+1;
4869 
4870  /*
4871  This statement will be replicated as a statement, even when using
4872  row-based replication. The flag will be reset at the end of the
4873  statement.
4874  */
4875  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
4876  thd->clear_current_stmt_binlog_format_row();
4877 
4878 #ifdef HAVE_REPLICATION
4879  /*
4880  GRANT and REVOKE are applied the slave in/exclusion rules as they are
4881  some kind of updates to the mysql.% tables.
4882  */
4883  if (thd->slave_thread && rpl_filter->is_on())
4884  {
4885  /*
4886  The tables must be marked "updating" so that tables_ok() takes them into
4887  account in tests.
4888  */
4889  tables[0].updating= tables[1].updating= 1;
4890  if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, tables)))
4891  {
4892  /* Restore the state of binlog format */
4893  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4894  if (save_binlog_row_based)
4895  thd->set_current_stmt_binlog_format_row();
4896  DBUG_RETURN(FALSE);
4897  }
4898  }
4899 #endif
4900 
4901  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
4902  { // Should never happen
4903  /* Restore the state of binlog format */
4904  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4905  if (save_binlog_row_based)
4906  thd->set_current_stmt_binlog_format_row();
4907  DBUG_RETURN(TRUE);
4908  }
4909 
4910  transactional_tables= (tables[0].table->file->has_transactions() ||
4911  tables[1].table->file->has_transactions());
4912 
4913  if (!revoke_grant)
4914  create_new_users= test_if_create_new_users(thd);
4915  mysql_rwlock_wrlock(&LOCK_grant);
4916  mysql_mutex_lock(&acl_cache->lock);
4917  MEM_ROOT *old_root= thd->mem_root;
4918  thd->mem_root= &memex;
4919 
4920  DBUG_PRINT("info",("now time to iterate and add users"));
4921 
4922  while ((tmp_Str= str_list++))
4923  {
4924  int error;
4925  GRANT_NAME *grant_name;
4926  if (!(Str= get_current_user(thd, tmp_Str)))
4927  {
4928  result= TRUE;
4929  continue;
4930  }
4931 
4932  /* Create user if needed */
4933  error=replace_user_table(thd, tables[0].table, Str,
4934  0, revoke_grant, create_new_users,
4935  test(thd->variables.sql_mode &
4936  MODE_NO_AUTO_CREATE_USER));
4937  if (error)
4938  {
4939  result= TRUE; // Remember error
4940  continue; // Add next user
4941  }
4942 
4943  db_name= table_list->db;
4944  if (write_to_binlog)
4945  thd->add_to_binlog_accessed_dbs(db_name);
4946  table_name= table_list->table_name;
4947  grant_name= routine_hash_search(Str->host.str, NullS, db_name,
4948  Str->user.str, table_name, is_proc, 1);
4949  if (!grant_name)
4950  {
4951  if (revoke_grant)
4952  {
4953  my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
4954  Str->user.str, Str->host.str, table_name);
4955  result= TRUE;
4956  continue;
4957  }
4958  grant_name= new GRANT_NAME(Str->host.str, db_name,
4959  Str->user.str, table_name,
4960  rights, TRUE);
4961  if (!grant_name ||
4962  my_hash_insert(is_proc ?
4963  &proc_priv_hash : &func_priv_hash,(uchar*) grant_name))
4964  {
4965  result= TRUE;
4966  continue;
4967  }
4968  }
4969 
4970  if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
4971  db_name, table_name, is_proc, rights,
4972  revoke_grant) != 0)
4973  {
4974  result= TRUE;
4975  continue;
4976  }
4977  }
4978  thd->mem_root= old_root;
4979  mysql_mutex_unlock(&acl_cache->lock);
4980 
4981  if (write_to_binlog)
4982  {
4983  if (result)
4984  mysql_bin_log.write_incident(thd, true /* need_lock_log=true */);
4985  else
4986  {
4987  /*
4988  For performance reasons, we don't rewrite the query if we don't have to.
4989  If that was the case, write the original query.
4990  */
4991  if (!thd->rewritten_query.length())
4992  {
4993  if (write_bin_log(thd, false, thd->query(), thd->query_length(),
4994  transactional_tables))
4995  result= TRUE;
4996  }
4997  else
4998  {
4999  if (write_bin_log(thd, false,
5000  thd->rewritten_query.c_ptr_safe(),
5001  thd->rewritten_query.length(),
5002  transactional_tables))
5003  result= TRUE;
5004  }
5005  }
5006  }
5007 
5008  mysql_rwlock_unlock(&LOCK_grant);
5009 
5010  result|= acl_trans_commit_and_close_tables(thd);
5011 
5012  /* Restore the state of binlog format */
5013  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5014  if (save_binlog_row_based)
5015  thd->set_current_stmt_binlog_format_row();
5016 
5017  DBUG_RETURN(result);
5018 }
5019 
5020 
5035 int digest_password(THD *thd, LEX_USER *user_record)
5036 {
5037  /* Empty passwords stay empty */
5038  if (user_record->password.length == 0)
5039  return 0;
5040 
5041 #if defined(HAVE_OPENSSL)
5042  /*
5043  Transform password into a password hash
5044  */
5045  if (user_record->plugin.str == sha256_password_plugin_name.str)
5046  {
5047  char *buff= (char *) thd->alloc(CRYPT_MAX_PASSWORD_SIZE+1);
5048  if (buff == NULL)
5049  return 1;
5050 
5051  my_make_scrambled_password(buff, user_record->password.str,
5052  user_record->password.length);
5053  user_record->password.str= buff;
5054  user_record->password.length= strlen(buff)+1;
5055  }
5056  else
5057 #endif
5058  if (user_record->plugin.str == native_password_plugin_name.str ||
5059  user_record->plugin.str == old_password_plugin_name.str)
5060  {
5061  if (thd->variables.old_passwords == 1)
5062  {
5063  char *buff=
5064  (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
5065  if (buff == NULL)
5066  return 1;
5067 
5068  my_make_scrambled_password_323(buff, user_record->password.str,
5069  user_record->password.length);
5070  user_record->password.str= buff;
5071  user_record->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
5072  }
5073  else
5074  {
5075  char *buff=
5076  (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
5077  if (buff == NULL)
5078  return 1;
5079 
5080  my_make_scrambled_password_sha1(buff, user_record->password.str,
5081  user_record->password.length);
5082  user_record->password.str= buff;
5083  user_record->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
5084  }
5085  } // end if native_password_plugin_name || old_password_plugin_name
5086  else
5087  {
5088  user_record->password.str= 0;
5089  user_record->password.length= 0;
5090  }
5091  return 0;
5092 }
5093 
5094 bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
5095  ulong rights, bool revoke_grant, bool is_proxy)
5096 {
5097  List_iterator <LEX_USER> str_list (list);
5098  LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
5099  char tmp_db[NAME_LEN+1];
5100  bool create_new_users=0;
5101  TABLE_LIST tables[2];
5102  bool save_binlog_row_based;
5103  bool transactional_tables;
5104  DBUG_ENTER("mysql_grant");
5105  if (!initialized)
5106  {
5107  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
5108  "--skip-grant-tables"); /* purecov: tested */
5109  DBUG_RETURN(TRUE); /* purecov: tested */
5110  }
5111 
5112  if (lower_case_table_names && db)
5113  {
5114  strnmov(tmp_db,db,NAME_LEN);
5115  tmp_db[NAME_LEN]= '\0';
5116  my_casedn_str(files_charset_info, tmp_db);
5117  db=tmp_db;
5118  }
5119 
5120  if (is_proxy)
5121  {
5122  DBUG_ASSERT(!db);
5123  proxied_user= str_list++;
5124  }
5125 
5126  /* open the mysql.user and mysql.db or mysql.proxies_priv tables */
5127  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
5128  C_STRING_WITH_LEN("user"), "user", TL_WRITE);
5129  if (is_proxy)
5130 
5131  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
5132  C_STRING_WITH_LEN("proxies_priv"),
5133  "proxies_priv",
5134  TL_WRITE);
5135  else
5136  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
5137  C_STRING_WITH_LEN("db"),
5138  "db",
5139  TL_WRITE);
5140  tables[0].next_local= tables[0].next_global= tables+1;
5141 
5142  /*
5143  This statement will be replicated as a statement, even when using
5144  row-based replication. The flag will be reset at the end of the
5145  statement.
5146  */
5147  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
5148  thd->clear_current_stmt_binlog_format_row();
5149 
5150 #ifdef HAVE_REPLICATION
5151  /*
5152  GRANT and REVOKE are applied the slave in/exclusion rules as they are
5153  some kind of updates to the mysql.% tables.
5154  */
5155  if (thd->slave_thread && rpl_filter->is_on())
5156  {
5157  /*
5158  The tables must be marked "updating" so that tables_ok() takes them into
5159  account in tests.
5160  */
5161  tables[0].updating= tables[1].updating= 1;
5162  if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, tables)))
5163  {
5164  /* Restore the state of binlog format */
5165  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5166  if (save_binlog_row_based)
5167  thd->set_current_stmt_binlog_format_row();
5168  DBUG_RETURN(FALSE);
5169  }
5170  }
5171 #endif
5172 
5173  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5174  { // This should never happen
5175  /* Restore the state of binlog format */
5176  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5177  if (save_binlog_row_based)
5178  thd->set_current_stmt_binlog_format_row();
5179  DBUG_RETURN(TRUE); /* purecov: deadcode */
5180  }
5181 
5182  transactional_tables= (tables[0].table->file->has_transactions() ||
5183  tables[1].table->file->has_transactions());
5184 
5185  if (!revoke_grant)
5186  create_new_users= test_if_create_new_users(thd);
5187 
5188  /* go through users in user_list */
5189  mysql_rwlock_wrlock(&LOCK_grant);
5190  mysql_mutex_lock(&acl_cache->lock);
5191  grant_version++;
5192 
5193  int result=0;
5194  while ((tmp_Str = str_list++))
5195  {
5196  if (!(Str= get_current_user(thd, tmp_Str)))
5197  {
5198  result= TRUE;
5199  continue;
5200  }
5201 
5202  /*
5203  No User, but a password?
5204  They did GRANT ... TO CURRENT_USER() IDENTIFIED BY ... !
5205  Get the current user, and shallow-copy the new password to them!
5206  */
5207  if (!tmp_Str->user.str && tmp_Str->password.str)
5208  Str->password= tmp_Str->password;
5209 
5210  if (replace_user_table(thd, tables[0].table, Str,
5211  (!db ? rights : 0), revoke_grant, create_new_users,
5212  test(thd->variables.sql_mode &
5213  MODE_NO_AUTO_CREATE_USER)))
5214  result= -1;
5215  else if (db)
5216  {
5217  ulong db_rights= rights & DB_ACLS;
5218  if (db_rights == rights)
5219  {
5220  if (replace_db_table(tables[1].table, db, *Str, db_rights,
5221  revoke_grant))
5222  result= -1;
5223  }
5224  else
5225  {
5226  my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
5227  result= -1;
5228  }
5229  thd->add_to_binlog_accessed_dbs(db);
5230  }
5231  else if (is_proxy)
5232  {
5233  if (replace_proxies_priv_table (thd, tables[1].table, Str, proxied_user,
5234  rights & GRANT_ACL ? TRUE : FALSE,
5235  revoke_grant))
5236  result= -1;
5237  }
5238  }
5239  mysql_mutex_unlock(&acl_cache->lock);
5240 
5241  if (result)
5242  mysql_bin_log.write_incident(thd, true /* need_lock_log=true */);
5243  else
5244  {
5245  if (thd->rewritten_query.length())
5246  result= result |
5247  write_bin_log(thd, FALSE,
5248  thd->rewritten_query.c_ptr_safe(),
5249  thd->rewritten_query.length(),
5250  transactional_tables);
5251  else
5252  result= result |
5253  write_bin_log(thd, FALSE, thd->query(), thd->query_length(),
5254  transactional_tables);
5255  }
5256 
5257  mysql_rwlock_unlock(&LOCK_grant);
5258 
5259  result|= acl_trans_commit_and_close_tables(thd);
5260 
5261  if (!result)
5262  my_ok(thd);
5263 
5264  /* Restore the state of binlog format */
5265  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5266  if (save_binlog_row_based)
5267  thd->set_current_stmt_binlog_format_row();
5268 
5269  DBUG_RETURN(result);
5270 }
5271 
5272 
5273 /* Free grant array if possible */
5274 
5275 void grant_free(void)
5276 {
5277  DBUG_ENTER("grant_free");
5278  my_hash_free(&column_priv_hash);
5279  my_hash_free(&proc_priv_hash);
5280  my_hash_free(&func_priv_hash);
5281  free_root(&memex,MYF(0));
5282  DBUG_VOID_RETURN;
5283 }
5284 
5285 
5295 my_bool grant_init()
5296 {
5297  THD *thd;
5298  my_bool return_val;
5299  DBUG_ENTER("grant_init");
5300 
5301  if (!(thd= new THD))
5302  DBUG_RETURN(1); /* purecov: deadcode */
5303  thd->thread_stack= (char*) &thd;
5304  thd->store_globals();
5305  return_val= grant_reload(thd);
5306  delete thd;
5307  /* Remember that we don't have a THD */
5308  my_pthread_setspecific_ptr(THR_THD, 0);
5309  DBUG_RETURN(return_val);
5310 }
5311 
5312 
5328 static my_bool grant_load_procs_priv(TABLE *p_table)
5329 {
5330  MEM_ROOT *memex_ptr;
5331  my_bool return_val= 1;
5332  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
5333  MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
5334  THR_MALLOC);
5335  DBUG_ENTER("grant_load_procs_priv");
5336  (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
5337  0,0,0, (my_hash_get_key) get_grant_table,
5338  0,0);
5339  (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
5340  0,0,0, (my_hash_get_key) get_grant_table,
5341  0,0);
5342  if (p_table->file->ha_index_init(0, 1))
5343  DBUG_RETURN(TRUE);
5344  p_table->use_all_columns();
5345 
5346  if (!p_table->file->ha_index_first(p_table->record[0]))
5347  {
5348  memex_ptr= &memex;
5349  my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
5350  do
5351  {
5352  GRANT_NAME *mem_check;
5353  HASH *hash;
5354  if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE)))
5355  {
5356  /* This could only happen if we are out memory */
5357  goto end_unlock;
5358  }
5359 
5360  if (check_no_resolve)
5361  {
5362  if (hostname_requires_resolving(mem_check->host.get_host()))
5363  {
5364  sql_print_warning("'procs_priv' entry '%s %s@%s' "
5365  "ignored in --skip-name-resolve mode.",
5366  mem_check->tname, mem_check->user,
5367  mem_check->host.get_host() ?
5368  mem_check->host.get_host() : "");
5369  continue;
5370  }
5371  }
5372  if (p_table->field[4]->val_int() == SP_TYPE_PROCEDURE)
5373  {
5374  hash= &proc_priv_hash;
5375  }
5376  else
5377  if (p_table->field[4]->val_int() == SP_TYPE_FUNCTION)
5378  {
5379  hash= &func_priv_hash;
5380  }
5381  else
5382  {
5383  sql_print_warning("'procs_priv' entry '%s' "
5384  "ignored, bad routine type",
5385  mem_check->tname);
5386  continue;
5387  }
5388 
5389  mem_check->privs= fix_rights_for_procedure(mem_check->privs);
5390  if (! mem_check->ok())
5391  delete mem_check;
5392  else if (my_hash_insert(hash, (uchar*) mem_check))
5393  {
5394  delete mem_check;
5395  goto end_unlock;
5396  }
5397  }
5398  while (!p_table->file->ha_index_next(p_table->record[0]));
5399  }
5400  /* Return ok */
5401  return_val= 0;
5402 
5403 end_unlock:
5404  p_table->file->ha_index_end();
5405  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
5406  DBUG_RETURN(return_val);
5407 }
5408 
5409 
5425 static my_bool grant_load(THD *thd, TABLE_LIST *tables)
5426 {
5427  MEM_ROOT *memex_ptr;
5428  my_bool return_val= 1;
5429  TABLE *t_table= 0, *c_table= 0;
5430  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
5431  MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
5432  THR_MALLOC);
5433  sql_mode_t old_sql_mode= thd->variables.sql_mode;
5434  DBUG_ENTER("grant_load");
5435 
5436  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
5437 
5438  (void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
5439  0,0,0, (my_hash_get_key) get_grant_table,
5440  (my_hash_free_key) free_grant_table,0);
5441 
5442  t_table = tables[0].table;
5443  c_table = tables[1].table;
5444  if (t_table->file->ha_index_init(0, 1))
5445  goto end_index_init;
5446  t_table->use_all_columns();
5447  c_table->use_all_columns();
5448 
5449  if (!t_table->file->ha_index_first(t_table->record[0]))
5450  {
5451  memex_ptr= &memex;
5452  my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
5453  do
5454  {
5455  GRANT_TABLE *mem_check;
5456  if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table)))
5457  {
5458  /* This could only happen if we are out memory */
5459  goto end_unlock;
5460  }
5461 
5462  if (check_no_resolve)
5463  {
5464  if (hostname_requires_resolving(mem_check->host.get_host()))
5465  {
5466  sql_print_warning("'tables_priv' entry '%s %s@%s' "
5467  "ignored in --skip-name-resolve mode.",
5468  mem_check->tname,
5469  mem_check->user ? mem_check->user : "",
5470  mem_check->host.get_host() ?
5471  mem_check->host.get_host() : "");
5472  continue;
5473  }
5474  }
5475 
5476  if (! mem_check->ok())
5477  delete mem_check;
5478  else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
5479  {
5480  delete mem_check;
5481  goto end_unlock;
5482  }
5483  }
5484  while (!t_table->file->ha_index_next(t_table->record[0]));
5485  }
5486 
5487  return_val=0; // Return ok
5488 
5489 end_unlock:
5490  t_table->file->ha_index_end();
5491  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
5492 end_index_init:
5493  thd->variables.sql_mode= old_sql_mode;
5494  DBUG_RETURN(return_val);
5495 }
5496 
5497 
5511 static my_bool grant_reload_procs_priv(THD *thd)
5512 {
5513  HASH old_proc_priv_hash, old_func_priv_hash;
5514  TABLE_LIST table;
5515  my_bool return_val= FALSE;
5516  DBUG_ENTER("grant_reload_procs_priv");
5517 
5518  table.init_one_table("mysql", 5, "procs_priv",
5519  strlen("procs_priv"), "procs_priv",
5520  TL_READ);
5521  table.open_type= OT_BASE_ONLY;
5522 
5523  if (open_and_lock_tables(thd, &table, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5524  DBUG_RETURN(TRUE);
5525 
5526  mysql_rwlock_wrlock(&LOCK_grant);
5527  /* Save a copy of the current hash if we need to undo the grant load */
5528  old_proc_priv_hash= proc_priv_hash;
5529  old_func_priv_hash= func_priv_hash;
5530 
5531  if ((return_val= grant_load_procs_priv(table.table)))
5532  {
5533  /* Error; Reverting to old hash */
5534  DBUG_PRINT("error",("Reverting to old privileges"));
5535  grant_free();
5536  proc_priv_hash= old_proc_priv_hash;
5537  func_priv_hash= old_func_priv_hash;
5538  }
5539  else
5540  {
5541  my_hash_free(&old_proc_priv_hash);
5542  my_hash_free(&old_func_priv_hash);
5543  }
5544  mysql_rwlock_unlock(&LOCK_grant);
5545 
5546  DBUG_RETURN(return_val);
5547 }
5548 
5549 
5565 my_bool grant_reload(THD *thd)
5566 {
5567  TABLE_LIST tables[2];
5568  HASH old_column_priv_hash;
5569  MEM_ROOT old_mem;
5570  my_bool return_val= 1;
5571  DBUG_ENTER("grant_reload");
5572 
5573  /* Don't do anything if running with --skip-grant-tables */
5574  if (!initialized)
5575  DBUG_RETURN(0);
5576 
5577  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
5578  C_STRING_WITH_LEN("tables_priv"),
5579  "tables_priv", TL_READ);
5580  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
5581  C_STRING_WITH_LEN("columns_priv"),
5582  "columns_priv", TL_READ);
5583  tables[0].next_local= tables[0].next_global= tables+1;
5584  tables[0].open_type= tables[1].open_type= OT_BASE_ONLY;
5585 
5586  /*
5587  To avoid deadlocks we should obtain table locks before
5588  obtaining LOCK_grant rwlock.
5589  */
5590  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5591  goto end;
5592 
5593  mysql_rwlock_wrlock(&LOCK_grant);
5594  old_column_priv_hash= column_priv_hash;
5595 
5596  /*
5597  Create a new memory pool but save the current memory pool to make an undo
5598  opertion possible in case of failure.
5599  */
5600  old_mem= memex;
5601  init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
5602 
5603  if ((return_val= grant_load(thd, tables)))
5604  { // Error. Revert to old hash
5605  DBUG_PRINT("error",("Reverting to old privileges"));
5606  grant_free(); /* purecov: deadcode */
5607  column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
5608  memex= old_mem; /* purecov: deadcode */
5609  }
5610  else
5611  {
5612  my_hash_free(&old_column_priv_hash);
5613  free_root(&old_mem,MYF(0));
5614  }
5615  mysql_rwlock_unlock(&LOCK_grant);
5616  close_acl_tables(thd);
5617 
5618  /*
5619  It is OK failing to load procs_priv table because we may be
5620  working with 4.1 privilege tables.
5621  */
5622  if (grant_reload_procs_priv(thd))
5623  return_val= 1;
5624 
5625  mysql_rwlock_wrlock(&LOCK_grant);
5626  grant_version++;
5627  mysql_rwlock_unlock(&LOCK_grant);
5628 
5629 end:
5630  close_acl_tables(thd);
5631  DBUG_RETURN(return_val);
5632 }
5633 
5634 
5674 bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
5675  bool any_combination_will_do, uint number, bool no_errors)
5676 {
5677  TABLE_LIST *tl;
5678  TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
5679  Security_context *sctx= thd->security_ctx;
5680  uint i;
5681  ulong orig_want_access= want_access;
5682  DBUG_ENTER("check_grant");
5683  DBUG_ASSERT(number > 0);
5684 
5685  /*
5686  Walk through the list of tables that belong to the query and save the
5687  requested access (orig_want_privilege) to be able to use it when
5688  checking access rights to the underlying tables of a view. Our grant
5689  system gradually eliminates checked bits from want_privilege and thus
5690  after all checks are done we can no longer use it.
5691  The check that first_not_own_table is not reached is for the case when
5692  the given table list refers to the list for prelocking (contains tables
5693  of other queries). For simple queries first_not_own_table is 0.
5694  */
5695  for (i= 0, tl= tables;
5696  i < number && tl != first_not_own_table;
5697  tl= tl->next_global, i++)
5698  {
5699  /*
5700  Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
5701  It will be checked during making view.
5702  */
5703  tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
5704  }
5705 
5706  mysql_rwlock_rdlock(&LOCK_grant);
5707  for (tl= tables;
5708  tl && number-- && tl != first_not_own_table;
5709  tl= tl->next_global)
5710  {
5711  sctx = test(tl->security_ctx) ? tl->security_ctx : thd->security_ctx;
5712 
5713  const ACL_internal_table_access *access=
5714  get_cached_table_access(&tl->grant.m_internal,
5715  tl->get_db_name(),
5716  tl->get_table_name());
5717 
5718  if (access)
5719  {
5720  switch(access->check(orig_want_access, &tl->grant.privilege))
5721  {
5722  case ACL_INTERNAL_ACCESS_GRANTED:
5723  /*
5724  Grant all access to the table to skip column checks.
5725  Depend on the controls in the P_S table itself.
5726  */
5727  tl->grant.privilege|= TMP_TABLE_ACLS;
5728  tl->grant.want_privilege= 0;
5729  continue;
5730  case ACL_INTERNAL_ACCESS_DENIED:
5731  goto err;
5732  case ACL_INTERNAL_ACCESS_CHECK_GRANT:
5733  break;
5734  }
5735  }
5736 
5737  want_access= orig_want_access;
5738  want_access&= ~sctx->master_access;
5739  if (!want_access)
5740  continue; // ok
5741 
5742  if (!(~tl->grant.privilege & want_access) ||
5743  tl->is_anonymous_derived_table() || tl->schema_table)
5744  {
5745  /*
5746  It is subquery in the FROM clause. VIEW set tl->derived after
5747  table opening, but this function always called before table opening.
5748  */
5749  if (!tl->referencing_view)
5750  {
5751  /*
5752  If it's a temporary table created for a subquery in the FROM
5753  clause, or an INFORMATION_SCHEMA table, drop the request for
5754  a privilege.
5755  */
5756  tl->grant.want_privilege= 0;
5757  }
5758  continue;
5759  }
5760 
5761  if (is_temporary_table(tl))
5762  {
5763  /*
5764  If this table list element corresponds to a pre-opened temporary
5765  table skip checking of all relevant table-level privileges for it.
5766  Note that during creation of temporary table we still need to check
5767  if user has CREATE_TMP_ACL.
5768  */
5769  tl->grant.privilege|= TMP_TABLE_ACLS;
5770  tl->grant.want_privilege= 0;
5771  continue;
5772  }
5773 
5774  GRANT_TABLE *grant_table= table_hash_search(sctx->get_host()->ptr(),
5775  sctx->get_ip()->ptr(),
5776  tl->get_db_name(),
5777  sctx->priv_user,
5778  tl->get_table_name(),
5779  FALSE);
5780 
5781  if (!grant_table)
5782  {
5783  want_access &= ~tl->grant.privilege;
5784  goto err; // No grants
5785  }
5786 
5787  /*
5788  For SHOW COLUMNS, SHOW INDEX it is enough to have some
5789  privileges on any column combination on the table.
5790  */
5791  if (any_combination_will_do)
5792  continue;
5793 
5794  tl->grant.grant_table= grant_table; // Remember for column test
5795  tl->grant.version= grant_version;
5796  tl->grant.privilege|= grant_table->privs;
5797  tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege);
5798 
5799  if (!(~tl->grant.privilege & want_access))
5800  continue;
5801 
5802  if (want_access & ~(grant_table->cols | tl->grant.privilege))
5803  {
5804  want_access &= ~(grant_table->cols | tl->grant.privilege);
5805  goto err; // impossible
5806  }
5807  }
5808  mysql_rwlock_unlock(&LOCK_grant);
5809  DBUG_RETURN(FALSE);
5810 
5811 err:
5812  mysql_rwlock_unlock(&LOCK_grant);
5813  if (!no_errors) // Not a silent skip of table
5814  {
5815  char command[128];
5816  get_privilege_desc(command, sizeof(command), want_access);
5817  my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
5818  command,
5819  sctx->priv_user,
5820  sctx->host_or_ip,
5821  tl ? tl->get_table_name() : "unknown");
5822  }
5823  DBUG_RETURN(TRUE);
5824 }
5825 
5826 
5827 /*
5828  Check column rights in given security context
5829 
5830  SYNOPSIS
5831  check_grant_column()
5832  thd thread handler
5833  grant grant information structure
5834  db_name db name
5835  table_name table name
5836  name column name
5837  length column name length
5838  sctx security context
5839 
5840  RETURN
5841  FALSE OK
5842  TRUE access denied
5843 */
5844 
5845 bool check_grant_column(THD *thd, GRANT_INFO *grant,
5846  const char *db_name, const char *table_name,
5847  const char *name, uint length, Security_context *sctx)
5848 {
5849  GRANT_TABLE *grant_table;
5850  GRANT_COLUMN *grant_column;
5851  ulong want_access= grant->want_privilege & ~grant->privilege;
5852  DBUG_ENTER("check_grant_column");
5853  DBUG_PRINT("enter", ("table: %s want_access: %lu", table_name, want_access));
5854 
5855  if (!want_access)
5856  DBUG_RETURN(0); // Already checked
5857 
5858  mysql_rwlock_rdlock(&LOCK_grant);
5859 
5860  /* reload table if someone has modified any grants */
5861 
5862  if (grant->version != grant_version)
5863  {
5864  grant->grant_table=
5865  table_hash_search(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
5866  db_name, sctx->priv_user,
5867  table_name, 0); /* purecov: inspected */
5868  grant->version= grant_version; /* purecov: inspected */
5869  }
5870  if (!(grant_table= grant->grant_table))
5871  goto err; /* purecov: deadcode */
5872 
5873  grant_column=column_hash_search(grant_table, name, length);
5874  if (grant_column && !(~grant_column->rights & want_access))
5875  {
5876  mysql_rwlock_unlock(&LOCK_grant);
5877  DBUG_RETURN(0);
5878  }
5879 
5880 err:
5881  mysql_rwlock_unlock(&LOCK_grant);
5882  char command[128];
5883  get_privilege_desc(command, sizeof(command), want_access);
5884  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
5885  command,
5886  sctx->priv_user,
5887  sctx->host_or_ip,
5888  name,
5889  table_name);
5890  DBUG_RETURN(1);
5891 }
5892 
5893 
5894 /*
5895  Check the access right to a column depending on the type of table.
5896 
5897  SYNOPSIS
5898  check_column_grant_in_table_ref()
5899  thd thread handler
5900  table_ref table reference where to check the field
5901  name name of field to check
5902  length length of name
5903 
5904  DESCRIPTION
5905  Check the access rights to a column depending on the type of table
5906  reference where the column is checked. The function provides a
5907  generic interface to check column access rights that hides the
5908  heterogeneity of the column representation - whether it is a view
5909  or a stored table colum.
5910 
5911  RETURN
5912  FALSE OK
5913  TRUE access denied
5914 */
5915 
5916 bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
5917  const char *name, uint length)
5918 {
5919  GRANT_INFO *grant;
5920  const char *db_name;
5921  const char *table_name;
5922  Security_context *sctx= test(table_ref->security_ctx) ?
5923  table_ref->security_ctx : thd->security_ctx;
5924 
5925  if (table_ref->view || table_ref->field_translation)
5926  {
5927  /* View or derived information schema table. */
5928  ulong view_privs;
5929  grant= &(table_ref->grant);
5930  db_name= table_ref->view_db.str;
5931  table_name= table_ref->view_name.str;
5932  if (table_ref->belong_to_view &&
5933  thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
5934  {
5935  view_privs= get_column_grant(thd, grant, db_name, table_name, name);
5936  if (view_privs & VIEW_ANY_ACL)
5937  {
5938  table_ref->belong_to_view->allowed_show= TRUE;
5939  return FALSE;
5940  }
5941  table_ref->belong_to_view->allowed_show= FALSE;
5942  my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
5943  return TRUE;
5944  }
5945  }
5946  else
5947  {
5948  /* Normal or temporary table. */
5949  TABLE *table= table_ref->table;
5950  grant= &(table->grant);
5951  db_name= table->s->db.str;
5952  table_name= table->s->table_name.str;
5953  }
5954 
5955  if (grant->want_privilege)
5956  return check_grant_column(thd, grant, db_name, table_name, name,
5957  length, sctx);
5958  else
5959  return FALSE;
5960 
5961 }
5962 
5963 
5979 bool check_grant_all_columns(THD *thd, ulong want_access_arg,
5980  Field_iterator_table_ref *fields)
5981 {
5982  Security_context *sctx= thd->security_ctx;
5983  ulong want_access= want_access_arg;
5984  const char *table_name= NULL;
5985 
5986  const char* db_name;
5987  GRANT_INFO *grant;
5988  /* Initialized only to make gcc happy */
5989  GRANT_TABLE *grant_table= NULL;
5990  /*
5991  Flag that gets set if privilege checking has to be performed on column
5992  level.
5993  */
5994  bool using_column_privileges= FALSE;
5995 
5996  mysql_rwlock_rdlock(&LOCK_grant);
5997 
5998  for (; !fields->end_of_fields(); fields->next())
5999  {
6000  const char *field_name= fields->name();
6001 
6002  if (table_name != fields->get_table_name())
6003  {
6004  table_name= fields->get_table_name();
6005  db_name= fields->get_db_name();
6006  grant= fields->grant();
6007  /* get a fresh one for each table */
6008  want_access= want_access_arg & ~grant->privilege;
6009  if (want_access)
6010  {
6011  /* reload table if someone has modified any grants */
6012  if (grant->version != grant_version)
6013  {
6014  grant->grant_table=
6015  table_hash_search(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
6016  db_name, sctx->priv_user,
6017  table_name, 0); /* purecov: inspected */
6018  grant->version= grant_version; /* purecov: inspected */
6019  }
6020 
6021  grant_table= grant->grant_table;
6022  DBUG_ASSERT (grant_table);
6023  }
6024  }
6025 
6026  if (want_access)
6027  {
6028  GRANT_COLUMN *grant_column=
6029  column_hash_search(grant_table, field_name,
6030  (uint) strlen(field_name));
6031  if (grant_column)
6032  using_column_privileges= TRUE;
6033  if (!grant_column || (~grant_column->rights & want_access))
6034  goto err;
6035  }
6036  }
6037  mysql_rwlock_unlock(&LOCK_grant);
6038  return 0;
6039 
6040 err:
6041  mysql_rwlock_unlock(&LOCK_grant);
6042 
6043  char command[128];
6044  get_privilege_desc(command, sizeof(command), want_access);
6045  /*
6046  Do not give an error message listing a column name unless the user has
6047  privilege to see all columns.
6048  */
6049  if (using_column_privileges)
6050  my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
6051  command, sctx->priv_user,
6052  sctx->host_or_ip, table_name);
6053  else
6054  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
6055  command,
6056  sctx->priv_user,
6057  sctx->host_or_ip,
6058  fields->name(),
6059  table_name);
6060  return 1;
6061 }
6062 
6063 
6064 static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
6065 {
6066  Security_context *sctx= thd->security_ctx;
6067 
6068  for (uint idx= 0; idx < hash->records; ++idx)
6069  {
6070  GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
6071 
6072  if (strcmp(item->user, sctx->priv_user) == 0 &&
6073  strcmp(item->db, db) == 0 &&
6074  item->host.compare_hostname(sctx->get_host()->ptr(),
6075  sctx->get_ip()->ptr()))
6076  {
6077  return FALSE;
6078  }
6079  }
6080 
6081  return TRUE;
6082 }
6083 
6084 
6085 /*
6086  Check if a user has the right to access a database
6087  Access is accepted if he has a grant for any table/routine in the database
6088  Return 1 if access is denied
6089 */
6090 
6091 bool check_grant_db(THD *thd,const char *db)
6092 {
6093  Security_context *sctx= thd->security_ctx;
6094  char helping [NAME_LEN+USERNAME_LENGTH+2];
6095  uint len;
6096  bool error= TRUE;
6097  size_t copy_length;
6098 
6099  copy_length= (size_t) (strlen(sctx->priv_user ? sctx->priv_user : "") +
6100  strlen(db ? db : ""));
6101 
6102  /*
6103  Make sure that strmov() operations do not result in buffer overflow.
6104  */
6105  if (copy_length >= (NAME_LEN+USERNAME_LENGTH+2))
6106  return 1;
6107 
6108  len= (uint) (strmov(strmov(helping, sctx->priv_user) + 1, db) - helping) + 1;
6109 
6110  mysql_rwlock_rdlock(&LOCK_grant);
6111 
6112  for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
6113  {
6114  GRANT_TABLE *grant_table= (GRANT_TABLE*)
6115  my_hash_element(&column_priv_hash,
6116  idx);
6117  if (len < grant_table->key_length &&
6118  !memcmp(grant_table->hash_key,helping,len) &&
6119  grant_table->host.compare_hostname(sctx->get_host()->ptr(),
6120  sctx->get_ip()->ptr()))
6121  {
6122  error= FALSE; /* Found match. */
6123  break;
6124  }
6125  }
6126 
6127  if (error)
6128  error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
6129  check_grant_db_routine(thd, db, &func_priv_hash);
6130 
6131  mysql_rwlock_unlock(&LOCK_grant);
6132 
6133  return error;
6134 }
6135 
6136 
6137 /****************************************************************************
6138  Check routine level grants
6139 
6140  SYNPOSIS
6141  bool check_grant_routine()
6142  thd Thread handler
6143  want_access Bits of privileges user needs to have
6144  procs List of routines to check. The user should have 'want_access'
6145  is_proc True if the list is all procedures, else functions
6146  no_errors If 0 then we write an error. The error is sent directly to
6147  the client
6148 
6149  RETURN
6150  0 ok
6151  1 Error: User did not have the requested privielges
6152 ****************************************************************************/
6153 
6154 bool check_grant_routine(THD *thd, ulong want_access,
6155  TABLE_LIST *procs, bool is_proc, bool no_errors)
6156 {
6157  TABLE_LIST *table;
6158  Security_context *sctx= thd->security_ctx;
6159  char *user= sctx->priv_user;
6160  char *host= sctx->priv_host;
6161  DBUG_ENTER("check_grant_routine");
6162 
6163  want_access&= ~sctx->master_access;
6164  if (!want_access)
6165  DBUG_RETURN(0); // ok
6166 
6167  mysql_rwlock_rdlock(&LOCK_grant);
6168  for (table= procs; table; table= table->next_global)
6169  {
6170  GRANT_NAME *grant_proc;
6171  if ((grant_proc= routine_hash_search(host, sctx->get_ip()->ptr(), table->db,
6172  user, table->table_name, is_proc, 0)))
6173  table->grant.privilege|= grant_proc->privs;
6174 
6175  if (want_access & ~table->grant.privilege)
6176  {
6177  want_access &= ~table->grant.privilege;
6178  goto err;
6179  }
6180  }
6181  mysql_rwlock_unlock(&LOCK_grant);
6182  DBUG_RETURN(0);
6183 err:
6184  mysql_rwlock_unlock(&LOCK_grant);
6185  if (!no_errors)
6186  {
6187  char buff[1024];
6188  const char *command="";
6189  if (table)
6190  strxmov(buff, table->db, ".", table->table_name, NullS);
6191  if (want_access & EXECUTE_ACL)
6192  command= "execute";
6193  else if (want_access & ALTER_PROC_ACL)
6194  command= "alter routine";
6195  else if (want_access & GRANT_ACL)
6196  command= "grant";
6197  my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
6198  command, user, host, table ? buff : "unknown");
6199  }
6200  DBUG_RETURN(1);
6201 }
6202 
6203 
6204 /*
6205  Check if routine has any of the
6206  routine level grants
6207 
6208  SYNPOSIS
6209  bool check_routine_level_acl()
6210  thd Thread handler
6211  db Database name
6212  name Routine name
6213 
6214  RETURN
6215  0 Ok
6216  1 error
6217 */
6218 
6219 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
6220  bool is_proc)
6221 {
6222  bool no_routine_acl= 1;
6223  GRANT_NAME *grant_proc;
6224  Security_context *sctx= thd->security_ctx;
6225  mysql_rwlock_rdlock(&LOCK_grant);
6226  if ((grant_proc= routine_hash_search(sctx->priv_host,
6227  sctx->get_ip()->ptr(), db,
6228  sctx->priv_user,
6229  name, is_proc, 0)))
6230  no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
6231  mysql_rwlock_unlock(&LOCK_grant);
6232  return no_routine_acl;
6233 }
6234 
6235 
6236 /*****************************************************************************
6237  Functions to retrieve the grant for a table/column (for SHOW functions)
6238 *****************************************************************************/
6239 
6240 ulong get_table_grant(THD *thd, TABLE_LIST *table)
6241 {
6242  ulong privilege;
6243  Security_context *sctx= thd->security_ctx;
6244  const char *db = table->db ? table->db : thd->db;
6245  GRANT_TABLE *grant_table;
6246 
6247  mysql_rwlock_rdlock(&LOCK_grant);
6248 #ifdef EMBEDDED_LIBRARY
6249  grant_table= NULL;
6250 #else
6251  grant_table= table_hash_search(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
6252  db, sctx->priv_user, table->table_name, 0);
6253 #endif
6254  table->grant.grant_table=grant_table; // Remember for column test
6255  table->grant.version=grant_version;
6256  if (grant_table)
6257  table->grant.privilege|= grant_table->privs;
6258  privilege= table->grant.privilege;
6259  mysql_rwlock_unlock(&LOCK_grant);
6260  return privilege;
6261 }
6262 
6263 
6264 /*
6265  Determine the access priviliges for a field.
6266 
6267  SYNOPSIS
6268  get_column_grant()
6269  thd thread handler
6270  grant grants table descriptor
6271  db_name name of database that the field belongs to
6272  table_name name of table that the field belongs to
6273  field_name name of field
6274 
6275  DESCRIPTION
6276  The procedure may also modify: grant->grant_table and grant->version.
6277 
6278  RETURN
6279  The access priviliges for the field db_name.table_name.field_name
6280 */
6281 
6282 ulong get_column_grant(THD *thd, GRANT_INFO *grant,
6283  const char *db_name, const char *table_name,
6284  const char *field_name)
6285 {
6286  GRANT_TABLE *grant_table;
6287  GRANT_COLUMN *grant_column;
6288  ulong priv;
6289 
6290  mysql_rwlock_rdlock(&LOCK_grant);
6291  /* reload table if someone has modified any grants */
6292  if (grant->version != grant_version)
6293  {
6294  Security_context *sctx= thd->security_ctx;
6295  grant->grant_table=
6296  table_hash_search(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
6297  db_name, sctx->priv_user,
6298  table_name, 0); /* purecov: inspected */
6299  grant->version= grant_version; /* purecov: inspected */
6300  }
6301 
6302  if (!(grant_table= grant->grant_table))
6303  priv= grant->privilege;
6304  else
6305  {
6306  grant_column= column_hash_search(grant_table, field_name,
6307  (uint) strlen(field_name));
6308  if (!grant_column)
6309  priv= (grant->privilege | grant_table->privs);
6310  else
6311  priv= (grant->privilege | grant_table->privs | grant_column->rights);
6312  }
6313  mysql_rwlock_unlock(&LOCK_grant);
6314  return priv;
6315 }
6316 
6317 
6318 /* Help function for mysql_show_grants */
6319 
6320 static void add_user_option(String *grant, ulong value, const char *name)
6321 {
6322  if (value)
6323  {
6324  char buff[22], *p; // just as in int2str
6325  grant->append(' ');
6326  grant->append(name, strlen(name));
6327  grant->append(' ');
6328  p=int10_to_str(value, buff, 10);
6329  grant->append(buff,p-buff);
6330  }
6331 }
6332 
6333 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
6334 
6335 const char *command_array[]=
6336 {
6337  "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
6338  "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
6339  "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
6340  "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
6341  "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
6342  "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE"
6343 };
6344 
6345 uint command_lengths[]=
6346 {
6347  6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
6348  14, 13, 11, 5, 7, 17
6349 };
6350 
6351 #ifndef NO_EMBEDDED_ACCESS_CHECKS
6352 
6353 static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
6354  const char *type, int typelen,
6355  char *buff, int buffsize);
6356 
6357 
6358 /*
6359  SHOW GRANTS; Send grants for a user to the client
6360 
6361  IMPLEMENTATION
6362  Send to client grant-like strings depicting user@host privileges
6363 */
6364 
6365 bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
6366 {
6367  ulong want_access;
6368  uint counter,index;
6369  int error = 0;
6370  ACL_USER *acl_user;
6371  ACL_DB *acl_db;
6372  char buff[1024];
6373  Protocol *protocol= thd->protocol;
6374  DBUG_ENTER("mysql_show_grants");
6375 
6376  LINT_INIT(acl_user);
6377  if (!initialized)
6378  {
6379  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
6380  DBUG_RETURN(TRUE);
6381  }
6382 
6383  mysql_rwlock_rdlock(&LOCK_grant);
6384  mysql_mutex_lock(&acl_cache->lock);
6385 
6386  acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE);
6387  if (!acl_user)
6388  {
6389  mysql_mutex_unlock(&acl_cache->lock);
6390  mysql_rwlock_unlock(&LOCK_grant);
6391 
6392  my_error(ER_NONEXISTING_GRANT, MYF(0),
6393  lex_user->user.str, lex_user->host.str);
6394  DBUG_RETURN(TRUE);
6395  }
6396 
6397  Item_string *field=new Item_string("",0,&my_charset_latin1);
6398  List<Item> field_list;
6399  field->max_length=1024;
6400  strxmov(buff,"Grants for ",lex_user->user.str,"@",
6401  lex_user->host.str,NullS);
6402  field->item_name.set(buff);
6403  field_list.push_back(field);
6404  if (protocol->send_result_set_metadata(&field_list,
6405  Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
6406  {
6407  mysql_mutex_unlock(&acl_cache->lock);
6408  mysql_rwlock_unlock(&LOCK_grant);
6409 
6410  DBUG_RETURN(TRUE);
6411  }
6412 
6413  /* Add first global access grants */
6414  {
6415  String global(buff,sizeof(buff),system_charset_info);
6416  global.length(0);
6417  global.append(STRING_WITH_LEN("GRANT "));
6418 
6419  want_access= acl_user->access;
6420  if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
6421  global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
6422  else if (!(want_access & ~GRANT_ACL))
6423  global.append(STRING_WITH_LEN("USAGE"));
6424  else
6425  {
6426  bool found=0;
6427  ulong j,test_access= want_access & ~GRANT_ACL;
6428  for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
6429  {
6430  if (test_access & j)
6431  {
6432  if (found)
6433  global.append(STRING_WITH_LEN(", "));
6434  found=1;
6435  global.append(command_array[counter],command_lengths[counter]);
6436  }
6437  }
6438  }
6439  global.append (STRING_WITH_LEN(" ON *.* TO '"));
6440  global.append(lex_user->user.str, lex_user->user.length,
6441  system_charset_info);
6442  global.append (STRING_WITH_LEN("'@'"));
6443  global.append(lex_user->host.str,lex_user->host.length,
6444  system_charset_info);
6445  global.append ('\'');
6446 #if defined(HAVE_OPENSSL)
6447  if (acl_user->plugin.str == sha256_password_plugin_name.str)
6448  {
6449  global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
6450  global.append((const char *) &acl_user->auth_string.str[0]);
6451  global.append('\'');
6452  }
6453  else
6454 #endif
6455  if (acl_user->salt_len)
6456  {
6457  char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
6458  if (acl_user->salt_len == SCRAMBLE_LENGTH)
6459  make_password_from_salt(passwd_buff, acl_user->salt);
6460  else
6461  make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt);
6462  global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
6463  global.append(passwd_buff);
6464  global.append('\'');
6465  }
6466  /* "show grants" SSL related stuff */
6467  if (acl_user->ssl_type == SSL_TYPE_ANY)
6468  global.append(STRING_WITH_LEN(" REQUIRE SSL"));
6469  else if (acl_user->ssl_type == SSL_TYPE_X509)
6470  global.append(STRING_WITH_LEN(" REQUIRE X509"));
6471  else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
6472  {
6473  int ssl_options = 0;
6474  global.append(STRING_WITH_LEN(" REQUIRE "));
6475  if (acl_user->x509_issuer)
6476  {
6477  ssl_options++;
6478  global.append(STRING_WITH_LEN("ISSUER \'"));
6479  global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
6480  global.append('\'');
6481  }
6482  if (acl_user->x509_subject)
6483  {
6484  if (ssl_options++)
6485  global.append(' ');
6486  global.append(STRING_WITH_LEN("SUBJECT \'"));
6487  global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
6488  system_charset_info);
6489  global.append('\'');
6490  }
6491  if (acl_user->ssl_cipher)
6492  {
6493  if (ssl_options++)
6494  global.append(' ');
6495  global.append(STRING_WITH_LEN("CIPHER '"));
6496  global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
6497  system_charset_info);
6498  global.append('\'');
6499  }
6500  }
6501  if ((want_access & GRANT_ACL) ||
6502  (acl_user->user_resource.questions ||
6503  acl_user->user_resource.updates ||
6504  acl_user->user_resource.conn_per_hour ||
6505  acl_user->user_resource.user_conn))
6506  {
6507  global.append(STRING_WITH_LEN(" WITH"));
6508  if (want_access & GRANT_ACL)
6509  global.append(STRING_WITH_LEN(" GRANT OPTION"));
6510  add_user_option(&global, acl_user->user_resource.questions,
6511  "MAX_QUERIES_PER_HOUR");
6512  add_user_option(&global, acl_user->user_resource.updates,
6513  "MAX_UPDATES_PER_HOUR");
6514  add_user_option(&global, acl_user->user_resource.conn_per_hour,
6515  "MAX_CONNECTIONS_PER_HOUR");
6516  add_user_option(&global, acl_user->user_resource.user_conn,
6517  "MAX_USER_CONNECTIONS");
6518  }
6519  protocol->prepare_for_resend();
6520  protocol->store(global.ptr(),global.length(),global.charset());
6521  if (protocol->write())
6522  {
6523  error= -1;
6524  goto end;
6525  }
6526  }
6527 
6528  /* Add database access */
6529  for (counter=0 ; counter < acl_dbs.elements ; counter++)
6530  {
6531  const char *user, *host;
6532 
6533  acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
6534  if (!(user=acl_db->user))
6535  user= "";
6536  if (!(host=acl_db->host.get_host()))
6537  host= "";
6538 
6539  /*
6540  We do not make SHOW GRANTS case-sensitive here (like REVOKE),
6541  but make it case-insensitive because that's the way they are
6542  actually applied, and showing fewer privileges than are applied
6543  would be wrong from a security point of view.
6544  */
6545 
6546  if (!strcmp(lex_user->user.str,user) &&
6547  !my_strcasecmp(system_charset_info, lex_user->host.str, host))
6548  {
6549  want_access=acl_db->access;
6550  if (want_access)
6551  {
6552  String db(buff,sizeof(buff),system_charset_info);
6553  db.length(0);
6554  db.append(STRING_WITH_LEN("GRANT "));
6555 
6556  if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
6557  db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
6558  else if (!(want_access & ~GRANT_ACL))
6559  db.append(STRING_WITH_LEN("USAGE"));
6560  else
6561  {
6562  int found=0, cnt;
6563  ulong j,test_access= want_access & ~GRANT_ACL;
6564  for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
6565  {
6566  if (test_access & j)
6567  {
6568  if (found)
6569  db.append(STRING_WITH_LEN(", "));
6570  found = 1;
6571  db.append(command_array[cnt],command_lengths[cnt]);
6572  }
6573  }
6574  }
6575  db.append (STRING_WITH_LEN(" ON "));
6576  append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
6577  db.append (STRING_WITH_LEN(".* TO '"));
6578  db.append(lex_user->user.str, lex_user->user.length,
6579  system_charset_info);
6580  db.append (STRING_WITH_LEN("'@'"));
6581  // host and lex_user->host are equal except for case
6582  db.append(host, strlen(host), system_charset_info);
6583  db.append ('\'');
6584  if (want_access & GRANT_ACL)
6585  db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
6586  protocol->prepare_for_resend();
6587  protocol->store(db.ptr(),db.length(),db.charset());
6588  if (protocol->write())
6589  {
6590  error= -1;
6591  goto end;
6592  }
6593  }
6594  }
6595  }
6596 
6597  /* Add table & column access */
6598  for (index=0 ; index < column_priv_hash.records ; index++)
6599  {
6600  const char *user, *host;
6601  GRANT_TABLE *grant_table= (GRANT_TABLE*)
6602  my_hash_element(&column_priv_hash, index);
6603 
6604  if (!(user=grant_table->user))
6605  user= "";
6606  if (!(host= grant_table->host.get_host()))
6607  host= "";
6608 
6609  /*
6610  We do not make SHOW GRANTS case-sensitive here (like REVOKE),
6611  but make it case-insensitive because that's the way they are
6612  actually applied, and showing fewer privileges than are applied
6613  would be wrong from a security point of view.
6614  */
6615 
6616  if (!strcmp(lex_user->user.str,user) &&
6617  !my_strcasecmp(system_charset_info, lex_user->host.str, host))
6618  {
6619  ulong table_access= grant_table->privs;
6620  if ((table_access | grant_table->cols) != 0)
6621  {
6622  String global(buff, sizeof(buff), system_charset_info);
6623  ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;
6624 
6625  global.length(0);
6626  global.append(STRING_WITH_LEN("GRANT "));
6627 
6628  if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
6629  global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
6630  else if (!test_access)
6631  global.append(STRING_WITH_LEN("USAGE"));
6632  else
6633  {
6634  /* Add specific column access */
6635  int found= 0;
6636  ulong j;
6637 
6638  for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
6639  {
6640  if (test_access & j)
6641  {
6642  if (found)
6643  global.append(STRING_WITH_LEN(", "));
6644  found= 1;
6645  global.append(command_array[counter],command_lengths[counter]);
6646 
6647  if (grant_table->cols)
6648  {
6649  uint found_col= 0;
6650  for (uint col_index=0 ;
6651  col_index < grant_table->hash_columns.records ;
6652  col_index++)
6653  {
6654  GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
6655  my_hash_element(&grant_table->hash_columns,col_index);
6656  if (grant_column->rights & j)
6657  {
6658  if (!found_col)
6659  {
6660  found_col= 1;
6661  /*
6662  If we have a duplicated table level privilege, we
6663  must write the access privilege name again.
6664  */
6665  if (table_access & j)
6666  {
6667  global.append(STRING_WITH_LEN(", "));
6668  global.append(command_array[counter],
6669  command_lengths[counter]);
6670  }
6671  global.append(STRING_WITH_LEN(" ("));
6672  }
6673  else
6674  global.append(STRING_WITH_LEN(", "));
6675  global.append(grant_column->column,
6676  grant_column->key_length,
6677  system_charset_info);
6678  }
6679  }
6680  if (found_col)
6681  global.append(')');
6682  }
6683  }
6684  }
6685  }
6686  global.append(STRING_WITH_LEN(" ON "));
6687  append_identifier(thd, &global, grant_table->db,
6688  strlen(grant_table->db));
6689  global.append('.');
6690  append_identifier(thd, &global, grant_table->tname,
6691  strlen(grant_table->tname));
6692  global.append(STRING_WITH_LEN(" TO '"));
6693  global.append(lex_user->user.str, lex_user->user.length,
6694  system_charset_info);
6695  global.append(STRING_WITH_LEN("'@'"));
6696  // host and lex_user->host are equal except for case
6697  global.append(host, strlen(host), system_charset_info);
6698  global.append('\'');
6699  if (table_access & GRANT_ACL)
6700  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
6701  protocol->prepare_for_resend();
6702  protocol->store(global.ptr(),global.length(),global.charset());
6703  if (protocol->write())
6704  {
6705  error= -1;
6706  break;
6707  }
6708  }
6709  }
6710  }
6711 
6712  if (show_routine_grants(thd, lex_user, &proc_priv_hash,
6713  STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
6714  {
6715  error= -1;
6716  goto end;
6717  }
6718 
6719  if (show_routine_grants(thd, lex_user, &func_priv_hash,
6720  STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
6721  {
6722  error= -1;
6723  goto end;
6724  }
6725 
6726  if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
6727  {
6728  error= -1;
6729  goto end;
6730  }
6731 
6732 end:
6733  mysql_mutex_unlock(&acl_cache->lock);
6734  mysql_rwlock_unlock(&LOCK_grant);
6735 
6736  my_eof(thd);
6737  DBUG_RETURN(error);
6738 }
6739 
6740 static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
6741  const char *type, int typelen,
6742  char *buff, int buffsize)
6743 {
6744  uint counter, index;
6745  int error= 0;
6746  Protocol *protocol= thd->protocol;
6747  /* Add routine access */
6748  for (index=0 ; index < hash->records ; index++)
6749  {
6750  const char *user, *host;
6751  GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
6752 
6753  if (!(user=grant_proc->user))
6754  user= "";
6755  if (!(host= grant_proc->host.get_host()))
6756  host= "";
6757 
6758  /*
6759  We do not make SHOW GRANTS case-sensitive here (like REVOKE),
6760  but make it case-insensitive because that's the way they are
6761  actually applied, and showing fewer privileges than are applied
6762  would be wrong from a security point of view.
6763  */
6764 
6765  if (!strcmp(lex_user->user.str,user) &&
6766  !my_strcasecmp(system_charset_info, lex_user->host.str, host))
6767  {
6768  ulong proc_access= grant_proc->privs;
6769  if (proc_access != 0)
6770  {
6771  String global(buff, buffsize, system_charset_info);
6772  ulong test_access= proc_access & ~GRANT_ACL;
6773 
6774  global.length(0);
6775  global.append(STRING_WITH_LEN("GRANT "));
6776 
6777  if (!test_access)
6778  global.append(STRING_WITH_LEN("USAGE"));
6779  else
6780  {
6781  /* Add specific procedure access */
6782  int found= 0;
6783  ulong j;
6784 
6785  for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
6786  {
6787  if (test_access & j)
6788  {
6789  if (found)
6790  global.append(STRING_WITH_LEN(", "));
6791  found= 1;
6792  global.append(command_array[counter],command_lengths[counter]);
6793  }
6794  }
6795  }
6796  global.append(STRING_WITH_LEN(" ON "));
6797  global.append(type,typelen);
6798  global.append(' ');
6799  append_identifier(thd, &global, grant_proc->db,
6800  strlen(grant_proc->db));
6801  global.append('.');
6802  append_identifier(thd, &global, grant_proc->tname,
6803  strlen(grant_proc->tname));
6804  global.append(STRING_WITH_LEN(" TO '"));
6805  global.append(lex_user->user.str, lex_user->user.length,
6806  system_charset_info);
6807  global.append(STRING_WITH_LEN("'@'"));
6808  // host and lex_user->host are equal except for case
6809  global.append(host, strlen(host), system_charset_info);
6810  global.append('\'');
6811  if (proc_access & GRANT_ACL)
6812  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
6813  protocol->prepare_for_resend();
6814  protocol->store(global.ptr(),global.length(),global.charset());
6815  if (protocol->write())
6816  {
6817  error= -1;
6818  break;
6819  }
6820  }
6821  }
6822  }
6823  return error;
6824 }
6825 
6826 /*
6827  Make a clear-text version of the requested privilege.
6828 */
6829 
6830 void get_privilege_desc(char *to, uint max_length, ulong access)
6831 {
6832  uint pos;
6833  char *start=to;
6834  DBUG_ASSERT(max_length >= 30); // For end ', ' removal
6835 
6836  if (access)
6837  {
6838  max_length--; // Reserve place for end-zero
6839  for (pos=0 ; access ; pos++, access>>=1)
6840  {
6841  if ((access & 1) &&
6842  command_lengths[pos] + (uint) (to-start) < max_length)
6843  {
6844  to= strmov(to, command_array[pos]);
6845  *to++= ',';
6846  *to++= ' ';
6847  }
6848  }
6849  to--; // Remove end ' '
6850  to--; // Remove end ','
6851  }
6852  *to=0;
6853 }
6854 
6855 
6856 void get_mqh(const char *user, const char *host, USER_CONN *uc)
6857 {
6858  ACL_USER *acl_user;
6859 
6860  mysql_mutex_lock(&acl_cache->lock);
6861 
6862  if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
6863  uc->user_resources= acl_user->user_resource;
6864  else
6865  memset(&uc->user_resources, 0, sizeof(uc->user_resources));
6866 
6867  mysql_mutex_unlock(&acl_cache->lock);
6868 }
6869 
6893 #define GRANT_TABLES 6
6894 
6895 static int
6896 open_grant_tables(THD *thd, TABLE_LIST *tables, bool *transactional_tables)
6897 {
6898  DBUG_ENTER("open_grant_tables");
6899 
6900  if (!initialized)
6901  {
6902  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
6903  DBUG_RETURN(-1);
6904  }
6905 
6906  *transactional_tables= false;
6907 
6908  tables->init_one_table(C_STRING_WITH_LEN("mysql"),
6909  C_STRING_WITH_LEN("user"), "user", TL_WRITE);
6910  (tables+1)->init_one_table(C_STRING_WITH_LEN("mysql"),
6911  C_STRING_WITH_LEN("db"), "db", TL_WRITE);
6912  (tables+2)->init_one_table(C_STRING_WITH_LEN("mysql"),
6913  C_STRING_WITH_LEN("tables_priv"),
6914  "tables_priv", TL_WRITE);
6915  (tables+3)->init_one_table(C_STRING_WITH_LEN("mysql"),
6916  C_STRING_WITH_LEN("columns_priv"),
6917  "columns_priv", TL_WRITE);
6918  (tables+4)->init_one_table(C_STRING_WITH_LEN("mysql"),
6919  C_STRING_WITH_LEN("procs_priv"),
6920  "procs_priv", TL_WRITE);
6921  (tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"),
6922  C_STRING_WITH_LEN("proxies_priv"),
6923  "proxies_priv", TL_WRITE);
6924  tables[5].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
6925 
6926  tables->next_local= tables->next_global= tables + 1;
6927  (tables+1)->next_local= (tables+1)->next_global= tables + 2;
6928  (tables+2)->next_local= (tables+2)->next_global= tables + 3;
6929  (tables+3)->next_local= (tables+3)->next_global= tables + 4;
6930  (tables+4)->next_local= (tables+4)->next_global= tables + 5;
6931 
6932 #ifdef HAVE_REPLICATION
6933  /*
6934  GRANT and REVOKE are applied the slave in/exclusion rules as they are
6935  some kind of updates to the mysql.% tables.
6936  */
6937  if (thd->slave_thread && rpl_filter->is_on())
6938  {
6939  /*
6940  The tables must be marked "updating" so that tables_ok() takes them into
6941  account in tests.
6942  */
6943  tables[0].updating= tables[1].updating= tables[2].updating=
6944  tables[3].updating= tables[4].updating= tables[5].updating= 1;
6945  if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, tables)))
6946  DBUG_RETURN(1);
6947  tables[0].updating= tables[1].updating= tables[2].updating=
6948  tables[3].updating= tables[4].updating= tables[5].updating= 0;
6949  }
6950 #endif
6951 
6952  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
6953  { // This should never happen
6954  DBUG_RETURN(-1);
6955  }
6956 
6957  for (uint i= 0; i < GRANT_TABLES; ++i)
6958  *transactional_tables= (*transactional_tables ||
6959  (tables[i].table &&
6960  tables[i].table->file->has_transactions()));
6961 
6962  DBUG_RETURN(0);
6963 }
6964 
6965 
6966 /*
6967  Modify a privilege table.
6968 
6969  SYNOPSIS
6970  modify_grant_table()
6971  table The table to modify.
6972  host_field The host name field.
6973  user_field The user name field.
6974  user_to The new name for the user if to be renamed,
6975  NULL otherwise.
6976 
6977  DESCRIPTION
6978  Update user/host in the current record if user_to is not NULL.
6979  Delete the current record if user_to is NULL.
6980 
6981  RETURN
6982  0 OK.
6983  != 0 Error.
6984 */
6985 
6986 static int modify_grant_table(TABLE *table, Field *host_field,
6987  Field *user_field, LEX_USER *user_to)
6988 {
6989  int error;
6990  DBUG_ENTER("modify_grant_table");
6991 
6992  if (user_to)
6993  {
6994  /* rename */
6995  store_record(table, record[1]);
6996  host_field->store(user_to->host.str, user_to->host.length,
6997  system_charset_info);
6998  user_field->store(user_to->user.str, user_to->user.length,
6999  system_charset_info);
7000  if ((error= table->file->ha_update_row(table->record[1],
7001  table->record[0])) &&
7002  error != HA_ERR_RECORD_IS_THE_SAME)
7003  table->file->print_error(error, MYF(0));
7004  else
7005  error= 0;
7006  }
7007  else
7008  {
7009  /* delete */
7010  if ((error=table->file->ha_delete_row(table->record[0])))
7011  table->file->print_error(error, MYF(0));
7012  }
7013 
7014  DBUG_RETURN(error);
7015 }
7016 
7017 /*
7018  Handle a privilege table.
7019 
7020  SYNOPSIS
7021  handle_grant_table()
7022  tables The array with the four open tables.
7023  table_no The number of the table to handle (0..4).
7024  drop If user_from is to be dropped.
7025  user_from The the user to be searched/dropped/renamed.
7026  user_to The new name for the user if to be renamed,
7027  NULL otherwise.
7028 
7029  DESCRIPTION
7030  Scan through all records in a grant table and apply the requested
7031  operation. For the "user" table, a single index access is sufficient,
7032  since there is an unique index on (host, user).
7033  Delete from grant table if drop is true.
7034  Update in grant table if drop is false and user_to is not NULL.
7035  Search in grant table if drop is false and user_to is NULL.
7036  Tables are numbered as follows:
7037  0 user
7038  1 db
7039  2 tables_priv
7040  3 columns_priv
7041  4 procs_priv
7042 
7043  RETURN
7044  > 0 At least one record matched.
7045  0 OK, but no record matched.
7046  < 0 Error.
7047 */
7048 
7049 static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
7050  LEX_USER *user_from, LEX_USER *user_to)
7051 {
7052  int result= 0;
7053  int error;
7054  TABLE *table= tables[table_no].table;
7055  Field *host_field= table->field[0];
7056  Field *user_field= table->field[table_no && table_no != 5 ? 2 : 1];
7057  char *host_str= user_from->host.str;
7058  char *user_str= user_from->user.str;
7059  const char *host;
7060  const char *user;
7061  uchar user_key[MAX_KEY_LENGTH];
7062  uint key_prefix_length;
7063  DBUG_ENTER("handle_grant_table");
7064  THD *thd= current_thd;
7065 
7066  table->use_all_columns();
7067  if (! table_no) // mysql.user table
7068  {
7069  /*
7070  The 'user' table has an unique index on (host, user).
7071  Thus, we can handle everything with a single index access.
7072  The host- and user fields are consecutive in the user table records.
7073  So we set host- and user fields of table->record[0] and use the
7074  pointer to the host field as key.
7075  index_read_idx() will replace table->record[0] (its first argument)
7076  by the searched record, if it exists.
7077  */
7078  DBUG_PRINT("info",("read table: '%s' search: '%s'@'%s'",
7079  table->s->table_name.str, user_str, host_str));
7080  host_field->store(host_str, user_from->host.length, system_charset_info);
7081  user_field->store(user_str, user_from->user.length, system_charset_info);
7082 
7083  if (!table->key_info)
7084  {
7085  my_error(ER_TABLE_CORRUPT, MYF(0), table->s->db.str,
7086  table->s->table_name.str);
7087  DBUG_RETURN(-1);
7088  }
7089 
7090  key_prefix_length= (table->key_info->key_part[0].store_length +
7091  table->key_info->key_part[1].store_length);
7092  key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
7093 
7094  if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
7095  user_key, (key_part_map)3,
7096  HA_READ_KEY_EXACT)))
7097  {
7098  if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
7099  {
7100  table->file->print_error(error, MYF(0));
7101  result= -1;
7102  }
7103  }
7104  else
7105  {
7106  /* If requested, delete or update the record. */
7107  result= ((drop || user_to) &&
7108  modify_grant_table(table, host_field, user_field, user_to)) ?
7109  -1 : 1; /* Error or found. */
7110  }
7111  DBUG_PRINT("info",("read result: %d", result));
7112  }
7113  else
7114  {
7115  /*
7116  The non-'user' table do not have indexes on (host, user).
7117  And their host- and user fields are not consecutive.
7118  Thus, we need to do a table scan to find all matching records.
7119  */
7120  if ((error= table->file->ha_rnd_init(1)))
7121  {
7122  table->file->print_error(error, MYF(0));
7123  result= -1;
7124  }
7125  else
7126  {
7127 #ifdef EXTRA_DEBUG
7128  DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'",
7129  table->s->table_name.str, user_str, host_str));
7130 #endif
7131  while ((error= table->file->ha_rnd_next(table->record[0])) !=
7132  HA_ERR_END_OF_FILE)
7133  {
7134  if (error)
7135  {
7136  /* Most probable 'deleted record'. */
7137  DBUG_PRINT("info",("scan error: %d", error));
7138  continue;
7139  }
7140  if (! (host= get_field(thd->mem_root, host_field)))
7141  host= "";
7142  if (! (user= get_field(thd->mem_root, user_field)))
7143  user= "";
7144 
7145 #ifdef EXTRA_DEBUG
7146  if (table_no != 5)
7147  {
7148  DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
7149  user, host,
7150  get_field(thd->mem_root, table->field[1]) /*db*/,
7151  get_field(thd->mem_root, table->field[3]) /*table*/,
7152  get_field(thd->mem_root,
7153  table->field[4]) /*column*/));
7154  }
7155 #endif
7156  if (strcmp(user_str, user) ||
7157  my_strcasecmp(system_charset_info, host_str, host))
7158  continue;
7159 
7160  /* If requested, delete or update the record. */
7161  result= ((drop || user_to) &&
7162  modify_grant_table(table, host_field, user_field, user_to)) ?
7163  -1 : result ? result : 1; /* Error or keep result or found. */
7164  /* If search is requested, we do not need to search further. */
7165  if (! drop && ! user_to)
7166  break ;
7167  }
7168  (void) table->file->ha_rnd_end();
7169  DBUG_PRINT("info",("scan result: %d", result));
7170  }
7171  }
7172 
7173  DBUG_RETURN(result);
7174 }
7175 
7176 
7204 static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
7205  LEX_USER *user_from, LEX_USER *user_to)
7206 {
7207  int result= 0;
7208  uint idx;
7209  uint elements;
7210  const char *user;
7211  const char *host;
7212  ACL_USER *acl_user= NULL;
7213  ACL_DB *acl_db= NULL;
7214  ACL_PROXY_USER *acl_proxy_user= NULL;
7215  GRANT_NAME *grant_name= NULL;
7216  /*
7217  Dynamic array acl_grant_name used to store pointers to all
7218  GRANT_NAME objects
7219  */
7220  Dynamic_array<GRANT_NAME *> acl_grant_name;
7221  HASH *grant_name_hash= NULL;
7222  DBUG_ENTER("handle_grant_struct");
7223  DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
7224  struct_no, user_from->user.str, user_from->host.str));
7225 
7226  LINT_INIT(user);
7227  LINT_INIT(host);
7228 
7229  mysql_mutex_assert_owner(&acl_cache->lock);
7230 
7231  /* Get the number of elements in the in-memory structure. */
7232  switch (struct_no) {
7233  case USER_ACL:
7234  elements= acl_users.elements;
7235  break;
7236  case DB_ACL:
7237  elements= acl_dbs.elements;
7238  break;
7239  case COLUMN_PRIVILEGES_HASH:
7240  elements= column_priv_hash.records;
7241  grant_name_hash= &column_priv_hash;
7242  break;
7243  case PROC_PRIVILEGES_HASH:
7244  elements= proc_priv_hash.records;
7245  grant_name_hash= &proc_priv_hash;
7246  break;
7247  case FUNC_PRIVILEGES_HASH:
7248  elements= func_priv_hash.records;
7249  grant_name_hash= &func_priv_hash;
7250  break;
7251  case PROXY_USERS_ACL:
7252  elements= acl_proxy_users.elements;
7253  break;
7254  default:
7255  return -1;
7256  }
7257 
7258 #ifdef EXTRA_DEBUG
7259  DBUG_PRINT("loop",("scan struct: %u search user: '%s' host: '%s'",
7260  struct_no, user_from->user.str, user_from->host.str));
7261 #endif
7262  /* Loop over all elements. */
7263  for (idx= 0; idx < elements; idx++)
7264  {
7265  /*
7266  Get a pointer to the element.
7267  */
7268  switch (struct_no) {
7269  case USER_ACL:
7270  acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
7271  user= acl_user->user;
7272  host= acl_user->host.get_host();
7273  break;
7274 
7275  case DB_ACL:
7276  acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
7277  user= acl_db->user;
7278  host= acl_db->host.get_host();
7279  break;
7280 
7281  case COLUMN_PRIVILEGES_HASH:
7282  case PROC_PRIVILEGES_HASH:
7283  case FUNC_PRIVILEGES_HASH:
7284  grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
7285  user= grant_name->user;
7286  host= grant_name->host.get_host();
7287  break;
7288 
7289  case PROXY_USERS_ACL:
7290  acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
7291  user= acl_proxy_user->get_user();
7292  host= acl_proxy_user->host.get_host();
7293  break;
7294 
7295  default:
7296  MY_ASSERT_UNREACHABLE();
7297  }
7298  if (! user)
7299  user= "";
7300  if (! host)
7301  host= "";
7302 
7303 #ifdef EXTRA_DEBUG
7304  DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'",
7305  struct_no, idx, user, host));
7306 #endif
7307  if (strcmp(user_from->user.str, user) ||
7308  my_strcasecmp(system_charset_info, user_from->host.str, host))
7309  continue;
7310 
7311  result= 1; /* At least one element found. */
7312  if ( drop )
7313  {
7314  switch ( struct_no ) {
7315  case USER_ACL:
7316  delete_dynamic_element(&acl_users, idx);
7317  elements--;
7318  /*
7319  - If we are iterating through an array then we just have moved all
7320  elements after the current element one position closer to its head.
7321  This means that we have to take another look at the element at
7322  current position as it is a new element from the array's tail.
7323  - This is valid for case USER_ACL, DB_ACL and PROXY_USERS_ACL.
7324  */
7325  idx--;
7326  break;
7327 
7328  case DB_ACL:
7329  delete_dynamic_element(&acl_dbs, idx);
7330  elements--;
7331  idx--;
7332  break;
7333 
7334  case COLUMN_PRIVILEGES_HASH:
7335  case PROC_PRIVILEGES_HASH:
7336  case FUNC_PRIVILEGES_HASH:
7337  /*
7338  Deleting while traversing a hash table is not valid procedure and
7339  hence we save pointers to GRANT_NAME objects for later processing.
7340  */
7341  if (acl_grant_name.append(grant_name))
7342  DBUG_RETURN(-1);
7343  break;
7344 
7345  case PROXY_USERS_ACL:
7346  delete_dynamic_element(&acl_proxy_users, idx);
7347  elements--;
7348  idx--;
7349  break;
7350  }
7351  }
7352  else if ( user_to )
7353  {
7354  switch ( struct_no ) {
7355  case USER_ACL:
7356  acl_user->user= strdup_root(&global_acl_memory, user_to->user.str);
7357  acl_user->host.update_hostname(strdup_root(&global_acl_memory, user_to->host.str));
7358  break;
7359 
7360  case DB_ACL:
7361  acl_db->user= strdup_root(&global_acl_memory, user_to->user.str);
7362  acl_db->host.update_hostname(strdup_root(&global_acl_memory, user_to->host.str));
7363  break;
7364 
7365  case COLUMN_PRIVILEGES_HASH:
7366  case PROC_PRIVILEGES_HASH:
7367  case FUNC_PRIVILEGES_HASH:
7368  /*
7369  Updating while traversing a hash table is not valid procedure and
7370  hence we save pointers to GRANT_NAME objects for later processing.
7371  */
7372  if (acl_grant_name.append(grant_name))
7373  DBUG_RETURN(-1);
7374  break;
7375 
7376  case PROXY_USERS_ACL:
7377  acl_proxy_user->set_user(&global_acl_memory, user_to->user.str);
7378  acl_proxy_user->host.update_hostname((user_to->host.str && *user_to->host.str) ?
7379  strdup_root(&global_acl_memory, user_to->host.str) : NULL);
7380  break;
7381  }
7382  }
7383  else
7384  {
7385  /* If search is requested, we do not need to search further. */
7386  break;
7387  }
7388  }
7389 
7390  if (drop || user_to)
7391  {
7392  /*
7393  Traversing the elements stored in acl_grant_name dynamic array
7394  to either delete or update them.
7395  */
7396  for (int i= 0; i < acl_grant_name.elements(); ++i)
7397  {
7398  grant_name= acl_grant_name.at(i);
7399 
7400  if (drop)
7401  {
7402  my_hash_delete(grant_name_hash, (uchar *) grant_name);
7403  }
7404  else
7405  {
7406  /*
7407  Save old hash key and its length to be able properly update
7408  element position in hash.
7409  */
7410  char *old_key= grant_name->hash_key;
7411  size_t old_key_length= grant_name->key_length;
7412 
7413  /*
7414  Update the grant structure with the new user name and host name.
7415  */
7416  grant_name->set_user_details(user_to->host.str, grant_name->db,
7417  user_to->user.str, grant_name->tname,
7418  TRUE);
7419 
7420  /*
7421  Since username is part of the hash key, when the user name
7422  is renamed, the hash key is changed. Update the hash to
7423  ensure that the position matches the new hash key value
7424  */
7425  my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
7426  old_key_length);
7427  }
7428  }
7429  }
7430 
7431 #ifdef EXTRA_DEBUG
7432  DBUG_PRINT("loop",("scan struct: %u result %d", struct_no, result));
7433 #endif
7434 
7435  DBUG_RETURN(result);
7436 }
7437 
7438 
7439 /*
7440  Handle all privilege tables and in-memory privilege structures.
7441 
7442  SYNOPSIS
7443  handle_grant_data()
7444  tables The array with the four open tables.
7445  drop If user_from is to be dropped.
7446  user_from The the user to be searched/dropped/renamed.
7447  user_to The new name for the user if to be renamed,
7448  NULL otherwise.
7449 
7450  DESCRIPTION
7451  Go through all grant tables and in-memory grant structures and apply
7452  the requested operation.
7453  Delete from grant data if drop is true.
7454  Update in grant data if drop is false and user_to is not NULL.
7455  Search in grant data if drop is false and user_to is NULL.
7456 
7457  RETURN
7458  > 0 At least one element matched.
7459  0 OK, but no element matched.
7460  < 0 Error.
7461 */
7462 
7463 static int handle_grant_data(TABLE_LIST *tables, bool drop,
7464  LEX_USER *user_from, LEX_USER *user_to)
7465 {
7466  int result= 0;
7467  int found;
7468  int ret;
7469  DBUG_ENTER("handle_grant_data");
7470 
7471  /* Handle user table. */
7472  if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
7473  {
7474  /* Handle of table failed, don't touch the in-memory array. */
7475  result= -1;
7476  }
7477  else
7478  {
7479  /* Handle user array. */
7480  if (((ret= handle_grant_struct(USER_ACL, drop, user_from, user_to) > 0) &&
7481  ! result) || found)
7482  {
7483  result= 1; /* At least one record/element found. */
7484  /* If search is requested, we do not need to search further. */
7485  if (! drop && ! user_to)
7486  goto end;
7487  }
7488  else if (ret < 0)
7489  {
7490  result= -1;
7491  goto end;
7492  }
7493  }
7494 
7495  /* Handle db table. */
7496  if ((found= handle_grant_table(tables, 1, drop, user_from, user_to)) < 0)
7497  {
7498  /* Handle of table failed, don't touch the in-memory array. */
7499  result= -1;
7500  }
7501  else
7502  {
7503  /* Handle db array. */
7504  if ((((ret= handle_grant_struct(DB_ACL, drop, user_from, user_to) > 0) &&
7505  ! result) || found) && ! result)
7506  {
7507  result= 1; /* At least one record/element found. */
7508  /* If search is requested, we do not need to search further. */
7509  if (! drop && ! user_to)
7510  goto end;
7511  }
7512  else if (ret < 0)
7513  {
7514  result= -1;
7515  goto end;
7516  }
7517  }
7518 
7519  /* Handle stored routines table. */
7520  if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0)
7521  {
7522  /* Handle of table failed, don't touch in-memory array. */
7523  result= -1;
7524  }
7525  else
7526  {
7527  /* Handle procs array. */
7528  if ((((ret= handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from,
7529  user_to) > 0) && ! result) || found) &&
7530  ! result)
7531  {
7532  result= 1; /* At least one record/element found. */
7533  /* If search is requested, we do not need to search further. */
7534  if (! drop && ! user_to)
7535  goto end;
7536  }
7537  else if (ret < 0)
7538  {
7539  result= -1;
7540  goto end;
7541  }
7542  /* Handle funcs array. */
7543  if ((((ret= handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from,
7544  user_to) > 0) && ! result) || found) &&
7545  ! result)
7546  {
7547  result= 1; /* At least one record/element found. */
7548  /* If search is requested, we do not need to search further. */
7549  if (! drop && ! user_to)
7550  goto end;
7551  }
7552  else if (ret < 0)
7553  {
7554  result= -1;
7555  goto end;
7556  }
7557  }
7558 
7559  /* Handle tables table. */
7560  if ((found= handle_grant_table(tables, 2, drop, user_from, user_to)) < 0)
7561  {
7562  /* Handle of table failed, don't touch columns and in-memory array. */
7563  result= -1;
7564  }
7565  else
7566  {
7567  if (found && ! result)
7568  {
7569  result= 1; /* At least one record found. */
7570  /* If search is requested, we do not need to search further. */
7571  if (! drop && ! user_to)
7572  goto end;
7573  }
7574 
7575  /* Handle columns table. */
7576  if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
7577  {
7578  /* Handle of table failed, don't touch the in-memory array. */
7579  result= -1;
7580  }
7581  else
7582  {
7583  /* Handle columns hash. */
7584  if ((((ret= handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from,
7585  user_to) > 0) && ! result) || found) &&
7586  ! result)
7587  result= 1; /* At least one record/element found. */
7588  else if (ret < 0)
7589  result= -1;
7590  }
7591  }
7592 
7593  /* Handle proxies_priv table. */
7594  if (tables[5].table)
7595  {
7596  if ((found= handle_grant_table(tables, 5, drop, user_from, user_to)) < 0)
7597  {
7598  /* Handle of table failed, don't touch the in-memory array. */
7599  result= -1;
7600  }
7601  else
7602  {
7603  /* Handle proxies_priv array. */
7604  if (((ret= handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) > 0)
7605  && !result) || found)
7606  result= 1; /* At least one record/element found. */
7607  else if (ret < 0)
7608  result= -1;
7609  }
7610  }
7611  end:
7612  DBUG_RETURN(result);
7613 }
7614 
7615 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
7616 
7628 void append_user(THD *thd, String *str, LEX_USER *user, bool comma= true,
7629  bool ident= false)
7630 {
7631  String from_user(user->user.str, user->user.length, system_charset_info);
7632  String from_plugin(user->plugin.str, user->plugin.length, system_charset_info);
7633  String from_auth(user->auth.str, user->auth.length, system_charset_info);
7634  String from_host(user->host.str, user->host.length, system_charset_info);
7635 
7636  if (comma)
7637  str->append(',');
7638  append_query_string(thd, system_charset_info, &from_user, str);
7639  str->append(STRING_WITH_LEN("@"));
7640  append_query_string(thd, system_charset_info, &from_host, str);
7641 
7642  if (ident)
7643  {
7644  if (user->plugin.str && (user->plugin.length > 0) &&
7645  memcmp(user->plugin.str, native_password_plugin_name.str,
7646  user->plugin.length) &&
7647  memcmp(user->plugin.str, old_password_plugin_name.str,
7648  user->plugin.length))
7649  {
7655  str->append(STRING_WITH_LEN(" IDENTIFIED WITH "));
7656  append_query_string(thd, system_charset_info, &from_plugin, str);
7657 
7658  if (user->auth.str && (user->auth.length > 0))
7659  {
7660  str->append(STRING_WITH_LEN(" AS "));
7661  append_query_string(thd, system_charset_info, &from_auth, str);
7662  }
7663  }
7664  else if (user->password.str)
7665  {
7666  str->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
7667  if (user->uses_identified_by_password_clause)
7668  {
7669  str->append(user->password.str, user->password.length);
7670  str->append("'");
7671  }
7672  else
7673  {
7674  /*
7675  Password algorithm is chosen based on old_passwords variable or
7676  TODO the new password_algorithm variable.
7677  It is assumed that the variable hasn't changed since parsing.
7678  */
7679  if (thd->variables.old_passwords == 0)
7680  {
7681  /*
7682  my_make_scrambled_password_sha1() requires a target buffer size of
7683  SCRAMBLED_PASSWORD_CHAR_LENGTH + 1.
7684  The extra character is for the probably originate from either '\0'
7685  or the initial '*' character.
7686  */
7687  char tmp[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1];
7688  my_make_scrambled_password_sha1(tmp, user->password.str,
7689  user->password.length);
7690  str->append(tmp);
7691  }
7692  else
7693  {
7694  /*
7695  Legacy password algorithm is just an obfuscation of a plain text
7696  so we're not going to write this.
7697  Same with old_passwords == 2 since the scrambled password will
7698  be binary anyway.
7699  */
7700  str->append("<secret>");
7701  }
7702  str->append("'");
7703  }
7704  }
7705  }
7706 }
7707 
7708 #ifndef NO_EMBEDDED_ACCESS_CHECKS
7709 
7710 /*
7711  Create a list of users.
7712 
7713  SYNOPSIS
7714  mysql_create_user()
7715  thd The current thread.
7716  list The users to create.
7717 
7718  RETURN
7719  FALSE OK.
7720  TRUE Error.
7721 */
7722 
7723 bool mysql_create_user(THD *thd, List <LEX_USER> &list)
7724 {
7725  int result;
7726  String wrong_users;
7727  LEX_USER *user_name, *tmp_user_name;
7728  List_iterator <LEX_USER> user_list(list);
7729  TABLE_LIST tables[GRANT_TABLES];
7730  bool some_users_created= FALSE;
7731  bool save_binlog_row_based;
7732  bool transactional_tables;
7733  DBUG_ENTER("mysql_create_user");
7734 
7735  /*
7736  This statement will be replicated as a statement, even when using
7737  row-based replication. The flag will be reset at the end of the
7738  statement.
7739  */
7740  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
7741  thd->clear_current_stmt_binlog_format_row();
7742 
7743  /* CREATE USER may be skipped on replication client. */
7744  if ((result= open_grant_tables(thd, tables, &transactional_tables)))
7745  {
7746  /* Restore the state of binlog format */
7747  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7748  if (save_binlog_row_based)
7749  thd->set_current_stmt_binlog_format_row();
7750  DBUG_RETURN(result != 1);
7751  }
7752 
7753  mysql_rwlock_wrlock(&LOCK_grant);
7754  mysql_mutex_lock(&acl_cache->lock);
7755 
7756  while ((tmp_user_name= user_list++))
7757  {
7758  /*
7759  If tmp_user_name.user.str is == NULL then
7760  user_name := tmp_user_name.
7761  Else user_name.user := sctx->user
7762  TODO and all else is turned to NULL !! Why?
7763  */
7764  if (!(user_name= get_current_user(thd, tmp_user_name)))
7765  {
7766  result= TRUE;
7767  continue;
7768  }
7769 
7770  /*
7771  If no plugin is given, set a default plugin
7772  */
7773  if (user_name->plugin.length == 0 && user_name->uses_identified_with_clause)
7774  {
7775  user_name->plugin.str= default_auth_plugin_name.str;
7776  user_name->plugin.length= default_auth_plugin_name.length;
7777  }
7778 
7779  /*
7780  Search all in-memory structures and grant tables
7781  for a mention of the new user name.
7782  */
7783  if (handle_grant_data(tables, 0, user_name, NULL))
7784  {
7785  append_user(thd, &wrong_users, user_name, wrong_users.length() > 0,
7786  false);
7787  result= TRUE;
7788  continue;
7789  }
7790 
7791  if (replace_user_table(thd, tables[0].table, user_name, 0, 0, 1, 0))
7792  {
7793  append_user(thd, &wrong_users, user_name, wrong_users.length() > 0,
7794  false);
7795  result= TRUE;
7796  continue;
7797  }
7798 
7799  some_users_created= TRUE;
7800  } // END while tmp_user_name= user_lists++
7801 
7802  mysql_mutex_unlock(&acl_cache->lock);
7803 
7804  if (result)
7805  my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
7806 
7807  if (some_users_created)
7808  {
7809  if (!thd->rewritten_query.length())
7810  result|= write_bin_log(thd, false, thd->query(), thd->query_length(),
7811  transactional_tables);
7812  else
7813  result|= write_bin_log(thd, false,
7814  thd->rewritten_query.c_ptr_safe(),
7815  thd->rewritten_query.length(),
7816  transactional_tables);
7817  }
7818 
7819  mysql_rwlock_unlock(&LOCK_grant);
7820 
7821  result|= acl_trans_commit_and_close_tables(thd);
7822 
7823  /* Restore the state of binlog format */
7824  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7825  if (save_binlog_row_based)
7826  thd->set_current_stmt_binlog_format_row();
7827  DBUG_RETURN(result);
7828 }
7829 
7830 
7831 /*
7832  Drop a list of users and all their privileges.
7833 
7834  SYNOPSIS
7835  mysql_drop_user()
7836  thd The current thread.
7837  list The users to drop.
7838 
7839  RETURN
7840  FALSE OK.
7841  TRUE Error.
7842 */
7843 
7844 bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
7845 {
7846  int result;
7847  String wrong_users;
7848  LEX_USER *user_name, *tmp_user_name;
7849  List_iterator <LEX_USER> user_list(list);
7850  TABLE_LIST tables[GRANT_TABLES];
7851  bool some_users_deleted= FALSE;
7852  sql_mode_t old_sql_mode= thd->variables.sql_mode;
7853  bool save_binlog_row_based;
7854  bool transactional_tables;
7855  DBUG_ENTER("mysql_drop_user");
7856 
7857  /*
7858  This statement will be replicated as a statement, even when using
7859  row-based replication. The flag will be reset at the end of the
7860  statement.
7861  */
7862  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
7863  thd->clear_current_stmt_binlog_format_row();
7864 
7865  /* DROP USER may be skipped on replication client. */
7866  if ((result= open_grant_tables(thd, tables, &transactional_tables)))
7867  {
7868  /* Restore the state of binlog format */
7869  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7870  if (save_binlog_row_based)
7871  thd->set_current_stmt_binlog_format_row();
7872  DBUG_RETURN(result != 1);
7873  }
7874 
7875  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
7876 
7877  mysql_rwlock_wrlock(&LOCK_grant);
7878  mysql_mutex_lock(&acl_cache->lock);
7879 
7880  while ((tmp_user_name= user_list++))
7881  {
7882  if (!(user_name= get_current_user(thd, tmp_user_name)))
7883  {
7884  result= TRUE;
7885  continue;
7886  }
7887  if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
7888  {
7889  append_user(thd, &wrong_users, user_name, wrong_users.length() > 0, FALSE);
7890  result= TRUE;
7891  continue;
7892  }
7893  some_users_deleted= TRUE;
7894  }
7895 
7896  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
7897  rebuild_check_host();
7898 
7899  mysql_mutex_unlock(&acl_cache->lock);
7900 
7901  if (result)
7902  my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
7903 
7904  if (some_users_deleted)
7905  result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length(),
7906  transactional_tables);
7907 
7908  mysql_rwlock_unlock(&LOCK_grant);
7909 
7910  result|= acl_trans_commit_and_close_tables(thd);
7911 
7912  thd->variables.sql_mode= old_sql_mode;
7913  /* Restore the state of binlog format */
7914  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7915  if (save_binlog_row_based)
7916  thd->set_current_stmt_binlog_format_row();
7917  DBUG_RETURN(result);
7918 }
7919 
7920 
7921 /*
7922  Rename a user.
7923 
7924  SYNOPSIS
7925  mysql_rename_user()
7926  thd The current thread.
7927  list The user name pairs: (from, to).
7928 
7929  RETURN
7930  FALSE OK.
7931  TRUE Error.
7932 */
7933 
7934 bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
7935 {
7936  int result;
7937  String wrong_users;
7938  LEX_USER *user_from, *tmp_user_from;
7939  LEX_USER *user_to, *tmp_user_to;
7940  List_iterator <LEX_USER> user_list(list);
7941  TABLE_LIST tables[GRANT_TABLES];
7942  bool some_users_renamed= FALSE;
7943  bool save_binlog_row_based;
7944  bool transactional_tables;
7945  DBUG_ENTER("mysql_rename_user");
7946 
7947  /*
7948  This statement will be replicated as a statement, even when using
7949  row-based replication. The flag will be reset at the end of the
7950  statement.
7951  */
7952  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
7953  thd->clear_current_stmt_binlog_format_row();
7954 
7955  /* RENAME USER may be skipped on replication client. */
7956  if ((result= open_grant_tables(thd, tables, &transactional_tables)))
7957  {
7958  /* Restore the state of binlog format */
7959  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7960  if (save_binlog_row_based)
7961  thd->set_current_stmt_binlog_format_row();
7962  DBUG_RETURN(result != 1);
7963  }
7964 
7965  mysql_rwlock_wrlock(&LOCK_grant);
7966  mysql_mutex_lock(&acl_cache->lock);
7967 
7968  while ((tmp_user_from= user_list++))
7969  {
7970  if (!(user_from= get_current_user(thd, tmp_user_from)))
7971  {
7972  result= TRUE;
7973  continue;
7974  }
7975  tmp_user_to= user_list++;
7976  if (!(user_to= get_current_user(thd, tmp_user_to)))
7977  {
7978  result= TRUE;
7979  continue;
7980  }
7981  DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
7982 
7983  /*
7984  Search all in-memory structures and grant tables
7985  for a mention of the new user name.
7986  */
7987  if (handle_grant_data(tables, 0, user_to, NULL) ||
7988  handle_grant_data(tables, 0, user_from, user_to) <= 0)
7989  {
7990  append_user(thd, &wrong_users, user_from, wrong_users.length() > 0, FALSE);
7991  result= TRUE;
7992  continue;
7993  }
7994  some_users_renamed= TRUE;
7995  }
7996 
7997  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
7998  rebuild_check_host();
7999 
8000  mysql_mutex_unlock(&acl_cache->lock);
8001 
8002  if (result)
8003  my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
8004 
8005  if (some_users_renamed)
8006  result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length(),
8007  transactional_tables);
8008 
8009  mysql_rwlock_unlock(&LOCK_grant);
8010 
8011  result|= acl_trans_commit_and_close_tables(thd);
8012 
8013  /* Restore the state of binlog format */
8014  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8015  if (save_binlog_row_based)
8016  thd->set_current_stmt_binlog_format_row();
8017  DBUG_RETURN(result);
8018 }
8019 
8020 
8021 /*
8022  Mark user's password as expired.
8023 
8024  SYNOPSIS
8025  mysql_user_password_expire()
8026  thd The current thread.
8027  list The user names.
8028 
8029  RETURN
8030  FALSE OK.
8031  TRUE Error.
8032 */
8033 
8034 bool mysql_user_password_expire(THD *thd, List <LEX_USER> &list)
8035 {
8036  bool result= false;
8037  String wrong_users;
8038  LEX_USER *user_from, *tmp_user_from;
8039  List_iterator <LEX_USER> user_list(list);
8040  TABLE_LIST tables;
8041  TABLE *table;
8042  bool some_passwords_expired= false;
8043  bool save_binlog_row_based;
8044  DBUG_ENTER("mysql_user_password_expire");
8045 
8046  if (!initialized)
8047  {
8048  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
8049  DBUG_RETURN(true);
8050  }
8051  tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
8052 
8053 #ifdef HAVE_REPLICATION
8054  /*
8055  GRANT and REVOKE are applied the slave in/exclusion rules as they are
8056  some kind of updates to the mysql.% tables.
8057  */
8058  if (thd->slave_thread && rpl_filter->is_on())
8059  {
8060  /*
8061  The tables must be marked "updating" so that tables_ok() takes them into
8062  account in tests. It's ok to leave 'updating' set after tables_ok.
8063  */
8064  tables.updating= 1;
8065  /* Thanks to memset, tables.next==0 */
8066  if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, &tables)))
8067  DBUG_RETURN(false);
8068  }
8069 #endif
8070  if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
8071  DBUG_RETURN(true);
8072 
8073  if (!table->key_info)
8074  {
8075  my_error(ER_TABLE_CORRUPT, MYF(0), table->s->db.str,
8076  table->s->table_name.str);
8077  DBUG_RETURN(true);
8078  }
8079 
8080  /*
8081  This statement will be replicated as a statement, even when using
8082  row-based replication. The flag will be reset at the end of the
8083  statement.
8084  */
8085  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
8086  thd->clear_current_stmt_binlog_format_row();
8087 
8088  mysql_rwlock_wrlock(&LOCK_grant);
8089  mysql_mutex_lock(&acl_cache->lock);
8090 
8091  while ((tmp_user_from= user_list++))
8092  {
8093  ACL_USER *acl_user;
8094 
8095  /* add the defaults where needed */
8096  if (!(user_from= get_current_user(thd, tmp_user_from)))
8097  {
8098  result= true;
8099  append_user(thd, &wrong_users, tmp_user_from, wrong_users.length() > 0,
8100  false);
8101  continue;
8102  }
8103 
8104  /* look up the user */
8105  if (!(acl_user= find_acl_user(user_from->host.str,
8106  user_from->user.str, TRUE)))
8107  {
8108  result= true;
8109  append_user(thd, &wrong_users, user_from, wrong_users.length() > 0,
8110  false);
8111  continue;
8112  }
8113 
8114  /* Check if the user's authentication method supports expiration */
8115  if (!auth_plugin_supports_expiration(acl_user->plugin.str))
8116  {
8117  result= true;
8118  append_user(thd, &wrong_users, user_from, wrong_users.length() > 0,
8119  false);
8120  continue;
8121  }
8122 
8123 
8124  /* update the mysql.user table */
8125  enum mysql_user_table_field password_field= MYSQL_USER_FIELD_PASSWORD;
8126  if (update_user_table(thd, table,
8127  acl_user->host.get_host() ?
8128  acl_user->host.get_host() : "",
8129  acl_user->user ? acl_user->user : "",
8130  NULL, 0, password_field, true, false))
8131  {
8132  result= true;
8133  append_user(thd, &wrong_users, user_from, wrong_users.length() > 0,
8134  false);
8135  continue;
8136  }
8137 
8138  acl_user->password_expired= true;
8139  some_passwords_expired= true;
8140  }
8141 
8142  acl_cache->clear(1); // Clear locked hostname cache
8143  mysql_mutex_unlock(&acl_cache->lock);
8144 
8145  if (result)
8146  my_error(ER_CANNOT_USER, MYF(0), "ALTER USER", wrong_users.c_ptr_safe());
8147 
8148  if (!result && some_passwords_expired)
8149  {
8150  const char *query= thd->rewritten_query.length() ?
8151  thd->rewritten_query.c_ptr_safe() : thd->query();
8152  const size_t query_length= thd->rewritten_query.length() ?
8153  thd->rewritten_query.length() : thd->query_length();
8154  result= (write_bin_log(thd, false, query, query_length,
8155  table->file->has_transactions()) != 0);
8156  }
8157 
8158  mysql_rwlock_unlock(&LOCK_grant);
8159 
8160  result|= acl_trans_commit_and_close_tables(thd);
8161 
8162  /* Restore the state of binlog format */
8163  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8164  if (save_binlog_row_based)
8165  thd->set_current_stmt_binlog_format_row();
8166  DBUG_RETURN(result);
8167 }
8168 
8169 
8170 /*
8171  Revoke all privileges from a list of users.
8172 
8173  SYNOPSIS
8174  mysql_revoke_all()
8175  thd The current thread.
8176  list The users to revoke all privileges from.
8177 
8178  RETURN
8179  > 0 Error. Error message already sent.
8180  0 OK.
8181  < 0 Error. Error message not yet sent.
8182 */
8183 
8184 bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
8185 {
8186  uint counter, revoked, is_proc;
8187  int result;
8188  ACL_DB *acl_db;
8189  TABLE_LIST tables[GRANT_TABLES];
8190  bool save_binlog_row_based;
8191  bool transactional_tables;
8192  DBUG_ENTER("mysql_revoke_all");
8193 
8194  /*
8195  This statement will be replicated as a statement, even when using
8196  row-based replication. The flag will be reset at the end of the
8197  statement.
8198  */
8199  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
8200  thd->clear_current_stmt_binlog_format_row();
8201 
8202  if ((result= open_grant_tables(thd, tables, &transactional_tables)))
8203  {
8204  /* Restore the state of binlog format */
8205  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8206  if (save_binlog_row_based)
8207  thd->set_current_stmt_binlog_format_row();
8208  DBUG_RETURN(result != 1);
8209  }
8210 
8211  mysql_rwlock_wrlock(&LOCK_grant);
8212  mysql_mutex_lock(&acl_cache->lock);
8213 
8214  LEX_USER *lex_user, *tmp_lex_user;
8215  List_iterator <LEX_USER> user_list(list);
8216  while ((tmp_lex_user= user_list++))
8217  {
8218  if (!(lex_user= get_current_user(thd, tmp_lex_user)))
8219  {
8220  result= -1;
8221  continue;
8222  }
8223  if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
8224  {
8225  result= -1;
8226  continue;
8227  }
8228 
8229  if (replace_user_table(thd, tables[0].table,
8230  lex_user, ~(ulong) 0, 1, 0, 0))
8231  {
8232  result= -1;
8233  continue;
8234  }
8235 
8236  /* Remove db access privileges */
8237  /*
8238  Because acl_dbs and column_priv_hash shrink and may re-order
8239  as privileges are removed, removal occurs in a repeated loop
8240  until no more privileges are revoked.
8241  */
8242  do
8243  {
8244  for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
8245  {
8246  const char *user,*host;
8247 
8248  acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
8249  if (!(user=acl_db->user))
8250  user= "";
8251  if (!(host=acl_db->host.get_host()))
8252  host= "";
8253 
8254  if (!strcmp(lex_user->user.str,user) &&
8255  !strcmp(lex_user->host.str, host))
8256  {
8257  if (!replace_db_table(tables[1].table, acl_db->db, *lex_user,
8258  ~(ulong)0, 1))
8259  {
8260  /*
8261  Don't increment counter as replace_db_table deleted the
8262  current element in acl_dbs.
8263  */
8264  revoked= 1;
8265  continue;
8266  }
8267  result= -1; // Something went wrong
8268  }
8269  counter++;
8270  }
8271  } while (revoked);
8272 
8273  /* Remove column access */
8274  do
8275  {
8276  for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
8277  {
8278  const char *user,*host;
8279  GRANT_TABLE *grant_table=
8280  (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
8281  if (!(user=grant_table->user))
8282  user= "";
8283  if (!(host=grant_table->host.get_host()))
8284  host= "";
8285 
8286  if (!strcmp(lex_user->user.str,user) &&
8287  !strcmp(lex_user->host.str, host))
8288  {
8289  if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
8290  grant_table->db,
8291  grant_table->tname,
8292  ~(ulong)0, 0, 1))
8293  {
8294  result= -1;
8295  }
8296  else
8297  {
8298  if (!grant_table->cols)
8299  {
8300  revoked= 1;
8301  continue;
8302  }
8303  List<LEX_COLUMN> columns;
8304  if (!replace_column_table(grant_table,tables[3].table, *lex_user,
8305  columns,
8306  grant_table->db,
8307  grant_table->tname,
8308  ~(ulong)0, 1))
8309  {
8310  revoked= 1;
8311  continue;
8312  }
8313  result= -1;
8314  }
8315  }
8316  counter++;
8317  }
8318  } while (revoked);
8319 
8320  /* Remove procedure access */
8321  for (is_proc=0; is_proc<2; is_proc++) do {
8322  HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
8323  for (counter= 0, revoked= 0 ; counter < hash->records ; )
8324  {
8325  const char *user,*host;
8326  GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
8327  if (!(user=grant_proc->user))
8328  user= "";
8329  if (!(host=grant_proc->host.get_host()))
8330  host= "";
8331 
8332  if (!strcmp(lex_user->user.str,user) &&
8333  !strcmp(lex_user->host.str, host))
8334  {
8335  if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
8336  grant_proc->db,
8337  grant_proc->tname,
8338  is_proc,
8339  ~(ulong)0, 1) == 0)
8340  {
8341  revoked= 1;
8342  continue;
8343  }
8344  result= -1; // Something went wrong
8345  }
8346  counter++;
8347  }
8348  } while (revoked);
8349  }
8350 
8351  mysql_mutex_unlock(&acl_cache->lock);
8352 
8353  if (result)
8354  my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
8355 
8356  if (result)
8357  mysql_bin_log.write_incident(thd, true /* need_lock_log=true */);
8358  else
8359  {
8360  result= result |
8361  write_bin_log(thd, FALSE, thd->query(), thd->query_length(),
8362  transactional_tables);
8363  }
8364 
8365  mysql_rwlock_unlock(&LOCK_grant);
8366 
8367  result|= acl_trans_commit_and_close_tables(thd);
8368 
8369  /* Restore the state of binlog format */
8370  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8371  if (save_binlog_row_based)
8372  thd->set_current_stmt_binlog_format_row();
8373 
8374  DBUG_RETURN(result);
8375 }
8376 
8377 
8378 
8379 
8388 class Silence_routine_definer_errors : public Internal_error_handler
8389 {
8390 public:
8392  : is_grave(FALSE)
8393  {}
8394 
8396  {}
8397 
8398  virtual bool handle_condition(THD *thd,
8399  uint sql_errno,
8400  const char* sqlstate,
8401  Sql_condition::enum_warning_level level,
8402  const char* msg,
8403  Sql_condition ** cond_hdl);
8404 
8405  bool has_errors() { return is_grave; }
8406 
8407 private:
8408  bool is_grave;
8409 };
8410 
8411 bool
8412 Silence_routine_definer_errors::handle_condition(
8413  THD *thd,
8414  uint sql_errno,
8415  const char*,
8416  Sql_condition::enum_warning_level level,
8417  const char* msg,
8418  Sql_condition ** cond_hdl)
8419 {
8420  *cond_hdl= NULL;
8421  if (level == Sql_condition::WARN_LEVEL_ERROR)
8422  {
8423  switch (sql_errno)
8424  {
8425  case ER_NONEXISTING_PROC_GRANT:
8426  /* Convert the error into a warning. */
8427  push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
8428  sql_errno, msg);
8429  return TRUE;
8430  default:
8431  is_grave= TRUE;
8432  }
8433  }
8434 
8435  return FALSE;
8436 }
8437 
8438 
8456 bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
8457  bool is_proc)
8458 {
8459  uint counter, revoked;
8460  int result;
8461  TABLE_LIST tables[GRANT_TABLES];
8462  HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
8463  Silence_routine_definer_errors error_handler;
8464  bool save_binlog_row_based;
8465  bool not_used;
8466  DBUG_ENTER("sp_revoke_privileges");
8467 
8468  if ((result= open_grant_tables(thd, tables, &not_used)))
8469  DBUG_RETURN(result != 1);
8470 
8471  /* Be sure to pop this before exiting this scope! */
8472  thd->push_internal_handler(&error_handler);
8473 
8474  mysql_rwlock_wrlock(&LOCK_grant);
8475  mysql_mutex_lock(&acl_cache->lock);
8476 
8477  /*
8478  This statement will be replicated as a statement, even when using
8479  row-based replication. The flag will be reset at the end of the
8480  statement.
8481  */
8482  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
8483  thd->clear_current_stmt_binlog_format_row();
8484 
8485  /* Remove procedure access */
8486  do
8487  {
8488  for (counter= 0, revoked= 0 ; counter < hash->records ; )
8489  {
8490  GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
8491  if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
8492  !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
8493  {
8494  LEX_USER lex_user;
8495  lex_user.user.str= grant_proc->user;
8496  lex_user.user.length= strlen(grant_proc->user);
8497  lex_user.host.str= (char*) (grant_proc->host.get_host() ?
8498  grant_proc->host.get_host() : "");
8499  lex_user.host.length= grant_proc->host.get_host() ?
8500  strlen(grant_proc->host.get_host()) : 0;
8501 
8502  if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
8503  grant_proc->db, grant_proc->tname,
8504  is_proc, ~(ulong)0, 1) == 0)
8505  {
8506  revoked= 1;
8507  continue;
8508  }
8509  }
8510  counter++;
8511  }
8512  } while (revoked);
8513 
8514  mysql_mutex_unlock(&acl_cache->lock);
8515  mysql_rwlock_unlock(&LOCK_grant);
8516 
8517  result= acl_trans_commit_and_close_tables(thd);
8518 
8519  thd->pop_internal_handler();
8520 
8521  /* Restore the state of binlog format */
8522  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8523  if (save_binlog_row_based)
8524  thd->set_current_stmt_binlog_format_row();
8525 
8526  DBUG_RETURN(error_handler.has_errors() || result);
8527 }
8528 
8529 
8543 bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
8544  bool is_proc)
8545 {
8546  Security_context *sctx= thd->security_ctx;
8547  LEX_USER *combo;
8548  TABLE_LIST tables[1];
8549  List<LEX_USER> user_list;
8550  bool result;
8551  ACL_USER *au;
8552  Dummy_error_handler error_handler;
8553  DBUG_ENTER("sp_grant_privileges");
8554 
8555  if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
8556  DBUG_RETURN(TRUE);
8557 
8558  combo->user.str= sctx->user;
8559 
8560  mysql_mutex_lock(&acl_cache->lock);
8561 
8562  if ((au= find_acl_user(combo->host.str=(char*)sctx->host_or_ip,combo->user.str,FALSE)))
8563  goto found_acl;
8564  if ((au= find_acl_user(combo->host.str=(char*)sctx->get_host()->ptr(),
8565  combo->user.str,FALSE)))
8566  goto found_acl;
8567  if ((au= find_acl_user(combo->host.str=(char*)sctx->get_ip()->ptr(),
8568  combo->user.str,FALSE)))
8569  goto found_acl;
8570  if((au= find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE)))
8571  goto found_acl;
8572 
8573  mysql_mutex_unlock(&acl_cache->lock);
8574  DBUG_RETURN(TRUE);
8575 
8576  found_acl:
8577  mysql_mutex_unlock(&acl_cache->lock);
8578 
8579  memset(tables, 0, sizeof(TABLE_LIST));
8580  user_list.empty();
8581 
8582  tables->db= (char*)sp_db;
8583  tables->table_name= tables->alias= (char*)sp_name;
8584 
8585  thd->make_lex_string(&combo->user,
8586  combo->user.str, strlen(combo->user.str), 0);
8587  thd->make_lex_string(&combo->host,
8588  combo->host.str, strlen(combo->host.str), 0);
8589 
8590  combo->password= empty_lex_str;
8591  combo->plugin= empty_lex_str;
8592  combo->auth= empty_lex_str;
8593  combo->uses_identified_by_clause= false;
8594  combo->uses_identified_with_clause= false;
8595  combo->uses_identified_by_password_clause= false;
8596  combo->uses_authentication_string_clause= false;
8597 
8598  if (user_list.push_back(combo))
8599  DBUG_RETURN(TRUE);
8600 
8601  thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
8602  thd->lex->ssl_cipher= thd->lex->x509_subject= thd->lex->x509_issuer= 0;
8603  memset(&thd->lex->mqh, 0, sizeof(thd->lex->mqh));
8604 
8605  /*
8606  Only care about whether the operation failed or succeeded
8607  as all errors will be handled later.
8608  */
8609  thd->push_internal_handler(&error_handler);
8610  result= mysql_routine_grant(thd, tables, is_proc, user_list,
8611  DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
8612  thd->pop_internal_handler();
8613  DBUG_RETURN(result);
8614 }
8615 
8616 
8629 static ACL_PROXY_USER *
8630 acl_find_proxy_user(const char *user, const char *host, const char *ip,
8631  const char *authenticated_as, bool *proxy_used)
8632 {
8633  uint i;
8634  /* if the proxied and proxy user are the same return OK */
8635  DBUG_ENTER("acl_find_proxy_user");
8636  DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
8637  user, host, ip, authenticated_as));
8638 
8639  if (!strcmp(authenticated_as, user))
8640  {
8641  DBUG_PRINT ("info", ("user is the same as authenticated_as"));
8642  DBUG_RETURN (NULL);
8643  }
8644 
8645  *proxy_used= TRUE;
8646  for (i=0; i < acl_proxy_users.elements; i++)
8647  {
8648  ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
8649  ACL_PROXY_USER *);
8650  if (proxy->matches(host, user, ip, authenticated_as))
8651  DBUG_RETURN(proxy);
8652  }
8653 
8654  DBUG_RETURN(NULL);
8655 }
8656 
8657 
8658 bool
8659 acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
8660  bool with_grant)
8661 {
8662  DBUG_ENTER("acl_check_proxy_grant_access");
8663  DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
8664  (int) with_grant));
8665  if (!initialized)
8666  {
8667  my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
8668  DBUG_RETURN(1);
8669  }
8670 
8671  /* replication slave thread can do anything */
8672  if (thd->slave_thread)
8673  {
8674  DBUG_PRINT("info", ("replication slave"));
8675  DBUG_RETURN(FALSE);
8676  }
8677 
8678  /*
8679  one can grant proxy for self to others.
8680  Security context in THD contains two pairs of (user,host):
8681  1. (user,host) pair referring to inbound connection.
8682  2. (priv_user,priv_host) pair obtained from mysql.user table after doing
8683  authnetication of incoming connection.
8684  Privileges should be checked wrt (priv_user, priv_host) tuple, because
8685  (user,host) pair obtained from inbound connection may have different
8686  values than what is actually stored in mysql.user table and while granting
8687  or revoking proxy privilege, user is expected to provide entries mentioned
8688  in mysql.user table.
8689  */
8690  if (!strcmp(thd->security_ctx->priv_user, user) &&
8691  !my_strcasecmp(system_charset_info, host,
8692  thd->security_ctx->priv_host))
8693  {
8694  DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
8695  thd->security_ctx->priv_user, user,
8696  host, thd->security_ctx->priv_host));
8697  DBUG_RETURN(FALSE);
8698  }
8699 
8700  /* check for matching WITH PROXY rights */
8701  for (uint i=0; i < acl_proxy_users.elements; i++)
8702  {
8703  ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
8704  ACL_PROXY_USER *);
8705  if (proxy->matches(thd->security_ctx->get_host()->ptr(),
8706  thd->security_ctx->user,
8707  thd->security_ctx->get_ip()->ptr(),
8708  user) &&
8709  proxy->get_with_grant())
8710  {
8711  DBUG_PRINT("info", ("found"));
8712  DBUG_RETURN(FALSE);
8713  }
8714  }
8715 
8716  my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
8717  thd->security_ctx->user,
8718  thd->security_ctx->host_or_ip);
8719  DBUG_RETURN(TRUE);
8720 }
8721 
8722 
8723 static bool
8724 show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
8725 {
8726  Protocol *protocol= thd->protocol;
8727  int error= 0;
8728 
8729  for (uint i=0; i < acl_proxy_users.elements; i++)
8730  {
8731  ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
8732  ACL_PROXY_USER *);
8733  if (proxy->granted_on(user->host.str, user->user.str))
8734  {
8735  String global(buff, buffsize, system_charset_info);
8736  global.length(0);
8737  proxy->print_grant(&global);
8738  protocol->prepare_for_resend();
8739  protocol->store(global.ptr(), global.length(), global.charset());
8740  if (protocol->write())
8741  {
8742  error= -1;
8743  break;
8744  }
8745  }
8746  }
8747  return error;
8748 }
8749 
8750 
8751 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
8752 
8753 
8754 int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
8755 {
8756  reg3 int flag;
8757  DBUG_ENTER("wild_case_compare");
8758  DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr));
8759  while (*wildstr)
8760  {
8761  while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
8762  {
8763  if (*wildstr == wild_prefix && wildstr[1])
8764  wildstr++;
8765  if (my_toupper(cs, *wildstr++) !=
8766  my_toupper(cs, *str++)) DBUG_RETURN(1);
8767  }
8768  if (! *wildstr ) DBUG_RETURN (*str != 0);
8769  if (*wildstr++ == wild_one)
8770  {
8771  if (! *str++) DBUG_RETURN (1); /* One char; skip */
8772  }
8773  else
8774  { /* Found '*' */
8775  if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */
8776  flag=(*wildstr != wild_many && *wildstr != wild_one);
8777  do
8778  {
8779  if (flag)
8780  {
8781  char cmp;
8782  if ((cmp= *wildstr) == wild_prefix && wildstr[1])
8783  cmp=wildstr[1];
8784  cmp=my_toupper(cs, cmp);
8785  while (*str && my_toupper(cs, *str) != cmp)
8786  str++;
8787  if (!*str) DBUG_RETURN (1);
8788  }
8789  if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
8790  } while (*str++);
8791  DBUG_RETURN(1);
8792  }
8793  }
8794  DBUG_RETURN (*str != '\0');
8795 }
8796 
8797 
8798 #ifndef NO_EMBEDDED_ACCESS_CHECKS
8799 static bool update_schema_privilege(THD *thd, TABLE *table, char *buff,
8800  const char* db, const char* t_name,
8801  const char* column, uint col_length,
8802  const char *priv, uint priv_length,
8803  const char* is_grantable)
8804 {
8805  int i= 2;
8806  CHARSET_INFO *cs= system_charset_info;
8807  restore_record(table, s->default_values);
8808  table->field[0]->store(buff, (uint) strlen(buff), cs);
8809  table->field[1]->store(STRING_WITH_LEN("def"), cs);
8810  if (db)
8811  table->field[i++]->store(db, (uint) strlen(db), cs);
8812  if (t_name)
8813  table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
8814  if (column)
8815  table->field[i++]->store(column, col_length, cs);
8816  table->field[i++]->store(priv, priv_length, cs);
8817  table->field[i]->store(is_grantable, strlen(is_grantable), cs);
8818  return schema_table_store_record(thd, table);
8819 }
8820 #endif
8821 
8822 
8823 int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
8824 {
8825 #ifndef NO_EMBEDDED_ACCESS_CHECKS
8826  int error= 0;
8827  uint counter;
8828  ACL_USER *acl_user;
8829  ulong want_access;
8830  char buff[100];
8831  TABLE *table= tables->table;
8832  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
8833  NULL, NULL, 1, 1);
8834  char *curr_host= thd->security_ctx->priv_host_name();
8835  DBUG_ENTER("fill_schema_user_privileges");
8836 
8837  if (!initialized)
8838  DBUG_RETURN(0);
8839  mysql_mutex_lock(&acl_cache->lock);
8840 
8841  for (counter=0 ; counter < acl_users.elements ; counter++)
8842  {
8843  const char *user,*host, *is_grantable="YES";
8844  acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
8845  if (!(user=acl_user->user))
8846  user= "";
8847  if (!(host=acl_user->host.get_host()))
8848  host= "";
8849 
8850  if (no_global_access &&
8851  (strcmp(thd->security_ctx->priv_user, user) ||
8852  my_strcasecmp(system_charset_info, curr_host, host)))
8853  continue;
8854 
8855  want_access= acl_user->access;
8856  if (!(want_access & GRANT_ACL))
8857  is_grantable= "NO";
8858 
8859  strxmov(buff,"'",user,"'@'",host,"'",NullS);
8860  if (!(want_access & ~GRANT_ACL))
8861  {
8862  if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
8863  STRING_WITH_LEN("USAGE"), is_grantable))
8864  {
8865  error= 1;
8866  goto err;
8867  }
8868  }
8869  else
8870  {
8871  uint priv_id;
8872  ulong j,test_access= want_access & ~GRANT_ACL;
8873  for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
8874  {
8875  if (test_access & j)
8876  {
8877  if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
8878  command_array[priv_id],
8879  command_lengths[priv_id], is_grantable))
8880  {
8881  error= 1;
8882  goto err;
8883  }
8884  }
8885  }
8886  }
8887  }
8888 err:
8889  mysql_mutex_unlock(&acl_cache->lock);
8890 
8891  DBUG_RETURN(error);
8892 #else
8893  return(0);
8894 #endif
8895 }
8896 
8897 
8898 int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
8899 {
8900 #ifndef NO_EMBEDDED_ACCESS_CHECKS
8901  int error= 0;
8902  uint counter;
8903  ACL_DB *acl_db;
8904  ulong want_access;
8905  char buff[100];
8906  TABLE *table= tables->table;
8907  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
8908  NULL, NULL, 1, 1);
8909  char *curr_host= thd->security_ctx->priv_host_name();
8910  DBUG_ENTER("fill_schema_schema_privileges");
8911 
8912  if (!initialized)
8913  DBUG_RETURN(0);
8914  mysql_mutex_lock(&acl_cache->lock);
8915 
8916  for (counter=0 ; counter < acl_dbs.elements ; counter++)
8917  {
8918  const char *user, *host, *is_grantable="YES";
8919 
8920  acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
8921  if (!(user=acl_db->user))
8922  user= "";
8923  if (!(host=acl_db->host.get_host()))
8924  host= "";
8925 
8926  if (no_global_access &&
8927  (strcmp(thd->security_ctx->priv_user, user) ||
8928  my_strcasecmp(system_charset_info, curr_host, host)))
8929  continue;
8930 
8931  want_access=acl_db->access;
8932  if (want_access)
8933  {
8934  if (!(want_access & GRANT_ACL))
8935  {
8936  is_grantable= "NO";
8937  }
8938  strxmov(buff,"'",user,"'@'",host,"'",NullS);
8939  if (!(want_access & ~GRANT_ACL))
8940  {
8941  if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0,
8942  0, STRING_WITH_LEN("USAGE"), is_grantable))
8943  {
8944  error= 1;
8945  goto err;
8946  }
8947  }
8948  else
8949  {
8950  int cnt;
8951  ulong j,test_access= want_access & ~GRANT_ACL;
8952  for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
8953  if (test_access & j)
8954  {
8955  if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0, 0,
8956  command_array[cnt], command_lengths[cnt],
8957  is_grantable))
8958  {
8959  error= 1;
8960  goto err;
8961  }
8962  }
8963  }
8964  }
8965  }
8966 err:
8967  mysql_mutex_unlock(&acl_cache->lock);
8968 
8969  DBUG_RETURN(error);
8970 #else
8971  return (0);
8972 #endif
8973 }
8974 
8975 
8976 int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
8977 {
8978 #ifndef NO_EMBEDDED_ACCESS_CHECKS
8979  int error= 0;
8980  uint index;
8981  char buff[100];
8982  TABLE *table= tables->table;
8983  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
8984  NULL, NULL, 1, 1);
8985  char *curr_host= thd->security_ctx->priv_host_name();
8986  DBUG_ENTER("fill_schema_table_privileges");
8987 
8988  mysql_rwlock_rdlock(&LOCK_grant);
8989 
8990  for (index=0 ; index < column_priv_hash.records ; index++)
8991  {
8992  const char *user, *host, *is_grantable= "YES";
8993  GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
8994  index);
8995  if (!(user=grant_table->user))
8996  user= "";
8997  if (!(host= grant_table->host.get_host()))
8998  host= "";
8999 
9000  if (no_global_access &&
9001  (strcmp(thd->security_ctx->priv_user, user) ||
9002  my_strcasecmp(system_charset_info, curr_host, host)))
9003  continue;
9004 
9005  ulong table_access= grant_table->privs;
9006  if (table_access)
9007  {
9008  ulong test_access= table_access & ~GRANT_ACL;
9009  /*
9010  We should skip 'usage' privilege on table if
9011  we have any privileges on column(s) of this table
9012  */
9013  if (!test_access && grant_table->cols)
9014  continue;
9015  if (!(table_access & GRANT_ACL))
9016  is_grantable= "NO";
9017 
9018  strxmov(buff, "'", user, "'@'", host, "'", NullS);
9019  if (!test_access)
9020  {
9021  if (update_schema_privilege(thd, table, buff, grant_table->db,
9022  grant_table->tname, 0, 0,
9023  STRING_WITH_LEN("USAGE"), is_grantable))
9024  {
9025  error= 1;
9026  goto err;
9027  }
9028  }
9029  else
9030  {
9031  ulong j;
9032  int cnt;
9033  for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
9034  {
9035  if (test_access & j)
9036  {
9037  if (update_schema_privilege(thd, table, buff, grant_table->db,
9038  grant_table->tname, 0, 0,
9039  command_array[cnt],
9040  command_lengths[cnt], is_grantable))
9041  {
9042  error= 1;
9043  goto err;
9044  }
9045  }
9046  }
9047  }
9048  }
9049  }
9050 err:
9051  mysql_rwlock_unlock(&LOCK_grant);
9052 
9053  DBUG_RETURN(error);
9054 #else
9055  return (0);
9056 #endif
9057 }
9058 
9059 
9060 int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
9061 {
9062 #ifndef NO_EMBEDDED_ACCESS_CHECKS
9063  int error= 0;
9064  uint index;
9065  char buff[100];
9066  TABLE *table= tables->table;
9067  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
9068  NULL, NULL, 1, 1);
9069  char *curr_host= thd->security_ctx->priv_host_name();
9070  DBUG_ENTER("fill_schema_table_privileges");
9071 
9072  mysql_rwlock_rdlock(&LOCK_grant);
9073 
9074  for (index=0 ; index < column_priv_hash.records ; index++)
9075  {
9076  const char *user, *host, *is_grantable= "YES";
9077  GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
9078  index);
9079  if (!(user=grant_table->user))
9080  user= "";
9081  if (!(host= grant_table->host.get_host()))
9082  host= "";
9083 
9084  if (no_global_access &&
9085  (strcmp(thd->security_ctx->priv_user, user) ||
9086  my_strcasecmp(system_charset_info, curr_host, host)))
9087  continue;
9088 
9089  ulong table_access= grant_table->cols;
9090  if (table_access != 0)
9091  {
9092  if (!(grant_table->privs & GRANT_ACL))
9093  is_grantable= "NO";
9094 
9095  ulong test_access= table_access & ~GRANT_ACL;
9096  strxmov(buff, "'", user, "'@'", host, "'", NullS);
9097  if (!test_access)
9098  continue;
9099  else
9100  {
9101  ulong j;
9102  int cnt;
9103  for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
9104  {
9105  if (test_access & j)
9106  {
9107  for (uint col_index=0 ;
9108  col_index < grant_table->hash_columns.records ;
9109  col_index++)
9110  {
9111  GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
9112  my_hash_element(&grant_table->hash_columns,col_index);
9113  if ((grant_column->rights & j) && (table_access & j))
9114  {
9115  if (update_schema_privilege(thd, table, buff, grant_table->db,
9116  grant_table->tname,
9117  grant_column->column,
9118  grant_column->key_length,
9119  command_array[cnt],
9120  command_lengths[cnt], is_grantable))
9121  {
9122  error= 1;
9123  goto err;
9124  }
9125  }
9126  }
9127  }
9128  }
9129  }
9130  }
9131  }
9132 err:
9133  mysql_rwlock_unlock(&LOCK_grant);
9134 
9135  DBUG_RETURN(error);
9136 #else
9137  return (0);
9138 #endif
9139 }
9140 
9141 
9142 #ifndef NO_EMBEDDED_ACCESS_CHECKS
9143 /*
9144  fill effective privileges for table
9145 
9146  SYNOPSIS
9147  fill_effective_table_privileges()
9148  thd thread handler
9149  grant grants table descriptor
9150  db db name
9151  table table name
9152 */
9153 
9154 void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
9155  const char *db, const char *table)
9156 {
9157  Security_context *sctx= thd->security_ctx;
9158  DBUG_ENTER("fill_effective_table_privileges");
9159  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
9160  sctx->priv_host, (sctx->get_ip()->length() ?
9161  sctx->get_ip()->ptr() : "(NULL)"),
9162  (sctx->priv_user ? sctx->priv_user : "(NULL)"),
9163  db, table));
9164  /* --skip-grants */
9165  if (!initialized)
9166  {
9167  DBUG_PRINT("info", ("skip grants"));
9168  grant->privilege= ~NO_ACCESS; // everything is allowed
9169  DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
9170  DBUG_VOID_RETURN;
9171  }
9172 
9173  /* global privileges */
9174  grant->privilege= sctx->master_access;
9175 
9176  if (!sctx->priv_user)
9177  {
9178  DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
9179  DBUG_VOID_RETURN; // it is slave
9180  }
9181 
9182  /* db privileges */
9183  grant->privilege|= acl_get(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
9184  sctx->priv_user, db, 0);
9185 
9186  /* table privileges */
9187  mysql_rwlock_rdlock(&LOCK_grant);
9188  if (grant->version != grant_version)
9189  {
9190  grant->grant_table=
9191  table_hash_search(sctx->get_host()->ptr(), sctx->get_ip()->ptr(), db,
9192  sctx->priv_user,
9193  table, 0); /* purecov: inspected */
9194  grant->version= grant_version; /* purecov: inspected */
9195  }
9196  if (grant->grant_table != 0)
9197  {
9198  grant->privilege|= grant->grant_table->privs;
9199  }
9200  mysql_rwlock_unlock(&LOCK_grant);
9201 
9202  DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
9203  DBUG_VOID_RETURN;
9204 }
9205 
9206 #else /* NO_EMBEDDED_ACCESS_CHECKS */
9207 
9208 /****************************************************************************
9209  Dummy wrappers when we don't have any access checks
9210 ****************************************************************************/
9211 
9212 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
9213  bool is_proc)
9214 {
9215  return FALSE;
9216 }
9217 
9218 #endif
9219 
9221 {
9222  const LEX_STRING *m_name;
9223  const ACL_internal_schema_access *m_access;
9224 };
9225 
9234 static ACL_internal_schema_registry_entry registry_array[2];
9235 static uint m_registry_array_size= 0;
9236 
9243  (const LEX_STRING *name, const ACL_internal_schema_access *access)
9244 {
9245  DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
9246 
9247  /* Not thread safe, and does not need to be. */
9248  registry_array[m_registry_array_size].m_name= name;
9249  registry_array[m_registry_array_size].m_access= access;
9250  m_registry_array_size++;
9251 }
9252 
9260 {
9261  DBUG_ASSERT(name != NULL);
9262 
9263  uint i;
9264 
9265  for (i= 0; i<m_registry_array_size; i++)
9266  {
9267  if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
9268  name) == 0)
9269  return registry_array[i].m_access;
9270  }
9271  return NULL;
9272 }
9273 
9280 get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
9281  const char *schema_name)
9282 {
9283  if (grant_internal_info)
9284  {
9285  if (! grant_internal_info->m_schema_lookup_done)
9286  {
9287  grant_internal_info->m_schema_access=
9289  grant_internal_info->m_schema_lookup_done= TRUE;
9290  }
9291  return grant_internal_info->m_schema_access;
9292  }
9293  return ACL_internal_schema_registry::lookup(schema_name);
9294 }
9295 
9303 get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
9304  const char *schema_name,
9305  const char *table_name)
9306 {
9307  DBUG_ASSERT(grant_internal_info);
9308  if (! grant_internal_info->m_table_lookup_done)
9309  {
9310  const ACL_internal_schema_access *schema_access;
9311  schema_access= get_cached_schema_access(grant_internal_info, schema_name);
9312  if (schema_access)
9313  grant_internal_info->m_table_access= schema_access->lookup(table_name);
9314  grant_internal_info->m_table_lookup_done= TRUE;
9315  }
9316  return grant_internal_info->m_table_access;
9317 }
9318 
9319 
9320 /****************************************************************************
9321  AUTHENTICATION CODE
9322  including initial connect handshake, invoking appropriate plugins,
9323  client-server plugin negotiation, COM_CHANGE_USER, and native
9324  MySQL authentication plugins.
9325 ****************************************************************************/
9326 
9327 /* few defines to have less ifdef's in the code below */
9328 #ifdef EMBEDDED_LIBRARY
9329 #undef HAVE_OPENSSL
9330 #ifdef NO_EMBEDDED_ACCESS_CHECKS
9331 #define initialized 0
9332 #endif
9333 #endif
9334 #ifndef HAVE_OPENSSL
9335 #define ssl_acceptor_fd 0
9336 #define sslaccept(A,B,C) 1
9337 #endif
9338 
9339 
9341 {
9342  THD *thd;
9343 public:
9344  Thd_charset_adapter(THD *thd_arg) : thd (thd_arg) {}
9345  bool init_client_charset(uint cs_number)
9346  {
9347  if (thd_init_client_charset(thd, cs_number))
9348  return true;
9349  thd->update_charset();
9350  return thd->is_error();
9351  }
9352 
9353  const CHARSET_INFO *charset() { return thd->charset(); }
9354 };
9355 
9356 
9362 {
9363  MYSQL_SERVER_AUTH_INFO auth_info;
9364  const ACL_USER *acl_user;
9367 
9368  struct {
9369  char *plugin, *pkt;
9370  uint pkt_len;
9373  struct {
9374  char *pkt;
9375  uint pkt_len;
9377  int packets_read, packets_written;
9378 
9379  enum { SUCCESS, FAILURE, RESTART } status;
9380 
9381  /* encapsulation members */
9382  ulong client_capabilities;
9383  char *scramble;
9384  MEM_ROOT *mem_root;
9385  struct rand_struct *rand;
9386  my_thread_id thread_id;
9387  uint *server_status;
9388  NET *net;
9389  ulong max_client_packet_length;
9390  char *ip;
9391  char *host;
9392  Thd_charset_adapter *charset_adapter;
9393  LEX_STRING acl_user_plugin;
9394  int vio_is_encrypted;
9395 };
9396 
9397 bool auth_plugin_is_built_in(const char *plugin_name)
9398 {
9399  return (plugin_name == native_password_plugin_name.str ||
9400 #if defined(HAVE_OPENSSL)
9401  plugin_name == sha256_password_plugin_name.str ||
9402 #endif
9403  plugin_name == old_password_plugin_name.str);
9404 }
9405 
9406 void optimize_plugin_compare_by_pointer(LEX_STRING *plugin_name)
9407 {
9408 #if defined(HAVE_OPENSSL)
9409  if (my_strcasecmp(system_charset_info, sha256_password_plugin_name.str,
9410  plugin_name->str) == 0)
9411  {
9412  plugin_name->str= sha256_password_plugin_name.str;
9413  plugin_name->length= sha256_password_plugin_name.length;
9414  }
9415  else
9416 #endif
9417  if (my_strcasecmp(system_charset_info, native_password_plugin_name.str,
9418  plugin_name->str) == 0)
9419  {
9420  plugin_name->str= native_password_plugin_name.str;
9421  plugin_name->length= native_password_plugin_name.length;
9422  }
9423  else
9424  if (my_strcasecmp(system_charset_info, old_password_plugin_name.str,
9425  plugin_name->str) == 0)
9426  {
9427  plugin_name->str= old_password_plugin_name.str;
9428  plugin_name->length= old_password_plugin_name.length;
9429  }
9430 }
9431 
9435 void init_default_auth_plugin()
9436 {
9437  default_auth_plugin_name.str= native_password_plugin_name.str;
9438  default_auth_plugin_name.length= native_password_plugin_name.length;
9439 }
9440 
9441 
9453 int set_default_auth_plugin(char *plugin_name, int plugin_name_length)
9454 {
9455  default_auth_plugin_name.str= plugin_name;
9456  default_auth_plugin_name.length= plugin_name_length;
9457 
9458  optimize_plugin_compare_by_pointer(&default_auth_plugin_name);
9459 
9460 #if defined(HAVE_OPENSSL)
9461  if (default_auth_plugin_name.str == sha256_password_plugin_name.str)
9462  {
9463  /*
9464  Adjust default password algorithm to fit the default authentication
9465  method.
9466  */
9467  global_system_variables.old_passwords= 2;
9468  }
9469  else
9470 #endif
9471  if (default_auth_plugin_name.str != native_password_plugin_name.str)
9472  return 1;
9473 
9474  return 0;
9475 }
9476 
9480 static void login_failed_error(MPVIO_EXT *mpvio, int passwd_used)
9481 {
9482  THD *thd= current_thd;
9483  if (passwd_used == 2)
9484  {
9485  my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
9486  mpvio->auth_info.user_name,
9487  mpvio->auth_info.host_or_ip);
9488  general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
9489  mpvio->auth_info.user_name,
9490  mpvio->auth_info.host_or_ip);
9491  /*
9492  Log access denied messages to the error log when log-warnings = 2
9493  so that the overhead of the general query log is not required to track
9494  failed connections.
9495  */
9496  if (log_warnings > 1)
9497  {
9498  sql_print_warning(ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
9499  mpvio->auth_info.user_name,
9500  mpvio->auth_info.host_or_ip);
9501  }
9502  }
9503  else
9504  {
9505  my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
9506  mpvio->auth_info.user_name,
9507  mpvio->auth_info.host_or_ip,
9508  passwd_used ? ER(ER_YES) : ER(ER_NO));
9509  general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
9510  mpvio->auth_info.user_name,
9511  mpvio->auth_info.host_or_ip,
9512  passwd_used ? ER(ER_YES) : ER(ER_NO));
9513  /*
9514  Log access denied messages to the error log when log-warnings = 2
9515  so that the overhead of the general query log is not required to track
9516  failed connections.
9517  */
9518  if (log_warnings > 1)
9519  {
9520  sql_print_warning(ER(ER_ACCESS_DENIED_ERROR),
9521  mpvio->auth_info.user_name,
9522  mpvio->auth_info.host_or_ip,
9523  passwd_used ? ER(ER_YES) : ER(ER_NO));
9524  }
9525  }
9526 }
9527 
9553 static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
9554  const char *data, uint data_len)
9555 {
9556  DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
9557  DBUG_ASSERT(data_len <= 255);
9558 
9559  char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + data_len + 64);
9560  char scramble_buf[SCRAMBLE_LENGTH];
9561  char *end= buff;
9562 
9563  DBUG_ENTER("send_server_handshake_packet");
9564  *end++= protocol_version;
9565 
9566  mpvio->client_capabilities= CLIENT_BASIC_FLAGS;
9567 
9568  if (opt_using_transactions)
9569  mpvio->client_capabilities|= CLIENT_TRANSACTIONS;
9570 
9571  mpvio->client_capabilities|= CAN_CLIENT_COMPRESS;
9572 
9573  if (ssl_acceptor_fd)
9574  {
9575  mpvio->client_capabilities|= CLIENT_SSL;
9576  mpvio->client_capabilities|= CLIENT_SSL_VERIFY_SERVER_CERT;
9577  }
9578 
9579  if (data_len)
9580  {
9581  mpvio->cached_server_packet.pkt= (char*) memdup_root(mpvio->mem_root,
9582  data, data_len);
9583  mpvio->cached_server_packet.pkt_len= data_len;
9584  }
9585 
9586  if (data_len < SCRAMBLE_LENGTH)
9587  {
9588  if (data_len)
9589  {
9590  /*
9591  the first packet *must* have at least 20 bytes of a scramble.
9592  if a plugin provided less, we pad it to 20 with zeros
9593  */
9594  memcpy(scramble_buf, data, data_len);
9595  memset(scramble_buf + data_len, 0, SCRAMBLE_LENGTH - data_len);
9596  data= scramble_buf;
9597  }
9598  else
9599  {
9600  /*
9601  if the default plugin does not provide the data for the scramble at
9602  all, we generate a scramble internally anyway, just in case the
9603  user account (that will be known only later) uses a
9604  native_password_plugin (which needs a scramble). If we don't send a
9605  scramble now - wasting 20 bytes in the packet -
9606  native_password_plugin will have to send it in a separate packet,
9607  adding one more round trip.
9608  */
9609  create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
9610  data= mpvio->scramble;
9611  }
9612  data_len= SCRAMBLE_LENGTH;
9613  }
9614 
9615  end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
9616  int4store((uchar*) end, mpvio->thread_id);
9617  end+= 4;
9618 
9619  /*
9620  Old clients does not understand long scrambles, but can ignore packet
9621  tail: that's why first part of the scramble is placed here, and second
9622  part at the end of packet.
9623  */
9624  end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
9625  end+= SCRAMBLE_LENGTH_323;
9626  *end++= 0;
9627 
9628  int2store(end, mpvio->client_capabilities);
9629  /* write server characteristics: up to 16 bytes allowed */
9630  end[2]= (char) default_charset_info->number;
9631  int2store(end + 3, mpvio->server_status[0]);
9632  int2store(end + 5, mpvio->client_capabilities >> 16);
9633  end[7]= data_len;
9634  DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
9635  memset(end + 8, 0, 10);
9636  end+= 18;
9637  /* write scramble tail */
9638  end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
9639  data_len - SCRAMBLE_LENGTH_323);
9640  end+= data_len - SCRAMBLE_LENGTH_323;
9641  end= strmake(end, plugin_name(mpvio->plugin)->str,
9642  plugin_name(mpvio->plugin)->length);
9643 
9644  int res= my_net_write(mpvio->net, (uchar*) buff, (size_t) (end - buff + 1)) ||
9645  net_flush(mpvio->net);
9646  my_afree(buff);
9647  DBUG_RETURN (res);
9648 }
9649 
9650 static bool secure_auth(MPVIO_EXT *mpvio)
9651 {
9652  THD *thd;
9653  if (!opt_secure_auth)
9654  return 0;
9655  /*
9656  If the server is running in secure auth mode, short scrambles are
9657  forbidden. Extra juggling to report the same error as the old code.
9658  */
9659 
9660  thd= current_thd;
9661  if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
9662  {
9663  my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
9664  mpvio->auth_info.user_name,
9665  mpvio->auth_info.host_or_ip);
9666  general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
9667  mpvio->auth_info.user_name,
9668  mpvio->auth_info.host_or_ip);
9669  }
9670  else
9671  {
9672  my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
9673  general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
9674  }
9675  return 1;
9676 }
9677 
9699 static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
9700  const uchar *data, uint data_len)
9701 {
9702  DBUG_ASSERT(mpvio->packets_written == 1);
9703  DBUG_ASSERT(mpvio->packets_read == 1);
9704  NET *net= mpvio->net;
9705  static uchar switch_plugin_request_buf[]= { 254 };
9706 
9707  DBUG_ENTER("send_plugin_request_packet");
9708  mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
9709 
9710  const char *client_auth_plugin=
9711  ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
9712 
9713  DBUG_ASSERT(client_auth_plugin);
9714 
9715  /*
9716  we send an old "short 4.0 scramble request", if we need to request a
9717  client to use 4.0 auth plugin (short scramble) and the scramble was
9718  already sent to the client
9719 
9720  below, cached_client_reply.plugin is the plugin name that client has used,
9721  client_auth_plugin is derived from mysql.user table, for the given
9722  user account, it's the plugin that the client need to use to login.
9723  */
9724  bool switch_from_long_to_short_scramble=
9725  native_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
9726  client_auth_plugin == old_password_plugin_name.str;
9727 
9728  if (switch_from_long_to_short_scramble)
9729  DBUG_RETURN (secure_auth(mpvio) ||
9730  my_net_write(net, switch_plugin_request_buf, 1) ||
9731  net_flush(net));
9732 
9733  /*
9734  We never request a client to switch from a short to long scramble.
9735  Plugin-aware clients can do that, but traditionally it meant to
9736  ask an old 4.0 client to use the new 4.1 authentication protocol.
9737  */
9738  bool switch_from_short_to_long_scramble=
9739  old_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
9740  client_auth_plugin == native_password_plugin_name.str;
9741 
9742  if (switch_from_short_to_long_scramble)
9743  {
9744  my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
9745  general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
9746  DBUG_RETURN (1);
9747  }
9748 
9749  /*
9750  If we're dealing with an older client we can't just send a change plugin
9751  packet to re-initiate the authentication handshake, because the client
9752  won't understand it. The good thing is that we don't need to : the old client
9753  expects us to just check the user credentials here, which we can do by just reading
9754  the cached data that are placed there by parse_com_change_user_packet()
9755  In this case we just do nothing and behave as if normal authentication
9756  should continue.
9757  */
9758  if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
9759  {
9760  DBUG_PRINT("info", ("old client sent a COM_CHANGE_USER"));
9761  DBUG_ASSERT(mpvio->cached_client_reply.pkt);
9762  /* get the status back so the read can process the cached result */
9763  mpvio->status= MPVIO_EXT::RESTART;
9764  DBUG_RETURN(0);
9765  }
9766 
9767  DBUG_PRINT("info", ("requesting client to use the %s plugin",
9768  client_auth_plugin));
9769  DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
9770  (uchar*) client_auth_plugin,
9771  strlen(client_auth_plugin) + 1,
9772  (uchar*) data, data_len));
9773 }
9774 
9775 #ifndef NO_EMBEDDED_ACCESS_CHECKS
9776 
9788 static bool find_mpvio_user(MPVIO_EXT *mpvio)
9789 {
9790  DBUG_ENTER("find_mpvio_user");
9791  DBUG_PRINT("info", ("entry: %s", mpvio->auth_info.user_name));
9792  DBUG_ASSERT(mpvio->acl_user == 0);
9793  mysql_mutex_lock(&acl_cache->lock);
9794  for (uint i=0; i < acl_users.elements; i++)
9795  {
9796  ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
9797  if ((!acl_user_tmp->user ||
9798  !strcmp(mpvio->auth_info.user_name, acl_user_tmp->user)) &&
9799  acl_user_tmp->host.compare_hostname(mpvio->host, mpvio->ip))
9800  {
9801  mpvio->acl_user= acl_user_tmp->copy(mpvio->mem_root);
9802 
9803  /*
9804  When setting mpvio->acl_user_plugin we can save memory allocation if
9805  this is a built in plugin.
9806  */
9807  if (auth_plugin_is_built_in(acl_user_tmp->plugin.str))
9808  mpvio->acl_user_plugin= mpvio->acl_user->plugin;
9809  else
9810  make_lex_string_root(mpvio->mem_root,
9811  &mpvio->acl_user_plugin,
9812  acl_user_tmp->plugin.str,
9813  acl_user_tmp->plugin.length, 0);
9814  break;
9815  }
9816  }
9817  mysql_mutex_unlock(&acl_cache->lock);
9818 
9819  if (!mpvio->acl_user)
9820  {
9821  login_failed_error(mpvio, mpvio->auth_info.password_used);
9822  DBUG_RETURN (1);
9823  }
9824 
9825  if (my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
9826  native_password_plugin_name.str) != 0 &&
9827  my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
9828  old_password_plugin_name.str) != 0 &&
9829  !(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
9830  {
9831  /* user account requires non-default plugin and the client is too old */
9832  DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
9833  native_password_plugin_name.str));
9834  DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
9835  old_password_plugin_name.str));
9836  my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
9837  general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
9838  DBUG_RETURN (1);
9839  }
9840 
9841  mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
9842  mpvio->auth_info.auth_string_length=
9843  (unsigned long) mpvio->acl_user->auth_string.length;
9844  strmake(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
9845  mpvio->acl_user->user : "", USERNAME_LENGTH);
9846  DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
9847  ", plugin=%s",
9848  mpvio->auth_info.user_name,
9849  mpvio->auth_info.auth_string,
9850  mpvio->auth_info.authenticated_as,
9851  mpvio->acl_user->plugin.str));
9852  DBUG_RETURN(0);
9853 }
9854 
9855 
9856 static bool
9857 read_client_connect_attrs(char **ptr, size_t *max_bytes_available,
9858  const CHARSET_INFO *from_cs)
9859 {
9860  size_t length, length_length;
9861  char *ptr_save;
9862  /* not enough bytes to hold the length */
9863  if (*max_bytes_available < 1)
9864  return true;
9865 
9866  /* read the length */
9867  ptr_save= *ptr;
9868  length= net_field_length_ll((uchar **) ptr);
9869  length_length= *ptr - ptr_save;
9870  if (*max_bytes_available < length_length)
9871  return true;
9872 
9873  *max_bytes_available-= length_length;
9874 
9875  /* length says there're more data than can fit into the packet */
9876  if (length > *max_bytes_available)
9877  return true;
9878 
9879  /* impose an artificial length limit of 64k */
9880  if (length > 65535)
9881  return true;
9882 
9883 #ifdef HAVE_PSI_THREAD_INTERFACE
9884  if (PSI_THREAD_CALL(set_thread_connect_attrs)(*ptr, length, from_cs) && log_warnings)
9885  sql_print_warning("Connection attributes of length %lu were truncated",
9886  (unsigned long) length);
9887 #endif
9888  return false;
9889 }
9890 
9891 
9892 #endif
9893 
9894 /* the packet format is described in send_change_user_packet() */
9895 static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
9896 {
9897  NET *net= mpvio->net;
9898 
9899  char *user= (char*) net->read_pos;
9900  char *end= user + packet_length;
9901  /* Safe because there is always a trailing \0 at the end of the packet */
9902  char *passwd= strend(user) + 1;
9903  uint user_len= passwd - user - 1;
9904  char *db= passwd;
9905  char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
9906  char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
9907  uint dummy_errors;
9908 
9909  DBUG_ENTER ("parse_com_change_user_packet");
9910  if (passwd >= end)
9911  {
9912  my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
9913  DBUG_RETURN (1);
9914  }
9915 
9916  /*
9917  Old clients send null-terminated string as password; new clients send
9918  the size (1 byte) + string (not null-terminated). Hence in case of empty
9919  password both send '\0'.
9920 
9921  This strlen() can't be easily deleted without changing protocol.
9922 
9923  Cast *passwd to an unsigned char, so that it doesn't extend the sign for
9924  *passwd > 127 and become 2**32-127+ after casting to uint.
9925  */
9926  uint passwd_len= (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION ?
9927  (uchar) (*passwd++) : strlen(passwd));
9928 
9929  db+= passwd_len + 1;
9930  /*
9931  Database name is always NUL-terminated, so in case of empty database
9932  the packet must contain at least the trailing '\0'.
9933  */
9934  if (db >= end)
9935  {
9936  my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
9937  DBUG_RETURN (1);
9938  }
9939 
9940  uint db_len= strlen(db);
9941 
9942  char *ptr= db + db_len + 1;
9943 
9944  if (ptr + 1 < end)
9945  {
9946  if (mpvio->charset_adapter->init_client_charset(uint2korr(ptr)))
9947  DBUG_RETURN(1);
9948  }
9949  else
9950  {
9951  sql_print_warning("Client failed to provide its character set. "
9952  "'%s' will be used as client character set.",
9953  mpvio->charset_adapter->charset()->csname);
9954  }
9955 
9956 
9957  /* Convert database and user names to utf8 */
9958  db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
9959  db, db_len, mpvio->charset_adapter->charset(),
9960  &dummy_errors);
9961  db_buff[db_len]= 0;
9962 
9963  user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
9964  system_charset_info, user, user_len,
9965  mpvio->charset_adapter->charset(),
9966  &dummy_errors);
9967  user_buff[user_len]= 0;
9968 
9969  /* we should not free mpvio->user here: it's saved by dispatch_command() */
9970  if (!(mpvio->auth_info.user_name= my_strndup(user_buff, user_len, MYF(MY_WME))))
9971  return 1;
9972  mpvio->auth_info.user_name_length= user_len;
9973 
9974  if (make_lex_string_root(mpvio->mem_root,
9975  &mpvio->db, db_buff, db_len, 0) == 0)
9976  DBUG_RETURN(1); /* The error is set by make_lex_string(). */
9977 
9978  if (!initialized)
9979  {
9980  // if mysqld's been started with --skip-grant-tables option
9981  strmake(mpvio->auth_info.authenticated_as,
9982  mpvio->auth_info.user_name, USERNAME_LENGTH);
9983 
9984  mpvio->status= MPVIO_EXT::SUCCESS;
9985  DBUG_RETURN(0);
9986  }
9987 
9988 #ifndef NO_EMBEDDED_ACCESS_CHECKS
9989  if (find_mpvio_user(mpvio))
9990  DBUG_RETURN(1);
9991 
9992  char *client_plugin;
9993  if (mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)
9994  {
9995  client_plugin= ptr + 2;
9996  if (client_plugin >= end)
9997  {
9998  my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
9999  DBUG_RETURN(1);
10000  }
10001  }
10002  else
10003  {
10004  if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
10005  client_plugin= native_password_plugin_name.str;
10006  else
10007  {
10008  client_plugin= old_password_plugin_name.str;
10009  /*
10010  For a passwordless accounts we use native_password_plugin.
10011  But when an old 4.0 client connects to it, we change it to
10012  old_password_plugin, otherwise MySQL will think that server
10013  and client plugins don't match.
10014  */
10015  if (mpvio->acl_user->salt_len == 0)
10016  mpvio->acl_user_plugin= old_password_plugin_name;
10017  }
10018  }
10019 
10020  size_t bytes_remaining_in_packet= end - ptr;
10021 
10022  if ((mpvio->client_capabilities & CLIENT_CONNECT_ATTRS) &&
10023  read_client_connect_attrs(&ptr, &bytes_remaining_in_packet,
10024  mpvio->charset_adapter->charset()))
10025  return packet_error;
10026 
10027  DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
10028  /*
10029  Remember the data part of the packet, to present it to plugin in
10030  read_packet()
10031  */
10032  mpvio->cached_client_reply.pkt= passwd;
10033  mpvio->cached_client_reply.pkt_len= passwd_len;
10034  mpvio->cached_client_reply.plugin= client_plugin;
10035  mpvio->status= MPVIO_EXT::RESTART;
10036 #endif
10037 
10038  DBUG_RETURN (0);
10039 }
10040 
10041 #ifndef EMBEDDED_LIBRARY
10042 
10043 typedef char * (*get_proto_string_func_t) (char **, size_t *, size_t *);
10044 
10065 static
10066 char *get_41_protocol_string(char **buffer,
10067  size_t *max_bytes_available,
10068  size_t *string_length)
10069 {
10070  char *str= (char *)memchr(*buffer, '\0', *max_bytes_available);
10071 
10072  if (str == NULL)
10073  return NULL;
10074 
10075  *string_length= (size_t)(str - *buffer);
10076  *max_bytes_available-= *string_length + 1;
10077  str= *buffer;
10078  *buffer += *string_length + 1;
10079 
10080  return str;
10081 }
10082 
10083 
10101 static
10102 char *get_40_protocol_string(char **buffer,
10103  size_t *max_bytes_available,
10104  size_t *string_length)
10105 {
10106  char *str;
10107  size_t len;
10108 
10109  /* No bytes to scan left, treat string as empty. */
10110  if ((*max_bytes_available) == 0)
10111  {
10112  *string_length= 0;
10113  return empty_c_string;
10114  }
10115 
10116  str= (char *) memchr(*buffer, '\0', *max_bytes_available);
10117 
10118  /*
10119  If the string was not null terminated by the client,
10120  the remainder of the packet is the string. Otherwise,
10121  advance the buffer past the end of the null terminated
10122  string.
10123  */
10124  if (str == NULL)
10125  len= *string_length= *max_bytes_available;
10126  else
10127  len= (*string_length= (size_t)(str - *buffer)) + 1;
10128 
10129  str= *buffer;
10130  *buffer+= len;
10131  *max_bytes_available-= len;
10132 
10133  return str;
10134 }
10135 
10150 static
10151 char *get_56_lenc_string(char **buffer,
10152  size_t *max_bytes_available,
10153  size_t *string_length)
10154 {
10155  static char empty_string[1]= { '\0' };
10156  char *begin= *buffer;
10157 
10158  if (*max_bytes_available == 0)
10159  return NULL;
10160 
10161  /*
10162  If the length encoded string has the length 0
10163  the total size of the string is only one byte long (the size byte)
10164  */
10165  if (*begin == 0)
10166  {
10167  *string_length= 0;
10168  --*max_bytes_available;
10169  ++*buffer;
10170  /*
10171  Return a pointer to the \0 character so the return value will be
10172  an empty string.
10173  */
10174  return empty_string;
10175  }
10176 
10177  *string_length= (size_t)net_field_length_ll((uchar **)buffer);
10178 
10179  size_t len_len= (size_t)(*buffer - begin);
10180 
10181  if (*string_length + len_len > *max_bytes_available)
10182  return NULL;
10183 
10184  *max_bytes_available -= *string_length + len_len;
10185  *buffer += *string_length;
10186  return (char *)(begin + len_len);
10187 }
10188 
10189 
10206 static
10207 char *get_41_lenc_string(char **buffer,
10208  size_t *max_bytes_available,
10209  size_t *string_length)
10210 {
10211  if (*max_bytes_available == 0)
10212  return NULL;
10213 
10214  /* Do double cast to prevent overflow from signed / unsigned conversion */
10215  size_t str_len= (size_t)(unsigned char)**buffer;
10216 
10217  /*
10218  If the length encoded string has the length 0
10219  the total size of the string is only one byte long (the size byte)
10220  */
10221  if (str_len == 0)
10222  {
10223  ++*buffer;
10224  *string_length= 0;
10225  /*
10226  Return a pointer to the 0 character so the return value will be
10227  an empty string.
10228  */
10229  return *buffer-1;
10230  }
10231 
10232  if (str_len >= *max_bytes_available)
10233  return NULL;
10234 
10235  char *str= *buffer+1;
10236  *string_length= str_len;
10237  *max_bytes_available-= *string_length + 1;
10238  *buffer+= *string_length + 1;
10239  return str;
10240 }
10241 #endif // EMBEDDED LIBRARY
10242 
10243 
10244 /* the packet format is described in send_client_reply_packet() */
10245 static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
10246  uchar **buff, ulong pkt_len)
10247 {
10248 #ifndef EMBEDDED_LIBRARY
10249  NET *net= mpvio->net;
10250  char *end;
10251  bool packet_has_required_size= false;
10252  DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
10253 
10254  uint charset_code= 0;
10255  end= (char *)net->read_pos;
10256  /*
10257  In order to safely scan a head for '\0' string terminators
10258  we must keep track of how many bytes remain in the allocated
10259  buffer or we might read past the end of the buffer.
10260  */
10261  size_t bytes_remaining_in_packet= pkt_len;
10262 
10263  /*
10264  Peek ahead on the client capability packet and determine which version of
10265  the protocol should be used.
10266  */
10267  if (bytes_remaining_in_packet < 2)
10268  return packet_error;
10269 
10270  mpvio->client_capabilities= uint2korr(end);
10271 
10272  /*
10273  JConnector only sends server capabilities before starting SSL
10274  negotiation. The below code is patch for this.
10275  */
10276  if (bytes_remaining_in_packet == 4 &&
10277  mpvio->client_capabilities & CLIENT_SSL)
10278  {
10279  mpvio->client_capabilities= uint4korr(end);
10280  mpvio->max_client_packet_length= 0xfffff;
10281  charset_code= global_system_variables.character_set_client->number;
10282  sql_print_warning("Client failed to provide its character set. "
10283  "'%s' will be used as client character set.",
10284  global_system_variables.character_set_client->csname);
10285  if (mpvio->charset_adapter->init_client_charset(charset_code))
10286  return packet_error;
10287  goto skip_to_ssl;
10288  }
10289 
10290  if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
10291  packet_has_required_size= bytes_remaining_in_packet >=
10292  AUTH_PACKET_HEADER_SIZE_PROTO_41;
10293  else
10294  packet_has_required_size= bytes_remaining_in_packet >=
10295  AUTH_PACKET_HEADER_SIZE_PROTO_40;
10296 
10297  if (!packet_has_required_size)
10298  return packet_error;
10299 
10300  if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
10301  {
10302  mpvio->client_capabilities= uint4korr(end);
10303  mpvio->max_client_packet_length= uint4korr(end + 4);
10304  charset_code= (uint)(uchar)*(end + 8);
10305  /*
10306  Skip 23 remaining filler bytes which have no particular meaning.
10307  */
10308  end+= AUTH_PACKET_HEADER_SIZE_PROTO_41;
10309  bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_41;
10310  }
10311  else
10312  {
10313  mpvio->client_capabilities= uint2korr(end);
10314  mpvio->max_client_packet_length= uint3korr(end + 2);
10315  end+= AUTH_PACKET_HEADER_SIZE_PROTO_40;
10316  bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_40;
10321  charset_code= global_system_variables.character_set_client->number;
10322  sql_print_warning("Client failed to provide its character set. "
10323  "'%s' will be used as client character set.",
10324  global_system_variables.character_set_client->csname);
10325  }
10326 
10327  DBUG_PRINT("info", ("client_character_set: %u", charset_code));
10328  if (mpvio->charset_adapter->init_client_charset(charset_code))
10329  return packet_error;
10330 
10331 skip_to_ssl:
10332 #if defined(HAVE_OPENSSL)
10333  DBUG_PRINT("info", ("client capabilities: %lu", mpvio->client_capabilities));
10334 
10335  /*
10336  If client requested SSL then we must stop parsing, try to switch to SSL,
10337  and wait for the client to send a new handshake packet.
10338  The client isn't expected to send any more bytes until SSL is initialized.
10339  */
10340  if (mpvio->client_capabilities & CLIENT_SSL)
10341  {
10342  unsigned long errptr;
10343 
10344  /* Do the SSL layering. */
10345  if (!ssl_acceptor_fd)
10346  return packet_error;
10347 
10348  DBUG_PRINT("info", ("IO layer change in progress..."));
10349  if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr))
10350  {
10351  DBUG_PRINT("error", ("Failed to accept new SSL connection"));
10352  return packet_error;
10353  }
10354 
10355  DBUG_PRINT("info", ("Reading user information over SSL layer"));
10356  if ((pkt_len= my_net_read(net)) == packet_error)
10357  {
10358  DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
10359  pkt_len));
10360  return packet_error;
10361  }
10362  /* mark vio as encrypted */
10363  mpvio->vio_is_encrypted= 1;
10364 
10365  /*
10366  A new packet was read and the statistics reflecting the remaining bytes
10367  in the packet must be updated.
10368  */
10369  bytes_remaining_in_packet= pkt_len;
10370 
10371  /*
10372  After the SSL handshake is performed the client resends the handshake
10373  packet but because of legacy reasons we chose not to parse the packet
10374  fields a second time and instead only assert the length of the packet.
10375  */
10376  if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
10377  {
10378  packet_has_required_size= bytes_remaining_in_packet >=
10379  AUTH_PACKET_HEADER_SIZE_PROTO_41;
10380  end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_41;
10381  bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_41;
10382  }
10383  else
10384  {
10385  packet_has_required_size= bytes_remaining_in_packet >=
10386  AUTH_PACKET_HEADER_SIZE_PROTO_40;
10387  end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_40;
10388  bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_40;
10389  }
10390 
10391  if (!packet_has_required_size)
10392  return packet_error;
10393  }
10394 #endif /* HAVE_OPENSSL */
10395 
10396  if ((mpvio->client_capabilities & CLIENT_TRANSACTIONS) &&
10397  opt_using_transactions)
10398  net->return_status= mpvio->server_status;
10399 
10400  /*
10401  The 4.0 and 4.1 versions of the protocol differ on how strings
10402  are terminated. In the 4.0 version, if a string is at the end
10403  of the packet, the string is not null terminated. Do not assume
10404  that the returned string is always null terminated.
10405  */
10406  get_proto_string_func_t get_string;
10407 
10408  if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
10409  get_string= get_41_protocol_string;
10410  else
10411  get_string= get_40_protocol_string;
10412 
10413  /*
10414  When the ability to change default plugin require that the initial password
10415  field can be of arbitrary size. However, the 41 client-server protocol limits
10416  the length of the auth-data-field sent from client to server to 255 bytes
10417  (CLIENT_SECURE_CONNECTION). The solution is to change the type of the field
10418  to a true length encoded string and indicate the protocol change with a new
10419  client capability flag: CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA.
10420  */
10421  get_proto_string_func_t get_length_encoded_string;
10422 
10423  if (mpvio->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA)
10424  get_length_encoded_string= get_56_lenc_string;
10425  else
10426  get_length_encoded_string= get_41_lenc_string;
10427 
10428  /*
10429  The CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA capability depends on the
10430  CLIENT_SECURE_CONNECTION. Refuse any connection which have the first but
10431  not the latter.
10432  */
10433  if ((mpvio->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) &&
10434  !(mpvio->client_capabilities & CLIENT_SECURE_CONNECTION))
10435  return packet_error;
10436 
10437  /*
10438  In order to safely scan a head for '\0' string terminators
10439  we must keep track of how many bytes remain in the allocated
10440  buffer or we might read past the end of the buffer.
10441  */
10442  bytes_remaining_in_packet= pkt_len - (end - (char *)net->read_pos);
10443 
10444  size_t user_len;
10445  char *user= get_string(&end, &bytes_remaining_in_packet, &user_len);
10446  if (user == NULL)
10447  return packet_error;
10448 
10449  /*
10450  Old clients send a null-terminated string as password; new clients send
10451  the size (1 byte) + string (not null-terminated). Hence in case of empty
10452  password both send '\0'.
10453  */
10454  size_t passwd_len= 0;
10455  char *passwd= NULL;
10456 
10457  if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
10458  {
10459  /*
10460  Get the password field.
10461  */
10462  passwd= get_length_encoded_string(&end, &bytes_remaining_in_packet,
10463  &passwd_len);
10464  }
10465  else
10466  {
10467  /*
10468  Old passwords are zero terminated strings.
10469  */
10470  passwd= get_string(&end, &bytes_remaining_in_packet, &passwd_len);
10471  }
10472 
10473  if (passwd == NULL)
10474  return packet_error;
10475 
10476  size_t db_len= 0;
10477  char *db= NULL;
10478 
10479  if (mpvio->client_capabilities & CLIENT_CONNECT_WITH_DB)
10480  {
10481  db= get_string(&end, &bytes_remaining_in_packet, &db_len);
10482  if (db == NULL)
10483  return packet_error;
10484  }
10485 
10486  /*
10487  Set the default for the password supplied flag for non-existing users
10488  as the default plugin (native passsword authentication) would do it
10489  for compatibility reasons.
10490  */
10491  if (passwd_len)
10492  mpvio->auth_info.password_used= PASSWORD_USED_YES;
10493 
10494  size_t client_plugin_len= 0;
10495  char *client_plugin= get_string(&end, &bytes_remaining_in_packet,
10496  &client_plugin_len);
10497  if (client_plugin == NULL)
10498  client_plugin= &empty_c_string[0];
10499 
10500  if ((mpvio->client_capabilities & CLIENT_CONNECT_ATTRS) &&
10501  read_client_connect_attrs(&end, &bytes_remaining_in_packet,
10502  mpvio->charset_adapter->charset()))
10503  return packet_error;
10504 
10505  char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
10506  char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
10507  uint dummy_errors;
10508 
10509 
10510  /*
10511  Copy and convert the user and database names to the character set used
10512  by the server. Since 4.1 all database names are stored in UTF-8. Also,
10513  ensure that the names are properly null-terminated as this is relied
10514  upon later.
10515  */
10516  if (db)
10517  {
10518  db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
10519  db, db_len, mpvio->charset_adapter->charset(),
10520  &dummy_errors);
10521  db_buff[db_len]= '\0';
10522  db= db_buff;
10523  }
10524 
10525  user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
10526  system_charset_info, user, user_len,
10527  mpvio->charset_adapter->charset(),
10528  &dummy_errors);
10529  user_buff[user_len]= '\0';
10530  user= user_buff;
10531 
10532  /* If username starts and ends in "'", chop them off */
10533  if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
10534  {
10535  user[user_len - 1]= 0;
10536  user++;
10537  user_len-= 2;
10538  }
10539 
10540  if (make_lex_string_root(mpvio->mem_root,
10541  &mpvio->db, db, db_len, 0) == 0)
10542  return packet_error; /* The error is set by make_lex_string(). */
10543  if (mpvio->auth_info.user_name)
10544  my_free(mpvio->auth_info.user_name);
10545  if (!(mpvio->auth_info.user_name= my_strndup(user, user_len, MYF(MY_WME))))
10546  return packet_error; /* The error is set by my_strdup(). */
10547  mpvio->auth_info.user_name_length= user_len;
10548 
10549  if (!initialized)
10550  {
10551  // if mysqld's been started with --skip-grant-tables option
10552  mpvio->status= MPVIO_EXT::SUCCESS;
10553  return packet_error;
10554  }
10555 
10556  if (find_mpvio_user(mpvio))
10557  return packet_error;
10558 
10559  if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
10560  {
10561  /*
10562  An old client is connecting
10563  */
10564  if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
10565  client_plugin= native_password_plugin_name.str;
10566  else
10567  {
10568  /*
10569  A really old client is connecting
10570  */
10571  client_plugin= old_password_plugin_name.str;
10572  /*
10573  For a passwordless accounts we use native_password_plugin.
10574  But when an old 4.0 client connects to it, we change it to
10575  old_password_plugin, otherwise MySQL will think that server
10576  and client plugins don't match.
10577  */
10578  if (mpvio->acl_user->salt_len == 0)
10579  mpvio->acl_user_plugin= old_password_plugin_name;
10580  }
10581  }
10582 
10583  /*
10584  if the acl_user needs a different plugin to authenticate
10585  (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
10586  we need to restart the authentication in the server.
10587  But perhaps the client has already used the correct plugin -
10588  in that case the authentication on the client may not need to be
10589  restarted and a server auth plugin will read the data that the client
10590  has just send. Cache them to return in the next server_mpvio_read_packet().
10591  */
10592  if (my_strcasecmp(system_charset_info, mpvio->acl_user_plugin.str,
10593  plugin_name(mpvio->plugin)->str) != 0)
10594  {
10595  mpvio->cached_client_reply.pkt= passwd;
10596  mpvio->cached_client_reply.pkt_len= passwd_len;
10597  mpvio->cached_client_reply.plugin= client_plugin;
10598  mpvio->status= MPVIO_EXT::RESTART;
10599  return packet_error;
10600  }
10601 
10602  /*
10603  ok, we don't need to restart the authentication on the server.
10604  but if the client used the wrong plugin, we need to restart
10605  the authentication on the client. Do it here, the server plugin
10606  doesn't need to know.
10607  */
10608  const char *client_auth_plugin=
10609  ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
10610 
10611  if (client_auth_plugin &&
10612  my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
10613  {
10614  mpvio->cached_client_reply.plugin= client_plugin;
10615  if (send_plugin_request_packet(mpvio,
10616  (uchar*) mpvio->cached_server_packet.pkt,
10617  mpvio->cached_server_packet.pkt_len))
10618  return packet_error;
10619 
10620  passwd_len= my_net_read(mpvio->net);
10621  passwd = (char*) mpvio->net->read_pos;
10622  }
10623 
10624  *buff= (uchar*) passwd;
10625  return passwd_len;
10626 #else
10627  return 0;
10628 #endif
10629 }
10630 
10631 
10641 static inline int
10642 wrap_plguin_data_into_proper_command(NET *net,
10643  const uchar *packet, int packet_len)
10644 {
10645  return net_write_command(net, 1, (uchar *) "", 0, packet, packet_len);
10646 }
10647 
10648 
10659 static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
10660  const uchar *packet, int packet_len)
10661 {
10662  MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
10663  int res;
10664 
10665  DBUG_ENTER("server_mpvio_write_packet");
10666  /*
10667  Reset cached_client_reply if not an old client doing mysql_change_user,
10668  as this is where the password from COM_CHANGE_USER is stored.
10669  */
10670  if (!((!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)) &&
10671  mpvio->status == MPVIO_EXT::RESTART &&
10672  mpvio->cached_client_reply.plugin ==
10673  ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin
10674  ))
10675  mpvio->cached_client_reply.pkt= 0;
10676  /* for the 1st packet we wrap plugin data into the handshake packet */
10677  if (mpvio->packets_written == 0)
10678  res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
10679  else if (mpvio->status == MPVIO_EXT::RESTART)
10680  res= send_plugin_request_packet(mpvio, packet, packet_len);
10681  else
10682  res= wrap_plguin_data_into_proper_command(mpvio->net, packet, packet_len);
10683  mpvio->packets_written++;
10684  DBUG_RETURN(res);
10685 }
10686 
10697 static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
10698 {
10699  MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
10700  ulong pkt_len;
10701 
10702  DBUG_ENTER("server_mpvio_read_packet");
10703  if (mpvio->packets_written == 0)
10704  {
10705  /*
10706  plugin wants to read the data without sending anything first.
10707  send an empty packet to force a server handshake packet to be sent
10708  */
10709  if (mpvio->write_packet(mpvio, 0, 0))
10710  pkt_len= packet_error;
10711  else
10712  pkt_len= my_net_read(mpvio->net);
10713  }
10714  else if (mpvio->cached_client_reply.pkt)
10715  {
10716  DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
10717  DBUG_ASSERT(mpvio->packets_read > 0);
10718  /*
10719  if the have the data cached from the last server_mpvio_read_packet
10720  (which can be the case if it's a restarted authentication)
10721  and a client has used the correct plugin, then we can return the
10722  cached data straight away and avoid one round trip.
10723  */
10724  const char *client_auth_plugin=
10725  ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
10726  if (client_auth_plugin == 0 ||
10727  my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
10728  client_auth_plugin) == 0)
10729  {
10730  mpvio->status= MPVIO_EXT::FAILURE;
10731  *buf= (uchar*) mpvio->cached_client_reply.pkt;
10732  mpvio->cached_client_reply.pkt= 0;
10733  mpvio->packets_read++;
10734  DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
10735  }
10736 
10737  /* older clients don't support change of client plugin request */
10738  if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
10739  {
10740  mpvio->status= MPVIO_EXT::FAILURE;
10741  pkt_len= packet_error;
10742  goto err;
10743  }
10744 
10745  /*
10746  But if the client has used the wrong plugin, the cached data are
10747  useless. Furthermore, we have to send a "change plugin" request
10748  to the client.
10749  */
10750  if (mpvio->write_packet(mpvio, 0, 0))
10751  pkt_len= packet_error;
10752  else
10753  pkt_len= my_net_read(mpvio->net);
10754  }
10755  else
10756  pkt_len= my_net_read(mpvio->net);
10757 
10758  if (pkt_len == packet_error)
10759  goto err;
10760 
10761  mpvio->packets_read++;
10762 
10763  /*
10764  the 1st packet has the plugin data wrapped into the client authentication
10765  handshake packet
10766  */
10767  if (mpvio->packets_read == 1)
10768  {
10769  pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
10770  if (pkt_len == packet_error)
10771  goto err;
10772  }
10773  else
10774  *buf= mpvio->net->read_pos;
10775 
10776  DBUG_RETURN((int)pkt_len);
10777 
10778 err:
10779  if (mpvio->status == MPVIO_EXT::FAILURE)
10780  {
10781  my_error(ER_HANDSHAKE_ERROR, MYF(0));
10782  }
10783  DBUG_RETURN(-1);
10784 }
10785 
10790 static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
10791  MYSQL_PLUGIN_VIO_INFO *info)
10792 {
10793  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
10794  mpvio_info(mpvio->net->vio, info);
10795 }
10796 
10797 #ifndef NO_EMBEDDED_ACCESS_CHECKS
10798 static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
10799 {
10800 #if defined(HAVE_OPENSSL)
10801  Vio *vio= thd->net.vio;
10802  SSL *ssl= (SSL *) vio->ssl_arg;
10803  X509 *cert;
10804 #endif
10805 
10806  /*
10807  At this point we know that user is allowed to connect
10808  from given host by given username/password pair. Now
10809  we check if SSL is required, if user is using SSL and
10810  if X509 certificate attributes are OK
10811  */
10812  switch (acl_user->ssl_type) {
10813  case SSL_TYPE_NOT_SPECIFIED: // Impossible
10814  case SSL_TYPE_NONE: // SSL is not required
10815  return 0;
10816 #if defined(HAVE_OPENSSL)
10817  case SSL_TYPE_ANY: // Any kind of SSL is ok
10818  return vio_type(vio) != VIO_TYPE_SSL;
10819  case SSL_TYPE_X509: /* Client should have any valid certificate. */
10820  /*
10821  Connections with non-valid certificates are dropped already
10822  in sslaccept() anyway, so we do not check validity here.
10823 
10824  We need to check for absence of SSL because without SSL
10825  we should reject connection.
10826  */
10827  if (vio_type(vio) == VIO_TYPE_SSL &&
10828  SSL_get_verify_result(ssl) == X509_V_OK &&
10829  (cert= SSL_get_peer_certificate(ssl)))
10830  {
10831  X509_free(cert);
10832  return 0;
10833  }
10834  return 1;
10835  case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
10836  /* If a cipher name is specified, we compare it to actual cipher in use. */
10837  if (vio_type(vio) != VIO_TYPE_SSL ||
10838  SSL_get_verify_result(ssl) != X509_V_OK)
10839  return 1;
10840  if (acl_user->ssl_cipher)
10841  {
10842  DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
10843  acl_user->ssl_cipher, SSL_get_cipher(ssl)));
10844  if (strcmp(acl_user->ssl_cipher, SSL_get_cipher(ssl)))
10845  {
10846  if (log_warnings)
10847  sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
10848  acl_user->ssl_cipher, SSL_get_cipher(ssl));
10849  return 1;
10850  }
10851  }
10852  /* Prepare certificate (if exists) */
10853  if (!(cert= SSL_get_peer_certificate(ssl)))
10854  return 1;
10855  /* If X509 issuer is specified, we check it... */
10856  if (acl_user->x509_issuer)
10857  {
10858  char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
10859  DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
10860  acl_user->x509_issuer, ptr));
10861  if (strcmp(acl_user->x509_issuer, ptr))
10862  {
10863  if (log_warnings)
10864  sql_print_information("X509 issuer mismatch: should be '%s' "
10865  "but is '%s'", acl_user->x509_issuer, ptr);
10866  OPENSSL_free(ptr);
10867  X509_free(cert);
10868  return 1;
10869  }
10870  OPENSSL_free(ptr);
10871  }
10872  /* X509 subject is specified, we check it .. */
10873  if (acl_user->x509_subject)
10874  {
10875  char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
10876  DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
10877  acl_user->x509_subject, ptr));
10878  if (strcmp(acl_user->x509_subject, ptr))
10879  {
10880  if (log_warnings)
10881  sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
10882  acl_user->x509_subject, ptr);
10883  OPENSSL_free(ptr);
10884  X509_free(cert);
10885  return 1;
10886  }
10887  OPENSSL_free(ptr);
10888  }
10889  X509_free(cert);
10890  return 0;
10891 #else /* HAVE_OPENSSL */
10892  default:
10893  /*
10894  If we don't have SSL but SSL is required for this user the
10895  authentication should fail.
10896  */
10897  return 1;
10898 #endif /* HAVE_OPENSSL */
10899  }
10900  return 1;
10901 }
10902 #endif
10903 
10904 
10905 static int do_auth_once(THD *thd, LEX_STRING *auth_plugin_name,
10906  MPVIO_EXT *mpvio)
10907 {
10908  DBUG_ENTER("do_auth_once");
10909  int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
10910  bool unlock_plugin= false;
10911  plugin_ref plugin= NULL;
10912 
10913  if (auth_plugin_name->str == native_password_plugin_name.str)
10914  plugin= native_password_plugin;
10915 #ifndef EMBEDDED_LIBRARY
10916  else
10917  if (auth_plugin_name->str == old_password_plugin_name.str)
10918  plugin= old_password_plugin;
10919  else
10920  {
10921  if (auth_plugin_name->length == 0)
10922  {
10923  auth_plugin_name->str= default_auth_plugin_name.str;
10924  auth_plugin_name->length= default_auth_plugin_name.length;
10925  }
10926  if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
10927  MYSQL_AUTHENTICATION_PLUGIN)))
10928  unlock_plugin= true;
10929  }
10930 #endif
10931 
10932 
10933  mpvio->plugin= plugin;
10934  old_status= mpvio->status;
10935 
10936  if (plugin)
10937  {
10938  st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
10939  res= auth->authenticate_user(mpvio, &mpvio->auth_info);
10940 
10941  if (unlock_plugin)
10942  plugin_unlock(thd, plugin);
10943  }
10944  else
10945  {
10946  /* Server cannot load the required plugin. */
10948  errors.m_no_auth_plugin= 1;
10949  inc_host_errors(mpvio->ip, &errors);
10950  my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
10951  res= CR_ERROR;
10952  }
10953 
10954  /*
10955  If the status was MPVIO_EXT::RESTART before the authenticate_user() call
10956  it can never be MPVIO_EXT::RESTART after the call, because any call
10957  to write_packet() or read_packet() will reset the status.
10958 
10959  But (!) if a plugin never called a read_packet() or write_packet(), the
10960  status will stay unchanged. We'll fix it, by resetting the status here.
10961  */
10962  if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART)
10963  mpvio->status= MPVIO_EXT::FAILURE; // reset to the default
10964 
10965  DBUG_RETURN(res);
10966 }
10967 
10968 
10969 static void
10970 server_mpvio_initialize(THD *thd, MPVIO_EXT *mpvio,
10971  Thd_charset_adapter *charset_adapter)
10972 {
10973  memset(mpvio, 0, sizeof(MPVIO_EXT));
10974  mpvio->read_packet= server_mpvio_read_packet;
10975  mpvio->write_packet= server_mpvio_write_packet;
10976  mpvio->info= server_mpvio_info;
10977  mpvio->auth_info.host_or_ip= thd->security_ctx->host_or_ip;
10978  mpvio->auth_info.host_or_ip_length=
10979  (unsigned int) strlen(thd->security_ctx->host_or_ip);
10980  mpvio->auth_info.user_name= NULL;
10981  mpvio->auth_info.user_name_length= 0;
10982 #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
10983  if (thd->net.vio && thd->net.vio->ssl_arg)
10984  mpvio->vio_is_encrypted= 1;
10985  else
10986 #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
10987  mpvio->vio_is_encrypted= 0;
10988  mpvio->status= MPVIO_EXT::FAILURE;
10989 
10990  mpvio->client_capabilities= thd->client_capabilities;
10991  mpvio->mem_root= thd->mem_root;
10992  mpvio->scramble= thd->scramble;
10993  mpvio->rand= &thd->rand;
10994  mpvio->thread_id= thd->thread_id;
10995  mpvio->server_status= &thd->server_status;
10996  mpvio->net= &thd->net;
10997  mpvio->ip= (char *) thd->security_ctx->get_ip()->ptr();
10998  mpvio->host= (char *) thd->security_ctx->get_host()->ptr();
10999  mpvio->charset_adapter= charset_adapter;
11000 }
11001 
11002 
11003 static void
11004 server_mpvio_update_thd(THD *thd, MPVIO_EXT *mpvio)
11005 {
11006  thd->client_capabilities= mpvio->client_capabilities;
11007  thd->max_client_packet_length= mpvio->max_client_packet_length;
11008  if (mpvio->client_capabilities & CLIENT_INTERACTIVE)
11009  thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
11010  thd->security_ctx->user= mpvio->auth_info.user_name;
11011  if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
11012  thd->variables.sql_mode|= MODE_IGNORE_SPACE;
11013 }
11014 
11027 int
11028 acl_authenticate(THD *thd, uint com_change_user_pkt_len)
11029 {
11030  int res= CR_OK;
11031  MPVIO_EXT mpvio;
11032  Thd_charset_adapter charset_adapter(thd);
11033 
11034  LEX_STRING auth_plugin_name= default_auth_plugin_name;
11035  enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
11036  : COM_CONNECT;
11037 
11038  DBUG_ENTER("acl_authenticate");
11039  compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
11040 
11041  server_mpvio_initialize(thd, &mpvio, &charset_adapter);
11042 
11043  DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
11044 
11045  /*
11046  Clear thd->db as it points to something, that will be freed when
11047  connection is closed. We don't want to accidentally free a wrong
11048  pointer if connect failed.
11049  */
11050  thd->reset_db(NULL, 0);
11051 
11052  if (command == COM_CHANGE_USER)
11053  {
11054  mpvio.packets_written++; // pretend that a server handshake packet was sent
11055  mpvio.packets_read++; // take COM_CHANGE_USER packet into account
11056 
11057  /* Clear variables that are allocated */
11058  thd->set_user_connect(NULL);
11059 
11060  if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
11061  {
11062  server_mpvio_update_thd(thd, &mpvio);
11063  DBUG_RETURN(1);
11064  }
11065 
11066  DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
11067  mpvio.status == MPVIO_EXT::SUCCESS);
11068  }
11069  else
11070  {
11071  /* mark the thd as having no scramble yet */
11072  mpvio.scramble[SCRAMBLE_LENGTH]= 1;
11073 
11074  /*
11075  perform the first authentication attempt, with the default plugin.
11076  This sends the server handshake packet, reads the client reply
11077  with a user name, and performs the authentication if everyone has used
11078  the correct plugin.
11079  */
11080 
11081  res= do_auth_once(thd, &auth_plugin_name, &mpvio);
11082  }
11083 
11084  /*
11085  retry the authentication, if - after receiving the user name -
11086  we found that we need to switch to a non-default plugin
11087  */
11088  if (mpvio.status == MPVIO_EXT::RESTART)
11089  {
11090  DBUG_ASSERT(mpvio.acl_user);
11091  DBUG_ASSERT(command == COM_CHANGE_USER ||
11092  my_strcasecmp(system_charset_info, auth_plugin_name.str,
11093  mpvio.acl_user->plugin.str));
11094  auth_plugin_name= mpvio.acl_user->plugin;
11095  res= do_auth_once(thd, &auth_plugin_name, &mpvio);
11096  if (res <= CR_OK)
11097  {
11098  if (auth_plugin_name.str == native_password_plugin_name.str)
11099  thd->variables.old_passwords= 0;
11100  if (auth_plugin_name.str == old_password_plugin_name.str)
11101  thd->variables.old_passwords= 1;
11102  if (auth_plugin_name.str == sha256_password_plugin_name.str)
11103  thd->variables.old_passwords= 2;
11104  }
11105  }
11106 
11107  server_mpvio_update_thd(thd, &mpvio);
11108 
11109  Security_context *sctx= thd->security_ctx;
11110  const ACL_USER *acl_user= mpvio.acl_user;
11111 
11112  thd->password= mpvio.auth_info.password_used; // remember for error messages
11113 
11114  /*
11115  Log the command here so that the user can check the log
11116  for the tried logins and also to detect break-in attempts.
11117 
11118  if sctx->user is unset it's protocol failure, bad packet.
11119  */
11120  if (mpvio.auth_info.user_name)
11121  {
11122  if (strcmp(mpvio.auth_info.authenticated_as, mpvio.auth_info.user_name))
11123  {
11124  general_log_print(thd, command, "%s@%s as %s on %s",
11125  mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
11126  mpvio.auth_info.authenticated_as ?
11127  mpvio.auth_info.authenticated_as : "anonymous",
11128  mpvio.db.str ? mpvio.db.str : (char*) "");
11129  }
11130  else
11131  general_log_print(thd, command, (char*) "%s@%s on %s",
11132  mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
11133  mpvio.db.str ? mpvio.db.str : (char*) "");
11134  }
11135 
11136  if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
11137  {
11138  Host_errors errors;
11139  DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
11140  switch (res)
11141  {
11142  case CR_AUTH_PLUGIN_ERROR:
11143  errors.m_auth_plugin= 1;
11144  break;
11145  case CR_AUTH_HANDSHAKE:
11146  errors.m_handshake= 1;
11147  break;
11149  errors.m_authentication= 1;
11150  break;
11151  case CR_ERROR:
11152  default:
11153  /* Unknown of unspecified auth plugin error. */
11154  errors.m_auth_plugin= 1;
11155  break;
11156  }
11157  inc_host_errors(mpvio.ip, &errors);
11158  if (!thd->is_error())
11159  login_failed_error(&mpvio, mpvio.auth_info.password_used);
11160  DBUG_RETURN (1);
11161  }
11162 
11163  sctx->proxy_user[0]= 0;
11164 
11165  if (initialized) // if not --skip-grant-tables
11166  {
11167 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11168  bool is_proxy_user= FALSE;
11169  const char *auth_user = acl_user->user ? acl_user->user : "";
11170  ACL_PROXY_USER *proxy_user;
11171  /* check if the user is allowed to proxy as another user */
11172  proxy_user= acl_find_proxy_user(auth_user, sctx->get_host()->ptr(),
11173  sctx->get_ip()->ptr(),
11174  mpvio.auth_info.authenticated_as,
11175  &is_proxy_user);
11176  if (is_proxy_user)
11177  {
11178  ACL_USER *acl_proxy_user;
11179 
11180  /* we need to find the proxy user, but there was none */
11181  if (!proxy_user)
11182  {
11183  Host_errors errors;
11184  errors.m_proxy_user= 1;
11185  inc_host_errors(mpvio.ip, &errors);
11186  if (!thd->is_error())
11187  login_failed_error(&mpvio, mpvio.auth_info.password_used);
11188  DBUG_RETURN(1);
11189  }
11190 
11191  my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
11192  "'%s'@'%s'", auth_user,
11193  acl_user->host.get_host() ? acl_user->host.get_host() : "");
11194 
11195  /* we're proxying : find the proxy user definition */
11196  mysql_mutex_lock(&acl_cache->lock);
11197  acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ?
11198  proxy_user->get_proxied_host() : "",
11199  mpvio.auth_info.authenticated_as, TRUE);
11200  if (!acl_proxy_user)
11201  {
11202  Host_errors errors;
11203  errors.m_proxy_user_acl= 1;
11204  inc_host_errors(mpvio.ip, &errors);
11205  if (!thd->is_error())
11206  login_failed_error(&mpvio, mpvio.auth_info.password_used);
11207  mysql_mutex_unlock(&acl_cache->lock);
11208  DBUG_RETURN(1);
11209  }
11210  acl_user= acl_proxy_user->copy(thd->mem_root);
11211  DBUG_PRINT("info", ("User %s is a PROXY and will assume a PROXIED"
11212  " identity %s", auth_user, acl_user->user));
11213  mysql_mutex_unlock(&acl_cache->lock);
11214  }
11215 #endif
11216 
11217  sctx->master_access= acl_user->access;
11218  if (acl_user->user)
11219  strmake(sctx->priv_user, acl_user->user, USERNAME_LENGTH - 1);
11220  else
11221  *sctx->priv_user= 0;
11222 
11223  if (acl_user->host.get_host())
11224  strmake(sctx->priv_host, acl_user->host.get_host(), MAX_HOSTNAME - 1);
11225  else
11226  *sctx->priv_host= 0;
11227 
11228 #ifndef NO_EMBEDDED_ACCESS_CHECKS
11229  /*
11230  OK. Let's check the SSL. Historically it was checked after the password,
11231  as an additional layer, not instead of the password
11232  (in which case it would've been a plugin too).
11233  */
11234  if (acl_check_ssl(thd, acl_user))
11235  {
11236  Host_errors errors;
11237  errors.m_ssl= 1;
11238  inc_host_errors(mpvio.ip, &errors);
11239  if (!thd->is_error())
11240  login_failed_error(&mpvio, thd->password);
11241  DBUG_RETURN(1);
11242  }
11243 
11244  if (unlikely(acl_user && acl_user->password_expired
11245  && !(mpvio.client_capabilities & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS)
11246  && disconnect_on_expired_password))
11247  {
11248  /*
11249  Clients that don't signal password expiration support
11250  get a connect error.
11251  */
11252  Host_errors errors;
11253 
11254  my_error(ER_MUST_CHANGE_PASSWORD_LOGIN, MYF(0));
11255  general_log_print(thd, COM_CONNECT, ER(ER_MUST_CHANGE_PASSWORD_LOGIN));
11256  if (log_warnings > 1)
11257  sql_print_warning("%s", ER(ER_MUST_CHANGE_PASSWORD_LOGIN));
11258 
11259  errors.m_authentication= 1;
11260  inc_host_errors(mpvio.ip, &errors);
11261  DBUG_RETURN(1);
11262  }
11263 
11264  /* Don't allow the user to connect if he has done too many queries */
11265  if ((acl_user->user_resource.questions || acl_user->user_resource.updates ||
11266  acl_user->user_resource.conn_per_hour ||
11267  acl_user->user_resource.user_conn ||
11268  global_system_variables.max_user_connections) &&
11269  get_or_create_user_conn(thd,
11270  (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
11271  (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
11272  &acl_user->user_resource))
11273  DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
11274 
11275  sctx->password_expired= acl_user->password_expired;
11276 #endif
11277  }
11278  else
11279  sctx->skip_grants();
11280 
11281  const USER_CONN *uc;
11282  if ((uc= thd->get_user_connect()) &&
11283  (uc->user_resources.conn_per_hour || uc->user_resources.user_conn ||
11284  global_system_variables.max_user_connections) &&
11285  check_for_max_user_connections(thd, uc))
11286  {
11287  DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
11288  }
11289 
11290  DBUG_PRINT("info",
11291  ("Capabilities: %lu packet_length: %ld Host: '%s' "
11292  "Login user: '%s' Priv_user: '%s' Using password: %s "
11293  "Access: %lu db: '%s'",
11294  thd->client_capabilities, thd->max_client_packet_length,
11295  sctx->host_or_ip, sctx->user, sctx->priv_user,
11296  thd->password ? "yes": "no",
11297  sctx->master_access, mpvio.db.str));
11298 
11299  if (command == COM_CONNECT &&
11300  !(thd->main_security_ctx.master_access & SUPER_ACL))
11301  {
11302  mysql_mutex_lock(&LOCK_connection_count);
11303  bool count_ok= (connection_count <= max_connections);
11304  mysql_mutex_unlock(&LOCK_connection_count);
11305  if (!count_ok)
11306  { // too many connections
11307  release_user_connection(thd);
11308  statistic_increment(connection_errors_max_connection, &LOCK_status);
11309  my_error(ER_CON_COUNT_ERROR, MYF(0));
11310  DBUG_RETURN(1);
11311  }
11312  }
11313 
11314  /*
11315  This is the default access rights for the current database. It's
11316  set to 0 here because we don't have an active database yet (and we
11317  may not have an active database to set.
11318  */
11319  sctx->db_access=0;
11320 
11321  /* Change a database if necessary */
11322  if (mpvio.db.length)
11323  {
11324  if (mysql_change_db(thd, &mpvio.db, FALSE))
11325  {
11326  /* mysql_change_db() has pushed the error message. */
11327  release_user_connection(thd);
11328  Host_errors errors;
11329  errors.m_default_database= 1;
11330  inc_host_errors(mpvio.ip, &errors);
11331  DBUG_RETURN(1);
11332  }
11333  }
11334 
11335  if (mpvio.auth_info.external_user[0])
11336  sctx->set_external_user(my_strdup(mpvio.auth_info.external_user, MYF(0)));
11337 
11338 
11339  if (res == CR_OK_HANDSHAKE_COMPLETE)
11340  thd->get_stmt_da()->disable_status();
11341  else
11342  my_ok(thd);
11343 
11344 #ifdef HAVE_PSI_THREAD_INTERFACE
11345  PSI_THREAD_CALL(set_thread_user_host)
11346  (thd->main_security_ctx.user, strlen(thd->main_security_ctx.user),
11347  thd->main_security_ctx.host_or_ip, strlen(thd->main_security_ctx.host_or_ip));
11348 #endif
11349 
11350  /* Ready to handle queries */
11351  DBUG_RETURN(0);
11352 }
11353 
11362 static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
11363  MYSQL_SERVER_AUTH_INFO *info)
11364 {
11365  uchar *pkt;
11366  int pkt_len;
11367  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
11368 
11369  DBUG_ENTER("native_password_authenticate");
11370 
11371  /* generate the scramble, or reuse the old one */
11372  if (mpvio->scramble[SCRAMBLE_LENGTH])
11373  create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
11374 
11375  /* send it to the client */
11376  if (mpvio->write_packet(mpvio, (uchar*) mpvio->scramble, SCRAMBLE_LENGTH + 1))
11377  DBUG_RETURN(CR_AUTH_HANDSHAKE);
11378 
11379  /* reply and authenticate */
11380 
11381  /*
11382  <digression>
11383  This is more complex than it looks.
11384 
11385  The plugin (we) may be called right after the client was connected -
11386  and will need to send a scramble, read reply, authenticate.
11387 
11388  Or the plugin may be called after another plugin has sent a scramble,
11389  and read the reply. If the client has used the correct client-plugin,
11390  we won't need to read anything here from the client, the client
11391  has already sent a reply with everything we need for authentication.
11392 
11393  Or the plugin may be called after another plugin has sent a scramble,
11394  and read the reply, but the client has used the wrong client-plugin.
11395  We'll need to sent a "switch to another plugin" packet to the
11396  client and read the reply. "Use the short scramble" packet is a special
11397  case of "switch to another plugin" packet.
11398 
11399  Or, perhaps, the plugin may be called after another plugin has
11400  done the handshake but did not send a useful scramble. We'll need
11401  to send a scramble (and perhaps a "switch to another plugin" packet)
11402  and read the reply.
11403 
11404  Besides, a client may be an old one, that doesn't understand plugins.
11405  Or doesn't even understand 4.0 scramble.
11406 
11407  And we want to keep the same protocol on the wire unless non-native
11408  plugins are involved.
11409 
11410  Anyway, it still looks simple from a plugin point of view:
11411  "send the scramble, read the reply and authenticate"
11412  All the magic is transparently handled by the server.
11413  </digression>
11414  */
11415 
11416  /* read the reply with the encrypted password */
11417  if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
11418  DBUG_RETURN(CR_AUTH_HANDSHAKE);
11419  DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
11420 
11421 #ifdef NO_EMBEDDED_ACCESS_CHECKS
11422  DBUG_RETURN(CR_OK);
11423 #endif
11424 
11425  DBUG_EXECUTE_IF("native_password_bad_reply",
11426  {
11427  pkt_len= 12;
11428  }
11429  );
11430 
11431  if (pkt_len == 0) /* no password */
11432  DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK);
11433 
11434  info->password_used= PASSWORD_USED_YES;
11435  if (pkt_len == SCRAMBLE_LENGTH)
11436  {
11437  if (!mpvio->acl_user->salt_len)
11438  DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
11439 
11440  DBUG_RETURN(check_scramble(pkt, mpvio->scramble, mpvio->acl_user->salt) ?
11442  }
11443 
11444  my_error(ER_HANDSHAKE_ERROR, MYF(0));
11445  DBUG_RETURN(CR_AUTH_HANDSHAKE);
11446 }
11447 
11448 static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
11449  MYSQL_SERVER_AUTH_INFO *info)
11450 {
11451  uchar *pkt;
11452  int pkt_len;
11453  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
11454 
11455  /* generate the scramble, or reuse the old one */
11456  if (mpvio->scramble[SCRAMBLE_LENGTH])
11457  create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
11458 
11459  /* send it to the client */
11460  if (mpvio->write_packet(mpvio, (uchar*) mpvio->scramble, SCRAMBLE_LENGTH + 1))
11461  return CR_AUTH_HANDSHAKE;
11462 
11463  /* read the reply and authenticate */
11464  if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
11465  return CR_AUTH_HANDSHAKE;
11466 
11467 #ifdef NO_EMBEDDED_ACCESS_CHECKS
11468  return CR_OK;
11469 #endif
11470 
11471  /*
11472  legacy: if switch_from_long_to_short_scramble,
11473  the password is sent \0-terminated, the pkt_len is always 9 bytes.
11474  We need to figure out the correct scramble length here.
11475  */
11476  if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
11477  pkt_len= strnlen((char*)pkt, pkt_len);
11478 
11479  if (pkt_len == 0) /* no password */
11480  return mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK;
11481 
11482  if (secure_auth(mpvio))
11483  return CR_AUTH_HANDSHAKE;
11484 
11485  info->password_used= PASSWORD_USED_YES;
11486 
11487  if (pkt_len == SCRAMBLE_LENGTH_323)
11488  {
11489  if (!mpvio->acl_user->salt_len)
11490  return CR_AUTH_USER_CREDENTIALS;
11491 
11492  return check_scramble_323(pkt, mpvio->scramble,
11493  (ulong *) mpvio->acl_user->salt) ?
11495  }
11496 
11497  my_error(ER_HANDSHAKE_ERROR, MYF(0));
11498  return CR_AUTH_HANDSHAKE;
11499 }
11500 
11501 
11507 int my_vio_is_encrypted(MYSQL_PLUGIN_VIO *vio)
11508 {
11509  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
11510  return (mpvio->vio_is_encrypted);
11511 }
11512 
11513 #if defined(HAVE_OPENSSL)
11514 #define MAX_CIPHER_LENGTH 1024
11515 #if !defined(HAVE_YASSL)
11516 #define AUTH_DEFAULT_RSA_PRIVATE_KEY "private_key.pem"
11517 #define AUTH_DEFAULT_RSA_PUBLIC_KEY "public_key.pem"
11518 
11519 char *auth_rsa_private_key_path;
11520 char *auth_rsa_public_key_path;
11521 
11522 class Rsa_authentication_keys
11523 {
11524 private:
11525  RSA *m_public_key;
11526  RSA *m_private_key;
11527  int m_cipher_len;
11528  char *m_pem_public_key;
11529 
11538  void get_key_file_path(char *key, String *key_file_path)
11539  {
11540  /*
11541  If a fully qualified path is entered use that, else assume the keys are
11542  stored in the data directory.
11543  */
11544  if (strchr(key, FN_LIBCHAR) != NULL ||
11545  strchr(key, FN_LIBCHAR2) != NULL)
11546  key_file_path->set_quick(key, strlen(key), system_charset_info);
11547  else
11548  {
11549  key_file_path->append(mysql_real_data_home, strlen(mysql_real_data_home));
11550  if ((*key_file_path)[key_file_path->length()] != FN_LIBCHAR)
11551  key_file_path->append(FN_LIBCHAR);
11552  key_file_path->append(key);
11553  }
11554  }
11555 
11571  bool read_key_file(RSA **key_ptr, bool is_priv_key, char **key_text_buffer)
11572  {
11573  String key_file_path;
11574  char *key;
11575  const char *key_type;
11576  FILE *key_file= NULL;
11577 
11578  key= is_priv_key ? auth_rsa_private_key_path : auth_rsa_public_key_path;
11579  key_type= is_priv_key ? "private" : "public";
11580  *key_ptr= NULL;
11581 
11582  get_key_file_path(key, &key_file_path);
11583 
11584  /*
11585  Check for existance of private key/public key file.
11586  */
11587  if ((key_file= fopen(key_file_path.c_ptr(), "r")) == NULL)
11588  {
11589  sql_print_information("RSA %s key file not found: %s."
11590  " Some authentication plugins will not work.",
11591  key_type, key_file_path.c_ptr());
11592  }
11593  else
11594  {
11595  *key_ptr= is_priv_key ? PEM_read_RSAPrivateKey(key_file, 0, 0, 0) :
11596  PEM_read_RSA_PUBKEY(key_file, 0, 0, 0);
11597 
11598  if (!(*key_ptr))
11599  {
11600  char error_buf[MYSQL_ERRMSG_SIZE];
11601  ERR_error_string_n(ERR_get_error(), error_buf, MYSQL_ERRMSG_SIZE);
11602  sql_print_error("Failure to parse RSA %s key (file exists): %s:"
11603  " %s", key_type, key_file_path.c_ptr(), error_buf);
11604 
11605  /*
11606  Call ERR_clear_error() just in case there are more than 1 entry in the
11607  OpenSSL thread's error queue.
11608  */
11609  ERR_clear_error();
11610 
11611  return true;
11612  }
11613 
11614  /* For public key, read key file content into a char buffer. */
11615  if (!is_priv_key)
11616  {
11617  int filesize;
11618  fseek(key_file, 0, SEEK_END);
11619  filesize= ftell(key_file);
11620  fseek(key_file, 0, SEEK_SET);
11621  *key_text_buffer= new char[filesize+1];
11622  (void) fread(*key_text_buffer, filesize, 1, key_file);
11623  (*key_text_buffer)[filesize]= '\0';
11624  }
11625  fclose(key_file);
11626  }
11627  return false;
11628  }
11629 
11630 public:
11631  Rsa_authentication_keys()
11632  {
11633  m_cipher_len= 0;
11634  m_private_key= 0;
11635  m_public_key= 0;
11636  m_pem_public_key= 0;
11637  }
11638 
11639  ~Rsa_authentication_keys()
11640  {
11641  }
11642 
11643  void free_memory()
11644  {
11645  if (m_private_key)
11646  RSA_free(m_private_key);
11647 
11648  if (m_public_key)
11649  {
11650  RSA_free(m_public_key);
11651  m_cipher_len= 0;
11652  }
11653 
11654  if (m_pem_public_key)
11655  delete [] m_pem_public_key;
11656  }
11657 
11658  void *allocate_pem_buffer(size_t buffer_len)
11659  {
11660  m_pem_public_key= new char[buffer_len];
11661  return m_pem_public_key;
11662  }
11663 
11664  RSA *get_private_key()
11665  {
11666  return m_private_key;
11667  }
11668 
11669  RSA *get_public_key()
11670  {
11671  return m_public_key;
11672  }
11673 
11674  int get_cipher_length()
11675  {
11676  return (m_cipher_len= RSA_size(m_public_key));
11677  }
11678 
11688  bool read_rsa_keys()
11689  {
11690  RSA *rsa_private_key_ptr= NULL;
11691  RSA *rsa_public_key_ptr= NULL;
11692  char *pub_key_buff= NULL;
11693 
11694  if ((strlen(auth_rsa_private_key_path) == 0) &&
11695  (strlen(auth_rsa_public_key_path) == 0))
11696  {
11697  sql_print_information("RSA key files not found."
11698  " Some authentication plugins will not work.");
11699  return false;
11700  }
11701 
11702  /*
11703  Read private key in RSA format.
11704  */
11705  if (read_key_file(&rsa_private_key_ptr, true, NULL))
11706  return true;
11707 
11708  /*
11709  Read public key in RSA format.
11710  */
11711  if (read_key_file(&rsa_public_key_ptr, false, &pub_key_buff))
11712  {
11713  if (rsa_private_key_ptr)
11714  RSA_free(rsa_private_key_ptr);
11715  return true;
11716  }
11717 
11718  /*
11719  If both key files are read successfully then assign values to following
11720  members of the class
11721  1. m_pem_public_key
11722  2. m_private_key
11723  3. m_public_key
11724 
11725  Else clean up.
11726  */
11727  if (rsa_private_key_ptr && rsa_public_key_ptr)
11728  {
11729  int buff_len= strlen(pub_key_buff);
11730  char *pem_file_buffer= (char *)allocate_pem_buffer(buff_len + 1);
11731  strncpy(pem_file_buffer, pub_key_buff, buff_len);
11732  pem_file_buffer[buff_len]= '\0';
11733 
11734  m_private_key= rsa_private_key_ptr;
11735  m_public_key= rsa_public_key_ptr;
11736 
11737  delete [] pub_key_buff;
11738  }
11739  else
11740  {
11741  if (rsa_private_key_ptr)
11742  RSA_free(rsa_private_key_ptr);
11743 
11744  if (rsa_public_key_ptr)
11745  {
11746  delete [] pub_key_buff;
11747  RSA_free(rsa_public_key_ptr);
11748  }
11749  }
11750  return false;
11751  }
11752 
11753  const char *get_public_key_as_pem(void)
11754  {
11755  return m_pem_public_key;
11756  }
11757 
11758 };
11759 
11760 static Rsa_authentication_keys g_rsa_keys;
11761 
11765 int show_rsa_public_key(THD *thd, SHOW_VAR *var, char *buff)
11766 {
11767  var->type= SHOW_CHAR;
11768  var->value= const_cast<char *>(g_rsa_keys.get_public_key_as_pem());
11769 
11770  return 0;
11771 }
11772 
11773 void deinit_rsa_keys(void)
11774 {
11775  g_rsa_keys.free_memory();
11776 }
11777 
11778 // Wraps a FILE handle, to ensure we always close it when returning.
11779 class FileCloser
11780 {
11781  FILE *m_file;
11782 public:
11783  FileCloser(FILE *to_be_closed) : m_file(to_be_closed) {}
11784  ~FileCloser()
11785  {
11786  if (m_file != NULL)
11787  fclose(m_file);
11788  }
11789 };
11790 
11801 bool init_rsa_keys(void)
11802 {
11803  return (g_rsa_keys.read_rsa_keys());
11804 }
11805 #endif // ifndef HAVE_YASSL
11806 
11807 static MYSQL_PLUGIN plugin_info_ptr;
11808 
11809 int init_sha256_password_handler(MYSQL_PLUGIN plugin_ref)
11810 {
11811  plugin_info_ptr= plugin_ref;
11812  return 0;
11813 }
11814 
11831 static int sha256_password_authenticate(MYSQL_PLUGIN_VIO *vio,
11832  MYSQL_SERVER_AUTH_INFO *info)
11833 {
11834  uchar *pkt;
11835  int pkt_len;
11836  char *user_salt_begin;
11837  char *user_salt_end;
11838  char scramble[SCRAMBLE_LENGTH + 1];
11839  char stage2[CRYPT_MAX_PASSWORD_SIZE + 1];
11840  String scramble_response_packet;
11841 #if !defined(HAVE_YASSL)
11842  int cipher_length= 0;
11843  unsigned char plain_text[MAX_CIPHER_LENGTH];
11844  RSA *private_key= NULL;
11845  RSA *public_key= NULL;
11846 #endif
11847 
11848  DBUG_ENTER("sha256_password_authenticate");
11849 
11850  generate_user_salt(scramble, SCRAMBLE_LENGTH + 1);
11851 
11852  if (vio->write_packet(vio, (unsigned char *) scramble, SCRAMBLE_LENGTH))
11853  DBUG_RETURN(CR_ERROR);
11854 
11855  /*
11856  After the call to read_packet() the user name will appear in
11857  mpvio->acl_user and info will contain current data.
11858  */
11859  if ((pkt_len= vio->read_packet(vio, &pkt)) == -1)
11860  DBUG_RETURN(CR_ERROR);
11861 
11862  /*
11863  If first packet is a 0 byte then the client isn't sending any password
11864  else the client will send a password.
11865  */
11866  if (pkt_len == 1 && *pkt == 0)
11867  {
11868  info->password_used= PASSWORD_USED_NO;
11869  /*
11870  Send OK signal; the authentication might still be rejected based on
11871  host mask.
11872  */
11873  if (info->auth_string_length == 0)
11874  DBUG_RETURN(CR_OK);
11875  else
11876  DBUG_RETURN(CR_ERROR);
11877  }
11878  else
11879  info->password_used= PASSWORD_USED_YES;
11880 
11881  if (!my_vio_is_encrypted(vio))
11882  {
11883  #if !defined(HAVE_YASSL)
11884  /*
11885  Since a password is being used it must be encrypted by RSA since no
11886  other encryption is being active.
11887  */
11888  private_key= g_rsa_keys.get_private_key();
11889  public_key= g_rsa_keys.get_public_key();
11890 
11891  /*
11892  Without the keys encryption isn't possible.
11893  */
11894  if (private_key == NULL || public_key == NULL)
11895  {
11896  my_plugin_log_message(&plugin_info_ptr, MY_ERROR_LEVEL,
11897  "Authentication requires either RSA keys or SSL encryption");
11898  DBUG_RETURN(CR_ERROR);
11899  }
11900 
11901 
11902  if ((cipher_length= g_rsa_keys.get_cipher_length()) > MAX_CIPHER_LENGTH)
11903  {
11904  my_plugin_log_message(&plugin_info_ptr, MY_ERROR_LEVEL,
11905  "RSA key cipher length of %u is too long. Max value is %u.",
11906  g_rsa_keys.get_cipher_length(), MAX_CIPHER_LENGTH);
11907  DBUG_RETURN(CR_ERROR);
11908  }
11909 
11910  /*
11911  Client sent a "public key request"-packet ?
11912  If the first packet is 1 then the client will require a public key before
11913  encrypting the password.
11914  */
11915  if (pkt_len == 1 && *pkt == 1)
11916  {
11917  uint pem_length= strlen(g_rsa_keys.get_public_key_as_pem());
11918  if (vio->write_packet(vio,
11919  (unsigned char *)g_rsa_keys.get_public_key_as_pem(),
11920  pem_length))
11921  DBUG_RETURN(CR_ERROR);
11922  /* Get the encrypted response from the client */
11923  if ((pkt_len= vio->read_packet(vio, &pkt)) == -1)
11924  DBUG_RETURN(CR_ERROR);
11925  }
11926 
11927  /*
11928  The packet will contain the cipher used. The length of the packet
11929  must correspond to the expected cipher length.
11930  */
11931  if (pkt_len != cipher_length)
11932  DBUG_RETURN(CR_ERROR);
11933 
11934  /* Decrypt password */
11935  RSA_private_decrypt(cipher_length, pkt, plain_text, private_key,
11936  RSA_PKCS1_OAEP_PADDING);
11937 
11938  plain_text[cipher_length]= '\0'; // safety
11939  xor_string((char *) plain_text, cipher_length,
11940  (char *) scramble, SCRAMBLE_LENGTH);
11941 
11942  /*
11943  Set packet pointers and length for the hash digest function below
11944  */
11945  pkt= plain_text;
11946  pkt_len= strlen((char *) plain_text) + 1; // include \0 intentionally.
11947 
11948  if (pkt_len == 1)
11949  DBUG_RETURN(CR_ERROR);
11950 #else
11951  DBUG_RETURN(CR_ERROR);
11952 #endif
11953  } // if(!my_vio_is_encrypter())
11954 
11955  /* A password was sent to an account without a password */
11956  if (info->auth_string_length == 0)
11957  DBUG_RETURN(CR_ERROR);
11958 
11959  /*
11960  Fetch user authentication_string and extract the password salt
11961  */
11962  user_salt_begin= (char *) info->auth_string;
11963  user_salt_end= (char *) (info->auth_string + info->auth_string_length);
11964  if (extract_user_salt(&user_salt_begin, &user_salt_end) != CRYPT_SALT_LENGTH)
11965  {
11966  /* User salt is not correct */
11967  my_plugin_log_message(&plugin_info_ptr, MY_ERROR_LEVEL,
11968  "Password salt for user '%s' is corrupt.",
11969  info->user_name);
11970  DBUG_RETURN(CR_ERROR);
11971  }
11972 
11973  /* Create hash digest */
11974  my_crypt_genhash(stage2,
11975  CRYPT_MAX_PASSWORD_SIZE,
11976  (char *) pkt,
11977  pkt_len-1,
11978  (char *) user_salt_begin,
11979  (const char **) 0);
11980 
11981  /* Compare the newly created hash digest with the password record */
11982  int result= memcmp(info->auth_string,
11983  stage2,
11984  info->auth_string_length);
11985 
11986  if (result == 0)
11987  DBUG_RETURN(CR_OK);
11988 
11989  DBUG_RETURN(CR_ERROR);
11990 }
11991 
11992 #if !defined(HAVE_YASSL)
11993 static MYSQL_SYSVAR_STR(private_key_path, auth_rsa_private_key_path,
11994  PLUGIN_VAR_READONLY,
11995  "A fully qualified path to the private RSA key used for authentication",
11996  NULL, NULL, AUTH_DEFAULT_RSA_PRIVATE_KEY);
11997 static MYSQL_SYSVAR_STR(public_key_path, auth_rsa_public_key_path,
11998  PLUGIN_VAR_READONLY,
11999  "A fully qualified path to the public RSA key used for authentication",
12000  NULL, NULL, AUTH_DEFAULT_RSA_PUBLIC_KEY);
12001 
12002 static struct st_mysql_sys_var* sha256_password_sysvars[]= {
12003  MYSQL_SYSVAR(private_key_path),
12004  MYSQL_SYSVAR(public_key_path),
12005  0
12006 };
12007 #endif // HAVE_YASSL
12008 #endif // HAVE_OPENSSL
12009 
12010 static struct st_mysql_auth native_password_handler=
12011 {
12012  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
12013  native_password_plugin_name.str,
12014  native_password_authenticate
12015 };
12016 
12017 static struct st_mysql_auth old_password_handler=
12018 {
12019  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
12020  old_password_plugin_name.str,
12021  old_password_authenticate
12022 };
12023 
12024 #if defined(HAVE_OPENSSL)
12025 static struct st_mysql_auth sha256_password_handler=
12026 {
12027  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
12028  sha256_password_plugin_name.str,
12029  sha256_password_authenticate
12030 };
12031 #endif
12032 
12033 mysql_declare_plugin(mysql_password)
12034 {
12035  MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
12036  &native_password_handler, /* type descriptor */
12037  native_password_plugin_name.str, /* Name */
12038  "R.J.Silk, Sergei Golubchik", /* Author */
12039  "Native MySQL authentication", /* Description */
12040  PLUGIN_LICENSE_GPL, /* License */
12041  NULL, /* Init function */
12042  NULL, /* Deinit function */
12043  0x0100, /* Version (1.0) */
12044  NULL, /* status variables */
12045  NULL, /* system variables */
12046  NULL, /* config options */
12047  0, /* flags */
12048 },
12049 {
12050  MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
12051  &old_password_handler, /* type descriptor */
12052  old_password_plugin_name.str, /* Name */
12053  "R.J.Silk, Sergei Golubchik", /* Author */
12054  "Old MySQL-4.0 authentication", /* Description */
12055  PLUGIN_LICENSE_GPL, /* License */
12056  NULL, /* Init function */
12057  NULL, /* Deinit function */
12058  0x0100, /* Version (1.0) */
12059  NULL, /* status variables */
12060  NULL, /* system variables */
12061  NULL, /* config options */
12062  0, /* flags */
12063 }
12064 #if defined(HAVE_OPENSSL)
12065 ,
12066 {
12067  MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
12068  &sha256_password_handler, /* type descriptor */
12069  sha256_password_plugin_name.str, /* Name */
12070  "Oracle", /* Author */
12071  "SHA256 password authentication", /* Description */
12072  PLUGIN_LICENSE_GPL, /* License */
12073  &init_sha256_password_handler, /* Init function */
12074  NULL, /* Deinit function */
12075  0x0100, /* Version (1.0) */
12076  NULL, /* status variables */
12077 #if !defined(HAVE_YASSL)
12078  sha256_password_sysvars, /* system variables */
12079 #else
12080  NULL,
12081 #endif
12082  NULL, /* config options */
12083  0 /* flags */
12084 }
12085 #endif
12086 mysql_declare_plugin_end;
12087 
12088 /*
12089  PASSWORD_VALIDATION_CODE, invoking appropriate plugin to validate
12090  the password strength.
12091 */
12092 
12093 /* for validate_password_strength SQL function */
12094 int check_password_strength(String *password)
12095 {
12096  int res= 0;
12097  DBUG_ASSERT(password != NULL);
12098  plugin_ref plugin= my_plugin_lock_by_name(0, &validate_password_plugin_name,
12099  MYSQL_VALIDATE_PASSWORD_PLUGIN);
12100  if (plugin)
12101  {
12102  st_mysql_validate_password *password_strength=
12103  (st_mysql_validate_password *) plugin_decl(plugin)->info;
12104 
12105  res= password_strength->get_password_strength(password);
12106  plugin_unlock(0, plugin);
12107  }
12108  return(res);
12109 }
12110 
12111 /* called when new user is created or exsisting password is changed */
12112 int check_password_policy(String *password)
12113 {
12114  plugin_ref plugin;
12115  String empty_string;
12116 
12117  if (!password)
12118  password= &empty_string;
12119 
12120  plugin= my_plugin_lock_by_name(0, &validate_password_plugin_name,
12121  MYSQL_VALIDATE_PASSWORD_PLUGIN);
12122  if (plugin)
12123  {
12124  st_mysql_validate_password *password_validate=
12125  (st_mysql_validate_password *) plugin_decl(plugin)->info;
12126 
12127  if (!password_validate->validate_password(password))
12128  {
12129  my_error(ER_NOT_VALID_PASSWORD, MYF(0));
12130  plugin_unlock(0, plugin);
12131  return (1);
12132  }
12133  plugin_unlock(0, plugin);
12134  }
12135  return (0);
12136 }
12137 
12138 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12139 my_bool validate_user_plugins= TRUE;
12140 
12147 static void
12148 validate_user_plugin_records()
12149 {
12150  DBUG_ENTER("validate_user_plugin_records");
12151  if (!validate_user_plugins)
12152  DBUG_VOID_RETURN;
12153 
12154  lock_plugin_data();
12155  for (uint i=0 ; i < acl_users.elements ; i++)
12156  {
12157  struct st_plugin_int *plugin;
12158  ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
12159 
12160  if (acl_user->plugin.length)
12161  {
12162  /* rule 1 : plugin does exit */
12163  if (!auth_plugin_is_built_in(acl_user->plugin.str))
12164  {
12165  plugin= plugin_find_by_type(&acl_user->plugin,
12166  MYSQL_AUTHENTICATION_PLUGIN);
12167 
12168  if (!plugin)
12169  {
12170  sql_print_warning("The plugin '%.*s' used to authenticate "
12171  "user '%s'@'%.*s' is not loaded."
12172  " Nobody can currently login using this account.",
12173  (int) acl_user->plugin.length, acl_user->plugin.str,
12174  acl_user->user,
12175  acl_user->host.get_host_len(),
12176  acl_user->host.get_host());
12177  }
12178  }
12179  if (acl_user->plugin.str == sha256_password_plugin_name.str &&
12180 #if !defined(HAVE_YASSL)
12181  (!g_rsa_keys.get_private_key() || !g_rsa_keys.get_public_key()) &&
12182 #endif
12183  !ssl_acceptor_fd)
12184  {
12185  sql_print_warning("The plugin '%s' is used to authenticate "
12186  "user '%s'@'%.*s', "
12187 #if !defined(HAVE_YASSL)
12188  "but neither SSL nor RSA keys are "
12189 #else
12190  "but no SSL is "
12191 #endif
12192  "configured. "
12193  "Nobody can currently login using this account.",
12194  sha256_password_plugin_name.str,
12195  acl_user->user,
12196  acl_user->host.get_host_len(),
12197  acl_user->host.get_host());
12198  }
12199  }
12200  }
12201  unlock_plugin_data();
12202  DBUG_VOID_RETURN;
12203 }
12204 
12205 #endif // NO_EMBEDDED_ACCESS_CHECKS
12206