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