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