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