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