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