1 /* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23
24 /*
25 The privileges are saved in the following tables:
26 mysql/user ; super user who are allowed to do almost anything
27 mysql/host ; host privileges. This is used if host is empty in mysql/db.
28 mysql/db ; database privileges / user
29
30 data in tables is sorted according to how many not-wild-cards there is
31 in the relevant fields. Empty strings comes last.
32 */
33
34
35 #include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
36 #include "sql_priv.h"
37 #include "sql_acl.h" // MYSQL_DB_FIELD_COUNT, ACL_ACCESS
38 #include "sql_base.h" // close_mysql_tables
39 #include "key.h" // key_copy, key_cmp_if_same, key_restore
40 #include "sql_show.h" // append_identifier
41 #include "sql_table.h" // build_table_filename
42 #include "hash_filo.h"
43 #include "sql_parse.h" // check_access
44 #include "sql_view.h" // VIEW_ANY_ACL
45 #include "records.h" // READ_RECORD, read_record_info,
46 // init_read_record, end_read_record
47 #include "rpl_filter.h" // rpl_filter
48 #include <m_ctype.h>
49 #include <stdarg.h>
50 #include "sp_head.h"
51 #include "sp.h"
52 #include "transaction.h"
53 #include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
54 #include "records.h" // init_read_record, end_read_record
55 #include <sql_common.h>
56 #include <mysql/plugin_auth.h>
57 #include "sql_connect.h"
58 #include "hostname.h"
59 #include "sql_db.h"
60 #include <mysql/plugin_validate_password.h>
61 #include "password.h"
62 #include "crypt_genhash_impl.h"
63 #include "debug_sync.h"
64
65 #if defined(HAVE_OPENSSL)
66 #include <openssl/rsa.h>
67 #include <openssl/pem.h>
68 #include <openssl/err.h>
69 #endif
70
71 #ifndef DBUG_OFF
72 #define HASH_STRING_WITH_QUOTE \
73 "$5$BVZy9O>'a+2MH]_?$fpWyabcdiHjfCVqId/quykZzjaA7adpkcen/uiQrtmOK4p4"
74 #endif
75
76 #if defined(HAVE_OPENSSL)
77 #define SHA256_PASSWORD_MAX_PASSWORD_LENGTH MAX_PLAINTEXT_LENGTH
78 #endif /* HAVE_OPENSSL */
79
80 #include <string>
81
82 using std::min;
83 using std::max;
84
85 #include "my_user.h"
86 #include "password.h"
87 #include "sha1.h"
88
89 bool mysql_user_table_is_in_short_password_format= false;
90 my_bool disconnect_on_expired_password= TRUE;
91 bool auth_plugin_is_built_in(const char *plugin_name);
92 bool auth_plugin_supports_expiration(const char *plugin_name);
93 void optimize_plugin_compare_by_pointer(LEX_STRING *plugin_name);
94
95 static const
96 TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
97 {
98 { C_STRING_WITH_LEN("Host") },
99 { C_STRING_WITH_LEN("char(60)") },
100 {NULL, 0}
101 },
102 {
103 { C_STRING_WITH_LEN("Db") },
104 { C_STRING_WITH_LEN("char(64)") },
105 {NULL, 0}
106 },
107 {
108 { C_STRING_WITH_LEN("User") },
109 { C_STRING_WITH_LEN("char(16)") },
110 {NULL, 0}
111 },
112 {
113 { C_STRING_WITH_LEN("Select_priv") },
114 { C_STRING_WITH_LEN("enum('N','Y')") },
115 { C_STRING_WITH_LEN("utf8") }
116 },
117 {
118 { C_STRING_WITH_LEN("Insert_priv") },
119 { C_STRING_WITH_LEN("enum('N','Y')") },
120 { C_STRING_WITH_LEN("utf8") }
121 },
122 {
123 { C_STRING_WITH_LEN("Update_priv") },
124 { C_STRING_WITH_LEN("enum('N','Y')") },
125 { C_STRING_WITH_LEN("utf8") }
126 },
127 {
128 { C_STRING_WITH_LEN("Delete_priv") },
129 { C_STRING_WITH_LEN("enum('N','Y')") },
130 { C_STRING_WITH_LEN("utf8") }
131 },
132 {
133 { C_STRING_WITH_LEN("Create_priv") },
134 { C_STRING_WITH_LEN("enum('N','Y')") },
135 { C_STRING_WITH_LEN("utf8") }
136 },
137 {
138 { C_STRING_WITH_LEN("Drop_priv") },
139 { C_STRING_WITH_LEN("enum('N','Y')") },
140 { C_STRING_WITH_LEN("utf8") }
141 },
142 {
143 { C_STRING_WITH_LEN("Grant_priv") },
144 { C_STRING_WITH_LEN("enum('N','Y')") },
145 { C_STRING_WITH_LEN("utf8") }
146 },
147 {
148 { C_STRING_WITH_LEN("References_priv") },
149 { C_STRING_WITH_LEN("enum('N','Y')") },
150 { C_STRING_WITH_LEN("utf8") }
151 },
152 {
153 { C_STRING_WITH_LEN("Index_priv") },
154 { C_STRING_WITH_LEN("enum('N','Y')") },
155 { C_STRING_WITH_LEN("utf8") }
156 },
157 {
158 { C_STRING_WITH_LEN("Alter_priv") },
159 { C_STRING_WITH_LEN("enum('N','Y')") },
160 { C_STRING_WITH_LEN("utf8") }
161 },
162 {
163 { C_STRING_WITH_LEN("Create_tmp_table_priv") },
164 { C_STRING_WITH_LEN("enum('N','Y')") },
165 { C_STRING_WITH_LEN("utf8") }
166 },
167 {
168 { C_STRING_WITH_LEN("Lock_tables_priv") },
169 { C_STRING_WITH_LEN("enum('N','Y')") },
170 { C_STRING_WITH_LEN("utf8") }
171 },
172 {
173 { C_STRING_WITH_LEN("Create_view_priv") },
174 { C_STRING_WITH_LEN("enum('N','Y')") },
175 { C_STRING_WITH_LEN("utf8") }
176 },
177 {
178 { C_STRING_WITH_LEN("Show_view_priv") },
179 { C_STRING_WITH_LEN("enum('N','Y')") },
180 { C_STRING_WITH_LEN("utf8") }
181 },
182 {
183 { C_STRING_WITH_LEN("Create_routine_priv") },
184 { C_STRING_WITH_LEN("enum('N','Y')") },
185 { C_STRING_WITH_LEN("utf8") }
186 },
187 {
188 { C_STRING_WITH_LEN("Alter_routine_priv") },
189 { C_STRING_WITH_LEN("enum('N','Y')") },
190 { C_STRING_WITH_LEN("utf8") }
191 },
192 {
193 { C_STRING_WITH_LEN("Execute_priv") },
194 { C_STRING_WITH_LEN("enum('N','Y')") },
195 { C_STRING_WITH_LEN("utf8") }
196 },
197 {
198 { C_STRING_WITH_LEN("Event_priv") },
199 { C_STRING_WITH_LEN("enum('N','Y')") },
200 { C_STRING_WITH_LEN("utf8") }
201 },
202 {
203 { C_STRING_WITH_LEN("Trigger_priv") },
204 { C_STRING_WITH_LEN("enum('N','Y')") },
205 { C_STRING_WITH_LEN("utf8") }
206 }
207 };
208
209 #ifndef NO_EMBEDDED_ACCESS_CHECKS
210 static const
211 TABLE_FIELD_TYPE mysql_user_table_fields[MYSQL_USER_FIELD_COUNT] = {
212 {
213 { C_STRING_WITH_LEN("Host") },
214 { C_STRING_WITH_LEN("char(60)") },
215 {NULL, 0}
216 },
217 {
218 { C_STRING_WITH_LEN("User") },
219 { C_STRING_WITH_LEN("char(16)") },
220 {NULL, 0}
221 },
222 {
223 { C_STRING_WITH_LEN("Password") },
224 { C_STRING_WITH_LEN("char(41)") },
225 { C_STRING_WITH_LEN("latin1") }
226 },
227 {
228 { C_STRING_WITH_LEN("Select_priv") },
229 { C_STRING_WITH_LEN("enum('N','Y')") },
230 { C_STRING_WITH_LEN("utf8") }
231 },
232 {
233 { C_STRING_WITH_LEN("Insert_priv") },
234 { C_STRING_WITH_LEN("enum('N','Y')") },
235 { C_STRING_WITH_LEN("utf8") }
236 },
237 {
238 { C_STRING_WITH_LEN("Update_priv") },
239 { C_STRING_WITH_LEN("enum('N','Y')") },
240 { C_STRING_WITH_LEN("utf8") }
241 },
242 {
243 { C_STRING_WITH_LEN("Delete_priv") },
244 { C_STRING_WITH_LEN("enum('N','Y')") },
245 { C_STRING_WITH_LEN("utf8") }
246 },
247 {
248 { C_STRING_WITH_LEN("Create_priv") },
249 { C_STRING_WITH_LEN("enum('N','Y')") },
250 { C_STRING_WITH_LEN("utf8") }
251 },
252 {
253 { C_STRING_WITH_LEN("Drop_priv") },
254 { C_STRING_WITH_LEN("enum('N','Y')") },
255 { C_STRING_WITH_LEN("utf8") }
256 },
257 {
258 { C_STRING_WITH_LEN("Reload_priv") },
259 { C_STRING_WITH_LEN("enum('N','Y')") },
260 { C_STRING_WITH_LEN("utf8") }
261 },
262 {
263 { C_STRING_WITH_LEN("Shutdown_priv") },
264 { C_STRING_WITH_LEN("enum('N','Y')") },
265 { C_STRING_WITH_LEN("utf8") }
266 },
267 {
268 { C_STRING_WITH_LEN("Process_priv") },
269 { C_STRING_WITH_LEN("enum('N','Y')") },
270 { C_STRING_WITH_LEN("utf8") }
271 },
272 {
273 { C_STRING_WITH_LEN("File_priv") },
274 { C_STRING_WITH_LEN("enum('N','Y')") },
275 { C_STRING_WITH_LEN("utf8") }
276 },
277 {
278 { C_STRING_WITH_LEN("Grant_priv") },
279 { C_STRING_WITH_LEN("enum('N','Y')") },
280 { C_STRING_WITH_LEN("utf8") }
281 },
282 {
283 { C_STRING_WITH_LEN("References_priv") },
284 { C_STRING_WITH_LEN("enum('N','Y')") },
285 { C_STRING_WITH_LEN("utf8") }
286 },
287 {
288 { C_STRING_WITH_LEN("Index_priv") },
289 { C_STRING_WITH_LEN("enum('N','Y')") },
290 { C_STRING_WITH_LEN("utf8") }
291 },
292 {
293 { C_STRING_WITH_LEN("Alter_priv") },
294 { C_STRING_WITH_LEN("enum('N','Y')") },
295 { C_STRING_WITH_LEN("utf8") }
296 },
297 {
298 { C_STRING_WITH_LEN("Show_db_priv") },
299 { C_STRING_WITH_LEN("enum('N','Y')") },
300 { C_STRING_WITH_LEN("utf8") }
301 },
302 {
303 { C_STRING_WITH_LEN("Super_priv") },
304 { C_STRING_WITH_LEN("enum('N','Y')") },
305 { C_STRING_WITH_LEN("utf8") }
306 },
307 {
308 { C_STRING_WITH_LEN("Create_tmp_table_priv") },
309 { C_STRING_WITH_LEN("enum('N','Y')") },
310 { C_STRING_WITH_LEN("utf8") }
311 },
312 {
313 { C_STRING_WITH_LEN("Lock_tables_priv") },
314 { C_STRING_WITH_LEN("enum('N','Y')") },
315 { C_STRING_WITH_LEN("utf8") }
316 },
317 {
318 { C_STRING_WITH_LEN("Execute_priv") },
319 { C_STRING_WITH_LEN("enum('N','Y')") },
320 { C_STRING_WITH_LEN("utf8") }
321 },
322 {
323 { C_STRING_WITH_LEN("Repl_slave_priv") },
324 { C_STRING_WITH_LEN("enum('N','Y')") },
325 { C_STRING_WITH_LEN("utf8") }
326 },
327 {
328 { C_STRING_WITH_LEN("Repl_client_priv") },
329 { C_STRING_WITH_LEN("enum('N','Y')") },
330 { C_STRING_WITH_LEN("utf8") }
331 },
332 {
333 { C_STRING_WITH_LEN("Create_view_priv") },
334 { C_STRING_WITH_LEN("enum('N','Y')") },
335 { C_STRING_WITH_LEN("utf8") }
336 },
337 {
338 { C_STRING_WITH_LEN("Show_view_priv") },
339 { C_STRING_WITH_LEN("enum('N','Y')") },
340 { C_STRING_WITH_LEN("utf8") }
341 },
342 {
343 { C_STRING_WITH_LEN("Create_routine_priv") },
344 { C_STRING_WITH_LEN("enum('N','Y')") },
345 { C_STRING_WITH_LEN("utf8") }
346 },
347 {
348 { C_STRING_WITH_LEN("Alter_routine_priv") },
349 { C_STRING_WITH_LEN("enum('N','Y')") },
350 { C_STRING_WITH_LEN("utf8") }
351 },
352 {
353 { C_STRING_WITH_LEN("Create_user_priv") },
354 { C_STRING_WITH_LEN("enum('N','Y')") },
355 { C_STRING_WITH_LEN("utf8") }
356 },
357 {
358 { C_STRING_WITH_LEN("Event_priv") },
359 { C_STRING_WITH_LEN("enum('N','Y')") },
360 { C_STRING_WITH_LEN("utf8") }
361 },
362 {
363 { C_STRING_WITH_LEN("Trigger_priv") },
364 { C_STRING_WITH_LEN("enum('N','Y')") },
365 { C_STRING_WITH_LEN("utf8") }
366 },
367 {
368 { C_STRING_WITH_LEN("Create_tablespace_priv") },
369 { C_STRING_WITH_LEN("enum('N','Y')") },
370 { C_STRING_WITH_LEN("utf8") }
371 },
372 {
373 { C_STRING_WITH_LEN("ssl_type") },
374 { C_STRING_WITH_LEN("enum('','ANY','X509','SPECIFIED')") },
375 { C_STRING_WITH_LEN("utf8") }
376 },
377 {
378 { C_STRING_WITH_LEN("ssl_cipher") },
379 { C_STRING_WITH_LEN("blob") },
380 {NULL, 0}
381 },
382 {
383 { C_STRING_WITH_LEN("x509_issuer") },
384 { C_STRING_WITH_LEN("blob") },
385 {NULL, 0}
386 },
387 {
388 { C_STRING_WITH_LEN("x509_subject") },
389 { C_STRING_WITH_LEN("blob") },
390 {NULL, 0}
391 },
392 {
393 { C_STRING_WITH_LEN("max_questions") },
394 { C_STRING_WITH_LEN("int(11)") },
395 {NULL, 0}
396 },
397 {
398 { C_STRING_WITH_LEN("max_updates") },
399 { C_STRING_WITH_LEN("int(11)") },
400 {NULL, 0}
401 },
402 {
403 { C_STRING_WITH_LEN("max_connections") },
404 { C_STRING_WITH_LEN("int(11)") },
405 {NULL, 0}
406 },
407 {
408 { C_STRING_WITH_LEN("max_user_connections") },
409 { C_STRING_WITH_LEN("int(11)") },
410 {NULL, 0}
411 },
412 {
413 { C_STRING_WITH_LEN("plugin") },
414 { C_STRING_WITH_LEN("char(64)") },
415 {NULL, 0}
416 },
417 {
418 { C_STRING_WITH_LEN("authentication_string") },
419 { C_STRING_WITH_LEN("text") },
420 {NULL, 0}
421 },
422 {
423 { C_STRING_WITH_LEN("password_expired") },
424 { C_STRING_WITH_LEN("enum('N','Y')") },
425 { C_STRING_WITH_LEN("utf8") }
426 }
427 };
428
429 static const
430 TABLE_FIELD_TYPE mysql_proxies_priv_table_fields[MYSQL_PROXIES_PRIV_FIELD_COUNT] = {
431 {
432 { C_STRING_WITH_LEN("Host") },
433 { C_STRING_WITH_LEN("char(60)") },
434 {NULL, 0}
435 },
436 {
437 { C_STRING_WITH_LEN("User") },
438 { C_STRING_WITH_LEN("char(16)") },
439 {NULL, 0}
440 },
441 {
442 { C_STRING_WITH_LEN("Proxied_host") },
443 { C_STRING_WITH_LEN("char(60)") },
444 {NULL, 0}
445 },
446 {
447 { C_STRING_WITH_LEN("Proxied_user") },
448 { C_STRING_WITH_LEN("char(16)") },
449 {NULL, 0}
450 },
451 {
452 { C_STRING_WITH_LEN("With_grant") },
453 { C_STRING_WITH_LEN("tinyint(1)") },
454 {NULL, 0}
455 },
456 {
457 { C_STRING_WITH_LEN("Grantor") },
458 { C_STRING_WITH_LEN("char(77)") },
459 {NULL, 0}
460 },
461 {
462 { C_STRING_WITH_LEN("Timestamp") },
463 { C_STRING_WITH_LEN("timestamp") },
464 {NULL, 0}
465 }
466 };
467
468 static const
469 TABLE_FIELD_TYPE mysql_procs_priv_table_fields[MYSQL_PROCS_PRIV_FIELD_COUNT] = {
470 {
471 { C_STRING_WITH_LEN("Host") },
472 { C_STRING_WITH_LEN("char(60)") },
473 {NULL, 0}
474 },
475 {
476 { C_STRING_WITH_LEN("Db") },
477 { C_STRING_WITH_LEN("char(64)") },
478 {NULL, 0}
479 },
480 {
481 { C_STRING_WITH_LEN("User") },
482 { C_STRING_WITH_LEN("char(16)") },
483 {NULL, 0}
484 },
485 {
486 { C_STRING_WITH_LEN("Routine_name") },
487 { C_STRING_WITH_LEN("char(64)") },
488 { C_STRING_WITH_LEN("utf8") }
489 },
490 {
491 { C_STRING_WITH_LEN("Routine_type") },
492 { C_STRING_WITH_LEN("enum('FUNCTION','PROCEDURE')") },
493 {NULL, 0}
494 },
495 {
496 { C_STRING_WITH_LEN("Grantor") },
497 { C_STRING_WITH_LEN("char(77)") },
498 {NULL, 0}
499 },
500 {
501 { C_STRING_WITH_LEN("Proc_priv") },
502 { C_STRING_WITH_LEN("set('Execute','Alter Routine','Grant')") },
503 { C_STRING_WITH_LEN("utf8") }
504 },
505 {
506 { C_STRING_WITH_LEN("Timestamp") },
507 { C_STRING_WITH_LEN("timestamp") },
508 {NULL, 0}
509 }
510 };
511
512 static const
513 TABLE_FIELD_TYPE mysql_columns_priv_table_fields[MYSQL_COLUMNS_PRIV_FIELD_COUNT] = {
514 {
515 { C_STRING_WITH_LEN("Host") },
516 { C_STRING_WITH_LEN("char(60)") },
517 {NULL, 0}
518 },
519 {
520 { C_STRING_WITH_LEN("Db") },
521 { C_STRING_WITH_LEN("char(64)") },
522 {NULL, 0}
523 },
524 {
525 { C_STRING_WITH_LEN("User") },
526 { C_STRING_WITH_LEN("char(16)") },
527 {NULL, 0}
528 },
529 {
530 { C_STRING_WITH_LEN("Table_name") },
531 { C_STRING_WITH_LEN("char(64)") },
532 {NULL, 0}
533 },
534 {
535 { C_STRING_WITH_LEN("Column_name") },
536 { C_STRING_WITH_LEN("char(64)") },
537 {NULL, 0}
538 },
539 {
540 { C_STRING_WITH_LEN("Timestamp") },
541 { C_STRING_WITH_LEN("timestamp") },
542 {NULL, 0}
543 },
544 {
545 { C_STRING_WITH_LEN("Column_priv") },
546 { C_STRING_WITH_LEN("set('Select','Insert','Update','References')") },
547 { C_STRING_WITH_LEN("utf8") }
548 }
549 };
550
551 static const
552 TABLE_FIELD_TYPE mysql_tables_priv_table_fields[MYSQL_TABLES_PRIV_FIELD_COUNT] = {
553 {
554 { C_STRING_WITH_LEN("Host") },
555 { C_STRING_WITH_LEN("char(60)") },
556 {NULL, 0}
557 },
558 {
559 { C_STRING_WITH_LEN("Db") },
560 { C_STRING_WITH_LEN("char(64)") },
561 {NULL, 0}
562 },
563 {
564 { C_STRING_WITH_LEN("User") },
565 { C_STRING_WITH_LEN("char(16)") },
566 {NULL, 0}
567 },
568 {
569 { C_STRING_WITH_LEN("Table_name") },
570 { C_STRING_WITH_LEN("char(64)") },
571 {NULL, 0}
572 },
573 {
574 { C_STRING_WITH_LEN("Grantor") },
575 { C_STRING_WITH_LEN("char(77)") },
576 {NULL, 0}
577 },
578 {
579 { C_STRING_WITH_LEN("Timestamp") },
580 { C_STRING_WITH_LEN("timestamp") },
581 {NULL, 0}
582 },
583 {
584 { C_STRING_WITH_LEN("Table_priv") },
585 { C_STRING_WITH_LEN("set('Select','Insert','Update','Delete','Create',"
586 "'Drop','Grant','References','Index','Alter',"
587 "'Create View','Show view','Trigger')") },
588 { C_STRING_WITH_LEN("utf8") }
589 },
590 {
591 { C_STRING_WITH_LEN("Column_priv") },
592 { C_STRING_WITH_LEN("set('Select','Insert','Update','References')") },
593 { C_STRING_WITH_LEN("utf8") }
594 }
595 };
596 #endif // NO_EMBEDDED_ACCESS_CHECKS
597
598
599 const TABLE_FIELD_DEF
600 mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields};
601
602 #ifndef NO_EMBEDDED_ACCESS_CHECKS
603 const TABLE_FIELD_DEF
604 mysql_user_table_def= {MYSQL_USER_FIELD_COUNT, mysql_user_table_fields};
605
606 const TABLE_FIELD_DEF
607 mysql_proxies_priv_table_def= {MYSQL_PROXIES_PRIV_FIELD_COUNT,
608 mysql_proxies_priv_table_fields};
609
610 const TABLE_FIELD_DEF
611 mysql_procs_priv_table_def= {MYSQL_PROCS_PRIV_FIELD_COUNT,
612 mysql_procs_priv_table_fields};
613
614 const TABLE_FIELD_DEF
615 mysql_columns_priv_table_def= {MYSQL_COLUMNS_PRIV_FIELD_COUNT,
616 mysql_columns_priv_table_fields};
617
618 const TABLE_FIELD_DEF
619 mysql_tables_priv_table_def= {MYSQL_TABLES_PRIV_FIELD_COUNT,
620 mysql_tables_priv_table_fields};
621 #endif // NO_EMBEDDED_ACCESS_CHECKS
622
623 static LEX_STRING native_password_plugin_name= {
624 C_STRING_WITH_LEN("mysql_native_password")
625 };
626
627 static LEX_STRING old_password_plugin_name= {
628 C_STRING_WITH_LEN("mysql_old_password")
629 };
630
631 LEX_STRING sha256_password_plugin_name= {
632 C_STRING_WITH_LEN("sha256_password")
633 };
634
635 static LEX_STRING validate_password_plugin_name= {
636 C_STRING_WITH_LEN("validate_password")
637 };
638
639 LEX_STRING default_auth_plugin_name;
640
641 #ifndef NO_EMBEDDED_ACCESS_CHECKS
642 static plugin_ref old_password_plugin;
643 #endif
644 static plugin_ref native_password_plugin;
645
646 #define WARN_DEPRECATED_41_PWD_HASH(thd) \
647 WARN_DEPRECATED(thd, "pre-4.1 password hash", "post-4.1 password hash")
648
649 /* Classes */
650
651 class ACL_HOST_AND_IP
652 {
653 char *hostname;
654 uint hostname_length;
655 long ip, ip_mask; // Used with masked ip:s
656
calc_ip(const char * ip_arg,long * val,char end)657 const char *calc_ip(const char *ip_arg, long *val, char end)
658 {
659 long ip_val,tmp;
660 if (!(ip_arg=str2int(ip_arg,10,0,255,&ip_val)) || *ip_arg != '.')
661 return 0;
662 ip_val<<=24;
663 if (!(ip_arg=str2int(ip_arg+1,10,0,255,&tmp)) || *ip_arg != '.')
664 return 0;
665 ip_val+=tmp<<16;
666 if (!(ip_arg=str2int(ip_arg+1,10,0,255,&tmp)) || *ip_arg != '.')
667 return 0;
668 ip_val+=tmp<<8;
669 if (!(ip_arg=str2int(ip_arg+1,10,0,255,&tmp)) || *ip_arg != end)
670 return 0;
671 *val=ip_val+tmp;
672 return ip_arg;
673 }
674
675 public:
get_host() const676 const char *get_host() const { return hostname; }
get_host_len()677 int get_host_len() { return hostname_length; }
678
has_wildcard()679 bool has_wildcard()
680 {
681 return (strchr(hostname,wild_many) ||
682 strchr(hostname,wild_one) || ip_mask );
683 }
684
check_allow_all_hosts()685 bool check_allow_all_hosts()
686 {
687 return (!hostname ||
688 (hostname[0] == wild_many && !hostname[1]));
689 }
690
691 /**
692 @brief Update the hostname. Updates ip and ip_mask accordingly.
693
694 @param host_arg Value to be stored
695 */
update_hostname(const char * host_arg)696 void update_hostname(const char *host_arg)
697 {
698 hostname=(char*) host_arg; // This will not be modified!
699 hostname_length= hostname ? strlen( hostname ) : 0;
700 if (!host_arg ||
701 (!(host_arg=(char*) calc_ip(host_arg,&ip,'/')) ||
702 !(host_arg=(char*) calc_ip(host_arg+1,&ip_mask,'\0'))))
703 {
704 ip= ip_mask=0; // Not a masked ip
705 }
706 }
707
708 /*
709 @brief Comparing of hostnames
710
711 @param host_arg Hostname to be compared with
712 @param ip_arg IP address to be compared with
713
714 @notes
715 A hostname may be of type:
716 1) hostname (May include wildcards); monty.pp.sci.fi
717 2) ip (May include wildcards); 192.168.0.0
718 3) ip/netmask 192.168.0.0/255.255.255.0
719 A net mask of 0.0.0.0 is not allowed.
720
721 @return
722 true if matched
723 false if not matched
724 */
725
compare_hostname(const char * host_arg,const char * ip_arg)726 bool compare_hostname(const char *host_arg, const char *ip_arg)
727 {
728 long tmp;
729 if (ip_mask && ip_arg && calc_ip(ip_arg,&tmp,'\0'))
730 {
731 return (tmp & ip_mask) == ip;
732 }
733 return (!hostname ||
734 (host_arg &&
735 !wild_case_compare(system_charset_info, host_arg, hostname)) ||
736 (ip_arg && !wild_compare(ip_arg, hostname, 0)));
737 }
738
739 };
740
741 class ACL_ACCESS {
742 public:
743 ACL_HOST_AND_IP host;
744 ulong sort;
745 ulong access;
746 };
747
748 /* ACL_HOST is used if no host is specified */
749
750 class ACL_HOST :public ACL_ACCESS
751 {
752 public:
753 char *db;
754 };
755
756 class ACL_USER :public ACL_ACCESS
757 {
758 public:
759 USER_RESOURCES user_resource;
760 char *user;
761 /**
762 The salt variable is used as the password hash for
763 native_password_authetication and old_password_authentication.
764 */
765 uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form
766 /**
767 In the old protocol the salt_len indicated what type of autnetication
768 protocol was used: 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
769 */
770 uint8 salt_len;
771 enum SSL_type ssl_type;
772 const char *ssl_cipher, *x509_issuer, *x509_subject;
773 LEX_STRING plugin;
774 LEX_STRING auth_string;
775 bool password_expired;
776 bool can_authenticate;
777
copy(MEM_ROOT * root)778 ACL_USER *copy(MEM_ROOT *root)
779 {
780 ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
781 if (!dst)
782 return 0;
783 *dst= *this;
784 dst->user= safe_strdup_root(root, user);
785 dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
786 dst->x509_issuer= safe_strdup_root(root, x509_issuer);
787 dst->x509_subject= safe_strdup_root(root, x509_subject);
788 /*
789 If the plugin is built in we don't need to reallocate the name of the
790 plugin.
791 */
792 if (auth_plugin_is_built_in(dst->plugin.str))
793 dst->plugin= plugin;
794 else
795 {
796 dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
797 dst->plugin.length= plugin.length;
798 }
799 dst->auth_string.str= safe_strdup_root(root, auth_string.str);
800 dst->host.update_hostname(safe_strdup_root(root, host.get_host()));
801 return dst;
802 }
803 };
804
805 class ACL_DB :public ACL_ACCESS
806 {
807 public:
808 char *user,*db;
809 };
810
811
812 #ifndef NO_EMBEDDED_ACCESS_CHECKS
813 static void validate_user_plugin_records();
814
815 static ulong get_sort(uint count,...);
816 static bool show_proxy_grants (THD *thd, LEX_USER *user,
817 char *buff, size_t buffsize);
818
819
820 class ACL_PROXY_USER :public ACL_ACCESS
821 {
822 const char *user;
823 ACL_HOST_AND_IP proxied_host;
824 const char *proxied_user;
825 bool with_grant;
826
827 typedef enum {
828 MYSQL_PROXIES_PRIV_HOST,
829 MYSQL_PROXIES_PRIV_USER,
830 MYSQL_PROXIES_PRIV_PROXIED_HOST,
831 MYSQL_PROXIES_PRIV_PROXIED_USER,
832 MYSQL_PROXIES_PRIV_WITH_GRANT,
833 MYSQL_PROXIES_PRIV_GRANTOR,
834 MYSQL_PROXIES_PRIV_TIMESTAMP } old_acl_proxy_users;
835 public:
ACL_PROXY_USER()836 ACL_PROXY_USER () {};
837
init(const char * host_arg,const char * user_arg,const char * proxied_host_arg,const char * proxied_user_arg,bool with_grant_arg)838 void init(const char *host_arg, const char *user_arg,
839 const char *proxied_host_arg, const char *proxied_user_arg,
840 bool with_grant_arg)
841 {
842 user= (user_arg && *user_arg) ? user_arg : NULL;
843 host.update_hostname ((host_arg && *host_arg) ? host_arg : NULL);
844 proxied_user= (proxied_user_arg && *proxied_user_arg) ?
845 proxied_user_arg : NULL;
846 proxied_host.update_hostname ((proxied_host_arg && *proxied_host_arg) ?
847 proxied_host_arg : NULL);
848 with_grant= with_grant_arg;
849 sort= get_sort(4, host.get_host(), user,
850 proxied_host.get_host(), proxied_user);
851 }
852
init(MEM_ROOT * mem,const char * host_arg,const char * user_arg,const char * proxied_host_arg,const char * proxied_user_arg,bool with_grant_arg)853 void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
854 const char *proxied_host_arg, const char *proxied_user_arg,
855 bool with_grant_arg)
856 {
857 init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
858 (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
859 (proxied_host_arg && *proxied_host_arg) ?
860 strdup_root (mem, proxied_host_arg) : NULL,
861 (proxied_user_arg && *proxied_user_arg) ?
862 strdup_root (mem, proxied_user_arg) : NULL,
863 with_grant_arg);
864 }
865
init(TABLE * table,MEM_ROOT * mem)866 void init(TABLE *table, MEM_ROOT *mem)
867 {
868 init (get_field(mem, table->field[MYSQL_PROXIES_PRIV_HOST]),
869 get_field(mem, table->field[MYSQL_PROXIES_PRIV_USER]),
870 get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]),
871 get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]),
872 table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->val_int() != 0);
873 }
874
get_with_grant()875 bool get_with_grant() { return with_grant; }
get_user()876 const char *get_user() { return user; }
get_proxied_user()877 const char *get_proxied_user() { return proxied_user; }
get_proxied_host()878 const char *get_proxied_host() { return proxied_host.get_host(); }
set_user(MEM_ROOT * mem,const char * user_arg)879 void set_user(MEM_ROOT *mem, const char *user_arg)
880 {
881 user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
882 }
883
check_validity(bool check_no_resolve)884 bool check_validity(bool check_no_resolve)
885 {
886 if (check_no_resolve &&
887 (hostname_requires_resolving(host.get_host()) ||
888 hostname_requires_resolving(proxied_host.get_host())))
889 {
890 sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
891 "ignored in --skip-name-resolve mode.",
892 proxied_user ? proxied_user : "",
893 proxied_host.get_host() ? proxied_host.get_host() : "",
894 user ? user : "",
895 host.get_host() ? host.get_host() : "");
896 return TRUE;
897 }
898 return FALSE;
899 }
900
matches(const char * host_arg,const char * user_arg,const char * ip_arg,const char * proxied_user_arg)901 bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
902 const char *proxied_user_arg)
903 {
904 DBUG_ENTER("ACL_PROXY_USER::matches");
905 DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
906 "compare_hostname(%s,%s,%s) &&"
907 "wild_compare (%s,%s) &&"
908 "wild_compare (%s,%s)",
909 host.get_host() ? host.get_host() : "<NULL>",
910 host_arg ? host_arg : "<NULL>",
911 ip_arg ? ip_arg : "<NULL>",
912 proxied_host.get_host() ? proxied_host.get_host() : "<NULL>",
913 host_arg ? host_arg : "<NULL>",
914 ip_arg ? ip_arg : "<NULL>",
915 user_arg ? user_arg : "<NULL>",
916 user ? user : "<NULL>",
917 proxied_user_arg ? proxied_user_arg : "<NULL>",
918 proxied_user ? proxied_user : "<NULL>"));
919 DBUG_RETURN(host.compare_hostname(host_arg, ip_arg) &&
920 proxied_host.compare_hostname(host_arg, ip_arg) &&
921 (!user ||
922 (user_arg && !wild_compare(user_arg, user, TRUE))) &&
923 (!proxied_user ||
924 (proxied_user && !wild_compare(proxied_user_arg,
925 proxied_user, TRUE))));
926 }
927
928
auth_element_equals(const char * a,const char * b)929 inline static bool auth_element_equals(const char *a, const char *b)
930 {
931 return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
932 }
933
934
pk_equals(ACL_PROXY_USER * grant)935 bool pk_equals(ACL_PROXY_USER *grant)
936 {
937 DBUG_ENTER("pk_equals");
938 DBUG_PRINT("info", ("strcmp(%s,%s) &&"
939 "strcmp(%s,%s) &&"
940 "wild_compare (%s,%s) &&"
941 "wild_compare (%s,%s)",
942 user ? user : "<NULL>",
943 grant->user ? grant->user : "<NULL>",
944 proxied_user ? proxied_user : "<NULL>",
945 grant->proxied_user ? grant->proxied_user : "<NULL>",
946 host.get_host() ? host.get_host() : "<NULL>",
947 grant->host.get_host() ? grant->host.get_host() : "<NULL>",
948 proxied_host.get_host() ? proxied_host.get_host() : "<NULL>",
949 grant->proxied_host.get_host() ?
950 grant->proxied_host.get_host() : "<NULL>"));
951
952 DBUG_RETURN(auth_element_equals(user, grant->user) &&
953 auth_element_equals(proxied_user, grant->proxied_user) &&
954 auth_element_equals(host.get_host(), grant->host.get_host()) &&
955 auth_element_equals(proxied_host.get_host(),
956 grant->proxied_host.get_host()));
957 }
958
959
granted_on(const char * host_arg,const char * user_arg)960 bool granted_on(const char *host_arg, const char *user_arg)
961 {
962 return (((!user && (!user_arg || !user_arg[0])) ||
963 (user && user_arg && !strcmp(user, user_arg))) &&
964 ((!host.get_host() && (!host_arg || !host_arg[0])) ||
965 (host.get_host() && host_arg && !strcmp(host.get_host(), host_arg))));
966 }
967
968
print_grant(String * str)969 void print_grant(String *str)
970 {
971 str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
972 if (proxied_user)
973 str->append(proxied_user, strlen(proxied_user));
974 str->append(STRING_WITH_LEN("'@'"));
975 if (proxied_host.get_host())
976 str->append(proxied_host.get_host(), strlen(proxied_host.get_host()));
977 str->append(STRING_WITH_LEN("' TO '"));
978 if (user)
979 str->append(user, strlen(user));
980 str->append(STRING_WITH_LEN("'@'"));
981 if (host.get_host())
982 str->append(host.get_host(), strlen(host.get_host()));
983 str->append(STRING_WITH_LEN("'"));
984 if (with_grant)
985 str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
986 }
987
set_data(ACL_PROXY_USER * grant)988 void set_data(ACL_PROXY_USER *grant)
989 {
990 with_grant= grant->with_grant;
991 }
992
store_pk(TABLE * table,const LEX_STRING * host,const LEX_STRING * user,const LEX_STRING * proxied_host,const LEX_STRING * proxied_user)993 static int store_pk(TABLE *table,
994 const LEX_STRING *host,
995 const LEX_STRING *user,
996 const LEX_STRING *proxied_host,
997 const LEX_STRING *proxied_user)
998 {
999 DBUG_ENTER("ACL_PROXY_USER::store_pk");
1000 DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
1001 host->str ? host->str : "<NULL>",
1002 user->str ? user->str : "<NULL>",
1003 proxied_host->str ? proxied_host->str : "<NULL>",
1004 proxied_user->str ? proxied_user->str : "<NULL>"));
1005 if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
1006 host->length,
1007 system_charset_info))
1008 DBUG_RETURN(TRUE);
1009 if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
1010 user->length,
1011 system_charset_info))
1012 DBUG_RETURN(TRUE);
1013 if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str,
1014 proxied_host->length,
1015 system_charset_info))
1016 DBUG_RETURN(TRUE);
1017 if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str,
1018 proxied_user->length,
1019 system_charset_info))
1020 DBUG_RETURN(TRUE);
1021
1022 DBUG_RETURN(FALSE);
1023 }
1024
store_data_record(TABLE * table,const LEX_STRING * host,const LEX_STRING * user,const LEX_STRING * proxied_host,const LEX_STRING * proxied_user,bool with_grant,const char * grantor)1025 static int store_data_record(TABLE *table,
1026 const LEX_STRING *host,
1027 const LEX_STRING *user,
1028 const LEX_STRING *proxied_host,
1029 const LEX_STRING *proxied_user,
1030 bool with_grant,
1031 const char *grantor)
1032 {
1033 DBUG_ENTER("ACL_PROXY_USER::store_pk");
1034 if (store_pk(table, host, user, proxied_host, proxied_user))
1035 DBUG_RETURN(TRUE);
1036 DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
1037 if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
1038 TRUE))
1039 DBUG_RETURN(TRUE);
1040 if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
1041 strlen(grantor),
1042 system_charset_info))
1043 DBUG_RETURN(TRUE);
1044
1045 DBUG_RETURN(FALSE);
1046 }
1047 };
1048
1049 #define FIRST_NON_YN_FIELD 26
1050
1051 class acl_entry :public hash_filo_element
1052 {
1053 public:
1054 ulong access;
1055 uint16 length;
1056 char key[1]; // Key will be stored here
1057 };
1058
1059
acl_entry_get_key(acl_entry * entry,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))1060 static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
1061 my_bool not_used MY_ATTRIBUTE((unused)))
1062 {
1063 *length=(uint) entry->length;
1064 return (uchar*) entry->key;
1065 }
1066
1067
1068 /**
1069 Class to validate the flawlessness of ACL table
1070 before performing ACL operations.
1071 */
1072 class Acl_table_intact : public Table_check_intact
1073 {
1074 protected:
report_error(uint code,const char * fmt,...)1075 void report_error(uint code, const char *fmt, ...)
1076 {
1077 va_list args;
1078 va_start(args, fmt);
1079
1080 if (code == 0)
1081 error_log_print(WARNING_LEVEL, fmt, args);
1082 else if (code == ER_CANNOT_LOAD_FROM_TABLE_V2)
1083 {
1084 char *db_name, *table_name;
1085 db_name= va_arg(args, char *);
1086 table_name= va_arg(args, char *);
1087 my_error(code, MYF(ME_NOREFRESH), db_name, table_name);
1088 }
1089 else
1090 my_printv_error(code, ER(code), MYF(ME_NOREFRESH), args);
1091
1092 va_end(args);
1093 }
1094 public:
Acl_table_intact()1095 Acl_table_intact() { has_keys= TRUE; }
1096 };
1097
1098 #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
1099 #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
1100 1 + USERNAME_LENGTH + 1)
1101
1102 /** Size of the header fields of an authentication packet. */
1103 #define AUTH_PACKET_HEADER_SIZE_PROTO_41 32
1104 #define AUTH_PACKET_HEADER_SIZE_PROTO_40 5
1105
1106 static DYNAMIC_ARRAY acl_users, acl_dbs, acl_proxy_users;
1107 static MEM_ROOT global_acl_memory, memex;
1108 static bool initialized=0;
1109 static bool allow_all_hosts=1;
1110 static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
1111 static DYNAMIC_ARRAY acl_wild_hosts;
1112 static hash_filo *acl_cache;
1113 static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
1114 static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
1115 static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
1116 static ulong get_sort(uint count,...);
1117 static void init_check_host(void);
1118 static void rebuild_check_host(void);
1119 static ACL_USER *find_acl_user(const char *host, const char *user,
1120 my_bool exact);
1121 static bool update_user_table(THD *, TABLE *table, const char *host,
1122 const char *user,
1123 const char *new_password,
1124 uint new_password_len,
1125 enum mysql_user_table_field password_field,
1126 bool password_expired, bool is_user_table_positioned);
1127 static my_bool acl_load(THD *thd, TABLE_LIST *tables);
1128 static my_bool grant_load(THD *thd, TABLE_LIST *tables);
1129 static inline void get_grantor(THD *thd, char* grantor);
1130
1131 /**
1132 Escapes special characters in the unescaped string, taking into account
1133 the current character set and sql mode.
1134
1135 @param thd [in] The thd structure.
1136 @param to [out] Escaped string output buffer.
1137 @param from [in] String to escape.
1138 @param length [in] String to escape length.
1139
1140 @return Result value.
1141 @retval != (ulong)-1 Succeeded. Number of bytes written to the output
1142 buffer without the '\0' character.
1143 @retval (ulong)-1 Failed.
1144 */
1145
escape_string_mysql(THD * thd,char * to,const char * from,ulong length)1146 inline ulong escape_string_mysql(THD *thd, char *to, const char *from,
1147 ulong length)
1148 {
1149 if (!(thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
1150 return (uint)escape_string_for_mysql(system_charset_info, to, 0, from,
1151 length);
1152 else
1153 return (uint)escape_quotes_for_mysql(system_charset_info, to, 0, from,
1154 length);
1155 }
1156
1157 /*
1158 Enumeration of various ACL's and Hashes used in handle_grant_struct()
1159 */
1160 enum enum_acl_lists
1161 {
1162 USER_ACL= 0,
1163 DB_ACL,
1164 COLUMN_PRIVILEGES_HASH,
1165 PROC_PRIVILEGES_HASH,
1166 FUNC_PRIVILEGES_HASH,
1167 PROXY_USERS_ACL
1168 };
1169
1170 static ACL_USER acl_utility_user;
1171 LEX_STRING acl_utility_user_name, acl_utility_user_host_name;
1172 static DYNAMIC_ARRAY acl_utility_user_schema_access;
1173 static bool acl_utility_user_initialized= false;
1174 static my_bool acl_init_utility_user(my_bool check_no_resolve);
1175 static void acl_free_utility_user();
1176
1177 /**
1178 Convert scrambled password to binary form, according to scramble type,
1179 Binary form is stored in user.salt.
1180
1181 @param acl_user The object where to store the salt
1182 @param password The password hash containing the salt
1183 @param password_len The length of the password hash
1184
1185 Despite the name of the function it is used when loading ACLs from disk
1186 to store the password hash in the ACL_USER object.
1187 Note that it works only for native and "old" mysql authentication built-in
1188 plugins.
1189
1190 Assumption : user's authentication plugin information is available.
1191
1192 @return Password hash validation
1193 @retval false Hash is of suitable length
1194 @retval true Hash is of wrong length or format
1195 */
1196
1197 static
1198 bool
set_user_salt(ACL_USER * acl_user,const char * password,uint password_len)1199 set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
1200 {
1201 bool result= false;
1202 /* Using old password protocol */
1203 if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
1204 {
1205 get_salt_from_password(acl_user->salt, password);
1206 acl_user->salt_len= SCRAMBLE_LENGTH;
1207 }
1208 else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1209 {
1210 get_salt_from_password_323((ulong *) acl_user->salt, password);
1211 acl_user->salt_len= SCRAMBLE_LENGTH_323;
1212 }
1213 else if (password_len == 0 || password == NULL)
1214 {
1215 /* This account doesn't use a password */
1216 acl_user->salt_len= 0;
1217 }
1218 else if (acl_user->plugin.str == native_password_plugin_name.str ||
1219 acl_user->plugin.str == old_password_plugin_name.str)
1220 {
1221 /* Unexpected format of the hash; login will probably be impossible */
1222 result= true;
1223 }
1224
1225 /*
1226 Since we're changing the password for the user we need to reset the
1227 expiration flag.
1228 */
1229 acl_user->password_expired= false;
1230
1231 return result;
1232 }
1233
1234 /*
1235 Initialize structures responsible for user/db-level privilege checking and
1236 load privilege information for them from tables in the 'mysql' database.
1237
1238 SYNOPSIS
1239 acl_init()
1240 dont_read_acl_tables TRUE if we want to skip loading data from
1241 privilege tables and disable privilege checking.
1242
1243 NOTES
1244 This function is mostly responsible for preparatory steps, main work
1245 on initialization and grants loading is done in acl_reload().
1246
1247 RETURN VALUES
1248 0 ok
1249 1 Could not initialize grant's
1250 */
1251
acl_init(bool dont_read_acl_tables)1252 my_bool acl_init(bool dont_read_acl_tables)
1253 {
1254 THD *thd;
1255 my_bool return_val;
1256 DBUG_ENTER("acl_init");
1257
1258 acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
1259 (my_hash_get_key) acl_entry_get_key,
1260 (my_hash_free_key) free,
1261 &my_charset_utf8_bin);
1262
1263 /*
1264 cache built-in native authentication plugins,
1265 to avoid hash searches and a global mutex lock on every connect
1266 */
1267 native_password_plugin= my_plugin_lock_by_name(0,
1268 &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
1269 old_password_plugin= my_plugin_lock_by_name(0,
1270 &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
1271
1272 if (!native_password_plugin || !old_password_plugin)
1273 DBUG_RETURN(1);
1274
1275 if (dont_read_acl_tables)
1276 {
1277 DBUG_RETURN(0); /* purecov: tested */
1278 }
1279
1280 /*
1281 To be able to run this from boot, we allocate a temporary THD
1282 */
1283 if (!(thd=new THD))
1284 DBUG_RETURN(1); /* purecov: inspected */
1285 thd->thread_stack= (char*) &thd;
1286 thd->store_globals();
1287 /*
1288 It is safe to call acl_reload() since acl_* arrays and hashes which
1289 will be freed there are global static objects and thus are initialized
1290 by zeros at startup.
1291 */
1292 return_val= acl_reload(thd);
1293 delete thd;
1294 /* Remember that we don't have a THD */
1295 my_pthread_setspecific_ptr(THR_THD, 0);
1296 DBUG_RETURN(return_val);
1297 }
1298
1299 /*
1300 Initialize structures responsible for user/db-level privilege checking
1301 and load information about grants from open privilege tables.
1302
1303 SYNOPSIS
1304 acl_load()
1305 thd Current thread
1306 tables List containing open "mysql.host", "mysql.user",
1307 "mysql.db" and "mysql.proxies_priv" tables in that order.
1308
1309 RETURN VALUES
1310 FALSE Success
1311 TRUE Error
1312 */
1313
acl_load(THD * thd,TABLE_LIST * tables)1314 static my_bool acl_load(THD *thd, TABLE_LIST *tables)
1315 {
1316 TABLE *table;
1317 READ_RECORD read_record_info;
1318 my_bool return_val= TRUE;
1319 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
1320 char tmp_name[NAME_LEN+1];
1321 int password_length;
1322 char *password;
1323 uint password_len;
1324 sql_mode_t old_sql_mode= thd->variables.sql_mode;
1325 bool password_expired= false;
1326 DBUG_ENTER("acl_load");
1327
1328 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
1329
1330 grant_version++; /* Privileges updated */
1331
1332
1333 acl_cache->clear(1); // Clear locked hostname cache
1334
1335 init_sql_alloc(&global_acl_memory, ACL_ALLOC_BLOCK_SIZE, 0);
1336 /*
1337 Prepare reading from the mysql.user table
1338 */
1339 if (init_read_record(&read_record_info, thd, table=tables[0].table,
1340 NULL, 1, 1, FALSE))
1341 goto end;
1342 table->use_all_columns();
1343
1344 allow_all_hosts=0;
1345 while (!(read_record_info.read_record(&read_record_info)))
1346 {
1347 password_expired= false;
1348 /* Reading record from mysql.user */
1349 ACL_USER user;
1350 memset(&user, 0, sizeof(user));
1351 user.host.update_hostname(get_field(&global_acl_memory,
1352 table->field[MYSQL_USER_FIELD_HOST]));
1353 user.user= get_field(&global_acl_memory,
1354 table->field[MYSQL_USER_FIELD_USER]);
1355
1356 /*
1357 All accounts can authenticate per default. This will change when
1358 we add a new field to the user table.
1359
1360 Currently this flag is only set to false when authentication is attempted
1361 using an unknown user name.
1362 */
1363 user.can_authenticate= true;
1364
1365 if (check_no_resolve && hostname_requires_resolving(user.host.get_host()))
1366 {
1367 sql_print_warning("'user' entry '%s@%s' "
1368 "ignored in --skip-name-resolve mode.",
1369 user.user ? user.user : "",
1370 user.host.get_host() ? user.host.get_host() : "");
1371 continue;
1372 }
1373
1374 /* Read legacy password */
1375 password= get_field(&global_acl_memory,
1376 table->field[MYSQL_USER_FIELD_PASSWORD]);
1377 password_len= password ? strlen(password) : 0;
1378 user.auth_string.str= password ? password : const_cast<char*>("");
1379 user.auth_string.length= password_len;
1380
1381 {
1382 uint next_field;
1383 user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
1384 /*
1385 if it is pre 5.0.1 privilege table then map CREATE privilege on
1386 CREATE VIEW & SHOW VIEW privileges
1387 */
1388 if (table->s->fields <= 31 && (user.access & CREATE_ACL))
1389 user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
1390
1391 /*
1392 if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
1393 CREATE PROCEDURE & ALTER PROCEDURE privileges
1394 */
1395 if (table->s->fields <= 33 && (user.access & CREATE_ACL))
1396 user.access|= CREATE_PROC_ACL;
1397 if (table->s->fields <= 33 && (user.access & ALTER_ACL))
1398 user.access|= ALTER_PROC_ACL;
1399
1400 /*
1401 pre 5.0.3 did not have CREATE_USER_ACL
1402 */
1403 if (table->s->fields <= 36 && (user.access & GRANT_ACL))
1404 user.access|= CREATE_USER_ACL;
1405
1406
1407 /*
1408 if it is pre 5.1.6 privilege table then map CREATE privilege on
1409 CREATE|ALTER|DROP|EXECUTE EVENT
1410 */
1411 if (table->s->fields <= 37 && (user.access & SUPER_ACL))
1412 user.access|= EVENT_ACL;
1413
1414 /*
1415 if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
1416 */
1417 if (table->s->fields <= 38 && (user.access & SUPER_ACL))
1418 user.access|= TRIGGER_ACL;
1419
1420 user.sort= get_sort(2,user.host.get_host(),user.user);
1421
1422 /* Starting from 4.0.2 we have more fields */
1423 if (table->s->fields >= 31)
1424 {
1425 char *ssl_type=
1426 get_field(thd->mem_root, table->field[MYSQL_USER_FIELD_SSL_TYPE]);
1427 if (!ssl_type)
1428 user.ssl_type=SSL_TYPE_NONE;
1429 else if (!strcmp(ssl_type, "ANY"))
1430 user.ssl_type=SSL_TYPE_ANY;
1431 else if (!strcmp(ssl_type, "X509"))
1432 user.ssl_type=SSL_TYPE_X509;
1433 else /* !strcmp(ssl_type, "SPECIFIED") */
1434 user.ssl_type=SSL_TYPE_SPECIFIED;
1435
1436 user.ssl_cipher=
1437 get_field(&global_acl_memory, table->field[MYSQL_USER_FIELD_SSL_CIPHER]);
1438 user.x509_issuer=
1439 get_field(&global_acl_memory, table->field[MYSQL_USER_FIELD_X509_ISSUER]);
1440 user.x509_subject=
1441 get_field(&global_acl_memory, table->field[MYSQL_USER_FIELD_X509_SUBJECT]);
1442
1443 char *ptr= get_field(thd->mem_root,
1444 table->field[MYSQL_USER_FIELD_MAX_QUESTIONS]);
1445 user.user_resource.questions=ptr ? atoi(ptr) : 0;
1446 ptr= get_field(thd->mem_root,
1447 table->field[MYSQL_USER_FIELD_MAX_UPDATES]);
1448 user.user_resource.updates=ptr ? atoi(ptr) : 0;
1449 ptr= get_field(thd->mem_root,
1450 table->field[MYSQL_USER_FIELD_MAX_CONNECTIONS]);
1451 user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
1452 if (user.user_resource.questions || user.user_resource.updates ||
1453 user.user_resource.conn_per_hour)
1454 mqh_used=1;
1455
1456 if (table->s->fields > MYSQL_USER_FIELD_MAX_USER_CONNECTIONS)
1457 {
1458 /* Starting from 5.0.3 we have max_user_connections field */
1459 ptr= get_field(thd->mem_root,
1460 table->field[MYSQL_USER_FIELD_MAX_USER_CONNECTIONS]);
1461 user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
1462 }
1463
1464 if (table->s->fields >= 41)
1465 {
1466 /* We may have plugin & auth_String fields */
1467 char *tmpstr= get_field(&global_acl_memory,
1468 table->field[MYSQL_USER_FIELD_PLUGIN]);
1469 if (tmpstr)
1470 {
1471 /*
1472 By comparing the plugin with the built in plugins it is possible
1473 to optimize the string allocation and comparision.
1474 */
1475 if (my_strcasecmp(system_charset_info, tmpstr,
1476 native_password_plugin_name.str) == 0)
1477 user.plugin= native_password_plugin_name;
1478 else
1479 if (my_strcasecmp(system_charset_info, tmpstr,
1480 old_password_plugin_name.str) == 0)
1481 user.plugin= old_password_plugin_name;
1482 #if defined(HAVE_OPENSSL)
1483 else
1484 if (my_strcasecmp(system_charset_info, tmpstr,
1485 sha256_password_plugin_name.str) == 0)
1486 user.plugin= sha256_password_plugin_name;
1487 #endif
1488 else
1489 {
1490 user.plugin.str= tmpstr;
1491 user.plugin.length= strlen(tmpstr);
1492 }
1493 if (user.auth_string.length &&
1494 user.plugin.str != native_password_plugin_name.str &&
1495 user.plugin.str != old_password_plugin_name.str)
1496 {
1497 sql_print_warning("'user' entry '%s@%s' has both a password "
1498 "and an authentication plugin specified. The "
1499 "password will be ignored.",
1500 user.user ? user.user : "",
1501 user.host.get_host() ? user.host.get_host() : "");
1502 }
1503 user.auth_string.str=
1504 get_field(&global_acl_memory,
1505 table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]);
1506 if (!user.auth_string.str)
1507 user.auth_string.str= const_cast<char*>("");
1508 user.auth_string.length= strlen(user.auth_string.str);
1509 }
1510 else /* skip auth_string if there's no plugin */
1511 next_field++;
1512 }
1513
1514 if (table->s->fields > MYSQL_USER_FIELD_PASSWORD_EXPIRED)
1515 {
1516 char *tmpstr= get_field(&global_acl_memory,
1517 table->field[MYSQL_USER_FIELD_PASSWORD_EXPIRED]);
1518 if (tmpstr && (*tmpstr == 'Y' || *tmpstr == 'y'))
1519 {
1520 user.password_expired= true;
1521
1522 if (!auth_plugin_supports_expiration(user.plugin.str))
1523 {
1524 sql_print_warning("'user' entry '%s@%s' has the password ignore "
1525 "flag raised, but its authentication plugin "
1526 "doesn't support password expiration. "
1527 "The user id will be ignored.",
1528 user.user ? user.user : "",
1529 user.host.get_host() ? user.host.get_host() : "");
1530 continue;
1531 }
1532 password_expired= true;
1533 }
1534 }
1535 } // end if (table->s->fields >= 31)
1536 else
1537 {
1538 user.ssl_type=SSL_TYPE_NONE;
1539 #ifndef TO_BE_REMOVED
1540 if (table->s->fields <= 13)
1541 { // Without grant
1542 if (user.access & CREATE_ACL)
1543 user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
1544 }
1545 /* Convert old privileges */
1546 user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
1547 if (user.access & FILE_ACL)
1548 user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
1549 if (user.access & PROCESS_ACL)
1550 user.access|= SUPER_ACL | EXECUTE_ACL;
1551 #endif
1552 }
1553 if (!user.plugin.length)
1554 {
1555 /*
1556 Set plugin deduced from password length.
1557 We can reach here in two cases:
1558 1. mysql.user doesn't have plugin field
1559 2. Plugin field is empty
1560 */
1561 user.plugin= native_password_plugin_name;
1562 if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1563 user.plugin= old_password_plugin_name;
1564
1565 }
1566 /*
1567 Transform hex to octets and adjust the format.
1568 */
1569 if (set_user_salt(&user, password, password_len))
1570 {
1571 sql_print_warning("Found invalid password for user: '%s@%s'; "
1572 "Ignoring user", user.user ? user.user : "",
1573 user.host.get_host() ? user.host.get_host() : "");
1574 continue;
1575 }
1576
1577 /* set_user_salt resets expiration flag so restore it */
1578 user.password_expired= password_expired;
1579
1580 (void) push_dynamic(&acl_users,(uchar*) &user);
1581 if (user.host.check_allow_all_hosts())
1582 allow_all_hosts=1; // Anyone can connect
1583 }
1584 } // END while reading records from the mysql.user table
1585
1586 if(!acl_init_utility_user(check_no_resolve))
1587 goto end;
1588
1589 my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
1590 sizeof(ACL_USER),(qsort_cmp) acl_compare);
1591 end_read_record(&read_record_info);
1592 freeze_size(&acl_users);
1593
1594 /* Legacy password integrity checks ----------------------------------------*/
1595 {
1596 if (table->s->fields <= MYSQL_USER_FIELD_PASSWORD)
1597 {
1598 sql_print_error("Fatal error: mysql.user table is damaged with missing "
1599 "password column.");
1600 goto end;
1601 }
1602
1603 password_length= table->field[MYSQL_USER_FIELD_PASSWORD]->field_length /
1604 table->field[MYSQL_USER_FIELD_PASSWORD]->charset()->mbmaxlen;
1605 if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1606 {
1607 sql_print_error("Fatal error: mysql.user table is damaged or in "
1608 "unsupported 3.20 format.");
1609 goto end;
1610 }
1611
1612 DBUG_PRINT("info",("user table fields: %d, password length: %d",
1613 table->s->fields, password_length));
1614
1615 mysql_mutex_lock(&LOCK_global_system_variables);
1616 if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
1617 {
1618 if (opt_secure_auth)
1619 {
1620 mysql_mutex_unlock(&LOCK_global_system_variables);
1621 sql_print_error("Fatal error: mysql.user table is in old format, "
1622 "but server started with --secure-auth option.");
1623 goto end;
1624 }
1625 mysql_user_table_is_in_short_password_format= true;
1626 if (global_system_variables.old_passwords)
1627 mysql_mutex_unlock(&LOCK_global_system_variables);
1628 else
1629 {
1630 global_system_variables.old_passwords= 1;
1631 mysql_mutex_unlock(&LOCK_global_system_variables);
1632 sql_print_warning("mysql.user table is not updated to new password format; "
1633 "Disabling new password usage until "
1634 "mysql_fix_privilege_tables is run");
1635 }
1636 thd->variables.old_passwords= 1;
1637 }
1638 else
1639 {
1640 mysql_user_table_is_in_short_password_format= false;
1641 mysql_mutex_unlock(&LOCK_global_system_variables);
1642 }
1643 } /* End legacy password integrity checks ----------------------------------*/
1644
1645 /*
1646 Prepare reading from the mysql.db table
1647 */
1648 if (init_read_record(&read_record_info, thd, table=tables[1].table,
1649 NULL, 1, 1, FALSE))
1650 goto end;
1651 table->use_all_columns();
1652 while (!(read_record_info.read_record(&read_record_info)))
1653 {
1654 /* Reading record in mysql.db */
1655 ACL_DB db;
1656 db.host.update_hostname(get_field(&global_acl_memory,
1657 table->field[MYSQL_DB_FIELD_HOST]));
1658 db.db=get_field(&global_acl_memory, table->field[MYSQL_DB_FIELD_DB]);
1659 if (!db.db)
1660 {
1661 sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
1662 continue;
1663 }
1664 db.user=get_field(&global_acl_memory, table->field[MYSQL_DB_FIELD_USER]);
1665 if (check_no_resolve && hostname_requires_resolving(db.host.get_host()))
1666 {
1667 sql_print_warning("'db' entry '%s %s@%s' "
1668 "ignored in --skip-name-resolve mode.",
1669 db.db,
1670 db.user ? db.user : "",
1671 db.host.get_host() ? db.host.get_host() : "");
1672 continue;
1673 }
1674 db.access=get_access(table,3);
1675 db.access=fix_rights_for_db(db.access);
1676 if (lower_case_table_names)
1677 {
1678 /*
1679 convert db to lower case and give a warning if the db wasn't
1680 already in lower case
1681 */
1682 (void)strmov(tmp_name, db.db);
1683 my_casedn_str(files_charset_info, db.db);
1684 if (strcmp(db.db, tmp_name) != 0)
1685 {
1686 sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
1687 "case that has been forced to lowercase because "
1688 "lower_case_table_names is set. It will not be "
1689 "possible to remove this privilege using REVOKE.",
1690 db.db,
1691 db.user ? db.user : "",
1692 db.host.get_host() ? db.host.get_host() : "");
1693 }
1694 }
1695 db.sort=get_sort(3,db.host.get_host(),db.db,db.user);
1696 #ifndef TO_BE_REMOVED
1697 if (table->s->fields <= 9)
1698 { // Without grant
1699 if (db.access & CREATE_ACL)
1700 db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
1701 }
1702 #endif
1703 (void) push_dynamic(&acl_dbs,(uchar*) &db);
1704 } // END reading records from mysql.db tables
1705
1706 my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
1707 sizeof(ACL_DB),(qsort_cmp) acl_compare);
1708 end_read_record(&read_record_info);
1709 freeze_size(&acl_dbs);
1710
1711 /* Prepare to read records from the mysql.proxies_priv table */
1712 if (tables[2].table)
1713 {
1714 if (init_read_record(&read_record_info, thd, table= tables[2].table,
1715 NULL, 1, 1, FALSE))
1716 goto end;
1717 table->use_all_columns();
1718 while (!(read_record_info.read_record(&read_record_info)))
1719 {
1720 /* Reading record in mysql.proxies_priv */
1721 ACL_PROXY_USER proxy;
1722 proxy.init(table, &global_acl_memory);
1723 if (proxy.check_validity(check_no_resolve))
1724 continue;
1725 if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
1726 {
1727 end_read_record(&read_record_info);
1728 goto end;
1729 }
1730 } // END reading records from the mysql.proxies_priv table
1731
1732 my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
1733 acl_proxy_users.elements,
1734 sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
1735 end_read_record(&read_record_info);
1736 }
1737 else
1738 {
1739 sql_print_error("Missing system table mysql.proxies_priv; "
1740 "please run mysql_upgrade to create it");
1741 }
1742 freeze_size(&acl_proxy_users);
1743
1744 validate_user_plugin_records();
1745 init_check_host();
1746
1747 initialized=1;
1748 return_val= FALSE;
1749
1750 end:
1751 thd->variables.sql_mode= old_sql_mode;
1752 DBUG_RETURN(return_val);
1753 }
1754
1755
acl_free(bool end)1756 void acl_free(bool end)
1757 {
1758 acl_free_utility_user();
1759 free_root(&global_acl_memory,MYF(0));
1760 delete_dynamic(&acl_users);
1761 delete_dynamic(&acl_dbs);
1762 delete_dynamic(&acl_wild_hosts);
1763 delete_dynamic(&acl_proxy_users);
1764 my_hash_free(&acl_check_hosts);
1765 if (!end)
1766 acl_cache->clear(1); /* purecov: inspected */
1767 else
1768 {
1769 plugin_unlock(0, native_password_plugin);
1770 plugin_unlock(0, old_password_plugin);
1771 delete acl_cache;
1772 acl_cache=0;
1773 }
1774 }
1775
1776
1777 /**
1778 A helper function to commit statement transaction and close
1779 ACL tables after reading some data from them as part of FLUSH
1780 PRIVILEGES statement or during server initialization.
1781
1782 @note We assume that we have only read from the tables so commit
1783 can't fail. @sa close_mysql_tables().
1784
1785 @note This function also rollbacks the transaction if rollback was
1786 requested (e.g. as result of deadlock).
1787 */
1788
close_acl_tables(THD * thd)1789 void close_acl_tables(THD *thd)
1790 {
1791 /* Transaction rollback request by SE is unlikely. Still we handle it. */
1792 if (thd->transaction_rollback_request)
1793 {
1794 trans_rollback_stmt(thd);
1795 trans_rollback_implicit(thd);
1796 }
1797 else
1798 {
1799 #ifndef DBUG_OFF
1800 bool res=
1801 #endif
1802 trans_commit_stmt(thd);
1803 DBUG_ASSERT(res == false);
1804 }
1805
1806 close_mysql_tables(thd);
1807 }
1808
1809
1810 /**
1811 Commit ACL statement (and transaction) ignoring the fact that it might have
1812 ended with an error, close tables which it has opened and release metadata
1813 locks.
1814
1815 @note In case of failure to commit transaction we try to restore correct
1816 state of in-memory structures by reloading privileges.
1817
1818 @retval False - Success.
1819 @retval True - Error.
1820 */
1821
acl_trans_commit_and_close_tables(THD * thd)1822 static bool acl_trans_commit_and_close_tables(THD *thd)
1823 {
1824 bool result;
1825 bool rollback= false;
1826
1827 /*
1828 Try to commit a transaction even if we had some failures.
1829
1830 Without this step changes to privilege tables will be rolled back at the
1831 end of mysql_execute_command() in the presence of error, leaving on-disk
1832 and in-memory descriptions of privileges out of sync and making behavior
1833 of ACL statements for transactional tables incompatible with legacy
1834 behavior.
1835
1836 We need to commit both statement and normal transaction to make behavior
1837 consistent with both autocommit on and off.
1838
1839 It is safe to do so since ACL statement always do implicit commit at the
1840 end of statement.
1841 */
1842 DBUG_ASSERT(stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END));
1843
1844 if (thd->transaction_rollback_request)
1845 {
1846 /*
1847 Transaction rollback request by SE is unlikely. Still let us
1848 handle it and also do ACL reload if it happens.
1849 */
1850 result= trans_rollback_stmt(thd);
1851 result|= trans_rollback_implicit(thd);
1852 rollback= true;
1853 }
1854 else
1855 {
1856 result= trans_commit_stmt(thd);
1857 result|= trans_commit_implicit(thd);
1858 }
1859 close_thread_tables(thd);
1860 thd->mdl_context.release_transactional_locks();
1861
1862 if (result || rollback)
1863 {
1864 /*
1865 Try to bring in-memory structures back in sync with on-disk data if we
1866 have failed to commit our changes.
1867 */
1868 (void) acl_reload(thd);
1869 (void) grant_reload(thd);
1870 }
1871
1872 return result;
1873 }
1874
1875
1876 /*
1877 Forget current user/db-level privileges and read new privileges
1878 from the privilege tables.
1879
1880 SYNOPSIS
1881 acl_reload()
1882 thd Current thread
1883
1884 NOTE
1885 All tables of calling thread which were open and locked by LOCK TABLES
1886 statement will be unlocked and closed.
1887 This function is also used for initialization of structures responsible
1888 for user/db-level privilege checking.
1889
1890 RETURN VALUE
1891 FALSE Success
1892 TRUE Failure
1893 */
1894
acl_reload(THD * thd)1895 my_bool acl_reload(THD *thd)
1896 {
1897 TABLE_LIST tables[3];
1898 DYNAMIC_ARRAY old_acl_users, old_acl_dbs, old_acl_proxy_users;
1899 MEM_ROOT old_mem;
1900 bool old_initialized;
1901 my_bool return_val= TRUE;
1902 DBUG_ENTER("acl_reload");
1903
1904 /*
1905 To avoid deadlocks we should obtain table locks before
1906 obtaining acl_cache->lock mutex.
1907 */
1908 tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
1909 C_STRING_WITH_LEN("user"), "user", TL_READ);
1910 tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
1911 C_STRING_WITH_LEN("db"), "db", TL_READ);
1912 tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
1913 C_STRING_WITH_LEN("proxies_priv"),
1914 "proxies_priv", TL_READ);
1915 tables[0].next_local= tables[0].next_global= tables + 1;
1916 tables[1].next_local= tables[1].next_global= tables + 2;
1917 tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
1918 tables[2].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
1919
1920 if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
1921 {
1922 /*
1923 Execution might have been interrupted; only print the error message
1924 if a user error condition has been raised.
1925 */
1926 if (thd->get_stmt_da()->is_error())
1927 {
1928 sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
1929 thd->get_stmt_da()->message());
1930 }
1931 goto end;
1932 }
1933
1934 if ((old_initialized=initialized))
1935 mysql_mutex_lock(&acl_cache->lock);
1936
1937 old_acl_users= acl_users;
1938 old_acl_proxy_users= acl_proxy_users;
1939 old_acl_dbs= acl_dbs;
1940 my_init_dynamic_array(&acl_users, sizeof(ACL_USER), 50, 100);
1941 my_init_dynamic_array(&acl_dbs, sizeof(ACL_DB), 50, 100);
1942 my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), 50, 100);
1943 old_mem= global_acl_memory;
1944 delete_dynamic(&acl_wild_hosts);
1945 my_hash_free(&acl_check_hosts);
1946
1947 if ((return_val= acl_load(thd, tables)))
1948 { // Error. Revert to old list
1949 DBUG_PRINT("error",("Reverting to old privileges"));
1950 acl_free(); /* purecov: inspected */
1951 acl_users= old_acl_users;
1952 acl_proxy_users= old_acl_proxy_users;
1953 acl_dbs= old_acl_dbs;
1954 global_acl_memory= old_mem;
1955 init_check_host();
1956 }
1957 else
1958 {
1959 free_root(&old_mem,MYF(0));
1960 delete_dynamic(&old_acl_users);
1961 delete_dynamic(&old_acl_proxy_users);
1962 delete_dynamic(&old_acl_dbs);
1963 }
1964 if (old_initialized)
1965 mysql_mutex_unlock(&acl_cache->lock);
1966 end:
1967 close_acl_tables(thd);
1968
1969 DEBUG_SYNC(thd, "after_acl_reload");
1970 DBUG_RETURN(return_val);
1971 }
1972
1973 /*
1974 Set up the acl_utility_user and add it to the acl_user list.
1975 */
1976 static
1977 my_bool
acl_init_utility_user(my_bool check_no_resolve)1978 acl_init_utility_user(my_bool check_no_resolve)
1979 {
1980 char password[CRYPT_MAX_PASSWORD_SIZE + 1];
1981 uint i, passlen;
1982 my_bool ret= TRUE;
1983
1984 if (!utility_user)
1985 goto end;
1986
1987 acl_free_utility_user();
1988
1989 /* Allocate all initial resources necessary */
1990 acl_utility_user_name.str= (char *) my_malloc(USERNAME_LENGTH+1, MY_ZEROFILL);
1991 acl_utility_user_host_name.str= (char *) my_malloc(HOSTNAME_LENGTH+1, MY_ZEROFILL);
1992 (void) my_init_dynamic_array(&acl_utility_user_schema_access, sizeof(char *),
1993 5, 10);
1994 acl_utility_user_initialized= true;
1995
1996 /* parse out the option to its component user and host name parts */
1997 parse_user(utility_user, strlen(utility_user),
1998 acl_utility_user_name.str, &acl_utility_user_name.length,
1999 acl_utility_user_host_name.str,
2000 &acl_utility_user_host_name.length);
2001
2002 /* Check to see if the username is anonymous */
2003 if (!acl_utility_user_name.str || acl_utility_user_name.str[0] == '\0')
2004 {
2005 sql_print_error("'utility user' specified as '%s' is anonymous"
2006 " and not allowed.",
2007 utility_user);
2008 ret= FALSE;
2009 goto cleanup;
2010 }
2011
2012 /* Check to see that a password was supplied */
2013 if (!utility_user_password || utility_user_password[0] == '\0')
2014 {
2015 sql_print_error("'utility user' specified as '%s' but has no "
2016 "password. Please see --utility_user_password.",
2017 utility_user);
2018 ret= FALSE;
2019 goto cleanup;
2020 }
2021
2022 /* set up some of the static utility user struct fields */
2023 acl_utility_user.user= acl_utility_user_name.str;
2024
2025 acl_utility_user.host.update_hostname(acl_utility_user_host_name.str);
2026
2027 acl_utility_user.sort= get_sort(2, acl_utility_user.host.get_host(),
2028 acl_utility_user.user);
2029
2030 /* Check to see if the utility user matches any existing user */
2031 for (i=0 ; i < acl_users.elements ; i++)
2032 {
2033 ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER*);
2034 if (user->user
2035 && strcmp(acl_utility_user_name.str, user->user) == 0)
2036 {
2037 if (user->sort == acl_utility_user.sort)
2038 {
2039 sql_print_error("'utility user' specification '%s' exactly"
2040 " matches existing user in mysql.user table.",
2041 utility_user);
2042 ret= FALSE;
2043 goto cleanup;
2044 }
2045 else if (user->sort < acl_utility_user.sort)
2046 {
2047 sql_print_warning("'utility user' specification '%s' closely"
2048 " matches more specific existing user '%s@%s' in"
2049 " mysql.user table which may render the utility_user"
2050 " inaccessable from certain hosts.", utility_user,
2051 user->user ? user->user : "",
2052 user->host.get_host());
2053 }
2054 }
2055 }
2056
2057 if (check_no_resolve
2058 && hostname_requires_resolving(acl_utility_user.host.get_host()))
2059 {
2060 sql_print_warning("'utility user' entry '%s@%s' "
2061 "ignored in --skip-name-resolve mode.",
2062 acl_utility_user.user ? acl_utility_user.user : "",
2063 acl_utility_user.host.get_host() ?
2064 acl_utility_user.host.get_host() : "");
2065 ret= FALSE;
2066 goto cleanup;
2067 }
2068
2069 /* Assume that the utility user is using the mysql_native_password-style
2070 authentication, fill out the rest of the static utility user struct, and add
2071 it into the acl_users list, then resort */
2072 my_make_scrambled_password_sha1(password, utility_user_password,
2073 strlen(utility_user_password));
2074
2075 passlen= strlen(password);
2076
2077 acl_utility_user.plugin= native_password_plugin_name;
2078
2079 if (set_user_salt(&acl_utility_user, password, passlen))
2080 {
2081 goto cleanup;
2082 }
2083
2084 DBUG_ASSERT(utility_user_privileges <= UINT_MAX32);
2085 acl_utility_user.access= utility_user_privileges & UINT_MAX32;
2086 if (acl_utility_user.access)
2087 {
2088 char privilege_desc[512];
2089 get_privilege_desc(privilege_desc, array_elements(privilege_desc), acl_utility_user.access);
2090 sql_print_information("Utility user '%s'@'%s' in use with access rights "
2091 "'%s'.",
2092 acl_utility_user.user,
2093 acl_utility_user.host.get_host(),
2094 privilege_desc);
2095 }
2096 else
2097 {
2098 sql_print_information("Utility user '%s'@'%s' in use with basic "
2099 "access rights.",
2100 acl_utility_user.user,
2101 acl_utility_user.host.get_host());
2102 }
2103
2104 acl_utility_user.ssl_type= SSL_TYPE_NONE;
2105
2106 acl_utility_user.can_authenticate= true;
2107
2108 (void) push_dynamic(&acl_users,(uchar*) &acl_utility_user);
2109
2110 /* initialize the schema access list if specified */
2111 if (utility_user_schema_access)
2112 {
2113 char *cur_pos= utility_user_schema_access;
2114 char *cur_db= cur_pos;
2115 do
2116 {
2117 if (*cur_pos == ',' || *cur_pos == '\0')
2118 {
2119 if (cur_pos - cur_db > 0)
2120 {
2121 char *dbname= my_strndup(cur_db, cur_pos-cur_db, MYF(MY_FAE));
2122 (void) push_dynamic(&acl_utility_user_schema_access, (uchar*) &dbname);
2123 }
2124 cur_db= cur_pos+1;
2125 if (*cur_pos == '\0')
2126 break;
2127 }
2128 cur_pos++;
2129 } while(1);
2130
2131 sql_print_information("Utility user '%s'@'%s' in use with full access to"
2132 " schemas '%s'.",
2133 acl_utility_user.user,
2134 acl_utility_user.host.get_host(),
2135 utility_user_schema_access);
2136 }
2137 else
2138 {
2139 sql_print_information("Utility user '%s'@'%s' in use with"
2140 " no schema access",
2141 acl_utility_user.user,
2142 acl_utility_user.host.get_host());
2143 }
2144 goto end;
2145
2146 cleanup:
2147 acl_free_utility_user();
2148
2149 end:
2150 return ret;
2151 }
2152
2153 /*
2154 Free up any resources allocated during acl_init_utility_user.
2155 */
2156 static
2157 void
acl_free_utility_user()2158 acl_free_utility_user()
2159 {
2160 if (acl_utility_user_initialized)
2161 {
2162 uint i;
2163 for (i=0 ; i < acl_utility_user_schema_access.elements ; i++)
2164 {
2165 char** dbname= dynamic_element(&acl_utility_user_schema_access, i, char**);
2166 if (*dbname)
2167 my_free(*dbname);
2168 }
2169 delete_dynamic(&acl_utility_user_schema_access);
2170
2171 my_free(acl_utility_user_name.str);
2172 my_free(acl_utility_user_host_name.str);
2173 memset(&acl_utility_user, 0, sizeof(acl_utility_user));
2174 acl_utility_user_initialized= false;
2175 }
2176 }
2177
2178 /*
2179 Determines if the user specified by user, host, ip matches the utility user
2180 */
2181 my_bool
acl_is_utility_user(const char * user,const char * host,const char * ip)2182 acl_is_utility_user(const char *user, const char *host, const char *ip)
2183 {
2184 if (user && acl_utility_user.user
2185 && strcmp(user, acl_utility_user.user) == 0
2186 && acl_utility_user.host.compare_hostname(host, ip))
2187 return TRUE;
2188 return FALSE;
2189 }
2190
2191
2192 /*
2193 Get all access bits from table after fieldnr
2194
2195 IMPLEMENTATION
2196 We know that the access privileges ends when there is no more fields
2197 or the field is not an enum with two elements.
2198
2199 SYNOPSIS
2200 get_access()
2201 form an open table to read privileges from.
2202 The record should be already read in table->record[0]
2203 fieldnr number of the first privilege (that is ENUM('N','Y') field
2204 next_field on return - number of the field next to the last ENUM
2205 (unless next_field == 0)
2206
2207 RETURN VALUE
2208 privilege mask
2209 */
2210
get_access(TABLE * form,uint fieldnr,uint * next_field)2211 static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
2212 {
2213 ulong access_bits=0,bit;
2214 char buff[2];
2215 String res(buff,sizeof(buff),&my_charset_latin1);
2216 Field **pos;
2217
2218 for (pos=form->field+fieldnr, bit=1;
2219 *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
2220 ((Field_enum*) (*pos))->typelib->count == 2 ;
2221 pos++, fieldnr++, bit<<=1)
2222 {
2223 (*pos)->val_str(&res);
2224 if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
2225 access_bits|= bit;
2226 }
2227 if (next_field)
2228 *next_field=fieldnr;
2229 return access_bits;
2230 }
2231
2232
2233 /*
2234 Return a number which, if sorted 'desc', puts strings in this order:
2235 no wildcards
2236 strings containg wildcards and non-wildcard characters
2237 single muilt-wildcard character('%')
2238 empty string
2239 */
2240
get_sort(uint count,...)2241 static ulong get_sort(uint count,...)
2242 {
2243 va_list args;
2244 va_start(args,count);
2245 ulong sort=0;
2246
2247 /* Should not use this function with more than 4 arguments for compare. */
2248 DBUG_ASSERT(count <= 4);
2249
2250 while (count--)
2251 {
2252 char *start, *str= va_arg(args,char*);
2253 uint chars= 0;
2254 uint wild_pos= 0;
2255
2256 /*
2257 wild_pos
2258 0 if string is empty
2259 1 if string is a single muilt-wildcard
2260 character('%')
2261 first wildcard position + 1 if string containg wildcards and
2262 non-wildcard characters
2263 */
2264
2265 if ((start= str))
2266 {
2267 for (; *str ; str++)
2268 {
2269 if (*str == wild_prefix && str[1])
2270 str++;
2271 else if (*str == wild_many || *str == wild_one)
2272 {
2273 wild_pos= (uint) (str - start) + 1;
2274 if (!(wild_pos == 1 && *str == wild_many && *(++str) == '\0'))
2275 wild_pos++;
2276 break;
2277 }
2278 chars= 128; // Marker that chars existed
2279 }
2280 }
2281 sort= (sort << 8) + (wild_pos ? min(wild_pos, 127U) : chars);
2282 }
2283 va_end(args);
2284 return sort;
2285 }
2286
2287
acl_compare(ACL_ACCESS * a,ACL_ACCESS * b)2288 static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
2289 {
2290 if (a->sort > b->sort)
2291 return -1;
2292 if (a->sort < b->sort)
2293 return 1;
2294 return 0;
2295 }
2296
2297
2298 /*
2299 Gets user credentials without authentication and resource limit checks.
2300
2301 SYNOPSIS
2302 acl_getroot()
2303 sctx Context which should be initialized
2304 user user name
2305 host host name
2306 ip IP
2307 db current data base name
2308
2309 RETURN
2310 FALSE OK
2311 TRUE Error
2312 */
2313
acl_getroot(Security_context * sctx,char * user,char * host,char * ip,char * db)2314 bool acl_getroot(Security_context *sctx, char *user, char *host,
2315 char *ip, char *db)
2316 {
2317 int res= 1;
2318 uint i;
2319 ACL_USER *acl_user= 0;
2320 DBUG_ENTER("acl_getroot");
2321
2322 DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
2323 (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
2324 user, (db ? db : "(NULL)")));
2325 sctx->user= user;
2326 sctx->set_host(host);
2327 sctx->set_ip(ip);
2328 sctx->host_or_ip= host ? host : (ip ? ip : "");
2329
2330 if (!initialized)
2331 {
2332 /*
2333 here if mysqld's been started with --skip-grant-tables option.
2334 */
2335 sctx->skip_grants();
2336 DBUG_RETURN(FALSE);
2337 }
2338
2339 mysql_mutex_lock(&acl_cache->lock);
2340
2341 sctx->master_access= 0;
2342 sctx->db_access= 0;
2343 *sctx->priv_user= *sctx->priv_host= 0;
2344
2345 /*
2346 Find acl entry in user database.
2347 This is specially tailored to suit the check we do for CALL of
2348 a stored procedure; user is set to what is actually a
2349 priv_user, which can be ''.
2350 */
2351 for (i=0 ; i < acl_users.elements ; i++)
2352 {
2353 ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
2354 if ((!acl_user_tmp->user && !user[0]) ||
2355 (acl_user_tmp->user && strcmp(user, acl_user_tmp->user) == 0))
2356 {
2357 if (acl_user_tmp->host.compare_hostname(host, ip))
2358 {
2359 acl_user= acl_user_tmp;
2360 res= 0;
2361 break;
2362 }
2363 }
2364 }
2365
2366 if (acl_user)
2367 {
2368 for (i=0 ; i < acl_dbs.elements ; i++)
2369 {
2370 ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
2371 if (!acl_db->user ||
2372 (user && user[0] && !strcmp(user, acl_db->user)))
2373 {
2374 if (acl_db->host.compare_hostname(host, ip))
2375 {
2376 if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
2377 {
2378 sctx->db_access= acl_db->access;
2379 break;
2380 }
2381 }
2382 }
2383 }
2384 sctx->master_access= acl_user->access;
2385
2386 if (acl_user->user)
2387 strmake(sctx->priv_user, user, USERNAME_LENGTH);
2388 else
2389 *sctx->priv_user= 0;
2390
2391 if (acl_user->host.get_host())
2392 strmake(sctx->priv_host, acl_user->host.get_host(), MAX_HOSTNAME - 1);
2393 else
2394 *sctx->priv_host= 0;
2395
2396 sctx->password_expired= acl_user->password_expired;
2397 }
2398 mysql_mutex_unlock(&acl_cache->lock);
2399 DBUG_RETURN(res);
2400 }
2401
check_get_key(ACL_USER * buff,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))2402 static uchar* check_get_key(ACL_USER *buff, size_t *length,
2403 my_bool not_used MY_ATTRIBUTE((unused)))
2404 {
2405 *length=buff->host.get_host_len();
2406 return (uchar*) buff->host.get_host();
2407 }
2408
2409
acl_update_user(const char * user,const char * host,const char * password,uint password_len,enum SSL_type ssl_type,const char * ssl_cipher,const char * x509_issuer,const char * x509_subject,USER_RESOURCES * mqh,ulong privileges,const LEX_STRING * plugin,const LEX_STRING * auth)2410 static void acl_update_user(const char *user, const char *host,
2411 const char *password, uint password_len,
2412 enum SSL_type ssl_type,
2413 const char *ssl_cipher,
2414 const char *x509_issuer,
2415 const char *x509_subject,
2416 USER_RESOURCES *mqh,
2417 ulong privileges,
2418 const LEX_STRING *plugin,
2419 const LEX_STRING *auth)
2420 {
2421 DBUG_ENTER("acl_update_user");
2422 mysql_mutex_assert_owner(&acl_cache->lock);
2423 for (uint i=0 ; i < acl_users.elements ; i++)
2424 {
2425 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
2426 if ((!acl_user->user && !user[0]) ||
2427 (acl_user->user && !strcmp(user,acl_user->user)))
2428 {
2429 if ((!acl_user->host.get_host() && !host[0]) ||
2430 (acl_user->host.get_host() &&
2431 !my_strcasecmp(system_charset_info, host, acl_user->host.get_host())))
2432 {
2433 if (plugin->length > 0)
2434 {
2435 acl_user->plugin= *plugin;
2436 optimize_plugin_compare_by_pointer(&acl_user->plugin);
2437 if (!auth_plugin_is_built_in(acl_user->plugin.str))
2438 acl_user->plugin.str= strmake_root(&global_acl_memory, plugin->str, plugin->length);
2439 acl_user->auth_string.str= auth->str ?
2440 strmake_root(&global_acl_memory, auth->str,
2441 auth->length) : const_cast<char*>("");
2442 acl_user->auth_string.length= auth->length;
2443 }
2444 acl_user->access=privileges;
2445 if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
2446 acl_user->user_resource.questions=mqh->questions;
2447 if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
2448 acl_user->user_resource.updates=mqh->updates;
2449 if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
2450 acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
2451 if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
2452 acl_user->user_resource.user_conn= mqh->user_conn;
2453 if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
2454 {
2455 acl_user->ssl_type= ssl_type;
2456 acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&global_acl_memory,
2457 ssl_cipher) : 0);
2458 acl_user->x509_issuer= (x509_issuer ? strdup_root(&global_acl_memory,
2459 x509_issuer) : 0);
2460 acl_user->x509_subject= (x509_subject ?
2461 strdup_root(&global_acl_memory, x509_subject) : 0);
2462 }
2463
2464
2465 if (password)
2466 {
2467 /*
2468 We just assert the hash is valid here since it's already
2469 checked in replace_user_table().
2470 */
2471 int hash_not_ok= set_user_salt(acl_user, password, password_len);
2472
2473 DBUG_ASSERT(hash_not_ok == 0);
2474 /* dummy addition to fool the compiler */
2475 password_len+= hash_not_ok;
2476 }
2477 /* search complete: */
2478 break;
2479 }
2480 }
2481 }
2482 DBUG_VOID_RETURN;
2483 }
2484
2485
acl_insert_user(const char * user,const char * host,const char * password,uint password_len,enum SSL_type ssl_type,const char * ssl_cipher,const char * x509_issuer,const char * x509_subject,USER_RESOURCES * mqh,ulong privileges,const LEX_STRING * plugin,const LEX_STRING * auth)2486 static void acl_insert_user(const char *user, const char *host,
2487 const char *password, uint password_len,
2488 enum SSL_type ssl_type,
2489 const char *ssl_cipher,
2490 const char *x509_issuer,
2491 const char *x509_subject,
2492 USER_RESOURCES *mqh,
2493 ulong privileges,
2494 const LEX_STRING *plugin,
2495 const LEX_STRING *auth)
2496 {
2497 DBUG_ENTER("acl_insert_user");
2498 ACL_USER acl_user;
2499 int hash_not_ok;
2500
2501 mysql_mutex_assert_owner(&acl_cache->lock);
2502 /*
2503 All accounts can authenticate per default. This will change when
2504 we add a new field to the user table.
2505
2506 Currently this flag is only set to false when authentication is attempted
2507 using an unknown user name.
2508 */
2509 acl_user.can_authenticate= true;
2510
2511 acl_user.user= *user ? strdup_root(&global_acl_memory,user) : 0;
2512 acl_user.host.update_hostname(*host ? strdup_root(&global_acl_memory, host) : 0);
2513 if (plugin->str[0])
2514 {
2515 acl_user.plugin= *plugin;
2516 optimize_plugin_compare_by_pointer(&acl_user.plugin);
2517 if (!auth_plugin_is_built_in(acl_user.plugin.str))
2518 acl_user.plugin.str= strmake_root(&global_acl_memory, plugin->str, plugin->length);
2519 acl_user.auth_string.str= auth->str ?
2520 strmake_root(&global_acl_memory, auth->str,
2521 auth->length) : const_cast<char*>("");
2522 acl_user.auth_string.length= auth->length;
2523
2524 optimize_plugin_compare_by_pointer(&acl_user.plugin);
2525 }
2526 else
2527 {
2528 acl_user.plugin= password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 ?
2529 old_password_plugin_name : native_password_plugin_name;
2530 acl_user.auth_string.str= const_cast<char*>("");
2531 acl_user.auth_string.length= 0;
2532 }
2533
2534 acl_user.access= privileges;
2535 acl_user.user_resource= *mqh;
2536 acl_user.sort= get_sort(2,acl_user.host.get_host(), acl_user.user);
2537 //acl_user.hostname_length=(uint) strlen(host);
2538 acl_user.ssl_type=
2539 (ssl_type != SSL_TYPE_NOT_SPECIFIED ? ssl_type : SSL_TYPE_NONE);
2540 acl_user.ssl_cipher=
2541 ssl_cipher ? strdup_root(&global_acl_memory, ssl_cipher) : 0;
2542 acl_user.x509_issuer=
2543 x509_issuer ? strdup_root(&global_acl_memory, x509_issuer) : 0;
2544 acl_user.x509_subject=
2545 x509_subject ? strdup_root(&global_acl_memory, x509_subject) : 0;
2546
2547 hash_not_ok= set_user_salt(&acl_user, password, password_len);
2548 DBUG_ASSERT(hash_not_ok == 0);
2549 /* dummy addition to fool the compiler */
2550 password_len+= hash_not_ok;
2551
2552
2553 (void) push_dynamic(&acl_users,(uchar*) &acl_user);
2554 if (acl_user.host.check_allow_all_hosts())
2555 allow_all_hosts=1; // Anyone can connect /* purecov: tested */
2556 my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
2557 sizeof(ACL_USER),(qsort_cmp) acl_compare);
2558
2559 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
2560 rebuild_check_host();
2561 DBUG_VOID_RETURN;
2562 }
2563
2564
acl_update_db(const char * user,const char * host,const char * db,ulong privileges)2565 static void acl_update_db(const char *user, const char *host, const char *db,
2566 ulong privileges)
2567 {
2568 mysql_mutex_assert_owner(&acl_cache->lock);
2569
2570 for (uint i=0 ; i < acl_dbs.elements ; i++)
2571 {
2572 ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
2573 if ((!acl_db->user && !user[0]) ||
2574 (acl_db->user &&
2575 !strcmp(user,acl_db->user)))
2576 {
2577 if ((!acl_db->host.get_host() && !host[0]) ||
2578 (acl_db->host.get_host() &&
2579 !strcmp(host, acl_db->host.get_host())))
2580 {
2581 if ((!acl_db->db && !db[0]) ||
2582 (acl_db->db && !strcmp(db,acl_db->db)))
2583 {
2584 if (privileges)
2585 acl_db->access=privileges;
2586 else
2587 delete_dynamic_element(&acl_dbs,i);
2588 }
2589 }
2590 }
2591 }
2592 }
2593
2594
2595 /*
2596 Insert a user/db/host combination into the global acl_cache
2597
2598 SYNOPSIS
2599 acl_insert_db()
2600 user User name
2601 host Host name
2602 db Database name
2603 privileges Bitmap of privileges
2604
2605 NOTES
2606 acl_cache->lock must be locked when calling this
2607 */
2608
acl_insert_db(const char * user,const char * host,const char * db,ulong privileges)2609 static void acl_insert_db(const char *user, const char *host, const char *db,
2610 ulong privileges)
2611 {
2612 ACL_DB acl_db;
2613 mysql_mutex_assert_owner(&acl_cache->lock);
2614 acl_db.user= strdup_root(&global_acl_memory,user);
2615 acl_db.host.update_hostname(*host ? strdup_root(&global_acl_memory, host) : 0);
2616 acl_db.db= strdup_root(&global_acl_memory, db);
2617 acl_db.access= privileges;
2618 acl_db.sort= get_sort(3,acl_db.host.get_host(), acl_db.db, acl_db.user);
2619 (void) push_dynamic(&acl_dbs, (uchar*) &acl_db);
2620 my_qsort((uchar*) dynamic_element(&acl_dbs, 0, ACL_DB*), acl_dbs.elements,
2621 sizeof(ACL_DB),(qsort_cmp) acl_compare);
2622 }
2623
2624
2625
2626 /*
2627 Get privilege for a host, user and db combination
2628
2629 as db_is_pattern changes the semantics of comparison,
2630 acl_cache is not used if db_is_pattern is set.
2631 */
2632
acl_get(const char * host,const char * ip,const char * user,const char * db,my_bool db_is_pattern)2633 ulong acl_get(const char *host, const char *ip,
2634 const char *user, const char *db, my_bool db_is_pattern)
2635 {
2636 ulong host_access= ~(ulong)0, db_access= 0;
2637 uint i;
2638 size_t key_length, copy_length;
2639 char key[ACL_KEY_LENGTH],*tmp_db,*end;
2640 acl_entry *entry;
2641 DBUG_ENTER("acl_get");
2642
2643 copy_length= (size_t) (strlen(ip ? ip : "") +
2644 strlen(user ? user : "") +
2645 strlen(db ? db : "")) + 2; /* Added 2 at the end to avoid
2646 buffer overflow at strmov()*/
2647 /*
2648 Make sure that strmov() operations do not result in buffer overflow.
2649 */
2650 if (copy_length >= ACL_KEY_LENGTH)
2651 DBUG_RETURN(0);
2652
2653 mysql_mutex_lock(&acl_cache->lock);
2654 end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db);
2655 if (lower_case_table_names)
2656 {
2657 my_casedn_str(files_charset_info, tmp_db);
2658 db=tmp_db;
2659 }
2660 key_length= (size_t) (end-key);
2661
2662 if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key,
2663 key_length)))
2664 {
2665 db_access=entry->access;
2666 mysql_mutex_unlock(&acl_cache->lock);
2667 DBUG_PRINT("exit", ("access: 0x%lx", db_access));
2668 DBUG_RETURN(db_access);
2669 }
2670
2671 /* Check to see if the inquiry is for the utility_user */
2672 if (acl_is_utility_user(user, host, ip))
2673 {
2674 /* Check to see if database is within the schema access list */
2675 for (i=0; i < acl_utility_user_schema_access.elements; i++)
2676 {
2677 char **dbname= dynamic_element(&acl_utility_user_schema_access,
2678 i, char**);
2679 if(strcmp(*dbname, db) == 0)
2680 {
2681 db_access= host_access= GLOBAL_ACLS;
2682 break;
2683 }
2684 }
2685 goto exit;
2686 }
2687
2688 /*
2689 Check if there are some access rights for database and user
2690 */
2691 for (i=0 ; i < acl_dbs.elements ; i++)
2692 {
2693 ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
2694 if (!acl_db->user || !strcmp(user,acl_db->user))
2695 {
2696 if (acl_db->host.compare_hostname(host,ip))
2697 {
2698 if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
2699 {
2700 db_access=acl_db->access;
2701 if (acl_db->host.get_host())
2702 goto exit; // Fully specified. Take it
2703 break; /* purecov: tested */
2704 }
2705 }
2706 }
2707 }
2708 if (!db_access)
2709 goto exit; // Can't be better
2710
2711 exit:
2712 /* Save entry in cache for quick retrieval */
2713 if (!db_is_pattern &&
2714 (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
2715 {
2716 entry->access=(db_access & host_access);
2717 entry->length=key_length;
2718 memcpy((uchar*) entry->key,key,key_length);
2719 acl_cache->add(entry);
2720 }
2721 mysql_mutex_unlock(&acl_cache->lock);
2722 DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
2723 DBUG_RETURN(db_access & host_access);
2724 }
2725
2726 /*
2727 Check if there are any possible matching entries for this host
2728
2729 NOTES
2730 All host names without wild cards are stored in a hash table,
2731 entries with wildcards are stored in a dynamic array
2732 */
2733
init_check_host(void)2734 static void init_check_host(void)
2735 {
2736 DBUG_ENTER("init_check_host");
2737 (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(class ACL_HOST_AND_IP),
2738 acl_users.elements,1);
2739 (void) my_hash_init(&acl_check_hosts,system_charset_info,
2740 acl_users.elements, 0, 0,
2741 (my_hash_get_key) check_get_key, 0, 0);
2742 if (!allow_all_hosts)
2743 {
2744 for (uint i=0 ; i < acl_users.elements ; i++)
2745 {
2746 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
2747 if (acl_user->host.has_wildcard())
2748 { // Has wildcard
2749 uint j;
2750 for (j=0 ; j < acl_wild_hosts.elements ; j++)
2751 { // Check if host already exists
2752 ACL_HOST_AND_IP *acl=dynamic_element(&acl_wild_hosts,j,
2753 ACL_HOST_AND_IP *);
2754 if (!my_strcasecmp(system_charset_info,
2755 acl_user->host.get_host(), acl->get_host()))
2756 break; // already stored
2757 }
2758 if (j == acl_wild_hosts.elements) // If new
2759 (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
2760 }
2761 else if (!my_hash_search(&acl_check_hosts,(uchar*)
2762 acl_user->host.get_host(),
2763 strlen(acl_user->host.get_host())))
2764 {
2765 if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
2766 { // End of memory
2767 allow_all_hosts=1; // Should never happen
2768 DBUG_VOID_RETURN;
2769 }
2770 }
2771 }
2772 }
2773 freeze_size(&acl_wild_hosts);
2774 freeze_size(&acl_check_hosts.array);
2775 DBUG_VOID_RETURN;
2776 }
2777
2778
2779 /*
2780 Rebuild lists used for checking of allowed hosts
2781
2782 We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
2783 dropping or renaming user, since they contain pointers to elements of
2784 'acl_user' array, which are invalidated by drop operation, and use
2785 ACL_USER::host::hostname as a key, which is changed by rename.
2786 */
rebuild_check_host(void)2787 void rebuild_check_host(void)
2788 {
2789 delete_dynamic(&acl_wild_hosts);
2790 my_hash_free(&acl_check_hosts);
2791 init_check_host();
2792 }
2793
2794
2795 /* Return true if there is no users that can match the given host */
2796
acl_check_host(const char * host,const char * ip)2797 bool acl_check_host(const char *host, const char *ip)
2798 {
2799 if (allow_all_hosts)
2800 return 0;
2801 mysql_mutex_lock(&acl_cache->lock);
2802
2803 if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
2804 (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
2805 {
2806 mysql_mutex_unlock(&acl_cache->lock);
2807 return 0; // Found host
2808 }
2809 for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
2810 {
2811 ACL_HOST_AND_IP *acl=dynamic_element(&acl_wild_hosts,i,ACL_HOST_AND_IP*);
2812 if (acl->compare_hostname(host, ip))
2813 {
2814 mysql_mutex_unlock(&acl_cache->lock);
2815 return 0; // Host ok
2816 }
2817 }
2818 mysql_mutex_unlock(&acl_cache->lock);
2819 if (ip != NULL)
2820 {
2821 /* Increment HOST_CACHE.COUNT_HOST_ACL_ERRORS. */
2822 Host_errors errors;
2823 errors.m_host_acl= 1;
2824 inc_host_errors(ip, &errors);
2825 }
2826 return 1; // Host is not allowed
2827 }
2828
2829
2830 /**
2831 Check if the user is allowed to change password
2832
2833 @param thd THD
2834 @param host Hostname for the user
2835 @param user User name
2836 @param new_password new password
2837
2838 new_password cannot be NULL
2839
2840 @return Error status
2841 @retval 0 OK
2842 @retval 1 ERROR; In this case the error is sent to the client.
2843 */
2844
check_change_password(THD * thd,const char * host,const char * user,char * new_password,uint new_password_len)2845 int check_change_password(THD *thd, const char *host, const char *user,
2846 char *new_password, uint new_password_len)
2847 {
2848 if (!initialized)
2849 {
2850 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2851 return(1);
2852 }
2853 if (!thd->slave_thread &&
2854 (strcmp(thd->security_ctx->user, user) ||
2855 my_strcasecmp(system_charset_info, host,
2856 thd->security_ctx->priv_host)))
2857 {
2858 if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
2859 return(1);
2860 }
2861 if (!thd->slave_thread && !thd->security_ctx->user[0])
2862 {
2863 my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
2864 MYF(0));
2865 return(1);
2866 }
2867
2868 return(0);
2869 }
2870
2871
2872 /**
2873 Update the security context when updating the user
2874
2875 Helper function.
2876 Update only if the security context is pointing to the same user.
2877 And return true if the update happens (i.e. we're operating on the
2878 user account of the current user).
2879 Normalize the names for a safe compare.
2880
2881 @param sctx The security context to update
2882 @param acl_user_ptr User account being updated
2883 @param expired new value of the expiration flag
2884 @return did the update happen ?
2885 */
2886 static bool
update_sctx_cache(Security_context * sctx,ACL_USER * acl_user_ptr,bool expired)2887 update_sctx_cache(Security_context *sctx, ACL_USER *acl_user_ptr, bool expired)
2888 {
2889 const char *acl_host= acl_user_ptr->host.get_host();
2890 const char *acl_user= acl_user_ptr->user;
2891 const char *sctx_user= sctx->priv_user;
2892 const char *sctx_host= sctx->priv_host;
2893
2894 if (!acl_host)
2895 acl_host= "";
2896 if(!acl_user)
2897 acl_user= "";
2898 if (!sctx_host)
2899 sctx_host= "";
2900 if (!sctx_user)
2901 sctx_user= "";
2902
2903 if (!strcmp(acl_user, sctx_user) && !strcmp(acl_host, sctx_host))
2904 {
2905 sctx->password_expired= expired;
2906 return true;
2907 }
2908
2909 return false;
2910 }
2911
2912
2913
2914 /**
2915 Change a password hash for a user.
2916
2917 @param thd Thread handle
2918 @param host Hostname
2919 @param user User name
2920 @param new_password New password hash for host@user
2921
2922 Note : it will also reset the change_password flag.
2923 This is safe to do unconditionally since the simple userless form
2924 SET PASSWORD = PASSWORD('text') will be the only allowed form when
2925 this flag is on. So we don't need to check user names here.
2926
2927
2928 @see set_var_password::update(THD *thd)
2929
2930 @return Error code
2931 @retval 0 ok
2932 @retval 1 ERROR; In this case the error is sent to the client.
2933 */
2934
change_password(THD * thd,const char * host,const char * user,char * new_password)2935 bool change_password(THD *thd, const char *host, const char *user,
2936 char *new_password)
2937 {
2938 TABLE_LIST tables;
2939 TABLE *table;
2940 Acl_table_intact table_intact;
2941 /* Buffer should be extended when password length is extended. */
2942 char buff[2048];
2943 ulong query_length;
2944 bool save_binlog_row_based;
2945 uchar user_key[MAX_KEY_LENGTH];
2946 char *plugin_temp= NULL;
2947 bool plugin_empty;
2948 char *new_escaped_password= NULL;
2949 uint new_password_len= (uint) strlen(new_password);
2950 bool result= 1;
2951 enum mysql_user_table_field password_field= MYSQL_USER_FIELD_PASSWORD;
2952 DBUG_ENTER("change_password");
2953 DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
2954 host,user,new_password));
2955 DBUG_ASSERT(host != 0); // Ensured by parent
2956
2957 if (check_change_password(thd, host, user, new_password, new_password_len))
2958 DBUG_RETURN(1);
2959
2960 tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
2961
2962 #ifdef HAVE_REPLICATION
2963 /*
2964 GRANT and REVOKE are applied the slave in/exclusion rules as they are
2965 some kind of updates to the mysql.% tables.
2966 */
2967 if (thd->slave_thread && rpl_filter->is_on())
2968 {
2969 /*
2970 The tables must be marked "updating" so that tables_ok() takes them into
2971 account in tests. It's ok to leave 'updating' set after tables_ok.
2972 */
2973 tables.updating= 1;
2974 /* Thanks to memset, tables.next==0 */
2975 if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, &tables)))
2976 DBUG_RETURN(0);
2977 }
2978 #endif
2979 if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
2980 DBUG_RETURN(1);
2981
2982 if (table_intact.check(table, &mysql_user_table_def))
2983 DBUG_RETURN(1);
2984
2985 /*
2986 This statement will be replicated as a statement, even when using
2987 row-based replication. The flag will be reset at the end of the
2988 statement.
2989 */
2990 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
2991 thd->clear_current_stmt_binlog_format_row();
2992
2993 mysql_mutex_lock(&acl_cache->lock);
2994 ACL_USER *acl_user;
2995 if (!(acl_user= find_acl_user(host, user, TRUE)))
2996 {
2997 mysql_mutex_unlock(&acl_cache->lock);
2998 my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
2999 goto end;
3000 }
3001
3002 /* trying to change the password of the utility user? */
3003 if (acl_is_utility_user(acl_user->user, acl_user->host.get_host(), NULL))
3004 {
3005 mysql_mutex_unlock(&acl_cache->lock);
3006 my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
3007 goto end;
3008 }
3009
3010 mysql_mutex_assert_owner(&acl_cache->lock);
3011 table->use_all_columns();
3012 DBUG_ASSERT(host != NULL);
3013 table->field[MYSQL_USER_FIELD_HOST]->store(host, strlen(host),
3014 system_charset_info);
3015 table->field[MYSQL_USER_FIELD_USER]->store(user, strlen(user),
3016 system_charset_info);
3017
3018 key_copy((uchar *) user_key, table->record[0], table->key_info,
3019 table->key_info->key_length);
3020 if (!table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
3021 HA_WHOLE_KEY,
3022 HA_READ_KEY_EXACT))
3023 plugin_temp= (table->s->fields > MYSQL_USER_FIELD_PLUGIN) ?
3024 get_field(&global_acl_memory, table->field[MYSQL_USER_FIELD_PLUGIN]) : NULL;
3025 else
3026 {
3027 my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
3028 mysql_mutex_unlock(&acl_cache->lock);
3029 goto end;
3030 }
3031
3032 plugin_empty= plugin_temp ? false: true;
3033
3034 if (acl_user->plugin.length == 0)
3035 {
3036 acl_user->plugin.length= default_auth_plugin_name.length;
3037 acl_user->plugin.str= default_auth_plugin_name.str;
3038 }
3039
3040 if (new_password_len == 0)
3041 {
3042 String *password_str= new (thd->mem_root) String(new_password,
3043 thd->variables.
3044 character_set_client);
3045 if (check_password_policy(password_str))
3046 {
3047 result= 1;
3048 mysql_mutex_unlock(&acl_cache->lock);
3049 goto end;
3050 }
3051 }
3052
3053 #if defined(HAVE_OPENSSL)
3054 /*
3055 update loaded acl entry:
3056 TODO Should password depend on @@old_variables here?
3057 - Probably not if the user exists and have a plugin set already.
3058 */
3059 if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
3060 sha256_password_plugin_name.str) == 0)
3061 {
3062 /*
3063 Accept empty passwords
3064 */
3065 if (new_password_len == 0)
3066 {
3067 /*
3068 Since we're changing the password for the user we need to reset the
3069 expiration flag.
3070 */
3071 if (!update_sctx_cache(thd->security_ctx, acl_user, false) &&
3072 thd->security_ctx->password_expired)
3073 {
3074 /* the current user is not the same as the user we operate on */
3075 my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
3076 result= 1;
3077 mysql_mutex_unlock(&acl_cache->lock);
3078 goto end;
3079 }
3080 acl_user->password_expired= false;
3081 acl_user->auth_string= empty_lex_str;
3082 }
3083 /*
3084 Check if password begins with correct magic number
3085 */
3086 else if (new_password[0] == '$' &&
3087 new_password[1] == '5' &&
3088 new_password[2] == '$')
3089 {
3090 password_field= MYSQL_USER_FIELD_AUTHENTICATION_STRING;
3091 if (new_password_len < CRYPT_MAX_PASSWORD_SIZE + 1)
3092 {
3093 /*
3094 Since we're changing the password for the user we need to reset the
3095 expiration flag.
3096 */
3097 if (!update_sctx_cache(thd->security_ctx, acl_user, false) &&
3098 thd->security_ctx->password_expired)
3099 {
3100 /* the current user is not the same as the user we operate on */
3101 my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
3102 result= 1;
3103 mysql_mutex_unlock(&acl_cache->lock);
3104 goto end;
3105 }
3106
3107 acl_user->password_expired= false;
3108 /* copy string including \0 */
3109 acl_user->auth_string.str= (char *) memdup_root(&global_acl_memory,
3110 new_password,
3111 new_password_len + 1);
3112 acl_user->auth_string.length= new_password_len;
3113 }
3114 } else
3115 {
3116 /*
3117 Password format is unexpected. The user probably is using the wrong
3118 password algorithm with the PASSWORD() function.
3119 */
3120 my_error(ER_PASSWORD_FORMAT, MYF(0));
3121 result= 1;
3122 mysql_mutex_unlock(&acl_cache->lock);
3123 goto end;
3124 }
3125 }
3126 else
3127 #endif
3128 if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
3129 native_password_plugin_name.str) == 0 ||
3130 my_strcasecmp(system_charset_info, acl_user->plugin.str,
3131 old_password_plugin_name.str) == 0)
3132 {
3133 password_field= MYSQL_USER_FIELD_PASSWORD;
3134
3135 /*
3136 Legacy code produced an error if the password hash didn't match the
3137 expectations.
3138 */
3139 if (new_password_len != 0)
3140 {
3141 if (plugin_empty)
3142 {
3143 if (new_password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
3144 acl_user->plugin= native_password_plugin_name;
3145 else if (new_password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
3146 acl_user->plugin= old_password_plugin_name;
3147 else
3148 {
3149 my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
3150 result= 1;
3151 mysql_mutex_unlock(&acl_cache->lock);
3152 goto end;
3153 }
3154 }
3155 else
3156 {
3157 if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
3158 native_password_plugin_name.str) == 0 &&
3159 new_password_len != SCRAMBLED_PASSWORD_CHAR_LENGTH)
3160 {
3161 my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
3162 result= 1;
3163 mysql_mutex_unlock(&acl_cache->lock);
3164 goto end;
3165 }
3166 else if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
3167 old_password_plugin_name.str) == 0 &&
3168 new_password_len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
3169 {
3170 my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH_323);
3171 result= 1;
3172 mysql_mutex_unlock(&acl_cache->lock);
3173 goto end;
3174 }
3175 }
3176 }
3177 else if (plugin_empty)
3178 acl_user->plugin= native_password_plugin_name;
3179
3180 /*
3181 Update loaded acl entry in memory.
3182 set_user_salt() stores a binary (compact) representation of the password
3183 in memory (acl_user->salt and salt_len).
3184 set_user_plugin() sets the appropriate plugin based on password length and
3185 if the length doesn't match a warning is issued.
3186 */
3187 if (set_user_salt(acl_user, new_password, new_password_len))
3188 {
3189 my_error(ER_PASSWORD_FORMAT, MYF(0));
3190 result= 1;
3191 mysql_mutex_unlock(&acl_cache->lock);
3192 goto end;
3193 }
3194 if (!update_sctx_cache(thd->security_ctx, acl_user, false) &&
3195 thd->security_ctx->password_expired)
3196 {
3197 my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
3198 result= 1;
3199 mysql_mutex_unlock(&acl_cache->lock);
3200 goto end;
3201 }
3202 }
3203 else
3204 {
3205 push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
3206 ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
3207 /*
3208 An undefined password factory could very well mean that the password
3209 field is empty.
3210 */
3211 new_password_len= 0;
3212 }
3213
3214 if (update_user_table(thd, table,
3215 acl_user->host.get_host() ? acl_user->host.get_host() : "",
3216 acl_user->user ? acl_user->user : "",
3217 new_password, new_password_len, password_field, false, true))
3218 {
3219 mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
3220 goto end;
3221 }
3222
3223 acl_cache->clear(1); // Clear locked hostname cache
3224 mysql_mutex_unlock(&acl_cache->lock);
3225 result= 0;
3226 /*
3227 Before writing the query to binlog, correctly escape the password
3228 string so that the slave can parse it correctly.
3229 */
3230
3231 new_escaped_password= (char *)alloc_root(thd->mem_root, (new_password_len*2+1));
3232 if (!new_escaped_password)
3233 {
3234 my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 0);
3235 result= 1;
3236 goto end;
3237 }
3238
3239 DBUG_EXECUTE_IF("force_hash_string_with_quote",
3240 strcpy(new_password, HASH_STRING_WITH_QUOTE);
3241 );
3242
3243 escape_string_mysql(thd, new_escaped_password, new_password, new_password_len);
3244
3245 query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
3246 acl_user->user ? acl_user->user : "",
3247 acl_user->host.get_host() ? acl_user->host.get_host() : "",
3248 new_escaped_password);
3249 result= write_bin_log(thd, true, buff, query_length,
3250 table->file->has_transactions());
3251 end:
3252 result|= acl_trans_commit_and_close_tables(thd);
3253
3254 /* Restore the state of binlog format */
3255 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3256 if (save_binlog_row_based)
3257 thd->set_current_stmt_binlog_format_row();
3258
3259 DBUG_RETURN(result);
3260 }
3261
3262
3263 /*
3264 Find user in ACL
3265
3266 SYNOPSIS
3267 is_acl_user()
3268 host host name
3269 user user name
3270
3271 RETURN
3272 FALSE user not fond
3273 TRUE there are such user
3274 */
3275
is_acl_user(const char * host,const char * user)3276 bool is_acl_user(const char *host, const char *user)
3277 {
3278 bool res;
3279
3280 /* --skip-grants */
3281 if (!initialized)
3282 return TRUE;
3283
3284 mysql_mutex_lock(&acl_cache->lock);
3285 res= find_acl_user(host, user, TRUE) != NULL;
3286 mysql_mutex_unlock(&acl_cache->lock);
3287 return res;
3288 }
3289
3290
3291 /*
3292 Find first entry that matches the current user
3293 */
3294
3295 static ACL_USER *
find_acl_user(const char * host,const char * user,my_bool exact)3296 find_acl_user(const char *host, const char *user, my_bool exact)
3297 {
3298 DBUG_ENTER("find_acl_user");
3299 DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user));
3300
3301 mysql_mutex_assert_owner(&acl_cache->lock);
3302
3303 for (uint i=0 ; i < acl_users.elements ; i++)
3304 {
3305 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
3306 DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
3307 user, acl_user->user ? acl_user->user : "",
3308 host,
3309 acl_user->host.get_host() ? acl_user->host.get_host() :
3310 ""));
3311 if ((!acl_user->user && !user[0]) ||
3312 (acl_user->user && !strcmp(user,acl_user->user)))
3313 {
3314 if (exact ? !my_strcasecmp(system_charset_info, host,
3315 acl_user->host.get_host() ?
3316 acl_user->host.get_host() : "") :
3317 acl_user->host.compare_hostname(host,host))
3318 {
3319 DBUG_RETURN(acl_user);
3320 }
3321 }
3322 }
3323 DBUG_RETURN(0);
3324 }
3325
3326 /**
3327 Check if the given host name needs to be resolved or not.
3328 Host name has to be resolved if it actually contains *name*.
3329
3330 For example:
3331 192.168.1.1 --> FALSE
3332 192.168.1.0/255.255.255.0 --> FALSE
3333 % --> FALSE
3334 192.168.1.% --> FALSE
3335 AB% --> FALSE
3336
3337 AAAAFFFF --> TRUE (Hostname)
3338 AAAA:FFFF:1234:5678 --> FALSE
3339 ::1 --> FALSE
3340
3341 This function does not check if the given string is a valid host name or
3342 not. It assumes that the argument is a valid host name.
3343
3344 @param hostname the string to check.
3345
3346 @return a flag telling if the argument needs to be resolved or not.
3347 @retval TRUE the argument is a host name and needs to be resolved.
3348 @retval FALSE the argument is either an IP address, or a patter and
3349 should not be resolved.
3350 */
3351
hostname_requires_resolving(const char * hostname)3352 bool hostname_requires_resolving(const char *hostname)
3353 {
3354 if (!hostname)
3355 return FALSE;
3356
3357 /* Check if hostname is the localhost. */
3358
3359 size_t hostname_len= strlen(hostname);
3360 size_t localhost_len= strlen(my_localhost);
3361
3362 if (hostname == my_localhost ||
3363 (hostname_len == localhost_len &&
3364 !my_strnncoll(system_charset_info,
3365 (const uchar *) hostname, hostname_len,
3366 (const uchar *) my_localhost, strlen(my_localhost))))
3367 {
3368 return FALSE;
3369 }
3370
3371 /*
3372 If the string contains any of {':', '%', '_', '/'}, it is definitely
3373 not a host name:
3374 - ':' means that the string is an IPv6 address;
3375 - '%' or '_' means that the string is a pattern;
3376 - '/' means that the string is an IPv4 network address;
3377 */
3378
3379 for (const char *p= hostname; *p; ++p)
3380 {
3381 switch (*p) {
3382 case ':':
3383 case '%':
3384 case '_':
3385 case '/':
3386 return FALSE;
3387 }
3388 }
3389
3390 /*
3391 Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
3392 (12.34.56.78). The assumption is that if the string contains only
3393 digits and dots, it is an IPv4 address. Otherwise -- a host name.
3394 */
3395
3396 for (const char *p= hostname; *p; ++p)
3397 {
3398 if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
3399 return TRUE; /* a "letter" has been found. */
3400 }
3401
3402 return FALSE; /* all characters are either dots or digits. */
3403 }
3404
3405
3406 /**
3407 Update record for user in mysql.user privilege table with new password.
3408
3409 @param table Pointer to TABLE object for open mysql.user table
3410 @param host Hostname
3411 @param user Username
3412 @param new_password New password hash
3413 @param new_password_len Length of new password hash
3414 @param password_field The password field to use
3415 @param password_expired Password expiration flag
3416
3417 @see change_password
3418
3419 */
3420
3421 static bool
update_user_table(THD * thd,TABLE * table,const char * host,const char * user,const char * new_password,uint new_password_len,enum mysql_user_table_field password_field,bool password_expired,bool is_user_table_positioned)3422 update_user_table(THD *thd, TABLE *table,
3423 const char *host, const char *user,
3424 const char *new_password, uint new_password_len,
3425 enum mysql_user_table_field password_field,
3426 bool password_expired, bool is_user_table_positioned)
3427 {
3428 char user_key[MAX_KEY_LENGTH];
3429 int error;
3430 DBUG_ENTER("update_user_table");
3431 DBUG_PRINT("enter",("user: %s host: %s",user,host));
3432
3433 /* ALTER USER PASSWORD EXPIRE makes no sense on old system tables */
3434 if (table->s->fields <= MYSQL_USER_FIELD_PASSWORD_EXPIRED &&
3435 password_expired)
3436 {
3437 my_error(ER_BAD_FIELD_ERROR, MYF(0), "password_expired", "mysql.user");
3438 DBUG_RETURN(1);
3439 }
3440
3441 /*
3442 If this function is reached through change_password, the user record is
3443 already available and hence need not be read again.
3444 */
3445
3446 if (!is_user_table_positioned)
3447 {
3448 table->use_all_columns();
3449 DBUG_ASSERT(host != NULL);
3450 table->field[MYSQL_USER_FIELD_HOST]->store(host, (uint) strlen(host),
3451 system_charset_info);
3452 table->field[MYSQL_USER_FIELD_USER]->store(user, (uint) strlen(user),
3453 system_charset_info);
3454 key_copy((uchar *) user_key, table->record[0], table->key_info,
3455 table->key_info->key_length);
3456
3457 if (table->file->ha_index_read_idx_map(table->record[0], 0,
3458 (uchar *) user_key, HA_WHOLE_KEY,
3459 HA_READ_KEY_EXACT))
3460 {
3461 my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
3462 MYF(0)); /* purecov: deadcode */
3463 DBUG_RETURN(1); /* purecov: deadcode */
3464 }
3465 }
3466 store_record(table,record[1]);
3467
3468 /*
3469 When the flag is on we're inside ALTER TABLE ... PASSWORD EXPIRE and we
3470 have no password to update.
3471 */
3472 if (!password_expired)
3473 {
3474 if (table->s->fields >= (uint) password_field)
3475 table->field[(uint) password_field]->store(new_password, new_password_len,
3476 system_charset_info);
3477 else
3478 {
3479 my_error(ER_BAD_FIELD_ERROR, MYF(0), "authentication string", "mysql.user");
3480 DBUG_RETURN(1);
3481 }
3482 if (new_password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 &&
3483 password_field == MYSQL_USER_FIELD_PASSWORD)
3484 {
3485 WARN_DEPRECATED_41_PWD_HASH(thd);
3486 }
3487 }
3488
3489 if (table->s->fields > MYSQL_USER_FIELD_PASSWORD_EXPIRED)
3490 {
3491 /* update password_expired if present */
3492 table->field[MYSQL_USER_FIELD_PASSWORD_EXPIRED]->store(password_expired ?
3493 "Y" : "N", 1,
3494 system_charset_info);
3495 }
3496
3497 if ((error=table->file->ha_update_row(table->record[1],table->record[0])) &&
3498 error != HA_ERR_RECORD_IS_THE_SAME)
3499 {
3500 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
3501 DBUG_RETURN(1);
3502 }
3503
3504 DBUG_RETURN(0);
3505 }
3506
3507
3508 /*
3509 Return 1 if we are allowed to create new users
3510 the logic here is: INSERT_ACL is sufficient.
3511 It's also a requirement in opt_safe_user_create,
3512 otherwise CREATE_USER_ACL is enough.
3513 */
3514
test_if_create_new_users(THD * thd)3515 static bool test_if_create_new_users(THD *thd)
3516 {
3517 Security_context *sctx= thd->security_ctx;
3518 bool create_new_users= MY_TEST(sctx->master_access & INSERT_ACL) ||
3519 (!opt_safe_user_create &&
3520 MY_TEST(sctx->master_access & CREATE_USER_ACL));
3521 if (!create_new_users)
3522 {
3523 TABLE_LIST tl;
3524 ulong db_access;
3525 tl.init_one_table(C_STRING_WITH_LEN("mysql"),
3526 C_STRING_WITH_LEN("user"), "user", TL_WRITE);
3527 create_new_users= 1;
3528
3529 db_access=acl_get(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
3530 sctx->priv_user, tl.db, 0);
3531 if (!(db_access & INSERT_ACL))
3532 {
3533 if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
3534 create_new_users=0;
3535 }
3536 }
3537 return create_new_users;
3538 }
3539
3540
3541 /**
3542 Only the plugins that are known to use the mysql.user table
3543 to store their passwords support password expiration atm.
3544 TODO: create a service and extend the plugin API to support
3545 password expiration for external plugins.
3546
3547 @retval false expiration not supported
3548 @retval true expiration supported
3549 */
auth_plugin_supports_expiration(const char * plugin_name)3550 bool auth_plugin_supports_expiration(const char *plugin_name)
3551 {
3552 return (!plugin_name || !*plugin_name ||
3553 plugin_name == native_password_plugin_name.str ||
3554 #if defined(HAVE_OPENSSL)
3555 plugin_name == sha256_password_plugin_name.str ||
3556 #endif
3557 plugin_name == old_password_plugin_name.str);
3558 }
3559
3560
3561 /****************************************************************************
3562 Handle GRANT commands
3563 ****************************************************************************/
3564
replace_user_table(THD * thd,TABLE * table,LEX_USER * combo,ulong rights,bool revoke_grant,bool can_create_user,bool no_auto_create)3565 static int replace_user_table(THD *thd, TABLE *table, LEX_USER *combo,
3566 ulong rights, bool revoke_grant,
3567 bool can_create_user, bool no_auto_create)
3568 {
3569 int error = -1;
3570 bool old_row_exists=0;
3571 char *password= empty_c_string;
3572 uint password_len= 0;
3573 char what= (revoke_grant) ? 'N' : 'Y';
3574 uchar user_key[MAX_KEY_LENGTH];
3575 LEX *lex= thd->lex;
3576 Acl_table_intact table_intact;
3577 DBUG_ENTER("replace_user_table");
3578
3579 mysql_mutex_assert_owner(&acl_cache->lock);
3580
3581 if (table_intact.check(table, &mysql_user_table_def))
3582 goto end;
3583
3584 if (acl_is_utility_user(combo->user.str, combo->host.str, NULL))
3585 {
3586 my_error(ER_NONEXISTING_GRANT, MYF(0), combo->user.str, combo->host.str);
3587 goto end;
3588 }
3589
3590 table->use_all_columns();
3591 DBUG_ASSERT(combo->host.str != NULL);
3592 table->field[MYSQL_USER_FIELD_HOST]->store(combo->host.str,combo->host.length,
3593 system_charset_info);
3594 table->field[MYSQL_USER_FIELD_USER]->store(combo->user.str,combo->user.length,
3595 system_charset_info);
3596 key_copy(user_key, table->record[0], table->key_info,
3597 table->key_info->key_length);
3598
3599 if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
3600 HA_WHOLE_KEY,
3601 HA_READ_KEY_EXACT))
3602 {
3603 /*
3604 The user record wasn't found; if the intention was to revoke privileges
3605 (indicated by what == 'N') then execution must fail now.
3606 */
3607 if (what == 'N')
3608 {
3609 my_error(ER_NONEXISTING_GRANT, MYF(0), combo->user.str, combo->host.str);
3610 goto end;
3611 }
3612
3613 if ((!combo->uses_identified_by_clause &&
3614 !combo->uses_identified_with_clause &&
3615 !combo->uses_identified_by_password_clause) ||
3616 (combo->uses_identified_with_clause &&
3617 (!my_strcasecmp(system_charset_info, combo->plugin.str,
3618 native_password_plugin_name.str) ||
3619 !my_strcasecmp(system_charset_info, combo->plugin.str,
3620 old_password_plugin_name.str))))
3621 {
3622 if (check_password_policy(NULL))
3623 {
3624 error= 1;
3625 goto end;
3626 }
3627 }
3628 /* 1. Unresolved plugins become default plugin */
3629 if (!combo->uses_identified_with_clause)
3630 {
3631 combo->plugin.str= default_auth_plugin_name.str;
3632 combo->plugin.length= default_auth_plugin_name.length;
3633 }
3634 /* 2. Digest password if needed (plugin must have been resolved) */
3635 if (combo->uses_identified_by_clause)
3636 {
3637 if (digest_password(thd, combo))
3638 {
3639 my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), CRYPT_MAX_PASSWORD_SIZE);
3640 error= 1;
3641 goto end;
3642 }
3643 }
3644 password= combo->password.str;
3645 password_len= combo->password.length;
3646 /*
3647 There are four options which affect the process of creation of
3648 a new user (mysqld option --safe-create-user, 'insert' privilege
3649 on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
3650 SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
3651 how it should work.
3652 if (safe-user-create && ! INSERT_priv) => reject
3653 else if (identified_by) => create
3654 else if (no_auto_create_user) => reject
3655 else create
3656
3657 see also test_if_create_new_users()
3658 */
3659 if (!password_len &&
3660 auth_plugin_is_built_in(combo->plugin.str) &&
3661 no_auto_create)
3662 {
3663 my_error(ER_PASSWORD_NO_MATCH, MYF(0), combo->user.str, combo->host.str);
3664 goto end;
3665 }
3666 else if (!can_create_user)
3667 {
3668 my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
3669 goto end;
3670 }
3671 else if (combo->plugin.str[0])
3672 {
3673 if (!plugin_is_ready(&combo->plugin, MYSQL_AUTHENTICATION_PLUGIN))
3674 {
3675 my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo->plugin.str);
3676 goto end;
3677 }
3678 }
3679
3680 old_row_exists = 0;
3681 restore_record(table,s->default_values);
3682 DBUG_ASSERT(combo->host.str != NULL);
3683 table->field[MYSQL_USER_FIELD_HOST]->store(combo->host.str,combo->host.length,
3684 system_charset_info);
3685 table->field[MYSQL_USER_FIELD_USER]->store(combo->user.str,combo->user.length,
3686 system_charset_info);
3687 #if defined(HAVE_OPENSSL)
3688 if (combo->plugin.str == sha256_password_plugin_name.str)
3689 {
3690 /* Use the authentication_string field */
3691 combo->auth.str= password;
3692 combo->auth.length= password_len;
3693 if (table->s->fields >= MYSQL_USER_FIELD_AUTHENTICATION_STRING)
3694 {
3695 if (password_len > 0)
3696 table->
3697 field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->
3698 store(password, password_len, &my_charset_utf8_bin);
3699 /* Assert that the proper plugin is set */
3700 table->
3701 field[MYSQL_USER_FIELD_PLUGIN]->
3702 store(sha256_password_plugin_name.str,
3703 sha256_password_plugin_name.length,
3704 system_charset_info);
3705 }
3706 else
3707 {
3708 my_error(ER_BAD_FIELD_ERROR, MYF(0), "plugin", "mysql.user");
3709 goto end;
3710 }
3711 }
3712 else
3713 #endif
3714 {
3715 /* Use the legacy Password field */
3716 table->field[MYSQL_USER_FIELD_PASSWORD]->store(password, password_len,
3717 system_charset_info);
3718 if (table->s->fields >= MYSQL_USER_FIELD_AUTHENTICATION_STRING)
3719 table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->store("\0", 0,
3720 &my_charset_utf8_bin);
3721 }
3722 }
3723 else // if (table->file->ha_index_read_idx_map [..]
3724 {
3725 /*
3726 There is a matching user record ------------------------------------------
3727 */
3728
3729 old_row_exists = 1;
3730 store_record(table,record[1]); // Save copy for update
3731
3732 /*
3733 GRANT statement using IDENTIFIED WITH clause can be used only to create
3734 user and apply privileges to it. Hence it throws an error when used on
3735 existing users.
3736 */
3737 if (combo->uses_identified_with_clause)
3738 {
3739 error= 1;
3740 my_error(ER_GRANT_PLUGIN_USER_EXISTS, MYF(0), combo->user.length,
3741 combo->user.str);
3742 goto end;
3743 }
3744
3745 /* 1. resolve plugins in the LEX_USER struct if needed */
3746 LEX_STRING old_plugin;
3747
3748 /*
3749 Get old plugin value from storage.
3750 */
3751
3752 old_plugin.str=
3753 get_field(thd->mem_root, table->field[MYSQL_USER_FIELD_PLUGIN]);
3754
3755 /*
3756 It is important not to include the trailing '\0' in the string length
3757 because otherwise the plugin hash search will fail.
3758 */
3759 if (old_plugin.str)
3760 {
3761 old_plugin.length= strlen(old_plugin.str);
3762
3763 /*
3764 Optimize for pointer comparision of built-in plugin name
3765 */
3766
3767 optimize_plugin_compare_by_pointer(&old_plugin);
3768
3769 /*
3770 Disable plugin change for existing rows with anything but
3771 the built in plugins.
3772 The idea is that all built in plugins support
3773 IDENTIFIED BY ... and none of the external ones currently do.
3774 */
3775 if ((combo->uses_identified_by_clause ||
3776 combo->uses_identified_by_password_clause) &&
3777 !auth_plugin_is_built_in(old_plugin.str))
3778 {
3779 const char *new_plugin= (combo->plugin.str && combo->plugin.str[0]) ?
3780 combo->plugin.str : default_auth_plugin_name.str;
3781
3782 if (my_strcasecmp(system_charset_info, new_plugin, old_plugin.str))
3783 {
3784 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
3785 ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
3786 }
3787 }
3788 }
3789 else
3790 old_plugin.length= 0;
3791 combo->plugin= old_plugin;
3792
3793 /*
3794 If the plugin value in user table is found to be null or an empty
3795 string, the following steps are followed:
3796
3797 * If GRANT is used with IDENTIFIED BY PASSWORD clause, and the hash
3798 is found to be of mysql_native_password or mysql_old_password
3799 type, the statement passes without an error and the password field
3800 is updated accordingly.
3801 * If GRANT is used with IDENTIFIED BY clause and the password is
3802 provided as a plain string, hashing of the string is done according
3803 to the value of old_passwords variable in the following way.
3804
3805 if old_passwords == 0, mysql_native hashing is used.
3806 if old_passwords == 1, mysql_old hashing is used.
3807 if old_passwords == 2, error.
3808 * An empty password is considered to be of mysql_native type.
3809 */
3810
3811 if (combo->plugin.str == NULL || combo->plugin.str[0] == '\0')
3812 {
3813 if (combo->uses_identified_by_password_clause)
3814 {
3815 if ((combo->password.length == SCRAMBLED_PASSWORD_CHAR_LENGTH) ||
3816 (combo->password.length == 0))
3817 {
3818 combo->plugin.str= native_password_plugin_name.str;
3819 combo->plugin.length= native_password_plugin_name.length;
3820 }
3821 else if (combo->password.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
3822 {
3823 combo->plugin.str= old_password_plugin_name.str;
3824 combo->plugin.length= old_password_plugin_name.length;
3825 }
3826 else
3827 {
3828 /*
3829 If hash length doesn't match either with mysql_native hash length or
3830 mysql_old hash length, throw an error.
3831 */
3832 my_error(ER_PASSWORD_FORMAT, MYF(0));
3833 error= 1;
3834 goto end;
3835 }
3836 }
3837 else
3838 {
3839 /*
3840 Handling of combo->plugin when IDENTIFIED BY PASSWORD clause is not
3841 used, i.e. when the password hash is not provided within the GRANT
3842 query.
3843 */
3844 if ((thd->variables.old_passwords == 1) && (combo->password.length != 0))
3845 {
3846 combo->plugin.str= old_password_plugin_name.str;
3847 combo->plugin.length= old_password_plugin_name.length;
3848 }
3849 else if ((thd->variables.old_passwords == 0) ||
3850 (combo->password.length == 0))
3851 {
3852 combo->plugin.str= native_password_plugin_name.str;
3853 combo->plugin.length= native_password_plugin_name.length;
3854 }
3855 else
3856 {
3857 /* If old_passwords variable is neither 0 nor 1, throw an error. */
3858 my_error(ER_PASSWORD_FORMAT, MYF(0));
3859 error= 1;
3860 goto end;
3861 }
3862 }
3863 }
3864
3865 if (!combo->uses_authentication_string_clause)
3866 {
3867 combo->auth.str= get_field(thd->mem_root,
3868 table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]);
3869 if (combo->auth.str)
3870 combo->auth.length= strlen(combo->auth.str);
3871 else
3872 combo->auth.length= 0;
3873 }
3874 /* 2. Digest password if needed (plugin must have been resolved */
3875 if (combo->uses_identified_by_clause)
3876 {
3877 if (digest_password(thd, combo))
3878 {
3879 error= 1;
3880 goto end;
3881 }
3882 }
3883 password= combo->password.str;
3884 password_len= combo->password.length;
3885
3886 if (password_len > 0)
3887 {
3888 #if defined(HAVE_OPENSSL)
3889 if (combo->plugin.str == sha256_password_plugin_name.str)
3890 {
3891 table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->
3892 store(password, password_len, &my_charset_utf8_bin);
3893 combo->auth.str= password;
3894 combo->auth.length= password_len;
3895 }
3896 else
3897 #endif
3898 {
3899 table->field[MYSQL_USER_FIELD_PASSWORD]->
3900 store(password, password_len, system_charset_info);
3901 table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->
3902 store("\0", 0, &my_charset_utf8_bin);
3903 }
3904 }
3905 else if (!rights && !revoke_grant &&
3906 lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
3907 !lex->mqh.specified_limits)
3908 {
3909
3910 DBUG_PRINT("info", ("Proxy user exit path"));
3911 DBUG_RETURN(0);
3912 }
3913 }
3914
3915 /* error checks on password */
3916 if (password_len > 0)
3917 {
3918 /*
3919 We need to check for hash validity here since later, when
3920 set_user_salt() is executed it will be too late to signal
3921 an error.
3922 */
3923 if ((combo->plugin.str == native_password_plugin_name.str &&
3924 password_len != SCRAMBLED_PASSWORD_CHAR_LENGTH) ||
3925 (combo->plugin.str == old_password_plugin_name.str &&
3926 password_len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323))
3927 {
3928 my_error(ER_PASSWORD_FORMAT, MYF(0));
3929 error= 1;
3930 goto end;
3931 }
3932 /* The legacy Password field is used */
3933 if (combo->plugin.str == old_password_plugin_name.str)
3934 WARN_DEPRECATED_41_PWD_HASH(thd);
3935 }
3936
3937 /* Update table columns with new privileges */
3938
3939 Field **tmp_field;
3940 ulong priv;
3941 uint next_field;
3942 for (tmp_field= table->field+3, priv = SELECT_ACL;
3943 *tmp_field && (*tmp_field)->real_type() == MYSQL_TYPE_ENUM &&
3944 ((Field_enum*) (*tmp_field))->typelib->count == 2 ;
3945 tmp_field++, priv <<= 1)
3946 {
3947 if (priv & rights) // set requested privileges
3948 (*tmp_field)->store(&what, 1, &my_charset_latin1);
3949 }
3950 rights= get_access(table, 3, &next_field);
3951 DBUG_PRINT("info",("table fields: %d",table->s->fields));
3952 if (table->s->fields >= 31) /* From 4.0.0 we have more fields */
3953 {
3954 /* We write down SSL related ACL stuff */
3955 switch (lex->ssl_type) {
3956 case SSL_TYPE_ANY:
3957 table->field[MYSQL_USER_FIELD_SSL_TYPE]->store(STRING_WITH_LEN("ANY"),
3958 &my_charset_latin1);
3959 table->field[MYSQL_USER_FIELD_SSL_CIPHER]->
3960 store("", 0, &my_charset_latin1);
3961 table->field[MYSQL_USER_FIELD_X509_ISSUER]->store("", 0, &my_charset_latin1);
3962 table->field[MYSQL_USER_FIELD_X509_SUBJECT]->store("", 0, &my_charset_latin1);
3963 break;
3964 case SSL_TYPE_X509:
3965 table->field[MYSQL_USER_FIELD_SSL_TYPE]->store(STRING_WITH_LEN("X509"),
3966 &my_charset_latin1);
3967 table->field[MYSQL_USER_FIELD_SSL_CIPHER]->
3968 store("", 0, &my_charset_latin1);
3969 table->field[MYSQL_USER_FIELD_X509_ISSUER]->store("", 0, &my_charset_latin1);
3970 table->field[MYSQL_USER_FIELD_X509_SUBJECT]->store("", 0, &my_charset_latin1);
3971 break;
3972 case SSL_TYPE_SPECIFIED:
3973 table->field[MYSQL_USER_FIELD_SSL_TYPE]->store(STRING_WITH_LEN("SPECIFIED"),
3974 &my_charset_latin1);
3975 table->field[MYSQL_USER_FIELD_SSL_CIPHER]->store("", 0, &my_charset_latin1);
3976 table->field[MYSQL_USER_FIELD_X509_ISSUER]->store("", 0, &my_charset_latin1);
3977 table->field[MYSQL_USER_FIELD_X509_SUBJECT]->store("", 0, &my_charset_latin1);
3978 if (lex->ssl_cipher)
3979 table->field[MYSQL_USER_FIELD_SSL_CIPHER]->store(lex->ssl_cipher,
3980 strlen(lex->ssl_cipher), system_charset_info);
3981 if (lex->x509_issuer)
3982 table->field[MYSQL_USER_FIELD_X509_ISSUER]->store(lex->x509_issuer,
3983 strlen(lex->x509_issuer), system_charset_info);
3984 if (lex->x509_subject)
3985 table->field[MYSQL_USER_FIELD_X509_SUBJECT]->store(lex->x509_subject,
3986 strlen(lex->x509_subject), system_charset_info);
3987 break;
3988 case SSL_TYPE_NOT_SPECIFIED:
3989 break;
3990 case SSL_TYPE_NONE:
3991 table->field[MYSQL_USER_FIELD_SSL_TYPE]->store("", 0, &my_charset_latin1);
3992 table->field[MYSQL_USER_FIELD_SSL_CIPHER]->store("", 0, &my_charset_latin1);
3993 table->field[MYSQL_USER_FIELD_X509_ISSUER]->store("", 0, &my_charset_latin1);
3994 table->field[MYSQL_USER_FIELD_X509_SUBJECT]->store("", 0, &my_charset_latin1);
3995 break;
3996 }
3997 next_field+=4;
3998
3999 USER_RESOURCES mqh= lex->mqh;
4000 if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
4001 table->field[MYSQL_USER_FIELD_MAX_QUESTIONS]->
4002 store((longlong) mqh.questions, TRUE);
4003 if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
4004 table->field[MYSQL_USER_FIELD_MAX_UPDATES]->
4005 store((longlong) mqh.updates, TRUE);
4006 if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
4007 table->field[MYSQL_USER_FIELD_MAX_CONNECTIONS]->
4008 store((longlong) mqh.conn_per_hour, TRUE);
4009 if (table->s->fields >= 36 &&
4010 (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
4011 table->field[MYSQL_USER_FIELD_MAX_USER_CONNECTIONS]->
4012 store((longlong) mqh.user_conn, TRUE);
4013 mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
4014
4015 next_field+= 4;
4016 if (combo->plugin.length > 0 && !old_row_exists)
4017 {
4018 if (table->s->fields >= 41)
4019 {
4020 table->field[MYSQL_USER_FIELD_PLUGIN]->
4021 store(combo->plugin.str, combo->plugin.length, system_charset_info);
4022 table->field[MYSQL_USER_FIELD_PLUGIN]->set_notnull();
4023 table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->
4024 store(combo->auth.str, combo->auth.length, &my_charset_utf8_bin);
4025 table->field[MYSQL_USER_FIELD_AUTHENTICATION_STRING]->set_notnull();
4026 }
4027 else
4028 {
4029 my_error(ER_BAD_FIELD_ERROR, MYF(0), "plugin", "mysql.user");
4030 goto end;
4031 }
4032 }
4033
4034 /* if we have a password supplied we update the expiration field */
4035 if (table->s->fields > MYSQL_USER_FIELD_PASSWORD_EXPIRED &&
4036 password_len > 0)
4037 table->field[MYSQL_USER_FIELD_PASSWORD_EXPIRED]->store("N", 1,
4038 system_charset_info);
4039 }
4040
4041 if (old_row_exists)
4042 {
4043 /*
4044 We should NEVER delete from the user table, as a uses can still
4045 use mysqld even if he doesn't have any privileges in the user table!
4046 */
4047 if (cmp_record(table,record[1]))
4048 {
4049 if ((error=
4050 table->file->ha_update_row(table->record[1],table->record[0])) &&
4051 error != HA_ERR_RECORD_IS_THE_SAME)
4052 { // This should never happen
4053 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4054 error= -1; /* purecov: deadcode */
4055 goto end; /* purecov: deadcode */
4056 }
4057 else
4058 error= 0;
4059 }
4060 }
4061 else if ((error=table->file->ha_write_row(table->record[0]))) // insert
4062 { // This should never happen
4063 if (table->file->is_fatal_error(error, HA_CHECK_DUP))
4064 {
4065 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4066 error= -1; /* purecov: deadcode */
4067 goto end; /* purecov: deadcode */
4068 }
4069 }
4070 error=0; // Privileges granted / revoked
4071
4072 end:
4073 if (!error)
4074 {
4075 acl_cache->clear(1); // Clear privilege cache
4076 if (old_row_exists)
4077 acl_update_user(combo->user.str, combo->host.str,
4078 combo->password.str, password_len,
4079 lex->ssl_type,
4080 lex->ssl_cipher,
4081 lex->x509_issuer,
4082 lex->x509_subject,
4083 &lex->mqh,
4084 rights,
4085 &combo->plugin,
4086 &combo->auth);
4087 else
4088 acl_insert_user(combo->user.str, combo->host.str, password, password_len,
4089 lex->ssl_type,
4090 lex->ssl_cipher,
4091 lex->x509_issuer,
4092 lex->x509_subject,
4093 &lex->mqh,
4094 rights,
4095 &combo->plugin,
4096 &combo->auth);
4097 }
4098 DBUG_RETURN(error);
4099 }
4100
4101
4102 /*
4103 change grants in the mysql.db table
4104 */
4105
replace_db_table(TABLE * table,const char * db,const LEX_USER & combo,ulong rights,bool revoke_grant)4106 static int replace_db_table(TABLE *table, const char *db,
4107 const LEX_USER &combo,
4108 ulong rights, bool revoke_grant)
4109 {
4110 uint i;
4111 ulong priv,store_rights;
4112 bool old_row_exists=0;
4113 int error;
4114 char what= (revoke_grant) ? 'N' : 'Y';
4115 uchar user_key[MAX_KEY_LENGTH];
4116 Acl_table_intact table_intact;
4117 DBUG_ENTER("replace_db_table");
4118
4119 if (!initialized)
4120 {
4121 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
4122 DBUG_RETURN(-1);
4123 }
4124
4125 if (table_intact.check(table, &mysql_db_table_def))
4126 DBUG_RETURN(-1);
4127
4128 /* Check if there is such a user in user table in memory? */
4129 if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
4130 {
4131 my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
4132 DBUG_RETURN(-1);
4133 }
4134
4135 table->use_all_columns();
4136 table->field[0]->store(combo.host.str,combo.host.length,
4137 system_charset_info);
4138 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4139 table->field[2]->store(combo.user.str,combo.user.length,
4140 system_charset_info);
4141 key_copy(user_key, table->record[0], table->key_info,
4142 table->key_info->key_length);
4143
4144 if (table->file->ha_index_read_idx_map(table->record[0],0, user_key,
4145 HA_WHOLE_KEY,
4146 HA_READ_KEY_EXACT))
4147 {
4148 if (what == 'N')
4149 { // no row, no revoke
4150 my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
4151 goto abort;
4152 }
4153 old_row_exists = 0;
4154 restore_record(table, s->default_values);
4155 table->field[0]->store(combo.host.str,combo.host.length,
4156 system_charset_info);
4157 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4158 table->field[2]->store(combo.user.str,combo.user.length,
4159 system_charset_info);
4160 }
4161 else
4162 {
4163 old_row_exists = 1;
4164 store_record(table,record[1]);
4165 }
4166
4167 store_rights=get_rights_for_db(rights);
4168 for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
4169 {
4170 if (priv & store_rights) // do it if priv is chosen
4171 table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
4172 }
4173 rights=get_access(table,3);
4174 rights=fix_rights_for_db(rights);
4175
4176 if (old_row_exists)
4177 {
4178 /* update old existing row */
4179 if (rights)
4180 {
4181 if ((error= table->file->ha_update_row(table->record[1],
4182 table->record[0])) &&
4183 error != HA_ERR_RECORD_IS_THE_SAME)
4184 goto table_error; /* purecov: deadcode */
4185 }
4186 else /* must have been a revoke of all privileges */
4187 {
4188 if ((error= table->file->ha_delete_row(table->record[1])))
4189 goto table_error; /* purecov: deadcode */
4190 }
4191 }
4192 else if (rights && (error= table->file->ha_write_row(table->record[0])))
4193 {
4194 if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4195 goto table_error; /* purecov: deadcode */
4196 }
4197
4198 acl_cache->clear(1); // Clear privilege cache
4199 if (old_row_exists)
4200 acl_update_db(combo.user.str,combo.host.str,db,rights);
4201 else
4202 if (rights)
4203 acl_insert_db(combo.user.str,combo.host.str,db,rights);
4204 DBUG_RETURN(0);
4205
4206 /* This could only happen if the grant tables got corrupted */
4207 table_error:
4208 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
4209
4210 abort:
4211 DBUG_RETURN(-1);
4212 }
4213
4214
4215 static void
acl_update_proxy_user(ACL_PROXY_USER * new_value,bool is_revoke)4216 acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
4217 {
4218 mysql_mutex_assert_owner(&acl_cache->lock);
4219
4220 DBUG_ENTER("acl_update_proxy_user");
4221 for (uint i= 0; i < acl_proxy_users.elements; i++)
4222 {
4223 ACL_PROXY_USER *acl_user=
4224 dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
4225
4226 if (acl_user->pk_equals(new_value))
4227 {
4228 if (is_revoke)
4229 {
4230 DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
4231 delete_dynamic_element(&acl_proxy_users, i);
4232 }
4233 else
4234 {
4235 DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
4236 acl_user->set_data(new_value);
4237 }
4238 break;
4239 }
4240 }
4241 DBUG_VOID_RETURN;
4242 }
4243
4244
4245 static void
acl_insert_proxy_user(ACL_PROXY_USER * new_value)4246 acl_insert_proxy_user(ACL_PROXY_USER *new_value)
4247 {
4248 DBUG_ENTER("acl_insert_proxy_user");
4249 mysql_mutex_assert_owner(&acl_cache->lock);
4250 (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
4251 my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
4252 acl_proxy_users.elements,
4253 sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
4254 DBUG_VOID_RETURN;
4255 }
4256
4257
4258 static int
replace_proxies_priv_table(THD * thd,TABLE * table,const LEX_USER * user,const LEX_USER * proxied_user,bool with_grant_arg,bool revoke_grant)4259 replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
4260 const LEX_USER *proxied_user, bool with_grant_arg,
4261 bool revoke_grant)
4262 {
4263 bool old_row_exists= 0;
4264 int error;
4265 uchar user_key[MAX_KEY_LENGTH];
4266 ACL_PROXY_USER new_grant;
4267 char grantor[USER_HOST_BUFF_SIZE];
4268 Acl_table_intact table_intact;
4269
4270 DBUG_ENTER("replace_proxies_priv_table");
4271
4272 if (!initialized)
4273 {
4274 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
4275 DBUG_RETURN(-1);
4276 }
4277
4278 if (table_intact.check(table, &mysql_proxies_priv_table_def))
4279 DBUG_RETURN(-1);
4280
4281 /* Check if there is such a user in user table in memory? */
4282 if (!find_acl_user(user->host.str,user->user.str, FALSE))
4283 {
4284 my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
4285 DBUG_RETURN(-1);
4286 }
4287
4288 table->use_all_columns();
4289 ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
4290 &proxied_user->host, &proxied_user->user);
4291
4292 key_copy(user_key, table->record[0], table->key_info,
4293 table->key_info->key_length);
4294
4295 get_grantor(thd, grantor);
4296
4297 if ((error= table->file->ha_index_init(0, 1)))
4298 {
4299 table->file->print_error(error, MYF(0));
4300 DBUG_PRINT("info", ("ha_index_init error"));
4301 DBUG_RETURN(-1);
4302 }
4303
4304 if (table->file->ha_index_read_map(table->record[0], user_key,
4305 HA_WHOLE_KEY,
4306 HA_READ_KEY_EXACT))
4307 {
4308 DBUG_PRINT ("info", ("Row not found"));
4309 if (revoke_grant)
4310 { // no row, no revoke
4311 my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
4312 goto abort;
4313 }
4314 old_row_exists= 0;
4315 restore_record(table, s->default_values);
4316 ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
4317 &proxied_user->host,
4318 &proxied_user->user,
4319 with_grant_arg,
4320 grantor);
4321 }
4322 else
4323 {
4324 DBUG_PRINT("info", ("Row found"));
4325 old_row_exists= 1;
4326 store_record(table, record[1]);
4327 }
4328
4329 if (old_row_exists)
4330 {
4331 /* update old existing row */
4332 if (!revoke_grant)
4333 {
4334 if ((error= table->file->ha_update_row(table->record[1],
4335 table->record[0])) &&
4336 error != HA_ERR_RECORD_IS_THE_SAME)
4337 goto table_error; /* purecov: inspected */
4338 }
4339 else
4340 {
4341 if ((error= table->file->ha_delete_row(table->record[1])))
4342 goto table_error; /* purecov: inspected */
4343 }
4344 }
4345 else if ((error= table->file->ha_write_row(table->record[0])))
4346 {
4347 DBUG_PRINT("info", ("error inserting the row"));
4348 if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4349 goto table_error; /* purecov: inspected */
4350 }
4351
4352 acl_cache->clear(1); // Clear privilege cache
4353 if (old_row_exists)
4354 {
4355 new_grant.init(user->host.str, user->user.str,
4356 proxied_user->host.str, proxied_user->user.str,
4357 with_grant_arg);
4358 acl_update_proxy_user(&new_grant, revoke_grant);
4359 }
4360 else
4361 {
4362 new_grant.init(&global_acl_memory, user->host.str, user->user.str,
4363 proxied_user->host.str, proxied_user->user.str,
4364 with_grant_arg);
4365 acl_insert_proxy_user(&new_grant);
4366 }
4367
4368 table->file->ha_index_end();
4369 DBUG_RETURN(0);
4370
4371 /* This could only happen if the grant tables got corrupted */
4372 table_error:
4373 DBUG_PRINT("info", ("table error"));
4374 table->file->print_error(error, MYF(0)); /* purecov: inspected */
4375
4376 abort:
4377 DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
4378 table->file->ha_index_end();
4379 DBUG_RETURN(-1);
4380 }
4381
4382
4383 class GRANT_COLUMN :public Sql_alloc
4384 {
4385 public:
4386 char *column;
4387 ulong rights;
4388 uint key_length;
GRANT_COLUMN(String & c,ulong y)4389 GRANT_COLUMN(String &c, ulong y) :rights (y)
4390 {
4391 column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length());
4392 }
4393 };
4394
4395
get_key_column(GRANT_COLUMN * buff,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))4396 static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
4397 my_bool not_used MY_ATTRIBUTE((unused)))
4398 {
4399 *length=buff->key_length;
4400 return (uchar*) buff->column;
4401 }
4402
4403
4404 class GRANT_NAME :public Sql_alloc
4405 {
4406 public:
4407 ACL_HOST_AND_IP host;
4408 char *db, *user, *tname, *hash_key;
4409 ulong privs;
4410 ulong sort;
4411 size_t key_length;
4412 GRANT_NAME(const char *h, const char *d,const char *u,
4413 const char *t, ulong p, bool is_routine);
4414 GRANT_NAME (TABLE *form, bool is_routine);
~GRANT_NAME()4415 virtual ~GRANT_NAME() {};
ok()4416 virtual bool ok() { return privs != 0; }
4417 void set_user_details(const char *h, const char *d,
4418 const char *u, const char *t,
4419 bool is_routine);
4420 };
4421
4422
4423 class GRANT_TABLE :public GRANT_NAME
4424 {
4425 public:
4426 ulong cols;
4427 HASH hash_columns;
4428
4429 GRANT_TABLE(const char *h, const char *d,const char *u,
4430 const char *t, ulong p, ulong c);
4431 GRANT_TABLE (TABLE *form, TABLE *col_privs);
4432 ~GRANT_TABLE();
ok()4433 bool ok() { return privs != 0 || cols != 0; }
4434 };
4435
4436
set_user_details(const char * h,const char * d,const char * u,const char * t,bool is_routine)4437 void GRANT_NAME::set_user_details(const char *h, const char *d,
4438 const char *u, const char *t,
4439 bool is_routine)
4440 {
4441 /* Host given by user */
4442 host.update_hostname(strdup_root(&memex, h));
4443 if (db != d)
4444 {
4445 db= strdup_root(&memex, d);
4446 if (lower_case_table_names)
4447 my_casedn_str(files_charset_info, db);
4448 }
4449 user = strdup_root(&memex,u);
4450 sort= get_sort(3,host.get_host(),db,user);
4451 if (tname != t)
4452 {
4453 tname= strdup_root(&memex, t);
4454 if (lower_case_table_names || is_routine)
4455 my_casedn_str(files_charset_info, tname);
4456 }
4457 key_length= strlen(d) + strlen(u)+ strlen(t)+3;
4458 hash_key= (char*) alloc_root(&memex,key_length);
4459 strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
4460 }
4461
GRANT_NAME(const char * h,const char * d,const char * u,const char * t,ulong p,bool is_routine)4462 GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
4463 const char *t, ulong p, bool is_routine)
4464 :db(0), tname(0), privs(p)
4465 {
4466 set_user_details(h, d, u, t, is_routine);
4467 }
4468
GRANT_TABLE(const char * h,const char * d,const char * u,const char * t,ulong p,ulong c)4469 GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
4470 const char *t, ulong p, ulong c)
4471 :GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
4472 {
4473 (void) my_hash_init2(&hash_columns,4,system_charset_info,
4474 0,0,0, (my_hash_get_key) get_key_column,0,0);
4475 }
4476
4477
GRANT_NAME(TABLE * form,bool is_routine)4478 GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
4479 {
4480 host.update_hostname(get_field(&memex, form->field[0]));
4481 db= get_field(&memex,form->field[1]);
4482 user= get_field(&memex,form->field[2]);
4483 if (!user)
4484 user= (char*) "";
4485 sort= get_sort(3, host.get_host(), db, user);
4486 tname= get_field(&memex,form->field[3]);
4487 if (!db || !tname) {
4488 /* Wrong table row; Ignore it */
4489 privs= 0;
4490 return; /* purecov: inspected */
4491 }
4492 if (lower_case_table_names)
4493 {
4494 my_casedn_str(files_charset_info, db);
4495 }
4496 if (lower_case_table_names || is_routine)
4497 {
4498 my_casedn_str(files_charset_info, tname);
4499 }
4500 key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
4501 hash_key= (char*) alloc_root(&memex, key_length);
4502 strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
4503 privs = (ulong) form->field[6]->val_int();
4504 privs = fix_rights_for_table(privs);
4505 }
4506
4507
GRANT_TABLE(TABLE * form,TABLE * col_privs)4508 GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
4509 :GRANT_NAME(form, FALSE)
4510 {
4511 uchar key[MAX_KEY_LENGTH];
4512
4513 if (!db || !tname)
4514 {
4515 /* Wrong table row; Ignore it */
4516 my_hash_clear(&hash_columns); /* allow for destruction */
4517 cols= 0;
4518 return;
4519 }
4520 cols= (ulong) form->field[7]->val_int();
4521 cols = fix_rights_for_column(cols);
4522
4523 (void) my_hash_init2(&hash_columns,4,system_charset_info,
4524 0,0,0, (my_hash_get_key) get_key_column,0,0);
4525 if (cols)
4526 {
4527 uint key_prefix_len;
4528 KEY_PART_INFO *key_part= col_privs->key_info->key_part;
4529 col_privs->field[0]->store(host.get_host(),
4530 host.get_host() ? (uint) host.get_host_len() : 0,
4531 system_charset_info);
4532 col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
4533 col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
4534 col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
4535
4536 key_prefix_len= (key_part[0].store_length +
4537 key_part[1].store_length +
4538 key_part[2].store_length +
4539 key_part[3].store_length);
4540 key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
4541 col_privs->field[4]->store("",0, &my_charset_latin1);
4542
4543 if (col_privs->file->ha_index_init(0, 1))
4544 {
4545 cols= 0;
4546 return;
4547 }
4548
4549 if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
4550 (key_part_map)15, HA_READ_KEY_EXACT))
4551 {
4552 cols = 0; /* purecov: deadcode */
4553 col_privs->file->ha_index_end();
4554 return;
4555 }
4556 do
4557 {
4558 String *res,column_name;
4559 GRANT_COLUMN *mem_check;
4560 /* As column name is a string, we don't have to supply a buffer */
4561 res=col_privs->field[4]->val_str(&column_name);
4562 ulong priv= (ulong) col_privs->field[6]->val_int();
4563 if (!(mem_check = new GRANT_COLUMN(*res,
4564 fix_rights_for_column(priv))))
4565 {
4566 /* Don't use this entry */
4567 privs = cols = 0; /* purecov: deadcode */
4568 return; /* purecov: deadcode */
4569 }
4570 if (my_hash_insert(&hash_columns, (uchar *) mem_check))
4571 {
4572 /* Invalidate this entry */
4573 privs= cols= 0;
4574 return;
4575 }
4576 } while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
4577 !key_cmp_if_same(col_privs,key,0,key_prefix_len));
4578 col_privs->file->ha_index_end();
4579 }
4580 }
4581
4582
~GRANT_TABLE()4583 GRANT_TABLE::~GRANT_TABLE()
4584 {
4585 my_hash_free(&hash_columns);
4586 }
4587
4588
get_grant_table(GRANT_NAME * buff,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))4589 static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
4590 my_bool not_used MY_ATTRIBUTE((unused)))
4591 {
4592 *length=buff->key_length;
4593 return (uchar*) buff->hash_key;
4594 }
4595
4596
free_grant_table(GRANT_TABLE * grant_table)4597 void free_grant_table(GRANT_TABLE *grant_table)
4598 {
4599 my_hash_free(&grant_table->hash_columns);
4600 }
4601
4602
4603 /* Search after a matching grant. Prefer exact grants before not exact ones */
4604
name_hash_search(HASH * name_hash,const char * host,const char * ip,const char * db,const char * user,const char * tname,bool exact,bool name_tolower)4605 static GRANT_NAME *name_hash_search(HASH *name_hash,
4606 const char *host,const char* ip,
4607 const char *db,
4608 const char *user, const char *tname,
4609 bool exact, bool name_tolower)
4610 {
4611 char helping [NAME_LEN*2+USERNAME_LENGTH+3], *name_ptr;
4612 uint len;
4613 GRANT_NAME *grant_name,*found=0;
4614 HASH_SEARCH_STATE state;
4615
4616 name_ptr= strmov(strmov(helping, user) + 1, db) + 1;
4617 len = (uint) (strmov(name_ptr, tname) - helping) + 1;
4618 if (name_tolower)
4619 my_casedn_str(files_charset_info, name_ptr);
4620 for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
4621 len, &state);
4622 grant_name ;
4623 grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
4624 len, &state))
4625 {
4626 if (exact)
4627 {
4628 if (!grant_name->host.get_host() ||
4629 (host &&
4630 !my_strcasecmp(system_charset_info, host,
4631 grant_name->host.get_host())) ||
4632 (ip && !strcmp(ip, grant_name->host.get_host())))
4633 return grant_name;
4634 }
4635 else
4636 {
4637 if (grant_name->host.compare_hostname(host, ip) &&
4638 (!found || found->sort < grant_name->sort))
4639 found=grant_name; // Host ok
4640 }
4641 }
4642 return found;
4643 }
4644
4645
4646 inline GRANT_NAME *
routine_hash_search(const char * host,const char * ip,const char * db,const char * user,const char * tname,bool proc,bool exact)4647 routine_hash_search(const char *host, const char *ip, const char *db,
4648 const char *user, const char *tname, bool proc, bool exact)
4649 {
4650 return (GRANT_TABLE*)
4651 name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
4652 host, ip, db, user, tname, exact, TRUE);
4653 }
4654
4655
4656 inline GRANT_TABLE *
table_hash_search(const char * host,const char * ip,const char * db,const char * user,const char * tname,bool exact)4657 table_hash_search(const char *host, const char *ip, const char *db,
4658 const char *user, const char *tname, bool exact)
4659 {
4660 return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
4661 user, tname, exact, FALSE);
4662 }
4663
4664
4665 inline GRANT_COLUMN *
column_hash_search(GRANT_TABLE * t,const char * cname,uint length)4666 column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
4667 {
4668 return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
4669 (uchar*) cname, length);
4670 }
4671
4672
replace_column_table(GRANT_TABLE * g_t,TABLE * table,const LEX_USER & combo,List<LEX_COLUMN> & columns,const char * db,const char * table_name,ulong rights,bool revoke_grant)4673 static int replace_column_table(GRANT_TABLE *g_t,
4674 TABLE *table, const LEX_USER &combo,
4675 List <LEX_COLUMN> &columns,
4676 const char *db, const char *table_name,
4677 ulong rights, bool revoke_grant)
4678 {
4679 int result=0;
4680 uchar key[MAX_KEY_LENGTH];
4681 uint key_prefix_length;
4682 KEY_PART_INFO *key_part;
4683 Acl_table_intact table_intact;
4684 DBUG_ENTER("replace_column_table");
4685
4686 if (table_intact.check(table, &mysql_columns_priv_table_def))
4687 DBUG_RETURN(-1);
4688
4689 key_part= table->key_info->key_part;
4690
4691 table->use_all_columns();
4692 table->field[0]->store(combo.host.str,combo.host.length,
4693 system_charset_info);
4694 table->field[1]->store(db,(uint) strlen(db),
4695 system_charset_info);
4696 table->field[2]->store(combo.user.str,combo.user.length,
4697 system_charset_info);
4698 table->field[3]->store(table_name,(uint) strlen(table_name),
4699 system_charset_info);
4700
4701 /* Get length of 4 first key parts */
4702 key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
4703 key_part[2].store_length + key_part[3].store_length);
4704 key_copy(key, table->record[0], table->key_info, key_prefix_length);
4705
4706 rights&= COL_ACLS; // Only ACL for columns
4707
4708 /* first fix privileges for all columns in column list */
4709
4710 List_iterator <LEX_COLUMN> iter(columns);
4711 class LEX_COLUMN *column;
4712 int error= table->file->ha_index_init(0, 1);
4713 if (error)
4714 {
4715 table->file->print_error(error, MYF(0));
4716 DBUG_RETURN(-1);
4717 }
4718
4719 while ((column= iter++))
4720 {
4721 ulong privileges= column->rights;
4722 bool old_row_exists=0;
4723 uchar user_key[MAX_KEY_LENGTH];
4724
4725 key_restore(table->record[0],key,table->key_info,
4726 key_prefix_length);
4727 table->field[4]->store(column->column.ptr(), column->column.length(),
4728 system_charset_info);
4729 /* Get key for the first 4 columns */
4730 key_copy(user_key, table->record[0], table->key_info,
4731 table->key_info->key_length);
4732
4733 if (table->file->ha_index_read_map(table->record[0], user_key, HA_WHOLE_KEY,
4734 HA_READ_KEY_EXACT))
4735 {
4736 if (revoke_grant)
4737 {
4738 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
4739 combo.user.str, combo.host.str,
4740 table_name); /* purecov: inspected */
4741 result= -1; /* purecov: inspected */
4742 continue; /* purecov: inspected */
4743 }
4744 old_row_exists = 0;
4745 restore_record(table, s->default_values); // Get empty record
4746 key_restore(table->record[0],key,table->key_info,
4747 key_prefix_length);
4748 table->field[4]->store(column->column.ptr(),column->column.length(),
4749 system_charset_info);
4750 }
4751 else
4752 {
4753 ulong tmp= (ulong) table->field[6]->val_int();
4754 tmp=fix_rights_for_column(tmp);
4755
4756 if (revoke_grant)
4757 privileges = tmp & ~(privileges | rights);
4758 else
4759 privileges |= tmp;
4760 old_row_exists = 1;
4761 store_record(table,record[1]); // copy original row
4762 }
4763
4764 table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
4765
4766 if (old_row_exists)
4767 {
4768 GRANT_COLUMN *grant_column;
4769 if (privileges)
4770 error=table->file->ha_update_row(table->record[1],table->record[0]);
4771 else
4772 error=table->file->ha_delete_row(table->record[1]);
4773 if (error && error != HA_ERR_RECORD_IS_THE_SAME)
4774 {
4775 table->file->print_error(error,MYF(0)); /* purecov: inspected */
4776 result= -1; /* purecov: inspected */
4777 goto end; /* purecov: inspected */
4778 }
4779 else
4780 error= 0;
4781 grant_column= column_hash_search(g_t, column->column.ptr(),
4782 column->column.length());
4783 if (grant_column) // Should always be true
4784 grant_column->rights= privileges; // Update hash
4785 }
4786 else // new grant
4787 {
4788 GRANT_COLUMN *grant_column;
4789 if ((error=table->file->ha_write_row(table->record[0])))
4790 {
4791 table->file->print_error(error,MYF(0)); /* purecov: inspected */
4792 result= -1; /* purecov: inspected */
4793 goto end; /* purecov: inspected */
4794 }
4795 grant_column= new GRANT_COLUMN(column->column,privileges);
4796 if (my_hash_insert(&g_t->hash_columns,(uchar*) grant_column))
4797 {
4798 result= -1;
4799 goto end;
4800 }
4801 }
4802 }
4803
4804 /*
4805 If revoke of privileges on the table level, remove all such privileges
4806 for all columns
4807 */
4808
4809 if (revoke_grant)
4810 {
4811 uchar user_key[MAX_KEY_LENGTH];
4812 key_copy(user_key, table->record[0], table->key_info,
4813 key_prefix_length);
4814
4815 if (table->file->ha_index_read_map(table->record[0], user_key,
4816 (key_part_map)15,
4817 HA_READ_KEY_EXACT))
4818 goto end;
4819
4820 /* Scan through all rows with the same host,db,user and table */
4821 do
4822 {
4823 ulong privileges = (ulong) table->field[6]->val_int();
4824 privileges=fix_rights_for_column(privileges);
4825 store_record(table,record[1]);
4826
4827 if (privileges & rights) // is in this record the priv to be revoked ??
4828 {
4829 GRANT_COLUMN *grant_column = NULL;
4830 char colum_name_buf[HOSTNAME_LENGTH+1];
4831 String column_name(colum_name_buf,sizeof(colum_name_buf),
4832 system_charset_info);
4833
4834 privileges&= ~rights;
4835 table->field[6]->store((longlong)
4836 get_rights_for_column(privileges), TRUE);
4837 table->field[4]->val_str(&column_name);
4838 grant_column = column_hash_search(g_t,
4839 column_name.ptr(),
4840 column_name.length());
4841 if (privileges)
4842 {
4843 int tmp_error;
4844 if ((tmp_error=table->file->ha_update_row(table->record[1],
4845 table->record[0])) &&
4846 tmp_error != HA_ERR_RECORD_IS_THE_SAME)
4847 { /* purecov: deadcode */
4848 table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
4849 result= -1; /* purecov: deadcode */
4850 goto end; /* purecov: deadcode */
4851 }
4852 if (grant_column)
4853 grant_column->rights = privileges; // Update hash
4854 }
4855 else
4856 {
4857 int tmp_error;
4858 if ((tmp_error = table->file->ha_delete_row(table->record[1])))
4859 { /* purecov: deadcode */
4860 table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
4861 result= -1; /* purecov: deadcode */
4862 goto end; /* purecov: deadcode */
4863 }
4864 if (grant_column)
4865 my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
4866 }
4867 }
4868 } while (!table->file->ha_index_next(table->record[0]) &&
4869 !key_cmp_if_same(table, key, 0, key_prefix_length));
4870 }
4871
4872 end:
4873 table->file->ha_index_end();
4874 DBUG_RETURN(result);
4875 }
4876
get_grantor(THD * thd,char * grantor)4877 static inline void get_grantor(THD *thd, char *grantor)
4878 {
4879 const char *user= thd->security_ctx->user;
4880 const char *host= thd->security_ctx->host_or_ip;
4881
4882 #if defined(HAVE_REPLICATION)
4883 if (thd->slave_thread && thd->has_invoker())
4884 {
4885 user= thd->get_invoker_user().str;
4886 host= thd->get_invoker_host().str;
4887 }
4888 #endif
4889 strxmov(grantor, user, "@", host, NullS);
4890 }
4891
replace_table_table(THD * thd,GRANT_TABLE * grant_table,TABLE * table,const LEX_USER & combo,const char * db,const char * table_name,ulong rights,ulong col_rights,bool revoke_grant)4892 static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
4893 TABLE *table, const LEX_USER &combo,
4894 const char *db, const char *table_name,
4895 ulong rights, ulong col_rights,
4896 bool revoke_grant)
4897 {
4898 char grantor[USER_HOST_BUFF_SIZE];
4899 int old_row_exists = 1;
4900 int error=0;
4901 ulong store_table_rights, store_col_rights;
4902 uchar user_key[MAX_KEY_LENGTH];
4903 Acl_table_intact table_intact;
4904 DBUG_ENTER("replace_table_table");
4905
4906 if (table_intact.check(table, &mysql_tables_priv_table_def))
4907 DBUG_RETURN(-1);
4908
4909 get_grantor(thd, grantor);
4910 /*
4911 The following should always succeed as new users are created before
4912 this function is called!
4913 */
4914 if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
4915 {
4916 my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
4917 MYF(0)); /* purecov: deadcode */
4918 DBUG_RETURN(-1); /* purecov: deadcode */
4919 }
4920
4921 table->use_all_columns();
4922 restore_record(table, s->default_values); // Get empty record
4923 table->field[0]->store(combo.host.str,combo.host.length,
4924 system_charset_info);
4925 table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4926 table->field[2]->store(combo.user.str,combo.user.length,
4927 system_charset_info);
4928 table->field[3]->store(table_name,(uint) strlen(table_name),
4929 system_charset_info);
4930 store_record(table,record[1]); // store at pos 1
4931 key_copy(user_key, table->record[0], table->key_info,
4932 table->key_info->key_length);
4933
4934 if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
4935 HA_WHOLE_KEY,
4936 HA_READ_KEY_EXACT))
4937 {
4938 /*
4939 The following should never happen as we first check the in memory
4940 grant tables for the user. There is however always a small change that
4941 the user has modified the grant tables directly.
4942 */
4943 if (revoke_grant)
4944 { // no row, no revoke
4945 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
4946 combo.user.str, combo.host.str,
4947 table_name); /* purecov: deadcode */
4948 DBUG_RETURN(-1); /* purecov: deadcode */
4949 }
4950 old_row_exists = 0;
4951 restore_record(table,record[1]); // Get saved record
4952 }
4953
4954 store_table_rights= get_rights_for_table(rights);
4955 store_col_rights= get_rights_for_column(col_rights);
4956 if (old_row_exists)
4957 {
4958 ulong j,k;
4959 store_record(table,record[1]);
4960 j = (ulong) table->field[6]->val_int();
4961 k = (ulong) table->field[7]->val_int();
4962
4963 if (revoke_grant)
4964 {
4965 /* column rights are already fixed in mysql_table_grant */
4966 store_table_rights=j & ~store_table_rights;
4967 }
4968 else
4969 {
4970 store_table_rights|= j;
4971 store_col_rights|= k;
4972 }
4973 }
4974
4975 table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
4976 table->field[6]->store((longlong) store_table_rights, TRUE);
4977 table->field[7]->store((longlong) store_col_rights, TRUE);
4978 rights=fix_rights_for_table(store_table_rights);
4979 col_rights=fix_rights_for_column(store_col_rights);
4980
4981 if (old_row_exists)
4982 {
4983 if (store_table_rights || store_col_rights)
4984 {
4985 if ((error=table->file->ha_update_row(table->record[1],
4986 table->record[0])) &&
4987 error != HA_ERR_RECORD_IS_THE_SAME)
4988 goto table_error; /* purecov: deadcode */
4989 }
4990 else if ((error = table->file->ha_delete_row(table->record[1])))
4991 goto table_error; /* purecov: deadcode */
4992 }
4993 else
4994 {
4995 error=table->file->ha_write_row(table->record[0]);
4996 if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4997 goto table_error; /* purecov: deadcode */
4998 }
4999
5000 if (rights | col_rights)
5001 {
5002 grant_table->privs= rights;
5003 grant_table->cols= col_rights;
5004 }
5005 else
5006 {
5007 my_hash_delete(&column_priv_hash,(uchar*) grant_table);
5008 }
5009 DBUG_RETURN(0);
5010
5011 /* This should never happen */
5012 table_error:
5013 table->file->print_error(error,MYF(0)); /* purecov: deadcode */
5014 DBUG_RETURN(-1); /* purecov: deadcode */
5015 }
5016
5017
5018 /**
5019 @retval 0 success
5020 @retval -1 error
5021 */
replace_routine_table(THD * thd,GRANT_NAME * grant_name,TABLE * table,const LEX_USER & combo,const char * db,const char * routine_name,bool is_proc,ulong rights,bool revoke_grant)5022 static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
5023 TABLE *table, const LEX_USER &combo,
5024 const char *db, const char *routine_name,
5025 bool is_proc, ulong rights, bool revoke_grant)
5026 {
5027 char grantor[USER_HOST_BUFF_SIZE];
5028 int old_row_exists= 1;
5029 int error=0;
5030 ulong store_proc_rights;
5031 Acl_table_intact table_intact;
5032 DBUG_ENTER("replace_routine_table");
5033
5034 if (!initialized)
5035 {
5036 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
5037 DBUG_RETURN(-1);
5038 }
5039
5040 if (table_intact.check(table, &mysql_procs_priv_table_def))
5041 DBUG_RETURN(-1);
5042
5043 get_grantor(thd, grantor);
5044 /*
5045 New users are created before this function is called.
5046
5047 There may be some cases where a routine's definer is removed but the
5048 routine remains.
5049 */
5050
5051 table->use_all_columns();
5052 restore_record(table, s->default_values); // Get empty record
5053 table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
5054 table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
5055 table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
5056 table->field[3]->store(routine_name,(uint) strlen(routine_name),
5057 &my_charset_latin1);
5058 table->field[4]->store((longlong)(is_proc ?
5059 SP_TYPE_PROCEDURE : SP_TYPE_FUNCTION),
5060 TRUE);
5061 store_record(table,record[1]); // store at pos 1
5062
5063 if (table->file->ha_index_read_idx_map(table->record[0], 0,
5064 (uchar*) table->field[0]->ptr,
5065 HA_WHOLE_KEY,
5066 HA_READ_KEY_EXACT))
5067 {
5068 /*
5069 The following should never happen as we first check the in memory
5070 grant tables for the user. There is however always a small change that
5071 the user has modified the grant tables directly.
5072 */
5073 if (revoke_grant)
5074 { // no row, no revoke
5075 my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
5076 combo.user.str, combo.host.str, routine_name);
5077 DBUG_RETURN(-1);
5078 }
5079 old_row_exists= 0;
5080 restore_record(table,record[1]); // Get saved record
5081 }
5082
5083 store_proc_rights= get_rights_for_procedure(rights);
5084 if (old_row_exists)
5085 {
5086 ulong j;
5087 store_record(table,record[1]);
5088 j= (ulong) table->field[6]->val_int();
5089
5090 if (revoke_grant)
5091 {
5092 /* column rights are already fixed in mysql_table_grant */
5093 store_proc_rights=j & ~store_proc_rights;
5094 }
5095 else
5096 {
5097 store_proc_rights|= j;
5098 }
5099 }
5100
5101 table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
5102 table->field[6]->store((longlong) store_proc_rights, TRUE);
5103 rights=fix_rights_for_procedure(store_proc_rights);
5104
5105 if (old_row_exists)
5106 {
5107 if (store_proc_rights)
5108 {
5109 if ((error=table->file->ha_update_row(table->record[1],
5110 table->record[0])) &&
5111 error != HA_ERR_RECORD_IS_THE_SAME)
5112 goto table_error;
5113 }
5114 else if ((error= table->file->ha_delete_row(table->record[1])))
5115 goto table_error;
5116 }
5117 else
5118 {
5119 error=table->file->ha_write_row(table->record[0]);
5120 if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
5121 goto table_error;
5122 }
5123
5124 if (rights)
5125 {
5126 grant_name->privs= rights;
5127 }
5128 else
5129 {
5130 my_hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*)
5131 grant_name);
5132 }
5133 DBUG_RETURN(0);
5134
5135 /* This should never happen */
5136 table_error:
5137 table->file->print_error(error,MYF(0));
5138 DBUG_RETURN(-1);
5139 }
5140
5141
5142 /*
5143 Store table level and column level grants in the privilege tables
5144
5145 SYNOPSIS
5146 mysql_table_grant()
5147 thd Thread handle
5148 table_list List of tables to give grant
5149 user_list List of users to give grant
5150 columns List of columns to give grant
5151 rights Table level grant
5152 revoke_grant Set to 1 if this is a REVOKE command
5153
5154 RETURN
5155 FALSE ok
5156 TRUE error
5157 */
5158
mysql_table_grant(THD * thd,TABLE_LIST * table_list,List<LEX_USER> & user_list,List<LEX_COLUMN> & columns,ulong rights,bool revoke_grant)5159 int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
5160 List <LEX_USER> &user_list,
5161 List <LEX_COLUMN> &columns, ulong rights,
5162 bool revoke_grant)
5163 {
5164 ulong column_priv= 0;
5165 List_iterator <LEX_USER> str_list (user_list);
5166 LEX_USER *Str, *tmp_Str;
5167 TABLE_LIST tables[3];
5168 bool create_new_users=0;
5169 char *db_name, *table_name;
5170 bool save_binlog_row_based;
5171 bool transactional_tables;
5172 DBUG_ENTER("mysql_table_grant");
5173
5174 if (!initialized)
5175 {
5176 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
5177 "--skip-grant-tables"); /* purecov: inspected */
5178 DBUG_RETURN(TRUE); /* purecov: inspected */
5179 }
5180 if (rights & ~TABLE_ACLS)
5181 {
5182 my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
5183 MYF(0));
5184 DBUG_RETURN(TRUE);
5185 }
5186
5187 if (!revoke_grant)
5188 {
5189 if (columns.elements)
5190 {
5191 class LEX_COLUMN *column;
5192 List_iterator <LEX_COLUMN> column_iter(columns);
5193
5194 if (open_normal_and_derived_tables(thd, table_list, 0))
5195 DBUG_RETURN(TRUE);
5196
5197 while ((column = column_iter++))
5198 {
5199 uint unused_field_idx= NO_CACHED_FIELD_INDEX;
5200 TABLE_LIST *dummy;
5201 Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
5202 column->column.length(),
5203 column->column.ptr(), NULL, NULL,
5204 NULL, TRUE, FALSE,
5205 &unused_field_idx, FALSE, &dummy);
5206 if (f == (Field*)0)
5207 {
5208 my_error(ER_BAD_FIELD_ERROR, MYF(0),
5209 column->column.c_ptr(), table_list->alias);
5210 DBUG_RETURN(TRUE);
5211 }
5212 if (f == (Field *)-1)
5213 DBUG_RETURN(TRUE);
5214 column_priv|= column->rights;
5215 }
5216 close_mysql_tables(thd);
5217 }
5218 else
5219 {
5220 if (!(rights & CREATE_ACL))
5221 {
5222 char buf[FN_REFLEN + 1];
5223 build_table_filename(buf, sizeof(buf) - 1, table_list->db,
5224 table_list->table_name, reg_ext, 0);
5225 fn_format(buf, buf, "", "", MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS |
5226 MY_RETURN_REAL_PATH | MY_APPEND_EXT);
5227 if (access(buf,F_OK))
5228 {
5229 my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
5230 DBUG_RETURN(TRUE);
5231 }
5232 }
5233 if (table_list->grant.want_privilege)
5234 {
5235 char command[128];
5236 get_privilege_desc(command, sizeof(command),
5237 table_list->grant.want_privilege);
5238 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
5239 command, thd->security_ctx->priv_user,
5240 thd->security_ctx->host_or_ip, table_list->alias);
5241 DBUG_RETURN(-1);
5242 }
5243 }
5244 }
5245
5246 /* open the mysql.tables_priv and mysql.columns_priv tables */
5247
5248 tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
5249 C_STRING_WITH_LEN("user"), "user", TL_WRITE);
5250 tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
5251 C_STRING_WITH_LEN("tables_priv"),
5252 "tables_priv", TL_WRITE);
5253 tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
5254 C_STRING_WITH_LEN("columns_priv"),
5255 "columns_priv", TL_WRITE);
5256 tables[0].next_local= tables[0].next_global= tables+1;
5257 /* Don't open column table if we don't need it ! */
5258 if (column_priv || (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
5259 tables[1].next_local= tables[1].next_global= tables+2;
5260
5261 /*
5262 This statement will be replicated as a statement, even when using
5263 row-based replication. The flag will be reset at the end of the
5264 statement.
5265 */
5266 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
5267 thd->clear_current_stmt_binlog_format_row();
5268
5269 #ifdef HAVE_REPLICATION
5270 /*
5271 GRANT and REVOKE are applied the slave in/exclusion rules as they are
5272 some kind of updates to the mysql.% tables.
5273 */
5274 if (thd->slave_thread && rpl_filter->is_on())
5275 {
5276 /*
5277 The tables must be marked "updating" so that tables_ok() takes them into
5278 account in tests.
5279 */
5280 tables[0].updating= tables[1].updating= tables[2].updating= 1;
5281 if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, tables)))
5282 {
5283 /* Restore the state of binlog format */
5284 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5285 if (save_binlog_row_based)
5286 thd->set_current_stmt_binlog_format_row();
5287 DBUG_RETURN(FALSE);
5288 }
5289 }
5290 #endif
5291
5292 /*
5293 The lock api is depending on the thd->lex variable which needs to be
5294 re-initialized.
5295 */
5296 Query_tables_list backup;
5297 thd->lex->reset_n_backup_query_tables_list(&backup);
5298 /*
5299 Restore Query_tables_list::sql_command value, which was reset
5300 above, as the code writing query to the binary log assumes that
5301 this value corresponds to the statement being executed.
5302 */
5303 thd->lex->sql_command= backup.sql_command;
5304 if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5305 { // Should never happen
5306 /* Restore the state of binlog format */
5307 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5308 thd->lex->restore_backup_query_tables_list(&backup);
5309 if (save_binlog_row_based)
5310 thd->set_current_stmt_binlog_format_row();
5311 DBUG_RETURN(TRUE); /* purecov: deadcode */
5312 }
5313
5314 transactional_tables= (tables[0].table->file->has_transactions() ||
5315 tables[1].table->file->has_transactions() ||
5316 (tables[2].table &&
5317 tables[2].table->file->has_transactions()));
5318
5319 if (!revoke_grant)
5320 create_new_users= test_if_create_new_users(thd);
5321 bool result= FALSE;
5322 bool is_partial_execution= false;
5323 mysql_rwlock_wrlock(&LOCK_grant);
5324 mysql_mutex_lock(&acl_cache->lock);
5325 MEM_ROOT *old_root= thd->mem_root;
5326 thd->mem_root= &memex;
5327 grant_version++;
5328
5329 while ((tmp_Str = str_list++))
5330 {
5331 int error;
5332 bool is_user_applied= true;
5333 GRANT_TABLE *grant_table;
5334 if (!(Str= get_current_user(thd, tmp_Str)))
5335 {
5336 result= TRUE;
5337 continue;
5338 }
5339
5340 /*
5341 No User, but a password?
5342 They did GRANT ... TO CURRENT_USER() IDENTIFIED BY ... !
5343 Get the current user, and shallow-copy the new password to them!
5344 */
5345 if (!tmp_Str->user.str && tmp_Str->password.str)
5346 Str->password= tmp_Str->password;
5347
5348 /* Create user if needed */
5349 error=replace_user_table(thd, tables[0].table, Str,
5350 0, revoke_grant, create_new_users,
5351 MY_TEST(thd->variables.sql_mode &
5352 MODE_NO_AUTO_CREATE_USER));
5353 if (error)
5354 {
5355 result= TRUE; // Remember error
5356 continue; // Add next user
5357 }
5358
5359 db_name= table_list->get_db_name();
5360 thd->add_to_binlog_accessed_dbs(db_name); // collecting db:s for MTS
5361 table_name= table_list->get_table_name();
5362
5363 /* Find/create cached table grant */
5364 grant_table= table_hash_search(Str->host.str, NullS, db_name,
5365 Str->user.str, table_name, 1);
5366 if (!grant_table)
5367 {
5368 if (revoke_grant)
5369 {
5370 my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
5371 Str->user.str, Str->host.str, table_list->table_name);
5372 result= TRUE;
5373 continue;
5374 }
5375 grant_table = new GRANT_TABLE (Str->host.str, db_name,
5376 Str->user.str, table_name,
5377 rights,
5378 column_priv);
5379 if (!grant_table ||
5380 my_hash_insert(&column_priv_hash,(uchar*) grant_table))
5381 {
5382 result= TRUE; /* purecov: deadcode */
5383 continue; /* purecov: deadcode */
5384 }
5385 }
5386
5387 /* If revoke_grant, calculate the new column privilege for tables_priv */
5388 if (revoke_grant)
5389 {
5390 class LEX_COLUMN *column;
5391 List_iterator <LEX_COLUMN> column_iter(columns);
5392 GRANT_COLUMN *grant_column;
5393
5394 /* Fix old grants */
5395 while ((column = column_iter++))
5396 {
5397 grant_column = column_hash_search(grant_table,
5398 column->column.ptr(),
5399 column->column.length());
5400 if (grant_column)
5401 grant_column->rights&= ~(column->rights | rights);
5402 }
5403 /* scan trough all columns to get new column grant */
5404 column_priv= 0;
5405 for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
5406 {
5407 grant_column= (GRANT_COLUMN*)
5408 my_hash_element(&grant_table->hash_columns, idx);
5409 grant_column->rights&= ~rights; // Fix other columns
5410 column_priv|= grant_column->rights;
5411 }
5412 }
5413 else
5414 {
5415 column_priv|= grant_table->cols;
5416 }
5417
5418
5419 /* update table and columns */
5420
5421 if (replace_table_table(thd, grant_table, tables[1].table, *Str,
5422 db_name, table_name,
5423 rights, column_priv, revoke_grant))
5424 {
5425 /* Should only happen if table is crashed */
5426 result= TRUE; /* purecov: deadcode */
5427 is_user_applied= false;
5428 }
5429 else if (tables[2].table)
5430 {
5431 if ((replace_column_table(grant_table, tables[2].table, *Str,
5432 columns,
5433 db_name, table_name,
5434 rights, revoke_grant)))
5435 {
5436 result= TRUE;
5437 is_user_applied= false;
5438 }
5439 }
5440 if (is_user_applied)
5441 is_partial_execution= true;
5442 }
5443 thd->mem_root= old_root;
5444 mysql_mutex_unlock(&acl_cache->lock);
5445
5446 /*
5447 We only log "complete" successful commands, because partially
5448 failed REVOKE/GRANTS that fail because of insufficient privileges
5449 on the master, will succeed on the slave due to SQL thread SUPER
5450 privilege. Even though replication will stop (the error code from
5451 the master will mismatch the error code on the slave), the
5452 operation will already be executed (thence revoking or granting
5453 additional privileges on the slave).
5454 Before ACLs are changed to execute fully or none at all, when
5455 some error happens, write an incident if one or more users are
5456 granted/revoked successfully (it has a partial execution), a
5457 warning if no user is granted/revoked successfully.
5458 */
5459 if (result)
5460 {
5461 if (is_partial_execution)
5462 mysql_bin_log.write_incident(thd, true /* need_lock_log=true */);
5463 else
5464 sql_print_warning("Did not write failed '%s' into binary log while "
5465 "storing table level and column level grants in "
5466 "the privilege tables.", thd->query());
5467 }
5468 else
5469 result= result |
5470 write_bin_log(thd, FALSE, thd->query(), thd->query_length(),
5471 transactional_tables);
5472
5473 mysql_rwlock_unlock(&LOCK_grant);
5474
5475 result|= acl_trans_commit_and_close_tables(thd);
5476
5477 if (!result) /* success */
5478 my_ok(thd);
5479
5480 thd->lex->restore_backup_query_tables_list(&backup);
5481 /* Restore the state of binlog format */
5482 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5483 if (save_binlog_row_based)
5484 thd->set_current_stmt_binlog_format_row();
5485 DBUG_RETURN(result);
5486 }
5487
5488
5489 /**
5490 Store routine level grants in the privilege tables
5491
5492 @param thd Thread handle
5493 @param table_list List of routines to give grant
5494 @param is_proc Is this a list of procedures?
5495 @param user_list List of users to give grant
5496 @param rights Table level grant
5497 @param revoke_grant Is this is a REVOKE command?
5498
5499 @return
5500 @retval FALSE Success.
5501 @retval TRUE An error occurred.
5502 */
5503
mysql_routine_grant(THD * thd,TABLE_LIST * table_list,bool is_proc,List<LEX_USER> & user_list,ulong rights,bool revoke_grant,bool write_to_binlog)5504 bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
5505 List <LEX_USER> &user_list, ulong rights,
5506 bool revoke_grant, bool write_to_binlog)
5507 {
5508 List_iterator <LEX_USER> str_list (user_list);
5509 LEX_USER *Str, *tmp_Str;
5510 TABLE_LIST tables[2];
5511 bool create_new_users=0, result=0;
5512 char *db_name, *table_name;
5513 bool save_binlog_row_based;
5514 bool transactional_tables;
5515 DBUG_ENTER("mysql_routine_grant");
5516
5517 if (!initialized)
5518 {
5519 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
5520 "--skip-grant-tables");
5521 DBUG_RETURN(TRUE);
5522 }
5523 if (rights & ~PROC_ACLS)
5524 {
5525 my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
5526 MYF(0));
5527 DBUG_RETURN(TRUE);
5528 }
5529
5530 if (!revoke_grant)
5531 {
5532 if (sp_exist_routines(thd, table_list, is_proc))
5533 DBUG_RETURN(TRUE);
5534 }
5535
5536 /* open the mysql.user and mysql.procs_priv tables */
5537
5538 tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
5539 C_STRING_WITH_LEN("user"), "user", TL_WRITE);
5540 tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
5541 C_STRING_WITH_LEN("procs_priv"), "procs_priv", TL_WRITE);
5542 tables[0].next_local= tables[0].next_global= tables+1;
5543
5544 /*
5545 This statement will be replicated as a statement, even when using
5546 row-based replication. The flag will be reset at the end of the
5547 statement.
5548 */
5549 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
5550 thd->clear_current_stmt_binlog_format_row();
5551
5552 #ifdef HAVE_REPLICATION
5553 /*
5554 GRANT and REVOKE are applied the slave in/exclusion rules as they are
5555 some kind of updates to the mysql.% tables.
5556 */
5557 if (thd->slave_thread && rpl_filter->is_on())
5558 {
5559 /*
5560 The tables must be marked "updating" so that tables_ok() takes them into
5561 account in tests.
5562 */
5563 tables[0].updating= tables[1].updating= 1;
5564 if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, tables)))
5565 {
5566 /* Restore the state of binlog format */
5567 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5568 if (save_binlog_row_based)
5569 thd->set_current_stmt_binlog_format_row();
5570 DBUG_RETURN(FALSE);
5571 }
5572 }
5573 #endif
5574
5575 if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5576 { // Should never happen
5577 /* Restore the state of binlog format */
5578 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5579 if (save_binlog_row_based)
5580 thd->set_current_stmt_binlog_format_row();
5581 DBUG_RETURN(TRUE);
5582 }
5583
5584 transactional_tables= (tables[0].table->file->has_transactions() ||
5585 tables[1].table->file->has_transactions());
5586
5587 if (!revoke_grant)
5588 create_new_users= test_if_create_new_users(thd);
5589 mysql_rwlock_wrlock(&LOCK_grant);
5590 mysql_mutex_lock(&acl_cache->lock);
5591 MEM_ROOT *old_root= thd->mem_root;
5592 thd->mem_root= &memex;
5593
5594 DBUG_PRINT("info",("now time to iterate and add users"));
5595
5596 bool is_partial_execution= false;
5597 while ((tmp_Str= str_list++))
5598 {
5599 int error;
5600 GRANT_NAME *grant_name;
5601 if (!(Str= get_current_user(thd, tmp_Str)))
5602 {
5603 result= TRUE;
5604 continue;
5605 }
5606
5607 /* Create user if needed */
5608 error=replace_user_table(thd, tables[0].table, Str,
5609 0, revoke_grant, create_new_users,
5610 MY_TEST(thd->variables.sql_mode &
5611 MODE_NO_AUTO_CREATE_USER));
5612 if (error)
5613 {
5614 result= TRUE; // Remember error
5615 continue; // Add next user
5616 }
5617
5618 db_name= table_list->db;
5619 if (write_to_binlog)
5620 thd->add_to_binlog_accessed_dbs(db_name);
5621 table_name= table_list->table_name;
5622 grant_name= routine_hash_search(Str->host.str, NullS, db_name,
5623 Str->user.str, table_name, is_proc, 1);
5624 if (!grant_name)
5625 {
5626 if (revoke_grant)
5627 {
5628 my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
5629 Str->user.str, Str->host.str, table_name);
5630 result= TRUE;
5631 continue;
5632 }
5633 grant_name= new GRANT_NAME(Str->host.str, db_name,
5634 Str->user.str, table_name,
5635 rights, TRUE);
5636 if (!grant_name ||
5637 my_hash_insert(is_proc ?
5638 &proc_priv_hash : &func_priv_hash,(uchar*) grant_name))
5639 {
5640 result= TRUE;
5641 continue;
5642 }
5643 }
5644
5645 if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
5646 db_name, table_name, is_proc, rights,
5647 revoke_grant) != 0)
5648 {
5649 result= TRUE;
5650 continue;
5651 }
5652 is_partial_execution= true;
5653 }
5654 thd->mem_root= old_root;
5655 mysql_mutex_unlock(&acl_cache->lock);
5656
5657 if (write_to_binlog)
5658 {
5659 /*
5660 Before ACLs are changed to execute fully or none at all, when
5661 some error happens, write an incident if one or more users are
5662 granted/revoked successfully (it has a partial execution), a
5663 warning if no user is granted/revoked successfully.
5664 */
5665 if (result)
5666 {
5667 if (is_partial_execution)
5668 mysql_bin_log.write_incident(thd, true /* need_lock_log=true */);
5669 else
5670 sql_print_warning("Did not write failed '%s' into binary log while "
5671 "storing routine level grants in the privilege "
5672 "tables.", thd->query());
5673 }
5674 else
5675 {
5676 /*
5677 For performance reasons, we don't rewrite the query if we don't have to.
5678 If that was the case, write the original query.
5679 */
5680 if (!thd->rewritten_query.length())
5681 {
5682 if (write_bin_log(thd, false, thd->query(), thd->query_length(),
5683 transactional_tables))
5684 result= TRUE;
5685 }
5686 else
5687 {
5688 if (write_bin_log(thd, false,
5689 thd->rewritten_query.c_ptr_safe(),
5690 thd->rewritten_query.length(),
5691 transactional_tables))
5692 result= TRUE;
5693 }
5694 }
5695 }
5696
5697 mysql_rwlock_unlock(&LOCK_grant);
5698
5699 result|= acl_trans_commit_and_close_tables(thd);
5700
5701 /* Restore the state of binlog format */
5702 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5703 if (save_binlog_row_based)
5704 thd->set_current_stmt_binlog_format_row();
5705
5706 DBUG_RETURN(result);
5707 }
5708
5709
5710 /**
5711 Allocates a new buffer and calculates digested password hash based on plugin
5712 and old_passwords. The old buffer containing the clear text password is
5713 simply discarded as this memory belongs to the LEX will be freed when the
5714 session ends.
5715
5716 @param THD the tread handler used for allocating memory
5717 @param user_record[in, out] The user record
5718
5719 @return Failure state
5720 @retval 0 OK
5721 @retval 1 ERROR
5722 */
5723
digest_password(THD * thd,LEX_USER * user_record)5724 int digest_password(THD *thd, LEX_USER *user_record)
5725 {
5726 /* Empty passwords stay empty */
5727 if (user_record->password.length == 0)
5728 return 0;
5729
5730 #if defined(HAVE_OPENSSL)
5731 /*
5732 Transform password into a password hash
5733 */
5734 if (user_record->plugin.str == sha256_password_plugin_name.str)
5735 {
5736 if (user_record->password.length > SHA256_PASSWORD_MAX_PASSWORD_LENGTH)
5737 return 1;
5738
5739 char *buff= (char *) thd->alloc(CRYPT_MAX_PASSWORD_SIZE+1);
5740 if (buff == NULL)
5741 return 1;
5742
5743 my_make_scrambled_password(buff, user_record->password.str,
5744 user_record->password.length);
5745 user_record->password.str= buff;
5746 user_record->password.length= strlen(buff)+1;
5747 }
5748 else
5749 #endif
5750 if (user_record->plugin.str == native_password_plugin_name.str ||
5751 user_record->plugin.str == old_password_plugin_name.str)
5752 {
5753 if (thd->variables.old_passwords == 1)
5754 {
5755 char *buff=
5756 (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
5757 if (buff == NULL)
5758 return 1;
5759
5760 my_make_scrambled_password_323(buff, user_record->password.str,
5761 user_record->password.length);
5762 user_record->password.str= buff;
5763 user_record->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
5764 }
5765 else
5766 {
5767 char *buff=
5768 (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
5769 if (buff == NULL)
5770 return 1;
5771
5772 my_make_scrambled_password_sha1(buff, user_record->password.str,
5773 user_record->password.length);
5774 user_record->password.str= buff;
5775 user_record->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
5776 }
5777 } // end if native_password_plugin_name || old_password_plugin_name
5778 else
5779 {
5780 user_record->password.str= 0;
5781 user_record->password.length= 0;
5782 }
5783 return 0;
5784 }
5785
mysql_grant(THD * thd,const char * db,List<LEX_USER> & list,ulong rights,bool revoke_grant,bool is_proxy)5786 bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
5787 ulong rights, bool revoke_grant, bool is_proxy)
5788 {
5789 List_iterator <LEX_USER> str_list (list);
5790 LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
5791 char tmp_db[NAME_LEN+1];
5792 bool create_new_users=0;
5793 TABLE_LIST tables[2];
5794 bool save_binlog_row_based;
5795 bool transactional_tables;
5796 DBUG_ENTER("mysql_grant");
5797 if (!initialized)
5798 {
5799 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
5800 "--skip-grant-tables"); /* purecov: tested */
5801 DBUG_RETURN(TRUE); /* purecov: tested */
5802 }
5803
5804 if (lower_case_table_names && db)
5805 {
5806 strnmov(tmp_db,db,NAME_LEN);
5807 tmp_db[NAME_LEN]= '\0';
5808 my_casedn_str(files_charset_info, tmp_db);
5809 db=tmp_db;
5810 }
5811
5812 if (is_proxy)
5813 {
5814 DBUG_ASSERT(!db);
5815 proxied_user= str_list++;
5816 }
5817
5818 /* open the mysql.user and mysql.db or mysql.proxies_priv tables */
5819 tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
5820 C_STRING_WITH_LEN("user"), "user", TL_WRITE);
5821 if (is_proxy)
5822
5823 tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
5824 C_STRING_WITH_LEN("proxies_priv"),
5825 "proxies_priv",
5826 TL_WRITE);
5827 else
5828 tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
5829 C_STRING_WITH_LEN("db"),
5830 "db",
5831 TL_WRITE);
5832 tables[0].next_local= tables[0].next_global= tables+1;
5833
5834 /*
5835 This statement will be replicated as a statement, even when using
5836 row-based replication. The flag will be reset at the end of the
5837 statement.
5838 */
5839 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
5840 thd->clear_current_stmt_binlog_format_row();
5841
5842 #ifdef HAVE_REPLICATION
5843 /*
5844 GRANT and REVOKE are applied the slave in/exclusion rules as they are
5845 some kind of updates to the mysql.% tables.
5846 */
5847 if (thd->slave_thread && rpl_filter->is_on())
5848 {
5849 /*
5850 The tables must be marked "updating" so that tables_ok() takes them into
5851 account in tests.
5852 */
5853 tables[0].updating= tables[1].updating= 1;
5854 if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, tables)))
5855 {
5856 /* Restore the state of binlog format */
5857 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5858 if (save_binlog_row_based)
5859 thd->set_current_stmt_binlog_format_row();
5860 DBUG_RETURN(FALSE);
5861 }
5862 }
5863 #endif
5864
5865 if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5866 { // This should never happen
5867 /* Restore the state of binlog format */
5868 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5869 if (save_binlog_row_based)
5870 thd->set_current_stmt_binlog_format_row();
5871 DBUG_RETURN(TRUE); /* purecov: deadcode */
5872 }
5873
5874 transactional_tables= (tables[0].table->file->has_transactions() ||
5875 tables[1].table->file->has_transactions());
5876
5877 if (!revoke_grant)
5878 create_new_users= test_if_create_new_users(thd);
5879
5880 /* go through users in user_list */
5881 mysql_rwlock_wrlock(&LOCK_grant);
5882 mysql_mutex_lock(&acl_cache->lock);
5883 grant_version++;
5884
5885 int result= 0;
5886 bool is_partial_execution= false;
5887 while ((tmp_Str = str_list++))
5888 {
5889 bool is_user_applied= true;
5890 if (!(Str= get_current_user(thd, tmp_Str)))
5891 {
5892 result= TRUE;
5893 continue;
5894 }
5895
5896 /*
5897 No User, but a password?
5898 They did GRANT ... TO CURRENT_USER() IDENTIFIED BY ... !
5899 Get the current user, and shallow-copy the new password to them!
5900 */
5901 if (!tmp_Str->user.str && tmp_Str->password.str)
5902 Str->password= tmp_Str->password;
5903
5904 if (replace_user_table(thd, tables[0].table, Str,
5905 (!db ? rights : 0), revoke_grant, create_new_users,
5906 MY_TEST(thd->variables.sql_mode &
5907 MODE_NO_AUTO_CREATE_USER)))
5908 {
5909 result= -1;
5910 is_user_applied= false;
5911 }
5912 else if (db)
5913 {
5914 ulong db_rights= rights & DB_ACLS;
5915 if (db_rights == rights)
5916 {
5917 if (replace_db_table(tables[1].table, db, *Str, db_rights,
5918 revoke_grant))
5919 {
5920 result= -1;
5921 is_user_applied= false;
5922 }
5923 }
5924 else
5925 {
5926 my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
5927 result= -1;
5928 is_user_applied= false;
5929 }
5930 thd->add_to_binlog_accessed_dbs(db);
5931 }
5932 else if (is_proxy)
5933 {
5934 if (replace_proxies_priv_table (thd, tables[1].table, Str, proxied_user,
5935 rights & GRANT_ACL ? TRUE : FALSE,
5936 revoke_grant))
5937 {
5938 result= -1;
5939 is_user_applied= false;
5940 }
5941 }
5942 if (is_user_applied)
5943 is_partial_execution= true;
5944 }
5945 mysql_mutex_unlock(&acl_cache->lock);
5946
5947 /*
5948 Before ACLs are changed to execute fully or none at all, when
5949 some error happens, write an incident if one or more users are
5950 granted/revoked successfully (it has a partial execution), a
5951 warning if no user is granted/revoked successfully.
5952 */
5953 if (result)
5954 {
5955 if (is_partial_execution)
5956 mysql_bin_log.write_incident(thd, true /* need_lock_log=true */);
5957 else
5958 sql_print_warning("Did not write failed '%s' into binary log while "
5959 "granting/revoking privileges in databases.",
5960 thd->query());
5961 }
5962 else
5963 {
5964 if (thd->rewritten_query.length())
5965 result= result |
5966 write_bin_log(thd, FALSE,
5967 thd->rewritten_query.c_ptr_safe(),
5968 thd->rewritten_query.length(),
5969 transactional_tables);
5970 else
5971 result= result |
5972 write_bin_log(thd, FALSE, thd->query(), thd->query_length(),
5973 transactional_tables);
5974 }
5975
5976 mysql_rwlock_unlock(&LOCK_grant);
5977
5978 result|= acl_trans_commit_and_close_tables(thd);
5979
5980 if (!result)
5981 my_ok(thd);
5982
5983 /* Restore the state of binlog format */
5984 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
5985 if (save_binlog_row_based)
5986 thd->set_current_stmt_binlog_format_row();
5987
5988 DBUG_RETURN(result);
5989 }
5990
5991
5992 /* Free grant array if possible */
5993
grant_free(void)5994 void grant_free(void)
5995 {
5996 DBUG_ENTER("grant_free");
5997 my_hash_free(&column_priv_hash);
5998 my_hash_free(&proc_priv_hash);
5999 my_hash_free(&func_priv_hash);
6000 free_root(&memex,MYF(0));
6001 DBUG_VOID_RETURN;
6002 }
6003
6004
6005 /**
6006 @brief Initialize structures responsible for table/column-level privilege
6007 checking and load information for them from tables in the 'mysql' database.
6008
6009 @return Error status
6010 @retval 0 OK
6011 @retval 1 Could not initialize grant subsystem.
6012 */
6013
grant_init()6014 my_bool grant_init()
6015 {
6016 THD *thd;
6017 my_bool return_val;
6018 DBUG_ENTER("grant_init");
6019
6020 if (!(thd= new THD))
6021 DBUG_RETURN(1); /* purecov: deadcode */
6022 thd->thread_stack= (char*) &thd;
6023 thd->store_globals();
6024 return_val= grant_reload(thd);
6025 delete thd;
6026 /* Remember that we don't have a THD */
6027 my_pthread_setspecific_ptr(THR_THD, 0);
6028 DBUG_RETURN(return_val);
6029 }
6030
6031
6032 /**
6033 @brief Helper function to grant_reload_procs_priv
6034
6035 Reads the procs_priv table into memory hash.
6036
6037 @param table A pointer to the procs_priv table structure.
6038
6039 @see grant_reload
6040 @see grant_reload_procs_priv
6041
6042 @return Error state
6043 @retval TRUE An error occurred
6044 @retval FALSE Success
6045 */
6046
grant_load_procs_priv(TABLE * p_table)6047 static my_bool grant_load_procs_priv(TABLE *p_table)
6048 {
6049 MEM_ROOT *memex_ptr;
6050 my_bool return_val= 1;
6051 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
6052 MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
6053 THR_MALLOC);
6054 DBUG_ENTER("grant_load_procs_priv");
6055 (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
6056 0,0,0, (my_hash_get_key) get_grant_table,
6057 0,0);
6058 (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
6059 0,0,0, (my_hash_get_key) get_grant_table,
6060 0,0);
6061 if (p_table->file->ha_index_init(0, 1))
6062 DBUG_RETURN(TRUE);
6063 p_table->use_all_columns();
6064
6065 if (!p_table->file->ha_index_first(p_table->record[0]))
6066 {
6067 memex_ptr= &memex;
6068 my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
6069 do
6070 {
6071 GRANT_NAME *mem_check;
6072 HASH *hash;
6073 if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE)))
6074 {
6075 /* This could only happen if we are out memory */
6076 goto end_unlock;
6077 }
6078
6079 if (check_no_resolve)
6080 {
6081 if (hostname_requires_resolving(mem_check->host.get_host()))
6082 {
6083 sql_print_warning("'procs_priv' entry '%s %s@%s' "
6084 "ignored in --skip-name-resolve mode.",
6085 mem_check->tname, mem_check->user,
6086 mem_check->host.get_host() ?
6087 mem_check->host.get_host() : "");
6088 continue;
6089 }
6090 }
6091 if (p_table->field[4]->val_int() == SP_TYPE_PROCEDURE)
6092 {
6093 hash= &proc_priv_hash;
6094 }
6095 else
6096 if (p_table->field[4]->val_int() == SP_TYPE_FUNCTION)
6097 {
6098 hash= &func_priv_hash;
6099 }
6100 else
6101 {
6102 sql_print_warning("'procs_priv' entry '%s' "
6103 "ignored, bad routine type",
6104 mem_check->tname);
6105 continue;
6106 }
6107
6108 mem_check->privs= fix_rights_for_procedure(mem_check->privs);
6109 if (! mem_check->ok())
6110 delete mem_check;
6111 else if (my_hash_insert(hash, (uchar*) mem_check))
6112 {
6113 delete mem_check;
6114 goto end_unlock;
6115 }
6116 }
6117 while (!p_table->file->ha_index_next(p_table->record[0]));
6118 }
6119 /* Return ok */
6120 return_val= 0;
6121
6122 end_unlock:
6123 p_table->file->ha_index_end();
6124 my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
6125 DBUG_RETURN(return_val);
6126 }
6127
6128
6129 /**
6130 @brief Initialize structures responsible for table/column-level privilege
6131 checking and load information about grants from open privilege tables.
6132
6133 @param thd Current thread
6134 @param tables List containing open "mysql.tables_priv" and
6135 "mysql.columns_priv" tables.
6136
6137 @see grant_reload
6138
6139 @return Error state
6140 @retval FALSE Success
6141 @retval TRUE Error
6142 */
6143
grant_load(THD * thd,TABLE_LIST * tables)6144 static my_bool grant_load(THD *thd, TABLE_LIST *tables)
6145 {
6146 MEM_ROOT *memex_ptr;
6147 my_bool return_val= 1;
6148 TABLE *t_table= 0, *c_table= 0;
6149 bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
6150 MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
6151 THR_MALLOC);
6152 sql_mode_t old_sql_mode= thd->variables.sql_mode;
6153 DBUG_ENTER("grant_load");
6154
6155 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
6156
6157 (void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
6158 0,0,0, (my_hash_get_key) get_grant_table,
6159 (my_hash_free_key) free_grant_table,0);
6160
6161 t_table = tables[0].table;
6162 c_table = tables[1].table;
6163 if (t_table->file->ha_index_init(0, 1))
6164 goto end_index_init;
6165 t_table->use_all_columns();
6166 c_table->use_all_columns();
6167
6168 if (!t_table->file->ha_index_first(t_table->record[0]))
6169 {
6170 memex_ptr= &memex;
6171 my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
6172 do
6173 {
6174 GRANT_TABLE *mem_check;
6175 if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table)))
6176 {
6177 /* This could only happen if we are out memory */
6178 goto end_unlock;
6179 }
6180
6181 if (check_no_resolve)
6182 {
6183 if (hostname_requires_resolving(mem_check->host.get_host()))
6184 {
6185 sql_print_warning("'tables_priv' entry '%s %s@%s' "
6186 "ignored in --skip-name-resolve mode.",
6187 mem_check->tname,
6188 mem_check->user ? mem_check->user : "",
6189 mem_check->host.get_host() ?
6190 mem_check->host.get_host() : "");
6191 continue;
6192 }
6193 }
6194
6195 if (! mem_check->ok())
6196 delete mem_check;
6197 else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
6198 {
6199 delete mem_check;
6200 goto end_unlock;
6201 }
6202 }
6203 while (!t_table->file->ha_index_next(t_table->record[0]));
6204 }
6205
6206 return_val=0; // Return ok
6207
6208 end_unlock:
6209 t_table->file->ha_index_end();
6210 my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
6211 end_index_init:
6212 thd->variables.sql_mode= old_sql_mode;
6213 DBUG_RETURN(return_val);
6214 }
6215
6216
6217 /**
6218 @brief Helper function to grant_reload. Reloads procs_priv table is it
6219 exists.
6220
6221 @param thd A pointer to the thread handler object.
6222 @param table A pointer to the table list.
6223
6224 @see grant_reload
6225
6226 @return Error state
6227 @retval FALSE Success
6228 @retval TRUE An error has occurred.
6229 */
6230
grant_reload_procs_priv(THD * thd,TABLE_LIST * table)6231 static my_bool grant_reload_procs_priv(THD *thd, TABLE_LIST *table)
6232 {
6233 HASH old_proc_priv_hash, old_func_priv_hash;
6234 my_bool return_val= FALSE;
6235 DBUG_ENTER("grant_reload_procs_priv");
6236
6237 /* Save a copy of the current hash if we need to undo the grant load */
6238 old_proc_priv_hash= proc_priv_hash;
6239 old_func_priv_hash= func_priv_hash;
6240
6241 if ((return_val= grant_load_procs_priv(table->table)))
6242 {
6243 /* Error; Reverting to old hash */
6244 DBUG_PRINT("error",("Reverting to old privileges"));
6245 my_hash_free(&proc_priv_hash);
6246 my_hash_free(&func_priv_hash);
6247 proc_priv_hash= old_proc_priv_hash;
6248 func_priv_hash= old_func_priv_hash;
6249 }
6250 else
6251 {
6252 my_hash_free(&old_proc_priv_hash);
6253 my_hash_free(&old_func_priv_hash);
6254 }
6255
6256 DBUG_RETURN(return_val);
6257 }
6258
6259
6260 /**
6261 @brief Reload information about table and column level privileges if possible
6262
6263 @param thd Current thread
6264
6265 Locked tables are checked by acl_reload() and doesn't have to be checked
6266 in this call.
6267 This function is also used for initialization of structures responsible
6268 for table/column-level privilege checking.
6269
6270 @return Error state
6271 @retval FALSE Success
6272 @retval TRUE Error
6273 */
6274
grant_reload(THD * thd)6275 my_bool grant_reload(THD *thd)
6276 {
6277 TABLE_LIST tables[3];
6278 HASH old_column_priv_hash;
6279 MEM_ROOT old_mem;
6280 my_bool return_val= 1;
6281 DBUG_ENTER("grant_reload");
6282
6283 /* Don't do anything if running with --skip-grant-tables */
6284 if (!initialized)
6285 DBUG_RETURN(0);
6286
6287 tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
6288 C_STRING_WITH_LEN("tables_priv"),
6289 "tables_priv", TL_READ);
6290 tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
6291 C_STRING_WITH_LEN("columns_priv"),
6292 "columns_priv", TL_READ);
6293 tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
6294 C_STRING_WITH_LEN("procs_priv"),
6295 "procs_priv", TL_READ);
6296
6297 tables[0].next_local= tables[0].next_global= tables+1;
6298 tables[1].next_local= tables[1].next_global= tables+2;
6299 tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
6300
6301 /*
6302 Reload will work in the following manner:-
6303
6304 proc_priv_hash structure
6305 / \
6306 not initialized initialized
6307 / \ |
6308 mysql.procs_priv table Server Startup |
6309 is missing \ |
6310 | open_and_lock_tables()
6311 Assume we are working on /success \failure
6312 pre 4.1 system tables. Normal Scenario. An error is thrown.
6313 A warning is printed Reload column privilege. Retain the old hash.
6314 and continue with Reload function and
6315 reloading the column procedure privileges,
6316 privileges. if available.
6317 */
6318
6319 if (!(my_hash_inited(&proc_priv_hash)))
6320 tables[2].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
6321
6322 /*
6323 To avoid deadlocks we should obtain table locks before
6324 obtaining LOCK_grant rwlock.
6325 */
6326 if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
6327 {
6328 if (thd->get_stmt_da()->is_error())
6329 {
6330 sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
6331 thd->get_stmt_da()->message());
6332 }
6333 goto end;
6334 }
6335
6336 if (tables[2].table == NULL)
6337 {
6338 sql_print_warning("Table 'mysql.procs_priv' does not exist. "
6339 "Please run mysql_upgrade.");
6340 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_SUCH_TABLE,
6341 ER(ER_NO_SUCH_TABLE), tables[2].db,
6342 tables[2].table_name);
6343 }
6344
6345 mysql_rwlock_wrlock(&LOCK_grant);
6346 old_column_priv_hash= column_priv_hash;
6347
6348 /*
6349 Create a new memory pool but save the current memory pool to make an undo
6350 opertion possible in case of failure.
6351 */
6352 old_mem= memex;
6353 init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
6354 /*
6355 tables[2].table i.e. procs_priv can be null if we are working with
6356 pre 4.1 privilage tables
6357 */
6358 if ((return_val= (grant_load(thd, tables) ||
6359 (tables[2].table != NULL &&
6360 grant_reload_procs_priv(thd, &tables[2])))
6361 ))
6362 { // Error. Revert to old hash
6363 DBUG_PRINT("error",("Reverting to old privileges"));
6364 my_hash_free(&column_priv_hash);
6365 free_root(&memex,MYF(0));
6366 column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
6367 memex= old_mem; /* purecov: deadcode */
6368 }
6369 else
6370 {
6371 my_hash_free(&old_column_priv_hash);
6372 free_root(&old_mem,MYF(0));
6373 grant_version++;
6374 }
6375 mysql_rwlock_unlock(&LOCK_grant);
6376
6377 end:
6378 close_acl_tables(thd);
6379 DBUG_RETURN(return_val);
6380 }
6381
6382
6383 /**
6384 @brief Check table level grants
6385
6386 @param thd Thread handler
6387 @param want_access Bits of privileges user needs to have.
6388 @param tables List of tables to check. The user should have
6389 'want_access' to all tables in list.
6390 @param any_combination_will_do TRUE if it's enough to have any privilege for
6391 any combination of the table columns.
6392 @param number Check at most this number of tables.
6393 @param no_errors TRUE if no error should be sent directly to the client.
6394
6395 If table->grant.want_privilege != 0 then the requested privileges where
6396 in the set of COL_ACLS but access was not granted on the table level. As
6397 a consequence an extra check of column privileges is required.
6398
6399 Specifically if this function returns FALSE the user has some kind of
6400 privilege on a combination of columns in each table.
6401
6402 This function is usually preceeded by check_access which establish the
6403 User-, Db- and Host access rights.
6404
6405 @see check_access
6406 @see check_table_access
6407
6408 @note This functions assumes that either number of tables to be inspected
6409 by it is limited explicitly (i.e. is is not UINT_MAX) or table list
6410 used and thd->lex->query_tables_own_last value correspond to each
6411 other (the latter should be either 0 or point to next_global member
6412 of one of elements of this table list).
6413
6414 @return Access status
6415 @retval FALSE Access granted; But column privileges might need to be
6416 checked.
6417 @retval TRUE The user did not have the requested privileges on any of the
6418 tables.
6419
6420 */
6421
check_grant(THD * thd,ulong want_access,TABLE_LIST * tables,bool any_combination_will_do,uint number,bool no_errors)6422 bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
6423 bool any_combination_will_do, uint number, bool no_errors)
6424 {
6425 TABLE_LIST *tl;
6426 TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
6427 Security_context *sctx= thd->security_ctx;
6428 uint i;
6429 ulong orig_want_access= want_access;
6430 DBUG_ENTER("check_grant");
6431 DBUG_ASSERT(number > 0);
6432
6433 /*
6434 Walk through the list of tables that belong to the query and save the
6435 requested access (orig_want_privilege) to be able to use it when
6436 checking access rights to the underlying tables of a view. Our grant
6437 system gradually eliminates checked bits from want_privilege and thus
6438 after all checks are done we can no longer use it.
6439 The check that first_not_own_table is not reached is for the case when
6440 the given table list refers to the list for prelocking (contains tables
6441 of other queries). For simple queries first_not_own_table is 0.
6442 */
6443 for (i= 0, tl= tables;
6444 tl != 0 && i < number && tl != first_not_own_table;
6445 tl= tl->next_global, i++)
6446 {
6447 /*
6448 Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
6449 It will be checked during making view.
6450 */
6451 tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
6452 }
6453
6454 mysql_rwlock_rdlock(&LOCK_grant);
6455 for (tl= tables;
6456 tl && number-- && tl != first_not_own_table;
6457 tl= tl->next_global)
6458 {
6459 TABLE_LIST *const t_ref=
6460 tl->correspondent_table ? tl->correspondent_table : tl;
6461 sctx = MY_TEST(t_ref->security_ctx) ? t_ref->security_ctx :
6462 thd->security_ctx;
6463
6464 const ACL_internal_table_access *access=
6465 get_cached_table_access(&t_ref->grant.m_internal,
6466 t_ref->get_db_name(),
6467 t_ref->get_table_name());
6468
6469 if (access)
6470 {
6471 switch(access->check(orig_want_access, &t_ref->grant.privilege))
6472 {
6473 case ACL_INTERNAL_ACCESS_GRANTED:
6474 /*
6475 Grant all access to the table to skip column checks.
6476 Depend on the controls in the P_S table itself.
6477 */
6478 t_ref->grant.privilege|= TMP_TABLE_ACLS;
6479 t_ref->grant.want_privilege= 0;
6480 continue;
6481 case ACL_INTERNAL_ACCESS_DENIED:
6482 goto err;
6483 case ACL_INTERNAL_ACCESS_CHECK_GRANT:
6484 break;
6485 }
6486 }
6487
6488 want_access= orig_want_access;
6489 want_access&= ~sctx->master_access;
6490 if (!want_access)
6491 continue; // ok
6492
6493 if (!(~t_ref->grant.privilege & want_access) ||
6494 t_ref->is_anonymous_derived_table() || t_ref->schema_table)
6495 {
6496 /*
6497 It is subquery in the FROM clause. VIEW set t_ref->derived after
6498 table opening, but this function always called before table opening.
6499 */
6500 if (!t_ref->referencing_view)
6501 {
6502 /*
6503 If it's a temporary table created for a subquery in the FROM
6504 clause, or an INFORMATION_SCHEMA table, drop the request for
6505 a privilege.
6506 */
6507 t_ref->grant.want_privilege= 0;
6508 }
6509 continue;
6510 }
6511
6512 if (is_temporary_table(t_ref))
6513 {
6514 /*
6515 If this table list element corresponds to a pre-opened temporary
6516 table skip checking of all relevant table-level privileges for it.
6517 Note that during creation of temporary table we still need to check
6518 if user has CREATE_TMP_ACL.
6519 */
6520 t_ref->grant.privilege|= TMP_TABLE_ACLS;
6521 t_ref->grant.want_privilege= 0;
6522 continue;
6523 }
6524
6525 GRANT_TABLE *grant_table= table_hash_search(sctx->get_host()->ptr(),
6526 sctx->get_ip()->ptr(),
6527 t_ref->get_db_name(),
6528 sctx->priv_user,
6529 t_ref->get_table_name(),
6530 FALSE);
6531
6532 if (!grant_table)
6533 {
6534 want_access &= ~t_ref->grant.privilege;
6535 goto err; // No grants
6536 }
6537
6538 /*
6539 For SHOW COLUMNS, SHOW INDEX it is enough to have some
6540 privileges on any column combination on the table.
6541 */
6542 if (any_combination_will_do)
6543 continue;
6544
6545 t_ref->grant.grant_table= grant_table; // Remember for column test
6546 t_ref->grant.version= grant_version;
6547 t_ref->grant.privilege|= grant_table->privs;
6548 t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege);
6549
6550 if (!(~t_ref->grant.privilege & want_access))
6551 continue;
6552
6553 if (want_access & ~(grant_table->cols | t_ref->grant.privilege))
6554 {
6555 want_access &= ~(grant_table->cols | t_ref->grant.privilege);
6556 goto err; // impossible
6557 }
6558 }
6559 mysql_rwlock_unlock(&LOCK_grant);
6560 DBUG_RETURN(FALSE);
6561
6562 err:
6563 mysql_rwlock_unlock(&LOCK_grant);
6564 if (!no_errors) // Not a silent skip of table
6565 {
6566 char command[128];
6567 get_privilege_desc(command, sizeof(command), want_access);
6568 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
6569 command,
6570 sctx->priv_user,
6571 sctx->host_or_ip,
6572 tl ? tl->get_table_name() : "unknown");
6573 }
6574 DBUG_RETURN(TRUE);
6575 }
6576
6577
6578 /*
6579 Check column rights in given security context
6580
6581 SYNOPSIS
6582 check_grant_column()
6583 thd thread handler
6584 grant grant information structure
6585 db_name db name
6586 table_name table name
6587 name column name
6588 length column name length
6589 sctx security context
6590
6591 RETURN
6592 FALSE OK
6593 TRUE access denied
6594 */
6595
check_grant_column(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * name,uint length,Security_context * sctx)6596 bool check_grant_column(THD *thd, GRANT_INFO *grant,
6597 const char *db_name, const char *table_name,
6598 const char *name, uint length, Security_context *sctx)
6599 {
6600 GRANT_TABLE *grant_table;
6601 GRANT_COLUMN *grant_column;
6602 ulong want_access= grant->want_privilege & ~grant->privilege;
6603 DBUG_ENTER("check_grant_column");
6604 DBUG_PRINT("enter", ("table: %s want_access: %lu", table_name, want_access));
6605
6606 if (!want_access)
6607 DBUG_RETURN(0); // Already checked
6608
6609 mysql_rwlock_rdlock(&LOCK_grant);
6610
6611 /* reload table if someone has modified any grants */
6612
6613 if (grant->version != grant_version)
6614 {
6615 grant->grant_table=
6616 table_hash_search(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
6617 db_name, sctx->priv_user,
6618 table_name, 0); /* purecov: inspected */
6619 grant->version= grant_version; /* purecov: inspected */
6620 }
6621 if (!(grant_table= grant->grant_table))
6622 goto err; /* purecov: deadcode */
6623
6624 grant_column=column_hash_search(grant_table, name, length);
6625 if (grant_column && !(~grant_column->rights & want_access))
6626 {
6627 mysql_rwlock_unlock(&LOCK_grant);
6628 DBUG_RETURN(0);
6629 }
6630
6631 err:
6632 mysql_rwlock_unlock(&LOCK_grant);
6633 char command[128];
6634 get_privilege_desc(command, sizeof(command), want_access);
6635 my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
6636 command,
6637 sctx->priv_user,
6638 sctx->host_or_ip,
6639 name,
6640 table_name);
6641 DBUG_RETURN(1);
6642 }
6643
6644
6645 /*
6646 Check the access right to a column depending on the type of table.
6647
6648 SYNOPSIS
6649 check_column_grant_in_table_ref()
6650 thd thread handler
6651 table_ref table reference where to check the field
6652 name name of field to check
6653 length length of name
6654
6655 DESCRIPTION
6656 Check the access rights to a column depending on the type of table
6657 reference where the column is checked. The function provides a
6658 generic interface to check column access rights that hides the
6659 heterogeneity of the column representation - whether it is a view
6660 or a stored table colum.
6661
6662 RETURN
6663 FALSE OK
6664 TRUE access denied
6665 */
6666
check_column_grant_in_table_ref(THD * thd,TABLE_LIST * table_ref,const char * name,uint length)6667 bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
6668 const char *name, uint length)
6669 {
6670 GRANT_INFO *grant;
6671 const char *db_name;
6672 const char *table_name;
6673 Security_context *sctx= MY_TEST(table_ref->security_ctx) ?
6674 table_ref->security_ctx : thd->security_ctx;
6675
6676 if (table_ref->view || table_ref->field_translation)
6677 {
6678 /* View or derived information schema table. */
6679 ulong view_privs;
6680 grant= &(table_ref->grant);
6681 db_name= table_ref->view_db.str;
6682 table_name= table_ref->view_name.str;
6683 if (table_ref->belong_to_view &&
6684 thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
6685 {
6686 view_privs= get_column_grant(thd, grant, db_name, table_name, name);
6687 if (view_privs & VIEW_ANY_ACL)
6688 {
6689 table_ref->belong_to_view->allowed_show= TRUE;
6690 return FALSE;
6691 }
6692 table_ref->belong_to_view->allowed_show= FALSE;
6693 my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
6694 return TRUE;
6695 }
6696 }
6697 else
6698 {
6699 /* Normal or temporary table. */
6700 TABLE *table= table_ref->table;
6701 grant= &(table->grant);
6702 db_name= table->s->db.str;
6703 table_name= table->s->table_name.str;
6704 }
6705
6706 if (grant->want_privilege)
6707 return check_grant_column(thd, grant, db_name, table_name, name,
6708 length, sctx);
6709 else
6710 return FALSE;
6711
6712 }
6713
6714
6715 /**
6716 @brief check if a query can access a set of columns
6717
6718 @param thd the current thread
6719 @param want_access_arg the privileges requested
6720 @param fields an iterator over the fields of a table reference.
6721 @return Operation status
6722 @retval 0 Success
6723 @retval 1 Falure
6724 @details This function walks over the columns of a table reference
6725 The columns may originate from different tables, depending on the kind of
6726 table reference, e.g. join, view.
6727 For each table it will retrieve the grant information and will use it
6728 to check the required access privileges for the fields requested from it.
6729 */
check_grant_all_columns(THD * thd,ulong want_access_arg,Field_iterator_table_ref * fields)6730 bool check_grant_all_columns(THD *thd, ulong want_access_arg,
6731 Field_iterator_table_ref *fields)
6732 {
6733 Security_context *sctx= thd->security_ctx;
6734 ulong want_access= want_access_arg;
6735 const char *table_name= NULL;
6736
6737 const char* db_name;
6738 GRANT_INFO *grant;
6739 /* Initialized only to make gcc happy */
6740 GRANT_TABLE *grant_table= NULL;
6741 /*
6742 Flag that gets set if privilege checking has to be performed on column
6743 level.
6744 */
6745 bool using_column_privileges= FALSE;
6746
6747 mysql_rwlock_rdlock(&LOCK_grant);
6748
6749 for (; !fields->end_of_fields(); fields->next())
6750 {
6751 const char *field_name= fields->name();
6752
6753 if (table_name != fields->get_table_name())
6754 {
6755 table_name= fields->get_table_name();
6756 db_name= fields->get_db_name();
6757 grant= fields->grant();
6758 /* get a fresh one for each table */
6759 want_access= want_access_arg & ~grant->privilege;
6760 if (want_access)
6761 {
6762 /* reload table if someone has modified any grants */
6763 if (grant->version != grant_version)
6764 {
6765 grant->grant_table=
6766 table_hash_search(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
6767 db_name, sctx->priv_user,
6768 table_name, 0); /* purecov: inspected */
6769 grant->version= grant_version; /* purecov: inspected */
6770 }
6771
6772 grant_table= grant->grant_table;
6773 DBUG_ASSERT (grant_table);
6774 }
6775 }
6776
6777 if (want_access)
6778 {
6779 GRANT_COLUMN *grant_column=
6780 column_hash_search(grant_table, field_name,
6781 (uint) strlen(field_name));
6782 if (grant_column)
6783 using_column_privileges= TRUE;
6784 if (!grant_column || (~grant_column->rights & want_access))
6785 goto err;
6786 }
6787 }
6788 mysql_rwlock_unlock(&LOCK_grant);
6789 return 0;
6790
6791 err:
6792 mysql_rwlock_unlock(&LOCK_grant);
6793
6794 char command[128];
6795 get_privilege_desc(command, sizeof(command), want_access);
6796 /*
6797 Do not give an error message listing a column name unless the user has
6798 privilege to see all columns.
6799 */
6800 if (using_column_privileges)
6801 my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
6802 command, sctx->priv_user,
6803 sctx->host_or_ip, table_name);
6804 else
6805 my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
6806 command,
6807 sctx->priv_user,
6808 sctx->host_or_ip,
6809 fields->name(),
6810 table_name);
6811 return 1;
6812 }
6813
6814
check_grant_db_routine(THD * thd,const char * db,HASH * hash)6815 static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
6816 {
6817 Security_context *sctx= thd->security_ctx;
6818
6819 for (uint idx= 0; idx < hash->records; ++idx)
6820 {
6821 GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
6822
6823 if (strcmp(item->user, sctx->priv_user) == 0 &&
6824 strcmp(item->db, db) == 0 &&
6825 item->host.compare_hostname(sctx->get_host()->ptr(),
6826 sctx->get_ip()->ptr()))
6827 {
6828 return FALSE;
6829 }
6830 }
6831
6832 return TRUE;
6833 }
6834
6835
6836 /*
6837 Check if a user has the right to access a database
6838 Access is accepted if he has a grant for any table/routine in the database
6839 Return 1 if access is denied
6840 */
6841
check_grant_db(THD * thd,const char * db)6842 bool check_grant_db(THD *thd,const char *db)
6843 {
6844 Security_context *sctx= thd->security_ctx;
6845 char helping [NAME_LEN+USERNAME_LENGTH+2], *end;
6846 uint len;
6847 bool error= TRUE;
6848 size_t copy_length;
6849
6850 copy_length= (size_t) (strlen(sctx->priv_user) +
6851 strlen(db ? db : "")) + 1; /* Added 1 at the end to avoid
6852 buffer overflow at strmov()*/
6853
6854 /*
6855 Make sure that strmov() operations do not result in buffer overflow.
6856 */
6857 if (copy_length >= (NAME_LEN+USERNAME_LENGTH+2))
6858 return 1;
6859
6860 end= strmov(helping, sctx->priv_user) + 1;
6861 end= strnmov(end, db, helping + sizeof(helping) - end);
6862
6863 if (end >= helping + sizeof(helping)) // db name was truncated
6864 return 1; // no privileges for an invalid db name
6865
6866 len= (uint) (end - helping) + 1;
6867
6868 mysql_rwlock_rdlock(&LOCK_grant);
6869
6870 for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
6871 {
6872 GRANT_TABLE *grant_table= (GRANT_TABLE*)
6873 my_hash_element(&column_priv_hash,
6874 idx);
6875 if (len < grant_table->key_length &&
6876 !memcmp(grant_table->hash_key,helping,len) &&
6877 grant_table->host.compare_hostname(sctx->get_host()->ptr(),
6878 sctx->get_ip()->ptr()))
6879 {
6880 error= FALSE; /* Found match. */
6881 break;
6882 }
6883 }
6884
6885 if (error)
6886 error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
6887 check_grant_db_routine(thd, db, &func_priv_hash);
6888
6889 mysql_rwlock_unlock(&LOCK_grant);
6890
6891 return error;
6892 }
6893
6894
6895 /****************************************************************************
6896 Check routine level grants
6897
6898 SYNPOSIS
6899 bool check_grant_routine()
6900 thd Thread handler
6901 want_access Bits of privileges user needs to have
6902 procs List of routines to check. The user should have 'want_access'
6903 is_proc True if the list is all procedures, else functions
6904 no_errors If 0 then we write an error. The error is sent directly to
6905 the client
6906
6907 RETURN
6908 0 ok
6909 1 Error: User did not have the requested privielges
6910 ****************************************************************************/
6911
check_grant_routine(THD * thd,ulong want_access,TABLE_LIST * procs,bool is_proc,bool no_errors)6912 bool check_grant_routine(THD *thd, ulong want_access,
6913 TABLE_LIST *procs, bool is_proc, bool no_errors)
6914 {
6915 TABLE_LIST *table;
6916 Security_context *sctx= thd->security_ctx;
6917 char *user= sctx->priv_user;
6918 char *host= sctx->priv_host;
6919 DBUG_ENTER("check_grant_routine");
6920
6921 want_access&= ~sctx->master_access;
6922 if (!want_access)
6923 DBUG_RETURN(0); // ok
6924
6925 mysql_rwlock_rdlock(&LOCK_grant);
6926 for (table= procs; table; table= table->next_global)
6927 {
6928 GRANT_NAME *grant_proc;
6929 if ((grant_proc= routine_hash_search(host, sctx->get_ip()->ptr(), table->db,
6930 user, table->table_name, is_proc, 0)))
6931 table->grant.privilege|= grant_proc->privs;
6932
6933 if (want_access & ~table->grant.privilege)
6934 {
6935 want_access &= ~table->grant.privilege;
6936 goto err;
6937 }
6938 }
6939 mysql_rwlock_unlock(&LOCK_grant);
6940 DBUG_RETURN(0);
6941 err:
6942 mysql_rwlock_unlock(&LOCK_grant);
6943 if (!no_errors)
6944 {
6945 char buff[1024];
6946 const char *command="";
6947 if (table)
6948 strxmov(buff, table->db, ".", table->table_name, NullS);
6949 if (want_access & EXECUTE_ACL)
6950 command= "execute";
6951 else if (want_access & ALTER_PROC_ACL)
6952 command= "alter routine";
6953 else if (want_access & GRANT_ACL)
6954 command= "grant";
6955 my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
6956 command, user, host, table ? buff : "unknown");
6957 }
6958 DBUG_RETURN(1);
6959 }
6960
6961
6962 /*
6963 Check if routine has any of the
6964 routine level grants
6965
6966 SYNPOSIS
6967 bool check_routine_level_acl()
6968 thd Thread handler
6969 db Database name
6970 name Routine name
6971
6972 RETURN
6973 0 Ok
6974 1 error
6975 */
6976
check_routine_level_acl(THD * thd,const char * db,const char * name,bool is_proc)6977 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
6978 bool is_proc)
6979 {
6980 bool no_routine_acl= 1;
6981 GRANT_NAME *grant_proc;
6982 Security_context *sctx= thd->security_ctx;
6983 mysql_rwlock_rdlock(&LOCK_grant);
6984 if ((grant_proc= routine_hash_search(sctx->priv_host,
6985 sctx->get_ip()->ptr(), db,
6986 sctx->priv_user,
6987 name, is_proc, 0)))
6988 no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
6989 mysql_rwlock_unlock(&LOCK_grant);
6990 return no_routine_acl;
6991 }
6992
6993
6994 /*****************************************************************************
6995 Functions to retrieve the grant for a table/column (for SHOW functions)
6996 *****************************************************************************/
6997
get_table_grant(THD * thd,TABLE_LIST * table)6998 ulong get_table_grant(THD *thd, TABLE_LIST *table)
6999 {
7000 ulong privilege;
7001 Security_context *sctx= thd->security_ctx;
7002 const char *db = table->db ? table->db : thd->db;
7003 GRANT_TABLE *grant_table;
7004
7005 mysql_rwlock_rdlock(&LOCK_grant);
7006 #ifdef EMBEDDED_LIBRARY
7007 grant_table= NULL;
7008 #else
7009 grant_table= table_hash_search(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
7010 db, sctx->priv_user, table->table_name, 0);
7011 #endif
7012 table->grant.grant_table=grant_table; // Remember for column test
7013 table->grant.version=grant_version;
7014 if (grant_table)
7015 table->grant.privilege|= grant_table->privs;
7016 privilege= table->grant.privilege;
7017 mysql_rwlock_unlock(&LOCK_grant);
7018 return privilege;
7019 }
7020
7021
7022 /*
7023 Determine the access priviliges for a field.
7024
7025 SYNOPSIS
7026 get_column_grant()
7027 thd thread handler
7028 grant grants table descriptor
7029 db_name name of database that the field belongs to
7030 table_name name of table that the field belongs to
7031 field_name name of field
7032
7033 DESCRIPTION
7034 The procedure may also modify: grant->grant_table and grant->version.
7035
7036 RETURN
7037 The access priviliges for the field db_name.table_name.field_name
7038 */
7039
get_column_grant(THD * thd,GRANT_INFO * grant,const char * db_name,const char * table_name,const char * field_name)7040 ulong get_column_grant(THD *thd, GRANT_INFO *grant,
7041 const char *db_name, const char *table_name,
7042 const char *field_name)
7043 {
7044 GRANT_TABLE *grant_table;
7045 GRANT_COLUMN *grant_column;
7046 ulong priv;
7047
7048 mysql_rwlock_rdlock(&LOCK_grant);
7049 /* reload table if someone has modified any grants */
7050 if (grant->version != grant_version)
7051 {
7052 Security_context *sctx= thd->security_ctx;
7053 grant->grant_table=
7054 table_hash_search(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
7055 db_name, sctx->priv_user,
7056 table_name, 0); /* purecov: inspected */
7057 grant->version= grant_version; /* purecov: inspected */
7058 }
7059
7060 if (!(grant_table= grant->grant_table))
7061 priv= grant->privilege;
7062 else
7063 {
7064 grant_column= column_hash_search(grant_table, field_name,
7065 (uint) strlen(field_name));
7066 if (!grant_column)
7067 priv= (grant->privilege | grant_table->privs);
7068 else
7069 priv= (grant->privilege | grant_table->privs | grant_column->rights);
7070 }
7071 mysql_rwlock_unlock(&LOCK_grant);
7072 return priv;
7073 }
7074
7075
7076 /* Help function for mysql_show_grants */
7077
add_user_option(String * grant,ulong value,const char * name)7078 static void add_user_option(String *grant, ulong value, const char *name)
7079 {
7080 if (value)
7081 {
7082 char buff[22], *p; // just as in int2str
7083 grant->append(' ');
7084 grant->append(name, strlen(name));
7085 grant->append(' ');
7086 p=int10_to_str(value, buff, 10);
7087 grant->append(buff,p-buff);
7088 }
7089 }
7090
7091 #else
7092
7093 /*
7094 Determines if the user specified by user, host, ip matches the utility user
7095 */
7096 my_bool
acl_is_utility_user(const char * user,const char * host,const char * ip)7097 acl_is_utility_user(const char *user, const char *host, const char *ip)
7098 {
7099 return FALSE;
7100 }
7101
7102 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
7103
7104 const char *command_array[]=
7105 {
7106 "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
7107 "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
7108 "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
7109 "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
7110 "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
7111 "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE", 0
7112 };
7113
7114 TYPELIB utility_user_privileges_typelib=
7115 {
7116 array_elements(command_array) - 1,
7117 "utility_user_privileges_typelib",
7118 command_array,
7119 NULL
7120 };
7121
7122 uint command_lengths[]=
7123 {
7124 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
7125 14, 13, 11, 5, 7, 17, 0
7126 };
7127
7128 #ifndef NO_EMBEDDED_ACCESS_CHECKS
7129
7130 static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
7131 const char *type, int typelen,
7132 char *buff, int buffsize);
7133
7134
7135 /*
7136 SHOW GRANTS; Send grants for a user to the client
7137
7138 IMPLEMENTATION
7139 Send to client grant-like strings depicting user@host privileges
7140 */
7141
mysql_show_grants(THD * thd,LEX_USER * lex_user)7142 bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
7143 {
7144 ulong want_access;
7145 uint counter,index;
7146 int error = 0;
7147 ACL_USER *acl_user;
7148 ACL_DB *acl_db;
7149 char buff[1024];
7150 Protocol *protocol= thd->protocol;
7151 DBUG_ENTER("mysql_show_grants");
7152
7153 LINT_INIT(acl_user);
7154 if (!initialized)
7155 {
7156 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
7157 DBUG_RETURN(TRUE);
7158 }
7159
7160 mysql_rwlock_rdlock(&LOCK_grant);
7161 mysql_mutex_lock(&acl_cache->lock);
7162
7163 acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE);
7164 if (!acl_user ||
7165 (acl_is_utility_user(acl_user->user, acl_user->host.get_host(), NULL)))
7166 {
7167 mysql_mutex_unlock(&acl_cache->lock);
7168 mysql_rwlock_unlock(&LOCK_grant);
7169
7170 my_error(ER_NONEXISTING_GRANT, MYF(0),
7171 lex_user->user.str, lex_user->host.str);
7172 DBUG_RETURN(TRUE);
7173 }
7174
7175 Item_string *field=new Item_string("",0,&my_charset_latin1);
7176 List<Item> field_list;
7177 field->max_length=1024;
7178 strxmov(buff,"Grants for ",lex_user->user.str,"@",
7179 lex_user->host.str,NullS);
7180 field->item_name.set(buff);
7181 field_list.push_back(field);
7182 if (protocol->send_result_set_metadata(&field_list,
7183 Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
7184 {
7185 mysql_mutex_unlock(&acl_cache->lock);
7186 mysql_rwlock_unlock(&LOCK_grant);
7187
7188 DBUG_RETURN(TRUE);
7189 }
7190
7191 /* Add first global access grants */
7192 {
7193 String global(buff,sizeof(buff),system_charset_info);
7194 global.length(0);
7195 global.append(STRING_WITH_LEN("GRANT "));
7196
7197 want_access= acl_user->access;
7198 if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
7199 global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
7200 else if (!(want_access & ~GRANT_ACL))
7201 global.append(STRING_WITH_LEN("USAGE"));
7202 else
7203 {
7204 bool found=0;
7205 ulong j,test_access= want_access & ~GRANT_ACL;
7206 for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
7207 {
7208 if (test_access & j)
7209 {
7210 if (found)
7211 global.append(STRING_WITH_LEN(", "));
7212 found=1;
7213 global.append(command_array[counter],command_lengths[counter]);
7214 }
7215 }
7216 }
7217 global.append (STRING_WITH_LEN(" ON *.* TO '"));
7218 global.append(lex_user->user.str, lex_user->user.length,
7219 system_charset_info);
7220 global.append (STRING_WITH_LEN("'@'"));
7221 global.append(lex_user->host.str,lex_user->host.length,
7222 system_charset_info);
7223 global.append ('\'');
7224 #if defined(HAVE_OPENSSL)
7225 if (acl_user->plugin.str == sha256_password_plugin_name.str &&
7226 acl_user->auth_string.length > 0)
7227 {
7228 global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD"));
7229 if ((thd->security_ctx->master_access & SUPER_ACL) == SUPER_ACL)
7230 {
7231 global.append(" \'");
7232 global.append((const char *) &acl_user->auth_string.str[0]);
7233 global.append('\'');
7234 }
7235 }
7236 else
7237 #endif
7238 if (acl_user->salt_len)
7239 {
7240 global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD"));
7241 if ((thd->security_ctx->master_access & SUPER_ACL) == SUPER_ACL)
7242 {
7243 char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
7244 if (acl_user->salt_len == SCRAMBLE_LENGTH)
7245 make_password_from_salt(passwd_buff, acl_user->salt);
7246 else
7247 make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt);
7248
7249 global.append(" \'");
7250 global.append(passwd_buff);
7251 global.append('\'');
7252 }
7253 else
7254 global.append(" <secret>");
7255 }
7256 /* "show grants" SSL related stuff */
7257 if (acl_user->ssl_type == SSL_TYPE_ANY)
7258 global.append(STRING_WITH_LEN(" REQUIRE SSL"));
7259 else if (acl_user->ssl_type == SSL_TYPE_X509)
7260 global.append(STRING_WITH_LEN(" REQUIRE X509"));
7261 else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
7262 {
7263 int ssl_options = 0;
7264 global.append(STRING_WITH_LEN(" REQUIRE "));
7265 if (acl_user->x509_issuer)
7266 {
7267 ssl_options++;
7268 global.append(STRING_WITH_LEN("ISSUER \'"));
7269 global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
7270 global.append('\'');
7271 }
7272 if (acl_user->x509_subject)
7273 {
7274 if (ssl_options++)
7275 global.append(' ');
7276 global.append(STRING_WITH_LEN("SUBJECT \'"));
7277 global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
7278 system_charset_info);
7279 global.append('\'');
7280 }
7281 if (acl_user->ssl_cipher)
7282 {
7283 if (ssl_options++)
7284 global.append(' ');
7285 global.append(STRING_WITH_LEN("CIPHER '"));
7286 global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
7287 system_charset_info);
7288 global.append('\'');
7289 }
7290 }
7291 if ((want_access & GRANT_ACL) ||
7292 (acl_user->user_resource.questions ||
7293 acl_user->user_resource.updates ||
7294 acl_user->user_resource.conn_per_hour ||
7295 acl_user->user_resource.user_conn))
7296 {
7297 global.append(STRING_WITH_LEN(" WITH"));
7298 if (want_access & GRANT_ACL)
7299 global.append(STRING_WITH_LEN(" GRANT OPTION"));
7300 add_user_option(&global, acl_user->user_resource.questions,
7301 "MAX_QUERIES_PER_HOUR");
7302 add_user_option(&global, acl_user->user_resource.updates,
7303 "MAX_UPDATES_PER_HOUR");
7304 add_user_option(&global, acl_user->user_resource.conn_per_hour,
7305 "MAX_CONNECTIONS_PER_HOUR");
7306 add_user_option(&global, acl_user->user_resource.user_conn,
7307 "MAX_USER_CONNECTIONS");
7308 }
7309 protocol->prepare_for_resend();
7310 protocol->store(global.ptr(),global.length(),global.charset());
7311 if (protocol->write())
7312 {
7313 error= -1;
7314 goto end;
7315 }
7316 }
7317
7318 /* Add database access */
7319 for (counter=0 ; counter < acl_dbs.elements ; counter++)
7320 {
7321 const char *user, *host;
7322
7323 acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
7324 if (!(user=acl_db->user))
7325 user= "";
7326 if (!(host=acl_db->host.get_host()))
7327 host= "";
7328
7329 /*
7330 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
7331 but make it case-insensitive because that's the way they are
7332 actually applied, and showing fewer privileges than are applied
7333 would be wrong from a security point of view.
7334 */
7335
7336 if (!strcmp(lex_user->user.str,user) &&
7337 acl_db->host.compare_hostname(lex_user->host.str, lex_user->host.str))
7338 {
7339 want_access=acl_db->access;
7340 if (want_access)
7341 {
7342 String db(buff,sizeof(buff),system_charset_info);
7343 db.length(0);
7344 db.append(STRING_WITH_LEN("GRANT "));
7345
7346 if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
7347 db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
7348 else if (!(want_access & ~GRANT_ACL))
7349 db.append(STRING_WITH_LEN("USAGE"));
7350 else
7351 {
7352 int found=0, cnt;
7353 ulong j,test_access= want_access & ~GRANT_ACL;
7354 for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
7355 {
7356 if (test_access & j)
7357 {
7358 if (found)
7359 db.append(STRING_WITH_LEN(", "));
7360 found = 1;
7361 db.append(command_array[cnt],command_lengths[cnt]);
7362 }
7363 }
7364 }
7365 db.append (STRING_WITH_LEN(" ON "));
7366 append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
7367 db.append (STRING_WITH_LEN(".* TO '"));
7368 db.append(lex_user->user.str, lex_user->user.length,
7369 system_charset_info);
7370 db.append (STRING_WITH_LEN("'@'"));
7371 // host and lex_user->host are equal except for case
7372 db.append(host, strlen(host), system_charset_info);
7373 db.append ('\'');
7374 if (want_access & GRANT_ACL)
7375 db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
7376 protocol->prepare_for_resend();
7377 protocol->store(db.ptr(),db.length(),db.charset());
7378 if (protocol->write())
7379 {
7380 error= -1;
7381 goto end;
7382 }
7383 }
7384 }
7385 }
7386
7387 /* Add table & column access */
7388 for (index=0 ; index < column_priv_hash.records ; index++)
7389 {
7390 const char *user, *host;
7391 GRANT_TABLE *grant_table= (GRANT_TABLE*)
7392 my_hash_element(&column_priv_hash, index);
7393
7394 if (!(user=grant_table->user))
7395 user= "";
7396 if (!(host= grant_table->host.get_host()))
7397 host= "";
7398
7399 /*
7400 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
7401 but make it case-insensitive because that's the way they are
7402 actually applied, and showing fewer privileges than are applied
7403 would be wrong from a security point of view.
7404 */
7405
7406 if (!strcmp(lex_user->user.str,user) &&
7407 grant_table->host.compare_hostname(lex_user->host.str,
7408 lex_user->host.str))
7409 {
7410 ulong table_access= grant_table->privs;
7411 if ((table_access | grant_table->cols) != 0)
7412 {
7413 String global(buff, sizeof(buff), system_charset_info);
7414 ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;
7415
7416 global.length(0);
7417 global.append(STRING_WITH_LEN("GRANT "));
7418
7419 if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
7420 global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
7421 else if (!test_access)
7422 global.append(STRING_WITH_LEN("USAGE"));
7423 else
7424 {
7425 /* Add specific column access */
7426 int found= 0;
7427 ulong j;
7428
7429 for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
7430 {
7431 if (test_access & j)
7432 {
7433 if (found)
7434 global.append(STRING_WITH_LEN(", "));
7435 found= 1;
7436 global.append(command_array[counter],command_lengths[counter]);
7437
7438 if (grant_table->cols)
7439 {
7440 uint found_col= 0;
7441 for (uint col_index=0 ;
7442 col_index < grant_table->hash_columns.records ;
7443 col_index++)
7444 {
7445 GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
7446 my_hash_element(&grant_table->hash_columns,col_index);
7447 if (grant_column->rights & j)
7448 {
7449 if (!found_col)
7450 {
7451 found_col= 1;
7452 /*
7453 If we have a duplicated table level privilege, we
7454 must write the access privilege name again.
7455 */
7456 if (table_access & j)
7457 {
7458 global.append(STRING_WITH_LEN(", "));
7459 global.append(command_array[counter],
7460 command_lengths[counter]);
7461 }
7462 global.append(STRING_WITH_LEN(" ("));
7463 }
7464 else
7465 global.append(STRING_WITH_LEN(", "));
7466 global.append(grant_column->column,
7467 grant_column->key_length,
7468 system_charset_info);
7469 }
7470 }
7471 if (found_col)
7472 global.append(')');
7473 }
7474 }
7475 }
7476 }
7477 global.append(STRING_WITH_LEN(" ON "));
7478 append_identifier(thd, &global, grant_table->db,
7479 strlen(grant_table->db));
7480 global.append('.');
7481 append_identifier(thd, &global, grant_table->tname,
7482 strlen(grant_table->tname));
7483 global.append(STRING_WITH_LEN(" TO '"));
7484 global.append(lex_user->user.str, lex_user->user.length,
7485 system_charset_info);
7486 global.append(STRING_WITH_LEN("'@'"));
7487 // host and lex_user->host are equal except for case
7488 global.append(host, strlen(host), system_charset_info);
7489 global.append('\'');
7490 if (table_access & GRANT_ACL)
7491 global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
7492 protocol->prepare_for_resend();
7493 protocol->store(global.ptr(),global.length(),global.charset());
7494 if (protocol->write())
7495 {
7496 error= -1;
7497 break;
7498 }
7499 }
7500 }
7501 }
7502
7503 if (show_routine_grants(thd, lex_user, &proc_priv_hash,
7504 STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
7505 {
7506 error= -1;
7507 goto end;
7508 }
7509
7510 if (show_routine_grants(thd, lex_user, &func_priv_hash,
7511 STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
7512 {
7513 error= -1;
7514 goto end;
7515 }
7516
7517 if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
7518 {
7519 error= -1;
7520 goto end;
7521 }
7522
7523 end:
7524 mysql_mutex_unlock(&acl_cache->lock);
7525 mysql_rwlock_unlock(&LOCK_grant);
7526
7527 my_eof(thd);
7528 DBUG_RETURN(error);
7529 }
7530
show_routine_grants(THD * thd,LEX_USER * lex_user,HASH * hash,const char * type,int typelen,char * buff,int buffsize)7531 static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
7532 const char *type, int typelen,
7533 char *buff, int buffsize)
7534 {
7535 uint counter, index;
7536 int error= 0;
7537 Protocol *protocol= thd->protocol;
7538 /* Add routine access */
7539 for (index=0 ; index < hash->records ; index++)
7540 {
7541 const char *user, *host;
7542 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
7543
7544 if (!(user=grant_proc->user))
7545 user= "";
7546 if (!(host= grant_proc->host.get_host()))
7547 host= "";
7548
7549 /*
7550 We do not make SHOW GRANTS case-sensitive here (like REVOKE),
7551 but make it case-insensitive because that's the way they are
7552 actually applied, and showing fewer privileges than are applied
7553 would be wrong from a security point of view.
7554 */
7555
7556 if (!strcmp(lex_user->user.str,user) &&
7557 grant_proc->host.compare_hostname(lex_user->host.str,
7558 lex_user->host.str))
7559 {
7560 ulong proc_access= grant_proc->privs;
7561 if (proc_access != 0)
7562 {
7563 String global(buff, buffsize, system_charset_info);
7564 ulong test_access= proc_access & ~GRANT_ACL;
7565
7566 global.length(0);
7567 global.append(STRING_WITH_LEN("GRANT "));
7568
7569 if (!test_access)
7570 global.append(STRING_WITH_LEN("USAGE"));
7571 else
7572 {
7573 /* Add specific procedure access */
7574 int found= 0;
7575 ulong j;
7576
7577 for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
7578 {
7579 if (test_access & j)
7580 {
7581 if (found)
7582 global.append(STRING_WITH_LEN(", "));
7583 found= 1;
7584 global.append(command_array[counter],command_lengths[counter]);
7585 }
7586 }
7587 }
7588 global.append(STRING_WITH_LEN(" ON "));
7589 global.append(type,typelen);
7590 global.append(' ');
7591 append_identifier(thd, &global, grant_proc->db,
7592 strlen(grant_proc->db));
7593 global.append('.');
7594 append_identifier(thd, &global, grant_proc->tname,
7595 strlen(grant_proc->tname));
7596 global.append(STRING_WITH_LEN(" TO '"));
7597 global.append(lex_user->user.str, lex_user->user.length,
7598 system_charset_info);
7599 global.append(STRING_WITH_LEN("'@'"));
7600 // host and lex_user->host are equal except for case
7601 global.append(host, strlen(host), system_charset_info);
7602 global.append('\'');
7603 if (proc_access & GRANT_ACL)
7604 global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
7605 protocol->prepare_for_resend();
7606 protocol->store(global.ptr(),global.length(),global.charset());
7607 if (protocol->write())
7608 {
7609 error= -1;
7610 break;
7611 }
7612 }
7613 }
7614 }
7615 return error;
7616 }
7617
7618 /*
7619 Make a clear-text version of the requested privilege.
7620 */
7621
get_privilege_desc(char * to,uint max_length,ulong access)7622 void get_privilege_desc(char *to, uint max_length, ulong access)
7623 {
7624 uint pos;
7625 char *start=to;
7626 DBUG_ASSERT(max_length >= 30); // For end ', ' removal
7627
7628 if (access)
7629 {
7630 max_length--; // Reserve place for end-zero
7631 for (pos=0 ; access ; pos++, access>>=1)
7632 {
7633 if ((access & 1) &&
7634 command_lengths[pos] + (uint) (to-start) < max_length)
7635 {
7636 to= strmov(to, command_array[pos]);
7637 *to++= ',';
7638 *to++= ' ';
7639 }
7640 }
7641 to--; // Remove end ' '
7642 to--; // Remove end ','
7643 }
7644 *to=0;
7645 }
7646
7647
get_mqh(const char * user,const char * host,USER_CONN * uc)7648 void get_mqh(const char *user, const char *host, USER_CONN *uc)
7649 {
7650 ACL_USER *acl_user;
7651
7652 mysql_mutex_lock(&acl_cache->lock);
7653
7654 if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
7655 uc->user_resources= acl_user->user_resource;
7656 else
7657 memset(&uc->user_resources, 0, sizeof(uc->user_resources));
7658
7659 mysql_mutex_unlock(&acl_cache->lock);
7660 }
7661
7662 /**
7663 Open the grant tables.
7664
7665 @param thd The current thread.
7666 @param[in/out] tables Array of GRANT_TABLES table list elements
7667 which will be used for opening tables.
7668 @param[out] transactional_tables Set to true if one of grant tables is
7669 transactional, false otherwise.
7670
7671 @note
7672 Tables are numbered as follows:
7673 0 user
7674 1 db
7675 2 tables_priv
7676 3 columns_priv
7677 4 procs_priv
7678 5 proxies_priv
7679
7680 @retval 1 Skip GRANT handling during replication.
7681 @retval 0 OK.
7682 @retval < 0 Error.
7683 */
7684
7685 #define GRANT_TABLES 6
7686
7687 static int
open_grant_tables(THD * thd,TABLE_LIST * tables,bool * transactional_tables)7688 open_grant_tables(THD *thd, TABLE_LIST *tables, bool *transactional_tables)
7689 {
7690 DBUG_ENTER("open_grant_tables");
7691
7692 if (!initialized)
7693 {
7694 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
7695 DBUG_RETURN(-1);
7696 }
7697
7698 *transactional_tables= false;
7699
7700 tables->init_one_table(C_STRING_WITH_LEN("mysql"),
7701 C_STRING_WITH_LEN("user"), "user", TL_WRITE);
7702 (tables+1)->init_one_table(C_STRING_WITH_LEN("mysql"),
7703 C_STRING_WITH_LEN("db"), "db", TL_WRITE);
7704 (tables+2)->init_one_table(C_STRING_WITH_LEN("mysql"),
7705 C_STRING_WITH_LEN("tables_priv"),
7706 "tables_priv", TL_WRITE);
7707 (tables+3)->init_one_table(C_STRING_WITH_LEN("mysql"),
7708 C_STRING_WITH_LEN("columns_priv"),
7709 "columns_priv", TL_WRITE);
7710 (tables+4)->init_one_table(C_STRING_WITH_LEN("mysql"),
7711 C_STRING_WITH_LEN("procs_priv"),
7712 "procs_priv", TL_WRITE);
7713 (tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"),
7714 C_STRING_WITH_LEN("proxies_priv"),
7715 "proxies_priv", TL_WRITE);
7716 tables[5].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
7717
7718 tables->next_local= tables->next_global= tables + 1;
7719 (tables+1)->next_local= (tables+1)->next_global= tables + 2;
7720 (tables+2)->next_local= (tables+2)->next_global= tables + 3;
7721 (tables+3)->next_local= (tables+3)->next_global= tables + 4;
7722 (tables+4)->next_local= (tables+4)->next_global= tables + 5;
7723
7724 #ifdef HAVE_REPLICATION
7725 /*
7726 GRANT and REVOKE are applied the slave in/exclusion rules as they are
7727 some kind of updates to the mysql.% tables.
7728 */
7729 if (thd->slave_thread && rpl_filter->is_on())
7730 {
7731 /*
7732 The tables must be marked "updating" so that tables_ok() takes them into
7733 account in tests.
7734 */
7735 tables[0].updating= tables[1].updating= tables[2].updating=
7736 tables[3].updating= tables[4].updating= tables[5].updating= 1;
7737 if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, tables)))
7738 DBUG_RETURN(1);
7739 tables[0].updating= tables[1].updating= tables[2].updating=
7740 tables[3].updating= tables[4].updating= tables[5].updating= 0;
7741 }
7742 #endif
7743
7744 if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
7745 { // This should never happen
7746 DBUG_RETURN(-1);
7747 }
7748
7749 for (uint i= 0; i < GRANT_TABLES; ++i)
7750 *transactional_tables= (*transactional_tables ||
7751 (tables[i].table &&
7752 tables[i].table->file->has_transactions()));
7753
7754 DBUG_RETURN(0);
7755 }
7756
7757
7758 /*
7759 Modify a privilege table.
7760
7761 SYNOPSIS
7762 modify_grant_table()
7763 table The table to modify.
7764 host_field The host name field.
7765 user_field The user name field.
7766 user_to The new name for the user if to be renamed,
7767 NULL otherwise.
7768
7769 DESCRIPTION
7770 Update user/host in the current record if user_to is not NULL.
7771 Delete the current record if user_to is NULL.
7772
7773 RETURN
7774 0 OK.
7775 != 0 Error.
7776 */
7777
modify_grant_table(TABLE * table,Field * host_field,Field * user_field,LEX_USER * user_to)7778 static int modify_grant_table(TABLE *table, Field *host_field,
7779 Field *user_field, LEX_USER *user_to)
7780 {
7781 int error;
7782 DBUG_ENTER("modify_grant_table");
7783
7784 if (user_to)
7785 {
7786 /* rename */
7787 store_record(table, record[1]);
7788 host_field->store(user_to->host.str, user_to->host.length,
7789 system_charset_info);
7790 user_field->store(user_to->user.str, user_to->user.length,
7791 system_charset_info);
7792 if ((error= table->file->ha_update_row(table->record[1],
7793 table->record[0])) &&
7794 error != HA_ERR_RECORD_IS_THE_SAME)
7795 table->file->print_error(error, MYF(0));
7796 else
7797 error= 0;
7798 }
7799 else
7800 {
7801 /* delete */
7802 if ((error=table->file->ha_delete_row(table->record[0])))
7803 table->file->print_error(error, MYF(0));
7804 }
7805
7806 DBUG_RETURN(error);
7807 }
7808
7809 /*
7810 Handle a privilege table.
7811
7812 SYNOPSIS
7813 handle_grant_table()
7814 tables The array with the four open tables.
7815 table_no The number of the table to handle (0..4).
7816 drop If user_from is to be dropped.
7817 user_from The the user to be searched/dropped/renamed.
7818 user_to The new name for the user if to be renamed,
7819 NULL otherwise.
7820
7821 DESCRIPTION
7822 Scan through all records in a grant table and apply the requested
7823 operation. For the "user" table, a single index access is sufficient,
7824 since there is an unique index on (host, user).
7825 Delete from grant table if drop is true.
7826 Update in grant table if drop is false and user_to is not NULL.
7827 Search in grant table if drop is false and user_to is NULL.
7828 Tables are numbered as follows:
7829 0 user
7830 1 db
7831 2 tables_priv
7832 3 columns_priv
7833 4 procs_priv
7834
7835 RETURN
7836 > 0 At least one record matched.
7837 0 OK, but no record matched.
7838 < 0 Error.
7839 */
7840
handle_grant_table(TABLE_LIST * tables,uint table_no,bool drop,LEX_USER * user_from,LEX_USER * user_to)7841 static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
7842 LEX_USER *user_from, LEX_USER *user_to)
7843 {
7844 int result= 0;
7845 int error;
7846 TABLE *table= tables[table_no].table;
7847 Field *host_field= table->field[0];
7848 Field *user_field= table->field[table_no && table_no != 5 ? 2 : 1];
7849 char *host_str= user_from->host.str;
7850 char *user_str= user_from->user.str;
7851 const char *host;
7852 const char *user;
7853 uchar user_key[MAX_KEY_LENGTH];
7854 uint key_prefix_length;
7855 DBUG_ENTER("handle_grant_table");
7856 THD *thd= current_thd;
7857
7858 table->use_all_columns();
7859 if (! table_no) // mysql.user table
7860 {
7861 /*
7862 The 'user' table has an unique index on (host, user).
7863 Thus, we can handle everything with a single index access.
7864 The host- and user fields are consecutive in the user table records.
7865 So we set host- and user fields of table->record[0] and use the
7866 pointer to the host field as key.
7867 index_read_idx() will replace table->record[0] (its first argument)
7868 by the searched record, if it exists.
7869 */
7870 DBUG_PRINT("info",("read table: '%s' search: '%s'@'%s'",
7871 table->s->table_name.str, user_str, host_str));
7872 host_field->store(host_str, user_from->host.length, system_charset_info);
7873 user_field->store(user_str, user_from->user.length, system_charset_info);
7874
7875 key_prefix_length= (table->key_info->key_part[0].store_length +
7876 table->key_info->key_part[1].store_length);
7877 key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
7878
7879 if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
7880 user_key, (key_part_map)3,
7881 HA_READ_KEY_EXACT)))
7882 {
7883 if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
7884 {
7885 table->file->print_error(error, MYF(0));
7886 result= -1;
7887 }
7888 }
7889 else
7890 {
7891 /* If requested, delete or update the record. */
7892 result= ((drop || user_to) &&
7893 modify_grant_table(table, host_field, user_field, user_to)) ?
7894 -1 : 1; /* Error or found. */
7895 }
7896 DBUG_PRINT("info",("read result: %d", result));
7897 }
7898 else
7899 {
7900 /*
7901 The non-'user' table do not have indexes on (host, user).
7902 And their host- and user fields are not consecutive.
7903 Thus, we need to do a table scan to find all matching records.
7904 */
7905 if ((error= table->file->ha_rnd_init(1)))
7906 {
7907 table->file->print_error(error, MYF(0));
7908 result= -1;
7909 }
7910 else
7911 {
7912 #ifdef EXTRA_DEBUG
7913 DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'",
7914 table->s->table_name.str, user_str, host_str));
7915 #endif
7916 while ((error= table->file->ha_rnd_next(table->record[0])) !=
7917 HA_ERR_END_OF_FILE)
7918 {
7919 if (error)
7920 {
7921 /* Most probable 'deleted record'. */
7922 DBUG_PRINT("info",("scan error: %d", error));
7923 continue;
7924 }
7925 if (! (host= get_field(thd->mem_root, host_field)))
7926 host= "";
7927 if (! (user= get_field(thd->mem_root, user_field)))
7928 user= "";
7929
7930 #ifdef EXTRA_DEBUG
7931 if (table_no != 5)
7932 {
7933 DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
7934 user, host,
7935 get_field(thd->mem_root, table->field[1]) /*db*/,
7936 get_field(thd->mem_root, table->field[3]) /*table*/,
7937 get_field(thd->mem_root,
7938 table->field[4]) /*column*/));
7939 }
7940 #endif
7941 if (strcmp(user_str, user) ||
7942 my_strcasecmp(system_charset_info, host_str, host))
7943 continue;
7944
7945 /* If requested, delete or update the record. */
7946 result= ((drop || user_to) &&
7947 modify_grant_table(table, host_field, user_field, user_to)) ?
7948 -1 : result ? result : 1; /* Error or keep result or found. */
7949 /* If search is requested, we do not need to search further. */
7950 if (! drop && ! user_to)
7951 break ;
7952 }
7953 (void) table->file->ha_rnd_end();
7954 DBUG_PRINT("info",("scan result: %d", result));
7955 }
7956 }
7957
7958 DBUG_RETURN(result);
7959 }
7960
7961
7962 /**
7963 Handle an in-memory privilege structure.
7964
7965 @param struct_no The number of the structure to handle (0..5).
7966 @param drop If user_from is to be dropped.
7967 @param user_from The the user to be searched/dropped/renamed.
7968 @param user_to The new name for the user if to be renamed, NULL otherwise.
7969
7970 @note
7971 Scan through all elements in an in-memory grant structure and apply
7972 the requested operation.
7973 Delete from grant structure if drop is true.
7974 Update in grant structure if drop is false and user_to is not NULL.
7975 Search in grant structure if drop is false and user_to is NULL.
7976 Structures are enumerated as follows:
7977 0 ACL_USER
7978 1 ACL_DB
7979 2 COLUMN_PRIVILIGES_HASH
7980 3 PROC_PRIVILEGES_HASH
7981 4 FUNC_PRIVILEGES_HASH
7982 5 ACL_PROXY_USERS
7983
7984 @retval > 0 At least one element matched.
7985 @retval 0 OK, but no element matched.
7986 @retval -1 Wrong arguments to function or Out of Memory.
7987 */
7988
handle_grant_struct(enum enum_acl_lists struct_no,bool drop,LEX_USER * user_from,LEX_USER * user_to)7989 static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
7990 LEX_USER *user_from, LEX_USER *user_to)
7991 {
7992 int result= 0;
7993 uint idx;
7994 uint elements;
7995 const char *user;
7996 const char *host;
7997 ACL_USER *acl_user= NULL;
7998 ACL_DB *acl_db= NULL;
7999 ACL_PROXY_USER *acl_proxy_user= NULL;
8000 GRANT_NAME *grant_name= NULL;
8001 /*
8002 Dynamic array acl_grant_name used to store pointers to all
8003 GRANT_NAME objects
8004 */
8005 Dynamic_array<GRANT_NAME *> acl_grant_name;
8006 HASH *grant_name_hash= NULL;
8007 DBUG_ENTER("handle_grant_struct");
8008 DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
8009 struct_no, user_from->user.str, user_from->host.str));
8010
8011 LINT_INIT(user);
8012 LINT_INIT(host);
8013
8014 mysql_mutex_assert_owner(&acl_cache->lock);
8015
8016 /* Get the number of elements in the in-memory structure. */
8017 switch (struct_no) {
8018 case USER_ACL:
8019 elements= acl_users.elements;
8020 break;
8021 case DB_ACL:
8022 elements= acl_dbs.elements;
8023 break;
8024 case COLUMN_PRIVILEGES_HASH:
8025 elements= column_priv_hash.records;
8026 grant_name_hash= &column_priv_hash;
8027 break;
8028 case PROC_PRIVILEGES_HASH:
8029 elements= proc_priv_hash.records;
8030 grant_name_hash= &proc_priv_hash;
8031 break;
8032 case FUNC_PRIVILEGES_HASH:
8033 elements= func_priv_hash.records;
8034 grant_name_hash= &func_priv_hash;
8035 break;
8036 case PROXY_USERS_ACL:
8037 elements= acl_proxy_users.elements;
8038 break;
8039 default:
8040 return -1;
8041 }
8042
8043 #ifdef EXTRA_DEBUG
8044 DBUG_PRINT("loop",("scan struct: %u search user: '%s' host: '%s'",
8045 struct_no, user_from->user.str, user_from->host.str));
8046 #endif
8047 /* Loop over all elements. */
8048 for (idx= 0; idx < elements; idx++)
8049 {
8050 /*
8051 Get a pointer to the element.
8052 */
8053 switch (struct_no) {
8054 case USER_ACL:
8055 acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
8056 user= acl_user->user;
8057 host= acl_user->host.get_host();
8058 break;
8059
8060 case DB_ACL:
8061 acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
8062 user= acl_db->user;
8063 host= acl_db->host.get_host();
8064 break;
8065
8066 case COLUMN_PRIVILEGES_HASH:
8067 case PROC_PRIVILEGES_HASH:
8068 case FUNC_PRIVILEGES_HASH:
8069 grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
8070 user= grant_name->user;
8071 host= grant_name->host.get_host();
8072 break;
8073
8074 case PROXY_USERS_ACL:
8075 acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
8076 user= acl_proxy_user->get_user();
8077 host= acl_proxy_user->host.get_host();
8078 break;
8079
8080 default:
8081 MY_ASSERT_UNREACHABLE();
8082 }
8083 if (! user)
8084 user= "";
8085 if (! host)
8086 host= "";
8087
8088 #ifdef EXTRA_DEBUG
8089 DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'",
8090 struct_no, idx, user, host));
8091 #endif
8092 if (strcmp(user_from->user.str, user) ||
8093 my_strcasecmp(system_charset_info, user_from->host.str, host))
8094 continue;
8095
8096 result= 1; /* At least one element found. */
8097 if ( drop )
8098 {
8099 switch ( struct_no ) {
8100 case USER_ACL:
8101 delete_dynamic_element(&acl_users, idx);
8102 elements--;
8103 /*
8104 - If we are iterating through an array then we just have moved all
8105 elements after the current element one position closer to its head.
8106 This means that we have to take another look at the element at
8107 current position as it is a new element from the array's tail.
8108 - This is valid for case USER_ACL, DB_ACL and PROXY_USERS_ACL.
8109 */
8110 idx--;
8111 break;
8112
8113 case DB_ACL:
8114 delete_dynamic_element(&acl_dbs, idx);
8115 elements--;
8116 idx--;
8117 break;
8118
8119 case COLUMN_PRIVILEGES_HASH:
8120 case PROC_PRIVILEGES_HASH:
8121 case FUNC_PRIVILEGES_HASH:
8122 /*
8123 Deleting while traversing a hash table is not valid procedure and
8124 hence we save pointers to GRANT_NAME objects for later processing.
8125 */
8126 if (acl_grant_name.append(grant_name))
8127 DBUG_RETURN(-1);
8128 break;
8129
8130 case PROXY_USERS_ACL:
8131 delete_dynamic_element(&acl_proxy_users, idx);
8132 elements--;
8133 idx--;
8134 break;
8135 }
8136 }
8137 else if ( user_to )
8138 {
8139 switch ( struct_no ) {
8140 case USER_ACL:
8141 acl_user->user= strdup_root(&global_acl_memory, user_to->user.str);
8142 acl_user->host.update_hostname(strdup_root(&global_acl_memory, user_to->host.str));
8143 break;
8144
8145 case DB_ACL:
8146 acl_db->user= strdup_root(&global_acl_memory, user_to->user.str);
8147 acl_db->host.update_hostname(strdup_root(&global_acl_memory, user_to->host.str));
8148 break;
8149
8150 case COLUMN_PRIVILEGES_HASH:
8151 case PROC_PRIVILEGES_HASH:
8152 case FUNC_PRIVILEGES_HASH:
8153 /*
8154 Updating while traversing a hash table is not valid procedure and
8155 hence we save pointers to GRANT_NAME objects for later processing.
8156 */
8157 if (acl_grant_name.append(grant_name))
8158 DBUG_RETURN(-1);
8159 break;
8160
8161 case PROXY_USERS_ACL:
8162 acl_proxy_user->set_user(&global_acl_memory, user_to->user.str);
8163 acl_proxy_user->host.update_hostname((user_to->host.str && *user_to->host.str) ?
8164 strdup_root(&global_acl_memory, user_to->host.str) : NULL);
8165 break;
8166 }
8167 }
8168 else
8169 {
8170 /* If search is requested, we do not need to search further. */
8171 break;
8172 }
8173 }
8174
8175 if (drop || user_to)
8176 {
8177 /*
8178 Traversing the elements stored in acl_grant_name dynamic array
8179 to either delete or update them.
8180 */
8181 for (int i= 0; i < acl_grant_name.elements(); ++i)
8182 {
8183 grant_name= acl_grant_name.at(i);
8184
8185 if (drop)
8186 {
8187 my_hash_delete(grant_name_hash, (uchar *) grant_name);
8188 }
8189 else
8190 {
8191 /*
8192 Save old hash key and its length to be able properly update
8193 element position in hash.
8194 */
8195 char *old_key= grant_name->hash_key;
8196 size_t old_key_length= grant_name->key_length;
8197
8198 /*
8199 Update the grant structure with the new user name and host name.
8200 */
8201 grant_name->set_user_details(user_to->host.str, grant_name->db,
8202 user_to->user.str, grant_name->tname,
8203 TRUE);
8204
8205 /*
8206 Since username is part of the hash key, when the user name
8207 is renamed, the hash key is changed. Update the hash to
8208 ensure that the position matches the new hash key value
8209 */
8210 my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
8211 old_key_length);
8212 }
8213 }
8214 }
8215
8216 #ifdef EXTRA_DEBUG
8217 DBUG_PRINT("loop",("scan struct: %u result %d", struct_no, result));
8218 #endif
8219
8220 DBUG_RETURN(result);
8221 }
8222
8223
8224 /*
8225 Handle all privilege tables and in-memory privilege structures.
8226
8227 SYNOPSIS
8228 handle_grant_data()
8229 tables The array with the four open tables.
8230 drop If user_from is to be dropped.
8231 user_from The the user to be searched/dropped/renamed.
8232 user_to The new name for the user if to be renamed,
8233 NULL otherwise.
8234
8235 DESCRIPTION
8236 Go through all grant tables and in-memory grant structures and apply
8237 the requested operation.
8238 Delete from grant data if drop is true.
8239 Update in grant data if drop is false and user_to is not NULL.
8240 Search in grant data if drop is false and user_to is NULL.
8241
8242 RETURN
8243 > 0 At least one element matched.
8244 0 OK, but no element matched.
8245 < 0 Error.
8246 */
8247
handle_grant_data(TABLE_LIST * tables,bool drop,LEX_USER * user_from,LEX_USER * user_to)8248 static int handle_grant_data(TABLE_LIST *tables, bool drop,
8249 LEX_USER *user_from, LEX_USER *user_to)
8250 {
8251 int result= 0;
8252 int found;
8253 int ret;
8254 Acl_table_intact table_intact;
8255 DBUG_ENTER("handle_grant_data");
8256
8257 /* Handle special utility user */
8258 if (acl_utility_user.user)
8259 {
8260 if (user_from
8261 && acl_is_utility_user(user_from->user.str, user_from->host.str, NULL))
8262 {
8263 result= -1;
8264 goto end;
8265 }
8266 else if (user_to
8267 && acl_is_utility_user(user_to->user.str, user_to->host.str, NULL))
8268 {
8269 result= -1;
8270 goto end;
8271 }
8272 }
8273 /* Handle user table. */
8274 if (table_intact.check(tables[0].table, &mysql_user_table_def))
8275 {
8276 result= -1;
8277 goto end;
8278 }
8279
8280 if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
8281 {
8282 /* Handle of table failed, don't touch the in-memory array. */
8283 result= -1;
8284 }
8285 else
8286 {
8287 /* Handle user array. */
8288 if (((ret= handle_grant_struct(USER_ACL, drop, user_from, user_to) > 0) &&
8289 ! result) || found)
8290 {
8291 result= 1; /* At least one record/element found. */
8292 /* If search is requested, we do not need to search further. */
8293 if (! drop && ! user_to)
8294 goto end;
8295 }
8296 else if (ret < 0)
8297 {
8298 result= -1;
8299 goto end;
8300 }
8301 }
8302
8303 /* Handle db table. */
8304 if (table_intact.check(tables[1].table, &mysql_db_table_def))
8305 {
8306 result= -1;
8307 goto end;
8308 }
8309
8310 if ((found= handle_grant_table(tables, 1, drop, user_from, user_to)) < 0)
8311 {
8312 /* Handle of table failed, don't touch the in-memory array. */
8313 result= -1;
8314 }
8315 else
8316 {
8317 /* Handle db array. */
8318 if ((((ret= handle_grant_struct(DB_ACL, drop, user_from, user_to) > 0) &&
8319 ! result) || found) && ! result)
8320 {
8321 result= 1; /* At least one record/element found. */
8322 /* If search is requested, we do not need to search further. */
8323 if (! drop && ! user_to)
8324 goto end;
8325 }
8326 else if (ret < 0)
8327 {
8328 result= -1;
8329 goto end;
8330 }
8331 }
8332
8333 /* Handle stored routines table. */
8334 if (table_intact.check(tables[4].table, &mysql_procs_priv_table_def))
8335 {
8336 result= -1;
8337 goto end;
8338 }
8339
8340 if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0)
8341 {
8342 /* Handle of table failed, don't touch in-memory array. */
8343 result= -1;
8344 }
8345 else
8346 {
8347 /* Handle procs array. */
8348 if ((((ret= handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from,
8349 user_to) > 0) && ! result) || found) &&
8350 ! result)
8351 {
8352 result= 1; /* At least one record/element found. */
8353 /* If search is requested, we do not need to search further. */
8354 if (! drop && ! user_to)
8355 goto end;
8356 }
8357 else if (ret < 0)
8358 {
8359 result= -1;
8360 goto end;
8361 }
8362 /* Handle funcs array. */
8363 if ((((ret= handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from,
8364 user_to) > 0) && ! result) || found) &&
8365 ! result)
8366 {
8367 result= 1; /* At least one record/element found. */
8368 /* If search is requested, we do not need to search further. */
8369 if (! drop && ! user_to)
8370 goto end;
8371 }
8372 else if (ret < 0)
8373 {
8374 result= -1;
8375 goto end;
8376 }
8377 }
8378
8379 /* Handle tables table. */
8380 if (table_intact.check(tables[2].table, &mysql_tables_priv_table_def))
8381 {
8382 result= -1;
8383 goto end;
8384 }
8385
8386 if ((found= handle_grant_table(tables, 2, drop, user_from, user_to)) < 0)
8387 {
8388 /* Handle of table failed, don't touch columns and in-memory array. */
8389 result= -1;
8390 }
8391 else
8392 {
8393 if (found && ! result)
8394 {
8395 result= 1; /* At least one record found. */
8396 /* If search is requested, we do not need to search further. */
8397 if (! drop && ! user_to)
8398 goto end;
8399 }
8400
8401 /* Handle columns table. */
8402 if (table_intact.check(tables[3].table, &mysql_columns_priv_table_def))
8403 {
8404 result= -1;
8405 goto end;
8406 }
8407
8408 if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
8409 {
8410 /* Handle of table failed, don't touch the in-memory array. */
8411 result= -1;
8412 }
8413 else
8414 {
8415 /* Handle columns hash. */
8416 if ((((ret= handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from,
8417 user_to) > 0) && ! result) || found) &&
8418 ! result)
8419 result= 1; /* At least one record/element found. */
8420 else if (ret < 0)
8421 result= -1;
8422 }
8423 }
8424
8425 /* Handle proxies_priv table. */
8426 if (tables[5].table)
8427 {
8428 if (table_intact.check(tables[5].table, &mysql_proxies_priv_table_def))
8429 {
8430 result= -1;
8431 goto end;
8432 }
8433
8434 if ((found= handle_grant_table(tables, 5, drop, user_from, user_to)) < 0)
8435 {
8436 /* Handle of table failed, don't touch the in-memory array. */
8437 result= -1;
8438 }
8439 else
8440 {
8441 /* Handle proxies_priv array. */
8442 if (((ret= handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) > 0)
8443 && !result) || found)
8444 result= 1; /* At least one record/element found. */
8445 else if (ret < 0)
8446 result= -1;
8447 }
8448 }
8449 end:
8450 DBUG_RETURN(result);
8451 }
8452
8453 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
8454
8455 /**
8456 Auxiliary function for constructing a user list string.
8457 This function is used for error reporting and logging.
8458
8459 @param thd Thread context
8460 @param str A String to store the user list.
8461 @param user A LEX_USER which will be appended into user list.
8462 @param comma If TRUE, append a ',' before the the user.
8463 @param ident If TRUE, append ' IDENTIFIED BY/WITH...' after the user,
8464 if the given user has credentials set with 'IDENTIFIED BY/WITH'
8465 */
append_user(THD * thd,String * str,LEX_USER * user,bool comma=true,bool ident=false)8466 void append_user(THD *thd, String *str, LEX_USER *user, bool comma= true,
8467 bool ident= false)
8468 {
8469 String from_user(user->user.str, user->user.length, system_charset_info);
8470 String from_plugin(user->plugin.str, user->plugin.length, system_charset_info);
8471 String from_auth(user->auth.str, user->auth.length, system_charset_info);
8472 String from_host(user->host.str, user->host.length, system_charset_info);
8473
8474 if (comma)
8475 str->append(',');
8476 append_query_string(thd, system_charset_info, &from_user, str);
8477 str->append(STRING_WITH_LEN("@"));
8478 append_query_string(thd, system_charset_info, &from_host, str);
8479
8480 if (ident)
8481 {
8482 if (user->plugin.str && (user->plugin.length > 0) &&
8483 memcmp(user->plugin.str, native_password_plugin_name.str,
8484 user->plugin.length) &&
8485 memcmp(user->plugin.str, old_password_plugin_name.str,
8486 user->plugin.length))
8487 {
8488 /**
8489 The plugin identifier is allowed to be specified,
8490 both with and without quote marks. We log it with
8491 quotes always.
8492 */
8493 str->append(STRING_WITH_LEN(" IDENTIFIED WITH "));
8494 append_query_string(thd, system_charset_info, &from_plugin, str);
8495
8496 if (user->auth.str && (user->auth.length > 0))
8497 {
8498 str->append(STRING_WITH_LEN(" AS "));
8499 append_query_string(thd, system_charset_info, &from_auth, str);
8500 }
8501 }
8502 else if (user->password.str)
8503 {
8504 str->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
8505 if (user->uses_identified_by_password_clause)
8506 {
8507 str->append(user->password.str, user->password.length);
8508 str->append("'");
8509 }
8510 else
8511 {
8512 /*
8513 Password algorithm is chosen based on old_passwords variable or
8514 TODO the new password_algorithm variable.
8515 It is assumed that the variable hasn't changed since parsing.
8516 */
8517 if (thd->variables.old_passwords == 0)
8518 {
8519 /*
8520 my_make_scrambled_password_sha1() requires a target buffer size of
8521 SCRAMBLED_PASSWORD_CHAR_LENGTH + 1.
8522 The extra character is for the probably originate from either '\0'
8523 or the initial '*' character.
8524 */
8525 char tmp[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1];
8526 my_make_scrambled_password_sha1(tmp, user->password.str,
8527 user->password.length);
8528 str->append(tmp);
8529 }
8530 else
8531 {
8532 /*
8533 Legacy password algorithm is just an obfuscation of a plain text
8534 so we're not going to write this.
8535 Same with old_passwords == 2 since the scrambled password will
8536 be binary anyway.
8537 */
8538 str->append("<secret>");
8539 }
8540 str->append("'");
8541 }
8542 }
8543 }
8544 }
8545
8546 #ifndef NO_EMBEDDED_ACCESS_CHECKS
8547
8548 /*
8549 Create a list of users.
8550
8551 SYNOPSIS
8552 mysql_create_user()
8553 thd The current thread.
8554 list The users to create.
8555
8556 RETURN
8557 FALSE OK.
8558 TRUE Error.
8559 */
8560
mysql_create_user(THD * thd,List<LEX_USER> & list)8561 bool mysql_create_user(THD *thd, List <LEX_USER> &list)
8562 {
8563 int result;
8564 String wrong_users;
8565 LEX_USER *user_name, *tmp_user_name;
8566 List_iterator <LEX_USER> user_list(list);
8567 TABLE_LIST tables[GRANT_TABLES];
8568 bool some_users_created= FALSE;
8569 bool save_binlog_row_based;
8570 bool transactional_tables;
8571 DBUG_ENTER("mysql_create_user");
8572
8573 /*
8574 This statement will be replicated as a statement, even when using
8575 row-based replication. The flag will be reset at the end of the
8576 statement.
8577 */
8578 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
8579 thd->clear_current_stmt_binlog_format_row();
8580
8581 /* CREATE USER may be skipped on replication client. */
8582 if ((result= open_grant_tables(thd, tables, &transactional_tables)))
8583 {
8584 /* Restore the state of binlog format */
8585 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8586 if (save_binlog_row_based)
8587 thd->set_current_stmt_binlog_format_row();
8588 DBUG_RETURN(result != 1);
8589 }
8590
8591 mysql_rwlock_wrlock(&LOCK_grant);
8592 mysql_mutex_lock(&acl_cache->lock);
8593
8594 while ((tmp_user_name= user_list++))
8595 {
8596 /*
8597 If tmp_user_name.user.str is == NULL then
8598 user_name := tmp_user_name.
8599 Else user_name.user := sctx->user
8600 TODO and all else is turned to NULL !! Why?
8601 */
8602 if (!(user_name= get_current_user(thd, tmp_user_name)))
8603 {
8604 result= TRUE;
8605 continue;
8606 }
8607
8608 /*
8609 If no plugin is given, set a default plugin
8610 */
8611 if (user_name->plugin.length == 0 && user_name->uses_identified_with_clause)
8612 {
8613 user_name->plugin.str= default_auth_plugin_name.str;
8614 user_name->plugin.length= default_auth_plugin_name.length;
8615 }
8616
8617 /*
8618 Search all in-memory structures and grant tables
8619 for a mention of the new user name.
8620 */
8621 if (handle_grant_data(tables, 0, user_name, NULL))
8622 {
8623 append_user(thd, &wrong_users, user_name, wrong_users.length() > 0,
8624 false);
8625 result= TRUE;
8626 continue;
8627 }
8628
8629 if (replace_user_table(thd, tables[0].table, user_name, 0, 0, 1, 0))
8630 {
8631 append_user(thd, &wrong_users, user_name, wrong_users.length() > 0,
8632 false);
8633 result= TRUE;
8634 continue;
8635 }
8636
8637 some_users_created= TRUE;
8638 } // END while tmp_user_name= user_lists++
8639
8640 mysql_mutex_unlock(&acl_cache->lock);
8641
8642 if (result)
8643 my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
8644
8645 if (some_users_created)
8646 {
8647 if (!thd->rewritten_query.length())
8648 result|= write_bin_log(thd, false, thd->query(), thd->query_length(),
8649 transactional_tables);
8650 else
8651 result|= write_bin_log(thd, false,
8652 thd->rewritten_query.c_ptr_safe(),
8653 thd->rewritten_query.length(),
8654 transactional_tables);
8655 }
8656
8657 mysql_rwlock_unlock(&LOCK_grant);
8658
8659 result|= acl_trans_commit_and_close_tables(thd);
8660
8661 /* Restore the state of binlog format */
8662 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8663 if (save_binlog_row_based)
8664 thd->set_current_stmt_binlog_format_row();
8665 DBUG_RETURN(result);
8666 }
8667
8668
8669 /*
8670 Drop a list of users and all their privileges.
8671
8672 SYNOPSIS
8673 mysql_drop_user()
8674 thd The current thread.
8675 list The users to drop.
8676
8677 RETURN
8678 FALSE OK.
8679 TRUE Error.
8680 */
8681
mysql_drop_user(THD * thd,List<LEX_USER> & list)8682 bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
8683 {
8684 int result;
8685 String wrong_users;
8686 LEX_USER *user_name, *tmp_user_name;
8687 List_iterator <LEX_USER> user_list(list);
8688 TABLE_LIST tables[GRANT_TABLES];
8689 bool some_users_deleted= FALSE;
8690 sql_mode_t old_sql_mode= thd->variables.sql_mode;
8691 bool save_binlog_row_based;
8692 bool transactional_tables;
8693 DBUG_ENTER("mysql_drop_user");
8694
8695 /*
8696 This statement will be replicated as a statement, even when using
8697 row-based replication. The flag will be reset at the end of the
8698 statement.
8699 */
8700 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
8701 thd->clear_current_stmt_binlog_format_row();
8702
8703 /* DROP USER may be skipped on replication client. */
8704 if ((result= open_grant_tables(thd, tables, &transactional_tables)))
8705 {
8706 /* Restore the state of binlog format */
8707 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8708 if (save_binlog_row_based)
8709 thd->set_current_stmt_binlog_format_row();
8710 DBUG_RETURN(result != 1);
8711 }
8712
8713 thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
8714
8715 mysql_rwlock_wrlock(&LOCK_grant);
8716 mysql_mutex_lock(&acl_cache->lock);
8717
8718 while ((tmp_user_name= user_list++))
8719 {
8720 if (!(user_name= get_current_user(thd, tmp_user_name)))
8721 {
8722 result= TRUE;
8723 continue;
8724 }
8725 if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
8726 {
8727 append_user(thd, &wrong_users, user_name, wrong_users.length() > 0, FALSE);
8728 result= TRUE;
8729 continue;
8730 }
8731 some_users_deleted= TRUE;
8732 }
8733
8734 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
8735 rebuild_check_host();
8736
8737 mysql_mutex_unlock(&acl_cache->lock);
8738
8739 if (result)
8740 my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
8741
8742 if (some_users_deleted)
8743 result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length(),
8744 transactional_tables);
8745
8746 mysql_rwlock_unlock(&LOCK_grant);
8747
8748 result|= acl_trans_commit_and_close_tables(thd);
8749
8750 thd->variables.sql_mode= old_sql_mode;
8751 /* Restore the state of binlog format */
8752 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8753 if (save_binlog_row_based)
8754 thd->set_current_stmt_binlog_format_row();
8755 DBUG_RETURN(result);
8756 }
8757
8758
8759 /*
8760 Rename a user.
8761
8762 SYNOPSIS
8763 mysql_rename_user()
8764 thd The current thread.
8765 list The user name pairs: (from, to).
8766
8767 RETURN
8768 FALSE OK.
8769 TRUE Error.
8770 */
8771
mysql_rename_user(THD * thd,List<LEX_USER> & list)8772 bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
8773 {
8774 int result;
8775 String wrong_users;
8776 LEX_USER *user_from, *tmp_user_from;
8777 LEX_USER *user_to, *tmp_user_to;
8778 List_iterator <LEX_USER> user_list(list);
8779 TABLE_LIST tables[GRANT_TABLES];
8780 bool some_users_renamed= FALSE;
8781 bool save_binlog_row_based;
8782 bool transactional_tables;
8783 DBUG_ENTER("mysql_rename_user");
8784
8785 /*
8786 This statement will be replicated as a statement, even when using
8787 row-based replication. The flag will be reset at the end of the
8788 statement.
8789 */
8790 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
8791 thd->clear_current_stmt_binlog_format_row();
8792
8793 /* RENAME USER may be skipped on replication client. */
8794 if ((result= open_grant_tables(thd, tables, &transactional_tables)))
8795 {
8796 /* Restore the state of binlog format */
8797 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8798 if (save_binlog_row_based)
8799 thd->set_current_stmt_binlog_format_row();
8800 DBUG_RETURN(result != 1);
8801 }
8802
8803 mysql_rwlock_wrlock(&LOCK_grant);
8804 mysql_mutex_lock(&acl_cache->lock);
8805
8806 while ((tmp_user_from= user_list++))
8807 {
8808 if (!(user_from= get_current_user(thd, tmp_user_from)))
8809 {
8810 result= TRUE;
8811 continue;
8812 }
8813 tmp_user_to= user_list++;
8814 if (!(user_to= get_current_user(thd, tmp_user_to)))
8815 {
8816 result= TRUE;
8817 continue;
8818 }
8819 DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
8820
8821 /*
8822 Search all in-memory structures and grant tables
8823 for a mention of the new user name.
8824 */
8825 if (handle_grant_data(tables, 0, user_to, NULL) ||
8826 handle_grant_data(tables, 0, user_from, user_to) <= 0)
8827 {
8828 append_user(thd, &wrong_users, user_from, wrong_users.length() > 0, FALSE);
8829 result= TRUE;
8830 continue;
8831 }
8832 some_users_renamed= TRUE;
8833 }
8834
8835 /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
8836 rebuild_check_host();
8837
8838 mysql_mutex_unlock(&acl_cache->lock);
8839
8840 if (result)
8841 my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
8842
8843 if (some_users_renamed)
8844 result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length(),
8845 transactional_tables);
8846
8847 mysql_rwlock_unlock(&LOCK_grant);
8848
8849 result|= acl_trans_commit_and_close_tables(thd);
8850
8851 /* Restore the state of binlog format */
8852 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8853 if (save_binlog_row_based)
8854 thd->set_current_stmt_binlog_format_row();
8855 DBUG_RETURN(result);
8856 }
8857
8858
8859 /*
8860 Mark user's password as expired.
8861
8862 SYNOPSIS
8863 mysql_user_password_expire()
8864 thd The current thread.
8865 list The user names.
8866
8867 RETURN
8868 FALSE OK.
8869 TRUE Error.
8870 */
8871
mysql_user_password_expire(THD * thd,List<LEX_USER> & list)8872 bool mysql_user_password_expire(THD *thd, List <LEX_USER> &list)
8873 {
8874 bool result= false;
8875 String wrong_users;
8876 LEX_USER *user_from, *tmp_user_from;
8877 List_iterator <LEX_USER> user_list(list);
8878 TABLE_LIST tables;
8879 TABLE *table;
8880 bool some_passwords_expired= false;
8881 bool save_binlog_row_based;
8882 Acl_table_intact table_intact;
8883 DBUG_ENTER("mysql_user_password_expire");
8884
8885 if (!initialized)
8886 {
8887 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
8888 DBUG_RETURN(true);
8889 }
8890 tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
8891
8892 #ifdef HAVE_REPLICATION
8893 /*
8894 GRANT and REVOKE are applied the slave in/exclusion rules as they are
8895 some kind of updates to the mysql.% tables.
8896 */
8897 if (thd->slave_thread && rpl_filter->is_on())
8898 {
8899 /*
8900 The tables must be marked "updating" so that tables_ok() takes them into
8901 account in tests. It's ok to leave 'updating' set after tables_ok.
8902 */
8903 tables.updating= 1;
8904 /* Thanks to memset, tables.next==0 */
8905 if (!(thd->sp_runtime_ctx || rpl_filter->tables_ok(0, &tables)))
8906 DBUG_RETURN(false);
8907 }
8908 #endif
8909 if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
8910 DBUG_RETURN(true);
8911
8912 if (table_intact.check(table, &mysql_user_table_def))
8913 DBUG_RETURN(true);
8914
8915 /*
8916 This statement will be replicated as a statement, even when using
8917 row-based replication. The flag will be reset at the end of the
8918 statement.
8919 */
8920 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
8921 thd->clear_current_stmt_binlog_format_row();
8922
8923 mysql_rwlock_wrlock(&LOCK_grant);
8924 mysql_mutex_lock(&acl_cache->lock);
8925
8926 while ((tmp_user_from= user_list++))
8927 {
8928 ACL_USER *acl_user;
8929
8930 /* add the defaults where needed */
8931 if (!(user_from= get_current_user(thd, tmp_user_from)))
8932 {
8933 result= true;
8934 append_user(thd, &wrong_users, tmp_user_from, wrong_users.length() > 0,
8935 false);
8936 continue;
8937 }
8938
8939 /* look up the user */
8940 if (!(acl_user= find_acl_user(user_from->host.str,
8941 user_from->user.str, TRUE)))
8942 {
8943 result= true;
8944 append_user(thd, &wrong_users, user_from, wrong_users.length() > 0,
8945 false);
8946 continue;
8947 }
8948
8949 /* Check if the user's authentication method supports expiration */
8950 if (!auth_plugin_supports_expiration(acl_user->plugin.str))
8951 {
8952 result= true;
8953 append_user(thd, &wrong_users, user_from, wrong_users.length() > 0,
8954 false);
8955 continue;
8956 }
8957
8958
8959 /* update the mysql.user table */
8960 enum mysql_user_table_field password_field= MYSQL_USER_FIELD_PASSWORD;
8961 if (update_user_table(thd, table,
8962 acl_user->host.get_host() ?
8963 acl_user->host.get_host() : "",
8964 acl_user->user ? acl_user->user : "",
8965 NULL, 0, password_field, true, false))
8966 {
8967 result= true;
8968 append_user(thd, &wrong_users, user_from, wrong_users.length() > 0,
8969 false);
8970 continue;
8971 }
8972
8973 acl_user->password_expired= true;
8974 some_passwords_expired= true;
8975 }
8976
8977 acl_cache->clear(1); // Clear locked hostname cache
8978 mysql_mutex_unlock(&acl_cache->lock);
8979
8980 if (result)
8981 my_error(ER_CANNOT_USER, MYF(0), "ALTER USER", wrong_users.c_ptr_safe());
8982
8983 if (!result && some_passwords_expired)
8984 {
8985 const char *query= thd->rewritten_query.length() ?
8986 thd->rewritten_query.c_ptr_safe() : thd->query();
8987 const size_t query_length= thd->rewritten_query.length() ?
8988 thd->rewritten_query.length() : thd->query_length();
8989 result= (write_bin_log(thd, false, query, query_length,
8990 table->file->has_transactions()) != 0);
8991 }
8992
8993 mysql_rwlock_unlock(&LOCK_grant);
8994
8995 result|= acl_trans_commit_and_close_tables(thd);
8996
8997 /* Restore the state of binlog format */
8998 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
8999 if (save_binlog_row_based)
9000 thd->set_current_stmt_binlog_format_row();
9001 DBUG_RETURN(result);
9002 }
9003
9004
9005 /*
9006 Revoke all privileges from a list of users.
9007
9008 SYNOPSIS
9009 mysql_revoke_all()
9010 thd The current thread.
9011 list The users to revoke all privileges from.
9012
9013 RETURN
9014 > 0 Error. Error message already sent.
9015 0 OK.
9016 < 0 Error. Error message not yet sent.
9017 */
9018
mysql_revoke_all(THD * thd,List<LEX_USER> & list)9019 bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
9020 {
9021 uint counter, revoked, is_proc;
9022 int result;
9023 ACL_DB *acl_db;
9024 TABLE_LIST tables[GRANT_TABLES];
9025 bool save_binlog_row_based;
9026 bool transactional_tables;
9027 DBUG_ENTER("mysql_revoke_all");
9028
9029 /*
9030 This statement will be replicated as a statement, even when using
9031 row-based replication. The flag will be reset at the end of the
9032 statement.
9033 */
9034 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
9035 thd->clear_current_stmt_binlog_format_row();
9036
9037 if ((result= open_grant_tables(thd, tables, &transactional_tables)))
9038 {
9039 /* Restore the state of binlog format */
9040 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
9041 if (save_binlog_row_based)
9042 thd->set_current_stmt_binlog_format_row();
9043 DBUG_RETURN(result != 1);
9044 }
9045
9046 mysql_rwlock_wrlock(&LOCK_grant);
9047 mysql_mutex_lock(&acl_cache->lock);
9048
9049 LEX_USER *lex_user, *tmp_lex_user;
9050 List_iterator <LEX_USER> user_list(list);
9051
9052 bool is_partial_execution= false;
9053 while ((tmp_lex_user= user_list++))
9054 {
9055 bool is_user_applied= true;
9056 if (!(lex_user= get_current_user(thd, tmp_lex_user)))
9057 {
9058 result= -1;
9059 continue;
9060 }
9061 if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
9062 {
9063 result= -1;
9064 continue;
9065 }
9066
9067 if (replace_user_table(thd, tables[0].table,
9068 lex_user, ~(ulong) 0, 1, 0, 0))
9069 {
9070 result= -1;
9071 continue;
9072 }
9073
9074 /* Remove db access privileges */
9075 /*
9076 Because acl_dbs and column_priv_hash shrink and may re-order
9077 as privileges are removed, removal occurs in a repeated loop
9078 until no more privileges are revoked.
9079 */
9080 do
9081 {
9082 for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
9083 {
9084 const char *user,*host;
9085
9086 acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
9087 if (!(user=acl_db->user))
9088 user= "";
9089 if (!(host=acl_db->host.get_host()))
9090 host= "";
9091
9092 if (!strcmp(lex_user->user.str,user) &&
9093 !strcmp(lex_user->host.str, host))
9094 {
9095 if (!replace_db_table(tables[1].table, acl_db->db, *lex_user,
9096 ~(ulong)0, 1))
9097 {
9098 /*
9099 Don't increment counter as replace_db_table deleted the
9100 current element in acl_dbs.
9101 */
9102 revoked= 1;
9103 continue;
9104 }
9105 result= -1; // Something went wrong
9106 is_user_applied= false;
9107 }
9108 counter++;
9109 }
9110 } while (revoked);
9111
9112 /* Remove column access */
9113 do
9114 {
9115 for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
9116 {
9117 const char *user,*host;
9118 GRANT_TABLE *grant_table=
9119 (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
9120 if (!(user=grant_table->user))
9121 user= "";
9122 if (!(host=grant_table->host.get_host()))
9123 host= "";
9124
9125 if (!strcmp(lex_user->user.str,user) &&
9126 !strcmp(lex_user->host.str, host))
9127 {
9128 if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
9129 grant_table->db,
9130 grant_table->tname,
9131 ~(ulong)0, 0, 1))
9132 {
9133 result= -1;
9134 is_user_applied= false;
9135 }
9136 else
9137 {
9138 if (!grant_table->cols)
9139 {
9140 revoked= 1;
9141 continue;
9142 }
9143 List<LEX_COLUMN> columns;
9144 if (!replace_column_table(grant_table,tables[3].table, *lex_user,
9145 columns,
9146 grant_table->db,
9147 grant_table->tname,
9148 ~(ulong)0, 1))
9149 {
9150 revoked= 1;
9151 continue;
9152 }
9153 result= -1;
9154 is_user_applied= false;
9155 }
9156 }
9157 counter++;
9158 }
9159 } while (revoked);
9160
9161 /* Remove procedure access */
9162 for (is_proc=0; is_proc<2; is_proc++) do {
9163 HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
9164 for (counter= 0, revoked= 0 ; counter < hash->records ; )
9165 {
9166 const char *user,*host;
9167 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
9168 if (!(user=grant_proc->user))
9169 user= "";
9170 if (!(host=grant_proc->host.get_host()))
9171 host= "";
9172
9173 if (!strcmp(lex_user->user.str,user) &&
9174 !strcmp(lex_user->host.str, host))
9175 {
9176 if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
9177 grant_proc->db,
9178 grant_proc->tname,
9179 is_proc,
9180 ~(ulong)0, 1) == 0)
9181 {
9182 revoked= 1;
9183 continue;
9184 }
9185 result= -1; // Something went wrong
9186 is_user_applied= false;
9187 }
9188 counter++;
9189 }
9190 } while (revoked);
9191 if (is_user_applied)
9192 is_partial_execution= true;
9193 }
9194
9195 mysql_mutex_unlock(&acl_cache->lock);
9196
9197 if (result)
9198 my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
9199
9200 /*
9201 Before ACLs are changed to execute fully or none at all, when
9202 some error happens, write an incident if one or more users are
9203 revoked successfully (it has a partial execution), a warning
9204 if no user is revoked successfully.
9205 */
9206 if (result)
9207 {
9208 if (is_partial_execution)
9209 mysql_bin_log.write_incident(thd, true /* need_lock_log=true */);
9210 else
9211 sql_print_warning("Did not write failed '%s' into binary log while "
9212 "revoking all_privileges from a list of users.",
9213 thd->query());
9214 }
9215 else
9216 {
9217 result= result |
9218 write_bin_log(thd, FALSE, thd->query(), thd->query_length(),
9219 transactional_tables);
9220 }
9221
9222 mysql_rwlock_unlock(&LOCK_grant);
9223
9224 result|= acl_trans_commit_and_close_tables(thd);
9225
9226 /* Restore the state of binlog format */
9227 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
9228 if (save_binlog_row_based)
9229 thd->set_current_stmt_binlog_format_row();
9230
9231 DBUG_RETURN(result);
9232 }
9233
9234
9235
9236
9237 /**
9238 If the defining user for a routine does not exist, then the ACL lookup
9239 code should raise two errors which we should intercept. We convert the more
9240 descriptive error into a warning, and consume the other.
9241
9242 If any other errors are raised, then we set a flag that should indicate
9243 that there was some failure we should complain at a higher level.
9244 */
9245 class Silence_routine_definer_errors : public Internal_error_handler
9246 {
9247 public:
Silence_routine_definer_errors()9248 Silence_routine_definer_errors()
9249 : is_grave(FALSE)
9250 {}
9251
~Silence_routine_definer_errors()9252 virtual ~Silence_routine_definer_errors()
9253 {}
9254
9255 virtual bool handle_condition(THD *thd,
9256 uint sql_errno,
9257 const char* sqlstate,
9258 Sql_condition::enum_warning_level level,
9259 const char* msg,
9260 Sql_condition ** cond_hdl);
9261
has_errors()9262 bool has_errors() { return is_grave; }
9263
9264 private:
9265 bool is_grave;
9266 };
9267
9268 bool
handle_condition(THD * thd,uint sql_errno,const char *,Sql_condition::enum_warning_level level,const char * msg,Sql_condition ** cond_hdl)9269 Silence_routine_definer_errors::handle_condition(
9270 THD *thd,
9271 uint sql_errno,
9272 const char*,
9273 Sql_condition::enum_warning_level level,
9274 const char* msg,
9275 Sql_condition ** cond_hdl)
9276 {
9277 *cond_hdl= NULL;
9278 if (level == Sql_condition::WARN_LEVEL_ERROR)
9279 {
9280 switch (sql_errno)
9281 {
9282 case ER_NONEXISTING_PROC_GRANT:
9283 /* Convert the error into a warning. */
9284 push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
9285 sql_errno, msg);
9286 return TRUE;
9287 default:
9288 is_grave= TRUE;
9289 }
9290 }
9291
9292 return FALSE;
9293 }
9294
9295
9296 /**
9297 Revoke privileges for all users on a stored procedure. Use an error handler
9298 that converts errors about missing grants into warnings.
9299
9300 @param
9301 thd The current thread.
9302 @param
9303 db DB of the stored procedure
9304 @param
9305 name Name of the stored procedure
9306
9307 @retval
9308 0 OK.
9309 @retval
9310 < 0 Error. Error message not yet sent.
9311 */
9312
sp_revoke_privileges(THD * thd,const char * sp_db,const char * sp_name,bool is_proc)9313 bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
9314 bool is_proc)
9315 {
9316 uint counter, revoked;
9317 int result;
9318 TABLE_LIST tables[GRANT_TABLES];
9319 HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
9320 Silence_routine_definer_errors error_handler;
9321 bool save_binlog_row_based;
9322 bool not_used;
9323 DBUG_ENTER("sp_revoke_privileges");
9324
9325 if ((result= open_grant_tables(thd, tables, ¬_used)))
9326 DBUG_RETURN(result != 1);
9327
9328 /* Be sure to pop this before exiting this scope! */
9329 thd->push_internal_handler(&error_handler);
9330
9331 mysql_rwlock_wrlock(&LOCK_grant);
9332 mysql_mutex_lock(&acl_cache->lock);
9333
9334 /*
9335 This statement will be replicated as a statement, even when using
9336 row-based replication. The flag will be reset at the end of the
9337 statement.
9338 */
9339 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
9340 thd->clear_current_stmt_binlog_format_row();
9341
9342 /* Remove procedure access */
9343 do
9344 {
9345 for (counter= 0, revoked= 0 ; counter < hash->records ; )
9346 {
9347 GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
9348 if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
9349 !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
9350 {
9351 LEX_USER lex_user;
9352 lex_user.user.str= grant_proc->user;
9353 lex_user.user.length= strlen(grant_proc->user);
9354 lex_user.host.str= (char*) (grant_proc->host.get_host() ?
9355 grant_proc->host.get_host() : "");
9356 lex_user.host.length= grant_proc->host.get_host() ?
9357 strlen(grant_proc->host.get_host()) : 0;
9358
9359 if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
9360 grant_proc->db, grant_proc->tname,
9361 is_proc, ~(ulong)0, 1) == 0)
9362 {
9363 revoked= 1;
9364 continue;
9365 }
9366 }
9367 counter++;
9368 }
9369 } while (revoked);
9370
9371 mysql_mutex_unlock(&acl_cache->lock);
9372 mysql_rwlock_unlock(&LOCK_grant);
9373
9374 result= acl_trans_commit_and_close_tables(thd);
9375
9376 thd->pop_internal_handler();
9377
9378 /* Restore the state of binlog format */
9379 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
9380 if (save_binlog_row_based)
9381 thd->set_current_stmt_binlog_format_row();
9382
9383 DBUG_RETURN(error_handler.has_errors() || result);
9384 }
9385
9386
9387 /**
9388 Grant EXECUTE,ALTER privilege for a stored procedure
9389
9390 @param thd The current thread.
9391 @param sp_db
9392 @param sp_name
9393 @param is_proc
9394
9395 @return
9396 @retval FALSE Success
9397 @retval TRUE An error occured. Error message not yet sent.
9398 */
9399
sp_grant_privileges(THD * thd,const char * sp_db,const char * sp_name,bool is_proc)9400 bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
9401 bool is_proc)
9402 {
9403 Security_context *sctx= thd->security_ctx;
9404 LEX_USER *combo;
9405 TABLE_LIST tables[1];
9406 List<LEX_USER> user_list;
9407 bool result;
9408 ACL_USER *au;
9409 Dummy_error_handler error_handler;
9410 DBUG_ENTER("sp_grant_privileges");
9411
9412 if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
9413 DBUG_RETURN(TRUE);
9414
9415 combo->user.str= (char *) sctx->priv_user;
9416
9417 mysql_mutex_lock(&acl_cache->lock);
9418
9419 if ((au= find_acl_user(combo->host.str= (char *) sctx->priv_host,
9420 combo->user.str, FALSE)))
9421 goto found_acl;
9422
9423 mysql_mutex_unlock(&acl_cache->lock);
9424 DBUG_RETURN(TRUE);
9425
9426 found_acl:
9427 mysql_mutex_unlock(&acl_cache->lock);
9428
9429 memset(tables, 0, sizeof(TABLE_LIST));
9430 user_list.empty();
9431
9432 tables->db= (char*)sp_db;
9433 tables->table_name= tables->alias= (char*)sp_name;
9434
9435 thd->make_lex_string(&combo->user,
9436 combo->user.str, strlen(combo->user.str), 0);
9437 thd->make_lex_string(&combo->host,
9438 combo->host.str, strlen(combo->host.str), 0);
9439
9440 combo->password= empty_lex_str;
9441 combo->plugin= empty_lex_str;
9442 combo->auth= empty_lex_str;
9443 combo->uses_identified_by_clause= false;
9444 combo->uses_identified_with_clause= false;
9445 combo->uses_identified_by_password_clause= false;
9446 combo->uses_authentication_string_clause= false;
9447
9448 if (user_list.push_back(combo))
9449 DBUG_RETURN(TRUE);
9450
9451 thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
9452 thd->lex->ssl_cipher= thd->lex->x509_subject= thd->lex->x509_issuer= 0;
9453 memset(&thd->lex->mqh, 0, sizeof(thd->lex->mqh));
9454
9455 /*
9456 Only care about whether the operation failed or succeeded
9457 as all errors will be handled later.
9458 */
9459 thd->push_internal_handler(&error_handler);
9460 result= mysql_routine_grant(thd, tables, is_proc, user_list,
9461 DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
9462 thd->pop_internal_handler();
9463 DBUG_RETURN(result);
9464 }
9465
9466
9467 /**
9468 Validate if a user can proxy as another user
9469
9470 @thd current thread
9471 @param user the logged in user (proxy user)
9472 @param authenticated_as the effective user a plugin is trying to
9473 impersonate as (proxied user)
9474 @return proxy user definition
9475 @retval NULL proxy user definition not found or not applicable
9476 @retval non-null the proxy user data
9477 */
9478
9479 static ACL_PROXY_USER *
acl_find_proxy_user(const char * user,const char * host,const char * ip,const char * authenticated_as,bool * proxy_used)9480 acl_find_proxy_user(const char *user, const char *host, const char *ip,
9481 const char *authenticated_as, bool *proxy_used)
9482 {
9483 uint i;
9484 /* if the proxied and proxy user are the same return OK */
9485 DBUG_ENTER("acl_find_proxy_user");
9486 DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
9487 user, host, ip, authenticated_as));
9488
9489 if (!strcmp(authenticated_as, user))
9490 {
9491 DBUG_PRINT ("info", ("user is the same as authenticated_as"));
9492 DBUG_RETURN (NULL);
9493 }
9494
9495 *proxy_used= TRUE;
9496 for (i=0; i < acl_proxy_users.elements; i++)
9497 {
9498 ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
9499 ACL_PROXY_USER *);
9500 if (proxy->matches(host, user, ip, authenticated_as))
9501 DBUG_RETURN(proxy);
9502 }
9503
9504 DBUG_RETURN(NULL);
9505 }
9506
9507
9508 bool
acl_check_proxy_grant_access(THD * thd,const char * host,const char * user,bool with_grant)9509 acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
9510 bool with_grant)
9511 {
9512 DBUG_ENTER("acl_check_proxy_grant_access");
9513 DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
9514 (int) with_grant));
9515 if (!initialized)
9516 {
9517 my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
9518 DBUG_RETURN(1);
9519 }
9520
9521 /* replication slave thread can do anything */
9522 if (thd->slave_thread)
9523 {
9524 DBUG_PRINT("info", ("replication slave"));
9525 DBUG_RETURN(FALSE);
9526 }
9527
9528 /*
9529 one can grant proxy for self to others.
9530 Security context in THD contains two pairs of (user,host):
9531 1. (user,host) pair referring to inbound connection.
9532 2. (priv_user,priv_host) pair obtained from mysql.user table after doing
9533 authnetication of incoming connection.
9534 Privileges should be checked wrt (priv_user, priv_host) tuple, because
9535 (user,host) pair obtained from inbound connection may have different
9536 values than what is actually stored in mysql.user table and while granting
9537 or revoking proxy privilege, user is expected to provide entries mentioned
9538 in mysql.user table.
9539 */
9540 if (!strcmp(thd->security_ctx->priv_user, user) &&
9541 !my_strcasecmp(system_charset_info, host,
9542 thd->security_ctx->priv_host))
9543 {
9544 DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
9545 thd->security_ctx->priv_user, user,
9546 host, thd->security_ctx->priv_host));
9547 DBUG_RETURN(FALSE);
9548 }
9549
9550 mysql_mutex_lock(&acl_cache->lock);
9551
9552 /* check for matching WITH PROXY rights */
9553 for (uint i=0; i < acl_proxy_users.elements; i++)
9554 {
9555 ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
9556 ACL_PROXY_USER *);
9557 DEBUG_SYNC(thd, "before_proxy_matches");
9558 if (proxy->matches(thd->security_ctx->get_host()->ptr(),
9559 thd->security_ctx->user,
9560 thd->security_ctx->get_ip()->ptr(),
9561 user) &&
9562 proxy->get_with_grant())
9563 {
9564 DBUG_PRINT("info", ("found"));
9565 mysql_mutex_unlock(&acl_cache->lock);
9566 DBUG_RETURN(FALSE);
9567 }
9568 }
9569
9570 mysql_mutex_unlock(&acl_cache->lock);
9571 my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
9572 thd->security_ctx->user,
9573 thd->security_ctx->host_or_ip);
9574 DBUG_RETURN(TRUE);
9575 }
9576
9577
9578 static bool
show_proxy_grants(THD * thd,LEX_USER * user,char * buff,size_t buffsize)9579 show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
9580 {
9581 Protocol *protocol= thd->protocol;
9582 int error= 0;
9583
9584 for (uint i=0; i < acl_proxy_users.elements; i++)
9585 {
9586 ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
9587 ACL_PROXY_USER *);
9588 if (proxy->granted_on(user->host.str, user->user.str))
9589 {
9590 String global(buff, buffsize, system_charset_info);
9591 global.length(0);
9592 proxy->print_grant(&global);
9593 protocol->prepare_for_resend();
9594 protocol->store(global.ptr(), global.length(), global.charset());
9595 if (protocol->write())
9596 {
9597 error= -1;
9598 break;
9599 }
9600 }
9601 }
9602 return error;
9603 }
9604
9605
9606 #endif /*NO_EMBEDDED_ACCESS_CHECKS */
9607
9608
wild_case_compare(CHARSET_INFO * cs,const char * str,const char * wildstr)9609 int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
9610 {
9611 int flag;
9612 DBUG_ENTER("wild_case_compare");
9613 DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr));
9614 while (*wildstr)
9615 {
9616 while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
9617 {
9618 if (*wildstr == wild_prefix && wildstr[1])
9619 wildstr++;
9620 if (my_toupper(cs, *wildstr++) !=
9621 my_toupper(cs, *str++)) DBUG_RETURN(1);
9622 }
9623 if (! *wildstr ) DBUG_RETURN (*str != 0);
9624 if (*wildstr++ == wild_one)
9625 {
9626 if (! *str++) DBUG_RETURN (1); /* One char; skip */
9627 }
9628 else
9629 { /* Found '*' */
9630 if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */
9631 flag=(*wildstr != wild_many && *wildstr != wild_one);
9632 do
9633 {
9634 if (flag)
9635 {
9636 char cmp;
9637 if ((cmp= *wildstr) == wild_prefix && wildstr[1])
9638 cmp=wildstr[1];
9639 cmp=my_toupper(cs, cmp);
9640 while (*str && my_toupper(cs, *str) != cmp)
9641 str++;
9642 if (!*str) DBUG_RETURN (1);
9643 }
9644 if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
9645 } while (*str++);
9646 DBUG_RETURN(1);
9647 }
9648 }
9649 DBUG_RETURN (*str != '\0');
9650 }
9651
9652
9653 #ifndef NO_EMBEDDED_ACCESS_CHECKS
update_schema_privilege(THD * thd,TABLE * table,char * buff,const char * db,const char * t_name,const char * column,uint col_length,const char * priv,uint priv_length,const char * is_grantable)9654 static bool update_schema_privilege(THD *thd, TABLE *table, char *buff,
9655 const char* db, const char* t_name,
9656 const char* column, uint col_length,
9657 const char *priv, uint priv_length,
9658 const char* is_grantable)
9659 {
9660 int i= 2;
9661 CHARSET_INFO *cs= system_charset_info;
9662 restore_record(table, s->default_values);
9663 table->field[0]->store(buff, (uint) strlen(buff), cs);
9664 table->field[1]->store(STRING_WITH_LEN("def"), cs);
9665 if (db)
9666 table->field[i++]->store(db, (uint) strlen(db), cs);
9667 if (t_name)
9668 table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
9669 if (column)
9670 table->field[i++]->store(column, col_length, cs);
9671 table->field[i++]->store(priv, priv_length, cs);
9672 table->field[i]->store(is_grantable, strlen(is_grantable), cs);
9673 return schema_table_store_record(thd, table);
9674 }
9675 #endif
9676
9677
fill_schema_user_privileges(THD * thd,TABLE_LIST * tables,Item * cond)9678 int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
9679 {
9680 #ifndef NO_EMBEDDED_ACCESS_CHECKS
9681 int error= 0;
9682 uint counter;
9683 ACL_USER *acl_user;
9684 ulong want_access;
9685 char buff[100];
9686 TABLE *table= tables->table;
9687 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
9688 NULL, NULL, 1, 1);
9689 char *curr_host= thd->security_ctx->priv_host_name();
9690 DBUG_ENTER("fill_schema_user_privileges");
9691
9692 if (!initialized)
9693 DBUG_RETURN(0);
9694 mysql_mutex_lock(&acl_cache->lock);
9695
9696 for (counter=0 ; counter < acl_users.elements ; counter++)
9697 {
9698 const char *user,*host, *is_grantable="YES";
9699 acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
9700 if (!(user=acl_user->user))
9701 user= "";
9702 if (!(host=acl_user->host.get_host()))
9703 host= "";
9704
9705 if (no_global_access &&
9706 (strcmp(thd->security_ctx->priv_user, user) ||
9707 my_strcasecmp(system_charset_info, curr_host, host)))
9708 continue;
9709
9710 if (acl_is_utility_user(user, host, NULL))
9711 continue;
9712
9713 want_access= acl_user->access;
9714 if (!(want_access & GRANT_ACL))
9715 is_grantable= "NO";
9716
9717 strxmov(buff,"'",user,"'@'",host,"'",NullS);
9718 if (!(want_access & ~GRANT_ACL))
9719 {
9720 if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
9721 STRING_WITH_LEN("USAGE"), is_grantable))
9722 {
9723 error= 1;
9724 goto err;
9725 }
9726 }
9727 else
9728 {
9729 uint priv_id;
9730 ulong j,test_access= want_access & ~GRANT_ACL;
9731 for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
9732 {
9733 if (test_access & j)
9734 {
9735 if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
9736 command_array[priv_id],
9737 command_lengths[priv_id], is_grantable))
9738 {
9739 error= 1;
9740 goto err;
9741 }
9742 }
9743 }
9744 }
9745 }
9746 err:
9747 mysql_mutex_unlock(&acl_cache->lock);
9748
9749 DBUG_RETURN(error);
9750 #else
9751 return(0);
9752 #endif
9753 }
9754
9755
fill_schema_schema_privileges(THD * thd,TABLE_LIST * tables,Item * cond)9756 int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
9757 {
9758 #ifndef NO_EMBEDDED_ACCESS_CHECKS
9759 int error= 0;
9760 uint counter;
9761 ACL_DB *acl_db;
9762 ulong want_access;
9763 char buff[100];
9764 TABLE *table= tables->table;
9765 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
9766 NULL, NULL, 1, 1);
9767 char *curr_host= thd->security_ctx->priv_host_name();
9768 DBUG_ENTER("fill_schema_schema_privileges");
9769
9770 if (!initialized)
9771 DBUG_RETURN(0);
9772 mysql_mutex_lock(&acl_cache->lock);
9773
9774 for (counter=0 ; counter < acl_dbs.elements ; counter++)
9775 {
9776 const char *user, *host, *is_grantable="YES";
9777
9778 acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
9779 if (!(user=acl_db->user))
9780 user= "";
9781 if (!(host=acl_db->host.get_host()))
9782 host= "";
9783
9784 if (no_global_access &&
9785 (strcmp(thd->security_ctx->priv_user, user) ||
9786 my_strcasecmp(system_charset_info, curr_host, host)))
9787 continue;
9788
9789 want_access=acl_db->access;
9790 if (want_access)
9791 {
9792 if (!(want_access & GRANT_ACL))
9793 {
9794 is_grantable= "NO";
9795 }
9796 strxmov(buff,"'",user,"'@'",host,"'",NullS);
9797 if (!(want_access & ~GRANT_ACL))
9798 {
9799 if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0,
9800 0, STRING_WITH_LEN("USAGE"), is_grantable))
9801 {
9802 error= 1;
9803 goto err;
9804 }
9805 }
9806 else
9807 {
9808 int cnt;
9809 ulong j,test_access= want_access & ~GRANT_ACL;
9810 for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
9811 if (test_access & j)
9812 {
9813 if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0, 0,
9814 command_array[cnt], command_lengths[cnt],
9815 is_grantable))
9816 {
9817 error= 1;
9818 goto err;
9819 }
9820 }
9821 }
9822 }
9823 }
9824 err:
9825 mysql_mutex_unlock(&acl_cache->lock);
9826
9827 DBUG_RETURN(error);
9828 #else
9829 return (0);
9830 #endif
9831 }
9832
9833
fill_schema_table_privileges(THD * thd,TABLE_LIST * tables,Item * cond)9834 int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
9835 {
9836 #ifndef NO_EMBEDDED_ACCESS_CHECKS
9837 int error= 0;
9838 uint index;
9839 char buff[100];
9840 TABLE *table= tables->table;
9841 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
9842 NULL, NULL, 1, 1);
9843 char *curr_host= thd->security_ctx->priv_host_name();
9844 DBUG_ENTER("fill_schema_table_privileges");
9845
9846 mysql_rwlock_rdlock(&LOCK_grant);
9847
9848 for (index=0 ; index < column_priv_hash.records ; index++)
9849 {
9850 const char *user, *host, *is_grantable= "YES";
9851 GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
9852 index);
9853 if (!(user=grant_table->user))
9854 user= "";
9855 if (!(host= grant_table->host.get_host()))
9856 host= "";
9857
9858 if (no_global_access &&
9859 (strcmp(thd->security_ctx->priv_user, user) ||
9860 my_strcasecmp(system_charset_info, curr_host, host)))
9861 continue;
9862
9863 ulong table_access= grant_table->privs;
9864 if (table_access)
9865 {
9866 ulong test_access= table_access & ~GRANT_ACL;
9867 /*
9868 We should skip 'usage' privilege on table if
9869 we have any privileges on column(s) of this table
9870 */
9871 if (!test_access && grant_table->cols)
9872 continue;
9873 if (!(table_access & GRANT_ACL))
9874 is_grantable= "NO";
9875
9876 strxmov(buff, "'", user, "'@'", host, "'", NullS);
9877 if (!test_access)
9878 {
9879 if (update_schema_privilege(thd, table, buff, grant_table->db,
9880 grant_table->tname, 0, 0,
9881 STRING_WITH_LEN("USAGE"), is_grantable))
9882 {
9883 error= 1;
9884 goto err;
9885 }
9886 }
9887 else
9888 {
9889 ulong j;
9890 int cnt;
9891 for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
9892 {
9893 if (test_access & j)
9894 {
9895 if (update_schema_privilege(thd, table, buff, grant_table->db,
9896 grant_table->tname, 0, 0,
9897 command_array[cnt],
9898 command_lengths[cnt], is_grantable))
9899 {
9900 error= 1;
9901 goto err;
9902 }
9903 }
9904 }
9905 }
9906 }
9907 }
9908 err:
9909 mysql_rwlock_unlock(&LOCK_grant);
9910
9911 DBUG_RETURN(error);
9912 #else
9913 return (0);
9914 #endif
9915 }
9916
9917
fill_schema_column_privileges(THD * thd,TABLE_LIST * tables,Item * cond)9918 int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, Item *cond)
9919 {
9920 #ifndef NO_EMBEDDED_ACCESS_CHECKS
9921 int error= 0;
9922 uint index;
9923 char buff[100];
9924 TABLE *table= tables->table;
9925 bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
9926 NULL, NULL, 1, 1);
9927 char *curr_host= thd->security_ctx->priv_host_name();
9928 DBUG_ENTER("fill_schema_table_privileges");
9929
9930 mysql_rwlock_rdlock(&LOCK_grant);
9931
9932 for (index=0 ; index < column_priv_hash.records ; index++)
9933 {
9934 const char *user, *host, *is_grantable= "YES";
9935 GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
9936 index);
9937 if (!(user=grant_table->user))
9938 user= "";
9939 if (!(host= grant_table->host.get_host()))
9940 host= "";
9941
9942 if (no_global_access &&
9943 (strcmp(thd->security_ctx->priv_user, user) ||
9944 my_strcasecmp(system_charset_info, curr_host, host)))
9945 continue;
9946
9947 ulong table_access= grant_table->cols;
9948 if (table_access != 0)
9949 {
9950 if (!(grant_table->privs & GRANT_ACL))
9951 is_grantable= "NO";
9952
9953 ulong test_access= table_access & ~GRANT_ACL;
9954 strxmov(buff, "'", user, "'@'", host, "'", NullS);
9955 if (!test_access)
9956 continue;
9957 else
9958 {
9959 ulong j;
9960 int cnt;
9961 for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
9962 {
9963 if (test_access & j)
9964 {
9965 for (uint col_index=0 ;
9966 col_index < grant_table->hash_columns.records ;
9967 col_index++)
9968 {
9969 GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
9970 my_hash_element(&grant_table->hash_columns,col_index);
9971 if ((grant_column->rights & j) && (table_access & j))
9972 {
9973 if (update_schema_privilege(thd, table, buff, grant_table->db,
9974 grant_table->tname,
9975 grant_column->column,
9976 grant_column->key_length,
9977 command_array[cnt],
9978 command_lengths[cnt], is_grantable))
9979 {
9980 error= 1;
9981 goto err;
9982 }
9983 }
9984 }
9985 }
9986 }
9987 }
9988 }
9989 }
9990 err:
9991 mysql_rwlock_unlock(&LOCK_grant);
9992
9993 DBUG_RETURN(error);
9994 #else
9995 return (0);
9996 #endif
9997 }
9998
9999
10000 #ifndef NO_EMBEDDED_ACCESS_CHECKS
10001 /*
10002 fill effective privileges for table
10003
10004 SYNOPSIS
10005 fill_effective_table_privileges()
10006 thd thread handler
10007 grant grants table descriptor
10008 db db name
10009 table table name
10010 */
10011
fill_effective_table_privileges(THD * thd,GRANT_INFO * grant,const char * db,const char * table)10012 void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
10013 const char *db, const char *table)
10014 {
10015 Security_context *sctx= thd->security_ctx;
10016 DBUG_ENTER("fill_effective_table_privileges");
10017 DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
10018 sctx->priv_host, (sctx->get_ip()->length() ?
10019 sctx->get_ip()->ptr() : "(NULL)"),
10020 sctx->priv_user,
10021 db, table));
10022 /* --skip-grants */
10023 if (!initialized)
10024 {
10025 DBUG_PRINT("info", ("skip grants"));
10026 grant->privilege= ~NO_ACCESS; // everything is allowed
10027 DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
10028 DBUG_VOID_RETURN;
10029 }
10030
10031 /* global privileges */
10032 grant->privilege= sctx->master_access;
10033
10034 /* db privileges */
10035 grant->privilege|= acl_get(sctx->get_host()->ptr(), sctx->get_ip()->ptr(),
10036 sctx->priv_user, db, 0);
10037
10038 /* table privileges */
10039 mysql_rwlock_rdlock(&LOCK_grant);
10040 if (grant->version != grant_version)
10041 {
10042 grant->grant_table=
10043 table_hash_search(sctx->get_host()->ptr(), sctx->get_ip()->ptr(), db,
10044 sctx->priv_user,
10045 table, 0); /* purecov: inspected */
10046 grant->version= grant_version; /* purecov: inspected */
10047 }
10048 if (grant->grant_table != 0)
10049 {
10050 grant->privilege|= grant->grant_table->privs;
10051 }
10052 mysql_rwlock_unlock(&LOCK_grant);
10053
10054 DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
10055 DBUG_VOID_RETURN;
10056 }
10057
10058 #else /* NO_EMBEDDED_ACCESS_CHECKS */
10059
10060 /****************************************************************************
10061 Dummy wrappers when we don't have any access checks
10062 ****************************************************************************/
10063
check_routine_level_acl(THD * thd,const char * db,const char * name,bool is_proc)10064 bool check_routine_level_acl(THD *thd, const char *db, const char *name,
10065 bool is_proc)
10066 {
10067 return FALSE;
10068 }
10069
10070 #endif
10071
10072 struct ACL_internal_schema_registry_entry
10073 {
10074 const LEX_STRING *m_name;
10075 const ACL_internal_schema_access *m_access;
10076 };
10077
10078 /**
10079 Internal schema registered.
10080 Currently, this is only:
10081 - performance_schema
10082 - information_schema,
10083 This can be reused later for:
10084 - mysql
10085 */
10086 static ACL_internal_schema_registry_entry registry_array[2];
10087 static uint m_registry_array_size= 0;
10088
10089 /**
10090 Add an internal schema to the registry.
10091 @param name the schema name
10092 @param access the schema ACL specific rules
10093 */
register_schema(const LEX_STRING * name,const ACL_internal_schema_access * access)10094 void ACL_internal_schema_registry::register_schema
10095 (const LEX_STRING *name, const ACL_internal_schema_access *access)
10096 {
10097 DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
10098
10099 /* Not thread safe, and does not need to be. */
10100 registry_array[m_registry_array_size].m_name= name;
10101 registry_array[m_registry_array_size].m_access= access;
10102 m_registry_array_size++;
10103 }
10104
10105 /**
10106 Search per internal schema ACL by name.
10107 @param name a schema name
10108 @return per schema rules, or NULL
10109 */
10110 const ACL_internal_schema_access *
lookup(const char * name)10111 ACL_internal_schema_registry::lookup(const char *name)
10112 {
10113 DBUG_ASSERT(name != NULL);
10114
10115 uint i;
10116
10117 for (i= 0; i<m_registry_array_size; i++)
10118 {
10119 if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
10120 name) == 0)
10121 return registry_array[i].m_access;
10122 }
10123 return NULL;
10124 }
10125
10126 /**
10127 Get a cached internal schema access.
10128 @param grant_internal_info the cache
10129 @param schema_name the name of the internal schema
10130 */
10131 const ACL_internal_schema_access *
get_cached_schema_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name)10132 get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
10133 const char *schema_name)
10134 {
10135 if (grant_internal_info)
10136 {
10137 if (! grant_internal_info->m_schema_lookup_done)
10138 {
10139 grant_internal_info->m_schema_access=
10140 ACL_internal_schema_registry::lookup(schema_name);
10141 grant_internal_info->m_schema_lookup_done= TRUE;
10142 }
10143 return grant_internal_info->m_schema_access;
10144 }
10145 return ACL_internal_schema_registry::lookup(schema_name);
10146 }
10147
10148 /**
10149 Get a cached internal table access.
10150 @param grant_internal_info the cache
10151 @param schema_name the name of the internal schema
10152 @param table_name the name of the internal table
10153 */
10154 const ACL_internal_table_access *
get_cached_table_access(GRANT_INTERNAL_INFO * grant_internal_info,const char * schema_name,const char * table_name)10155 get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
10156 const char *schema_name,
10157 const char *table_name)
10158 {
10159 DBUG_ASSERT(grant_internal_info);
10160 if (! grant_internal_info->m_table_lookup_done)
10161 {
10162 const ACL_internal_schema_access *schema_access;
10163 schema_access= get_cached_schema_access(grant_internal_info, schema_name);
10164 if (schema_access)
10165 grant_internal_info->m_table_access= schema_access->lookup(table_name);
10166 grant_internal_info->m_table_lookup_done= TRUE;
10167 }
10168 return grant_internal_info->m_table_access;
10169 }
10170
10171
10172 /****************************************************************************
10173 AUTHENTICATION CODE
10174 including initial connect handshake, invoking appropriate plugins,
10175 client-server plugin negotiation, COM_CHANGE_USER, and native
10176 MySQL authentication plugins.
10177 ****************************************************************************/
10178
10179 /* few defines to have less ifdef's in the code below */
10180 #ifdef EMBEDDED_LIBRARY
10181 #undef HAVE_OPENSSL
10182 #ifdef NO_EMBEDDED_ACCESS_CHECKS
10183 #define initialized 0
10184 #endif
10185 #endif
10186 #ifndef HAVE_OPENSSL
10187 #define ssl_acceptor_fd 0
10188 #define sslaccept(A,B,C) 1
10189 #endif
10190
10191
10192 class Thd_charset_adapter
10193 {
10194 THD *thd;
10195 public:
Thd_charset_adapter(THD * thd_arg)10196 Thd_charset_adapter(THD *thd_arg) : thd (thd_arg) {}
init_client_charset(uint cs_number)10197 bool init_client_charset(uint cs_number)
10198 {
10199 if (thd_init_client_charset(thd, cs_number))
10200 return true;
10201 thd->update_charset();
10202 return thd->is_error();
10203 }
10204
charset()10205 const CHARSET_INFO *charset() { return thd->charset(); }
10206 };
10207
10208
10209 /**
10210 The internal version of what plugins know as MYSQL_PLUGIN_VIO,
10211 basically the context of the authentication session
10212 */
10213 struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
10214 {
10215 MYSQL_SERVER_AUTH_INFO auth_info;
10216 const ACL_USER *acl_user;
10217 plugin_ref plugin; ///< what plugin we're under
10218 LEX_STRING db; ///< db name from the handshake packet
10219 /** when restarting a plugin this caches the last client reply */
10220 struct {
10221 char *plugin, *pkt; ///< pointers into NET::buff
10222 uint pkt_len;
10223 } cached_client_reply;
10224 /** this caches the first plugin packet for restart request on the client */
10225 struct {
10226 char *pkt;
10227 uint pkt_len;
10228 } cached_server_packet;
10229 int packets_read, packets_written; ///< counters for send/received packets
10230 /** when plugin returns a failure this tells us what really happened */
10231 enum { SUCCESS, FAILURE, RESTART } status;
10232
10233 /* encapsulation members */
10234 ulong client_capabilities;
10235 char *scramble;
10236 MEM_ROOT *mem_root;
10237 struct rand_struct *rand;
10238 my_thread_id thread_id;
10239 uint *server_status;
10240 NET *net;
10241 ulong max_client_packet_length;
10242 char *ip;
10243 char *host;
10244 Thd_charset_adapter *charset_adapter;
10245 LEX_STRING acl_user_plugin;
10246 int vio_is_encrypted;
can_authenticateMPVIO_EXT10247 bool can_authenticate()
10248 {
10249 return (acl_user && acl_user->can_authenticate);
10250 }
10251 };
10252
auth_plugin_is_built_in(const char * plugin_name)10253 bool auth_plugin_is_built_in(const char *plugin_name)
10254 {
10255 return (plugin_name == native_password_plugin_name.str ||
10256 #if defined(HAVE_OPENSSL)
10257 plugin_name == sha256_password_plugin_name.str ||
10258 #endif
10259 plugin_name == old_password_plugin_name.str);
10260 }
10261
optimize_plugin_compare_by_pointer(LEX_STRING * plugin_name)10262 void optimize_plugin_compare_by_pointer(LEX_STRING *plugin_name)
10263 {
10264 #if defined(HAVE_OPENSSL)
10265 if (my_strcasecmp(system_charset_info, sha256_password_plugin_name.str,
10266 plugin_name->str) == 0)
10267 {
10268 plugin_name->str= sha256_password_plugin_name.str;
10269 plugin_name->length= sha256_password_plugin_name.length;
10270 }
10271 else
10272 #endif
10273 if (my_strcasecmp(system_charset_info, native_password_plugin_name.str,
10274 plugin_name->str) == 0)
10275 {
10276 plugin_name->str= native_password_plugin_name.str;
10277 plugin_name->length= native_password_plugin_name.length;
10278 }
10279 else
10280 if (my_strcasecmp(system_charset_info, old_password_plugin_name.str,
10281 plugin_name->str) == 0)
10282 {
10283 plugin_name->str= old_password_plugin_name.str;
10284 plugin_name->length= old_password_plugin_name.length;
10285 }
10286 }
10287
10288 /**
10289 Sets the default default auth plugin value if no option was specified.
10290 */
init_default_auth_plugin()10291 void init_default_auth_plugin()
10292 {
10293 default_auth_plugin_name.str= native_password_plugin_name.str;
10294 default_auth_plugin_name.length= native_password_plugin_name.length;
10295 }
10296
10297
10298 /**
10299 Initialize default authentication plugin based on command line options or
10300 configuration file settings.
10301
10302 @param plugin_name Name of the plugin
10303 @param plugin_name_length Length of the string
10304
10305 Setting default_auth_plugin may also affect old_passwords
10306
10307 */
10308
set_default_auth_plugin(char * plugin_name,int plugin_name_length)10309 int set_default_auth_plugin(char *plugin_name, int plugin_name_length)
10310 {
10311 default_auth_plugin_name.str= plugin_name;
10312 default_auth_plugin_name.length= plugin_name_length;
10313
10314 optimize_plugin_compare_by_pointer(&default_auth_plugin_name);
10315
10316 #if defined(HAVE_OPENSSL)
10317 if (default_auth_plugin_name.str == sha256_password_plugin_name.str)
10318 {
10319 /*
10320 Adjust default password algorithm to fit the default authentication
10321 method.
10322 */
10323 global_system_variables.old_passwords= 2;
10324 }
10325 else
10326 #endif
10327 if (default_auth_plugin_name.str != native_password_plugin_name.str)
10328 return 1;
10329
10330 return 0;
10331 }
10332
10333 /**
10334 a helper function to report an access denied error in all the proper places
10335 */
login_failed_error(MPVIO_EXT * mpvio,int passwd_used)10336 static void login_failed_error(MPVIO_EXT *mpvio, int passwd_used)
10337 {
10338 THD *thd= current_thd;
10339 if (passwd_used == 2)
10340 {
10341 my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
10342 mpvio->auth_info.user_name,
10343 mpvio->auth_info.host_or_ip);
10344 general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
10345 mpvio->auth_info.user_name,
10346 mpvio->auth_info.host_or_ip);
10347 /*
10348 Log access denied messages to the error log when log-warnings = 2
10349 so that the overhead of the general query log is not required to track
10350 failed connections.
10351 */
10352 if (log_warnings > 1)
10353 {
10354 sql_print_warning(ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
10355 mpvio->auth_info.user_name,
10356 mpvio->auth_info.host_or_ip);
10357 }
10358 }
10359 else
10360 {
10361 my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
10362 mpvio->auth_info.user_name,
10363 mpvio->auth_info.host_or_ip,
10364 passwd_used ? ER(ER_YES) : ER(ER_NO));
10365 general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
10366 mpvio->auth_info.user_name,
10367 mpvio->auth_info.host_or_ip,
10368 passwd_used ? ER(ER_YES) : ER(ER_NO));
10369 /*
10370 Log access denied messages to the error log when log-warnings = 2
10371 so that the overhead of the general query log is not required to track
10372 failed connections.
10373 */
10374 if (log_warnings > 1)
10375 {
10376 sql_print_warning(ER(ER_ACCESS_DENIED_ERROR),
10377 mpvio->auth_info.user_name,
10378 mpvio->auth_info.host_or_ip,
10379 passwd_used ? ER(ER_YES) : ER(ER_NO));
10380 }
10381 }
10382 }
10383
10384 /**
10385 sends a server handshake initialization packet, the very first packet
10386 after the connection was established
10387
10388 Packet format:
10389
10390 Bytes Content
10391 ----- ----
10392 1 protocol version (always 10)
10393 n server version string, \0-terminated
10394 4 thread id
10395 8 first 8 bytes of the plugin provided data (scramble)
10396 1 \0 byte, terminating the first part of a scramble
10397 2 server capabilities (two lower bytes)
10398 1 server character set
10399 2 server status
10400 2 server capabilities (two upper bytes)
10401 1 length of the scramble
10402 10 reserved, always 0
10403 n rest of the plugin provided data (at least 12 bytes)
10404 1 \0 byte, terminating the second part of a scramble
10405
10406 @retval 0 ok
10407 @retval 1 error
10408 */
send_server_handshake_packet(MPVIO_EXT * mpvio,const char * data,uint data_len)10409 static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
10410 const char *data, uint data_len)
10411 {
10412 DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
10413 DBUG_ASSERT(data_len <= 255);
10414
10415 char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + data_len + 64);
10416 char scramble_buf[SCRAMBLE_LENGTH];
10417 char *end= buff;
10418
10419 DBUG_ENTER("send_server_handshake_packet");
10420 *end++= protocol_version;
10421
10422 mpvio->client_capabilities= CLIENT_BASIC_FLAGS;
10423
10424 if (opt_using_transactions)
10425 mpvio->client_capabilities|= CLIENT_TRANSACTIONS;
10426
10427 mpvio->client_capabilities|= CAN_CLIENT_COMPRESS;
10428
10429 if (ssl_acceptor_fd)
10430 {
10431 mpvio->client_capabilities|= CLIENT_SSL;
10432 mpvio->client_capabilities|= CLIENT_SSL_VERIFY_SERVER_CERT;
10433 }
10434
10435 if (data_len)
10436 {
10437 mpvio->cached_server_packet.pkt= (char*) memdup_root(mpvio->mem_root,
10438 data, data_len);
10439 mpvio->cached_server_packet.pkt_len= data_len;
10440 }
10441
10442 if (data_len < SCRAMBLE_LENGTH)
10443 {
10444 if (data_len)
10445 {
10446 /*
10447 the first packet *must* have at least 20 bytes of a scramble.
10448 if a plugin provided less, we pad it to 20 with zeros
10449 */
10450 memcpy(scramble_buf, data, data_len);
10451 memset(scramble_buf + data_len, 0, SCRAMBLE_LENGTH - data_len);
10452 data= scramble_buf;
10453 }
10454 else
10455 {
10456 /*
10457 if the default plugin does not provide the data for the scramble at
10458 all, we generate a scramble internally anyway, just in case the
10459 user account (that will be known only later) uses a
10460 native_password_plugin (which needs a scramble). If we don't send a
10461 scramble now - wasting 20 bytes in the packet -
10462 native_password_plugin will have to send it in a separate packet,
10463 adding one more round trip.
10464 */
10465 create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
10466 data= mpvio->scramble;
10467 }
10468 data_len= SCRAMBLE_LENGTH;
10469 }
10470
10471 end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
10472 int4store((uchar*) end, mpvio->thread_id);
10473 end+= 4;
10474
10475 /*
10476 Old clients does not understand long scrambles, but can ignore packet
10477 tail: that's why first part of the scramble is placed here, and second
10478 part at the end of packet.
10479 */
10480 end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
10481 end+= SCRAMBLE_LENGTH_323;
10482 *end++= 0;
10483
10484 int2store(end, mpvio->client_capabilities);
10485 /* write server characteristics: up to 16 bytes allowed */
10486 end[2]= (char) default_charset_info->number;
10487 int2store(end + 3, mpvio->server_status[0]);
10488 int2store(end + 5, mpvio->client_capabilities >> 16);
10489 end[7]= data_len;
10490 DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
10491 DBUG_EXECUTE_IF("increase_srv_handshake_scramble_len", end[7]= 50;);
10492 memset(end + 8, 0, 10);
10493 end+= 18;
10494 /* write scramble tail */
10495 end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
10496 data_len - SCRAMBLE_LENGTH_323);
10497 end+= data_len - SCRAMBLE_LENGTH_323;
10498 end= strmake(end, plugin_name(mpvio->plugin)->str,
10499 plugin_name(mpvio->plugin)->length);
10500
10501 int res= my_net_write(mpvio->net, (uchar*) buff, (size_t) (end - buff + 1)) ||
10502 net_flush(mpvio->net);
10503 my_afree(buff);
10504 DBUG_RETURN (res);
10505 }
10506
secure_auth(MPVIO_EXT * mpvio)10507 static bool secure_auth(MPVIO_EXT *mpvio)
10508 {
10509 THD *thd;
10510 if (!opt_secure_auth)
10511 return 0;
10512 /*
10513 If the server is running in secure auth mode, short scrambles are
10514 forbidden. Extra juggling to report the same error as the old code.
10515 */
10516
10517 thd= current_thd;
10518 if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
10519 {
10520 my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
10521 mpvio->auth_info.user_name,
10522 mpvio->auth_info.host_or_ip);
10523 general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
10524 mpvio->auth_info.user_name,
10525 mpvio->auth_info.host_or_ip);
10526 }
10527 else
10528 {
10529 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
10530 general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
10531 }
10532 return 1;
10533 }
10534
10535 /**
10536 sends a "change plugin" packet, requesting a client to restart authentication
10537 using a different authentication plugin
10538
10539 Packet format:
10540
10541 Bytes Content
10542 ----- ----
10543 1 byte with the value 254
10544 n client plugin to use, \0-terminated
10545 n plugin provided data
10546
10547 In a special case of switching from native_password_plugin to
10548 old_password_plugin, the packet contains only one - the first - byte,
10549 plugin name is omitted, plugin data aren't needed as the scramble was
10550 already sent. This one-byte packet is identical to the "use the short
10551 scramble" packet in the protocol before plugins were introduced.
10552
10553 @retval 0 ok
10554 @retval 1 error
10555 */
send_plugin_request_packet(MPVIO_EXT * mpvio,const uchar * data,uint data_len)10556 static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
10557 const uchar *data, uint data_len)
10558 {
10559 DBUG_ASSERT(mpvio->packets_written == 1);
10560 DBUG_ASSERT(mpvio->packets_read == 1);
10561 NET *net= mpvio->net;
10562 static uchar switch_plugin_request_buf[]= { 254 };
10563
10564 DBUG_ENTER("send_plugin_request_packet");
10565 mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
10566
10567 std::string client_auth_plugin(
10568 ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))
10569 ->client_auth_plugin);
10570
10571 DBUG_ASSERT(client_auth_plugin.c_str());
10572
10573 DBUG_EXECUTE_IF("invalidate_client_auth_plugin", {
10574 client_auth_plugin.clear();
10575 client_auth_plugin = std::string("..") + std::string(FN_DIRSEP) +
10576 std::string("..") + std::string(FN_DIRSEP) +
10577 std::string("mysql_native_password");
10578 });
10579
10580 /*
10581 we send an old "short 4.0 scramble request", if we need to request a
10582 client to use 4.0 auth plugin (short scramble) and the scramble was
10583 already sent to the client
10584
10585 below, cached_client_reply.plugin is the plugin name that client has used,
10586 client_auth_plugin is derived from mysql.user table, for the given
10587 user account, it's the plugin that the client need to use to login.
10588 */
10589 bool switch_from_long_to_short_scramble=
10590 native_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
10591 client_auth_plugin.c_str() == old_password_plugin_name.str;
10592
10593 if (switch_from_long_to_short_scramble)
10594 DBUG_RETURN (secure_auth(mpvio) ||
10595 my_net_write(net, switch_plugin_request_buf, 1) ||
10596 net_flush(net));
10597
10598 /*
10599 We never request a client to switch from a short to long scramble.
10600 Plugin-aware clients can do that, but traditionally it meant to
10601 ask an old 4.0 client to use the new 4.1 authentication protocol.
10602 */
10603 bool switch_from_short_to_long_scramble=
10604 old_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
10605 client_auth_plugin.c_str() == native_password_plugin_name.str;
10606
10607 if (switch_from_short_to_long_scramble)
10608 {
10609 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
10610 general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
10611 DBUG_RETURN (1);
10612 }
10613
10614 /*
10615 If we're dealing with an older client we can't just send a change plugin
10616 packet to re-initiate the authentication handshake, because the client
10617 won't understand it. The good thing is that we don't need to : the old client
10618 expects us to just check the user credentials here, which we can do by just reading
10619 the cached data that are placed there by parse_com_change_user_packet()
10620 In this case we just do nothing and behave as if normal authentication
10621 should continue.
10622 */
10623 if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
10624 {
10625 DBUG_PRINT("info", ("old client sent a COM_CHANGE_USER"));
10626 DBUG_ASSERT(mpvio->cached_client_reply.pkt);
10627 /* get the status back so the read can process the cached result */
10628 mpvio->status= MPVIO_EXT::RESTART;
10629 DBUG_RETURN(0);
10630 }
10631
10632 DBUG_PRINT("info", ("requesting client to use the %s plugin",
10633 client_auth_plugin.c_str()));
10634 DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
10635 (uchar*) client_auth_plugin.c_str(),
10636 client_auth_plugin.size() + 1,
10637 (uchar*) data, data_len));
10638 }
10639
10640 #ifndef NO_EMBEDDED_ACCESS_CHECKS
10641
10642 /**
10643 When authentication is attempted using an unknown username a dummy user
10644 account with no authentication capabilites is assigned to the connection.
10645 This is done increase the cost of enumerating user accounts based on
10646 authentication protocol.
10647 */
10648
decoy_user(const LEX_STRING & username,MEM_ROOT * mem)10649 ACL_USER *decoy_user(const LEX_STRING &username,
10650 MEM_ROOT *mem)
10651 {
10652 ACL_USER *user= (ACL_USER *) alloc_root(mem, sizeof(ACL_USER));
10653 user->can_authenticate= false;
10654 user->user= strmake_root(mem, username.str, username.length);
10655 user->auth_string= empty_lex_str;
10656 user->ssl_cipher= empty_c_string;
10657 user->x509_issuer= empty_c_string;
10658 user->x509_subject= empty_c_string;
10659 user->salt_len= 0;
10660
10661 /*
10662 For now the common default account is used. Improvements might involve
10663 mapping a consistent hash of a username to a range of plugins.
10664 */
10665 user->plugin= default_auth_plugin_name;
10666 return user;
10667 }
10668
10669 /**
10670 Finds acl entry in user database for authentication purposes.
10671
10672 Finds a user and copies it into mpvio. Reports an authentication
10673 failure if a user is not found.
10674
10675 @note find_acl_user is not the same, because it doesn't take into
10676 account the case when user is not empty, but acl_user->user is empty
10677
10678 @retval 0 found
10679 @retval 1 not found
10680 */
find_mpvio_user(MPVIO_EXT * mpvio)10681 static bool find_mpvio_user(MPVIO_EXT *mpvio)
10682 {
10683 DBUG_ENTER("find_mpvio_user");
10684 DBUG_PRINT("info", ("entry: %s", mpvio->auth_info.user_name));
10685 DBUG_ASSERT(mpvio->acl_user == 0);
10686 mysql_mutex_lock(&acl_cache->lock);
10687 for (uint i=0; i < acl_users.elements; i++)
10688 {
10689 ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
10690 if ((!acl_user_tmp->user ||
10691 !strcmp(mpvio->auth_info.user_name, acl_user_tmp->user)) &&
10692 acl_user_tmp->host.compare_hostname(mpvio->host, mpvio->ip))
10693 {
10694 mpvio->acl_user= acl_user_tmp->copy(mpvio->mem_root);
10695
10696 /*
10697 When setting mpvio->acl_user_plugin we can save memory allocation if
10698 this is a built in plugin.
10699 */
10700 if (auth_plugin_is_built_in(acl_user_tmp->plugin.str))
10701 mpvio->acl_user_plugin= mpvio->acl_user->plugin;
10702 else
10703 make_lex_string_root(mpvio->mem_root,
10704 &mpvio->acl_user_plugin,
10705 acl_user_tmp->plugin.str,
10706 acl_user_tmp->plugin.length, 0);
10707 break;
10708 }
10709 }
10710 mysql_mutex_unlock(&acl_cache->lock);
10711
10712 if (!mpvio->acl_user)
10713 {
10714 /*
10715 Pretend the user exists; let the plugin decide how to handle
10716 bad credentials.
10717 */
10718 LEX_STRING usr= { mpvio->auth_info.user_name,
10719 mpvio->auth_info.user_name_length };
10720 mpvio->acl_user= decoy_user(usr, mpvio->mem_root);
10721 mpvio->acl_user_plugin= mpvio->acl_user->plugin;
10722 }
10723
10724 if (my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
10725 native_password_plugin_name.str) != 0 &&
10726 my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
10727 old_password_plugin_name.str) != 0 &&
10728 !(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
10729 {
10730 /* user account requires non-default plugin and the client is too old */
10731 DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
10732 native_password_plugin_name.str));
10733 DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
10734 old_password_plugin_name.str));
10735 my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
10736 general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
10737 DBUG_RETURN (1);
10738 }
10739
10740 mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
10741 mpvio->auth_info.auth_string_length=
10742 (unsigned long) mpvio->acl_user->auth_string.length;
10743 strmake(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
10744 mpvio->acl_user->user : "", USERNAME_LENGTH);
10745 DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
10746 ", plugin=%s",
10747 mpvio->auth_info.user_name,
10748 mpvio->auth_info.auth_string,
10749 mpvio->auth_info.authenticated_as,
10750 mpvio->acl_user->plugin.str));
10751 DBUG_RETURN(0);
10752 }
10753
10754
10755 static bool
read_client_connect_attrs(char ** ptr,size_t * max_bytes_available,const CHARSET_INFO * from_cs)10756 read_client_connect_attrs(char **ptr, size_t *max_bytes_available,
10757 const CHARSET_INFO *from_cs)
10758 {
10759 size_t length, length_length;
10760 char *ptr_save;
10761 /* not enough bytes to hold the length */
10762 if (*max_bytes_available < 1)
10763 return true;
10764
10765 /* read the length */
10766 ptr_save= *ptr;
10767 length= net_field_length_ll((uchar **) ptr);
10768 length_length= *ptr - ptr_save;
10769 if (*max_bytes_available < length_length)
10770 return true;
10771
10772 *max_bytes_available-= length_length;
10773
10774 /* length says there're more data than can fit into the packet */
10775 if (length > *max_bytes_available)
10776 return true;
10777
10778 /* impose an artificial length limit of 64k */
10779 if (length > 65535)
10780 return true;
10781
10782 #ifdef HAVE_PSI_THREAD_INTERFACE
10783 if (PSI_THREAD_CALL(set_thread_connect_attrs)(*ptr, length, from_cs) && log_warnings)
10784 sql_print_warning("Connection attributes of length %lu were truncated",
10785 (unsigned long) length);
10786 #endif
10787 return false;
10788 }
10789
10790
10791 #endif
10792
10793 /* the packet format is described in send_change_user_packet() */
parse_com_change_user_packet(MPVIO_EXT * mpvio,uint packet_length)10794 static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
10795 {
10796 NET *net= mpvio->net;
10797
10798 char *user= (char*) net->read_pos;
10799 char *end= user + packet_length;
10800 /* Safe because there is always a trailing \0 at the end of the packet */
10801 char *passwd= strend(user) + 1;
10802 uint user_len= passwd - user - 1;
10803 char *db= passwd;
10804 char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
10805 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
10806 uint dummy_errors;
10807
10808 DBUG_ENTER ("parse_com_change_user_packet");
10809 if (passwd >= end)
10810 {
10811 my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
10812 DBUG_RETURN (1);
10813 }
10814
10815 /*
10816 Old clients send null-terminated string as password; new clients send
10817 the size (1 byte) + string (not null-terminated). Hence in case of empty
10818 password both send '\0'.
10819
10820 This strlen() can't be easily deleted without changing protocol.
10821
10822 Cast *passwd to an unsigned char, so that it doesn't extend the sign for
10823 *passwd > 127 and become 2**32-127+ after casting to uint.
10824 */
10825 uint passwd_len= (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION ?
10826 (uchar) (*passwd++) : strlen(passwd));
10827
10828 db+= passwd_len + 1;
10829 /*
10830 Database name is always NUL-terminated, so in case of empty database
10831 the packet must contain at least the trailing '\0'.
10832 */
10833 if (db >= end)
10834 {
10835 my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
10836 DBUG_RETURN (1);
10837 }
10838
10839 uint db_len= strlen(db);
10840
10841 char *ptr= db + db_len + 1;
10842
10843 if (ptr + 1 < end)
10844 {
10845 if (mpvio->charset_adapter->init_client_charset(uint2korr(ptr)))
10846 DBUG_RETURN(1);
10847 }
10848
10849 /* Convert database and user names to utf8 */
10850 db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
10851 db, db_len, mpvio->charset_adapter->charset(),
10852 &dummy_errors);
10853 db_buff[db_len]= 0;
10854
10855 user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
10856 system_charset_info, user, user_len,
10857 mpvio->charset_adapter->charset(),
10858 &dummy_errors);
10859 user_buff[user_len]= 0;
10860
10861 /* we should not free mpvio->user here: it's saved by dispatch_command() */
10862 if (!(mpvio->auth_info.user_name= my_strndup(user_buff, user_len, MYF(MY_WME))))
10863 return 1;
10864 mpvio->auth_info.user_name_length= user_len;
10865
10866 if (make_lex_string_root(mpvio->mem_root,
10867 &mpvio->db, db_buff, db_len, 0) == 0)
10868 DBUG_RETURN(1); /* The error is set by make_lex_string(). */
10869
10870 if (!initialized)
10871 {
10872 // if mysqld's been started with --skip-grant-tables option
10873 strmake(mpvio->auth_info.authenticated_as,
10874 mpvio->auth_info.user_name, USERNAME_LENGTH);
10875
10876 mpvio->status= MPVIO_EXT::SUCCESS;
10877 DBUG_RETURN(0);
10878 }
10879
10880 #ifndef NO_EMBEDDED_ACCESS_CHECKS
10881 if (find_mpvio_user(mpvio))
10882 {
10883 DBUG_RETURN(1);
10884 }
10885
10886 char *client_plugin;
10887 if (mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)
10888 {
10889 client_plugin= ptr + 2;
10890 if (client_plugin >= end)
10891 {
10892 my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
10893 DBUG_RETURN(1);
10894 }
10895 }
10896 else
10897 {
10898 if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
10899 client_plugin= native_password_plugin_name.str;
10900 else
10901 {
10902 client_plugin= old_password_plugin_name.str;
10903 /*
10904 For a passwordless accounts we use native_password_plugin.
10905 But when an old 4.0 client connects to it, we change it to
10906 old_password_plugin, otherwise MySQL will think that server
10907 and client plugins don't match.
10908 */
10909 if (mpvio->acl_user->salt_len == 0)
10910 mpvio->acl_user_plugin= old_password_plugin_name;
10911 }
10912 }
10913
10914 size_t bytes_remaining_in_packet= end - ptr;
10915
10916 if ((mpvio->client_capabilities & CLIENT_CONNECT_ATTRS) &&
10917 read_client_connect_attrs(&ptr, &bytes_remaining_in_packet,
10918 mpvio->charset_adapter->charset()))
10919 return packet_error;
10920
10921 DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
10922 /*
10923 Remember the data part of the packet, to present it to plugin in
10924 read_packet()
10925 */
10926 mpvio->cached_client_reply.pkt= passwd;
10927 mpvio->cached_client_reply.pkt_len= passwd_len;
10928 mpvio->cached_client_reply.plugin= client_plugin;
10929 mpvio->status= MPVIO_EXT::RESTART;
10930 #endif
10931
10932 DBUG_RETURN (0);
10933 }
10934
10935 #ifndef EMBEDDED_LIBRARY
10936 /** Get a string according to the protocol of the underlying buffer. */
10937 typedef char * (*get_proto_string_func_t) (char **, size_t *, size_t *);
10938
10939 /**
10940 Get a string formatted according to the 4.1 version of the MySQL protocol.
10941
10942 @param buffer[in, out] Pointer to the user-supplied buffer to be scanned.
10943 @param max_bytes_available[in, out] Limit the bytes to scan.
10944 @param string_length[out] The number of characters scanned not including
10945 the null character.
10946
10947 @remark Strings are always null character terminated in this version of the
10948 protocol.
10949
10950 @remark The string_length does not include the terminating null character.
10951 However, after the call, the buffer is increased by string_length+1
10952 bytes, beyond the null character if there still available bytes to
10953 scan.
10954
10955 @return pointer to beginning of the string scanned.
10956 @retval NULL The buffer content is malformed
10957 */
10958
10959 static
get_41_protocol_string(char ** buffer,size_t * max_bytes_available,size_t * string_length)10960 char *get_41_protocol_string(char **buffer,
10961 size_t *max_bytes_available,
10962 size_t *string_length)
10963 {
10964 char *str= (char *)memchr(*buffer, '\0', *max_bytes_available);
10965
10966 if (str == NULL)
10967 return NULL;
10968
10969 *string_length= (size_t)(str - *buffer);
10970 *max_bytes_available-= *string_length + 1;
10971 str= *buffer;
10972 *buffer += *string_length + 1;
10973
10974 return str;
10975 }
10976
10977
10978 /**
10979 Get a string formatted according to the 4.0 version of the MySQL protocol.
10980
10981 @param buffer[in, out] Pointer to the user-supplied buffer to be scanned.
10982 @param max_bytes_available[in, out] Limit the bytes to scan.
10983 @param string_length[out] The number of characters scanned not including
10984 the null character.
10985
10986 @remark If there are not enough bytes left after the current position of
10987 the buffer to satisfy the current string, the string is considered
10988 to be empty and a pointer to empty_c_string is returned.
10989
10990 @remark A string at the end of the packet is not null terminated.
10991
10992 @return Pointer to beginning of the string scanned, or a pointer to a empty
10993 string.
10994 */
10995 static
get_40_protocol_string(char ** buffer,size_t * max_bytes_available,size_t * string_length)10996 char *get_40_protocol_string(char **buffer,
10997 size_t *max_bytes_available,
10998 size_t *string_length)
10999 {
11000 char *str;
11001 size_t len;
11002
11003 /* No bytes to scan left, treat string as empty. */
11004 if ((*max_bytes_available) == 0)
11005 {
11006 *string_length= 0;
11007 return empty_c_string;
11008 }
11009
11010 str= (char *) memchr(*buffer, '\0', *max_bytes_available);
11011
11012 /*
11013 If the string was not null terminated by the client,
11014 the remainder of the packet is the string. Otherwise,
11015 advance the buffer past the end of the null terminated
11016 string.
11017 */
11018 if (str == NULL)
11019 len= *string_length= *max_bytes_available;
11020 else
11021 len= (*string_length= (size_t)(str - *buffer)) + 1;
11022
11023 str= *buffer;
11024 *buffer+= len;
11025 *max_bytes_available-= len;
11026
11027 return str;
11028 }
11029
11030 /**
11031 Get a length encoded string from a user-supplied buffer.
11032
11033 @param buffer[in, out] The buffer to scan; updates position after scan.
11034 @param max_bytes_available[in, out] Limit the number of bytes to scan
11035 @param string_length[out] Number of characters scanned
11036
11037 @remark In case the length is zero, then the total size of the string is
11038 considered to be 1 byte; the size byte.
11039
11040 @return pointer to first byte after the header in buffer.
11041 @retval NULL The buffer content is malformed
11042 */
11043
11044 static
get_56_lenc_string(char ** buffer,size_t * max_bytes_available,size_t * string_length)11045 char *get_56_lenc_string(char **buffer,
11046 size_t *max_bytes_available,
11047 size_t *string_length)
11048 {
11049 static char empty_string[1]= { '\0' };
11050 char *begin= *buffer;
11051 uchar *pos= (uchar *)begin;
11052 size_t required_length= 9;
11053
11054
11055 if (*max_bytes_available == 0)
11056 return NULL;
11057
11058 /*
11059 If the length encoded string has the length 0
11060 the total size of the string is only one byte long (the size byte)
11061 */
11062 if (*begin == 0)
11063 {
11064 *string_length= 0;
11065 --*max_bytes_available;
11066 ++*buffer;
11067 /*
11068 Return a pointer to the \0 character so the return value will be
11069 an empty string.
11070 */
11071 return empty_string;
11072 }
11073
11074 /* Make sure we have enough bytes available for net_field_length_ll */
11075 {
11076 DBUG_EXECUTE_IF("buffer_too_short_3",
11077 *pos= 252; *max_bytes_available= 2;
11078 );
11079 DBUG_EXECUTE_IF("buffer_too_short_4",
11080 *pos= 253; *max_bytes_available= 3;
11081 );
11082 DBUG_EXECUTE_IF("buffer_too_short_9",
11083 *pos= 254; *max_bytes_available= 8;
11084 );
11085
11086 if (*pos <= 251)
11087 required_length= 1;
11088 if (*pos == 252)
11089 required_length= 3;
11090 if (*pos == 253)
11091 required_length= 4;
11092
11093 if (*max_bytes_available < required_length)
11094 return NULL;
11095 }
11096
11097 *string_length= (size_t)net_field_length_ll((uchar **)buffer);
11098
11099 DBUG_EXECUTE_IF("sha256_password_scramble_too_long",
11100 *string_length= SIZE_T_MAX;
11101 );
11102
11103 size_t len_len= (size_t)(*buffer - begin);
11104
11105 DBUG_ASSERT((*max_bytes_available >= len_len) &&
11106 (len_len == required_length));
11107
11108 if (*string_length > *max_bytes_available - len_len)
11109 return NULL;
11110
11111 *max_bytes_available -= *string_length;
11112 *max_bytes_available -= len_len;
11113 *buffer += *string_length;
11114 return (char *)(begin + len_len);
11115 }
11116
11117
11118 /**
11119 Get a length encoded string from a user-supplied buffer.
11120
11121 @param buffer[in, out] The buffer to scan; updates position after scan.
11122 @param max_bytes_available[in, out] Limit the number of bytes to scan
11123 @param string_length[out] Number of characters scanned
11124
11125 @remark In case the length is zero, then the total size of the string is
11126 considered to be 1 byte; the size byte.
11127
11128 @note the maximum size of the string is 255 because the header is always
11129 1 byte.
11130 @return pointer to first byte after the header in buffer.
11131 @retval NULL The buffer content is malformed
11132 */
11133
11134 static
get_41_lenc_string(char ** buffer,size_t * max_bytes_available,size_t * string_length)11135 char *get_41_lenc_string(char **buffer,
11136 size_t *max_bytes_available,
11137 size_t *string_length)
11138 {
11139 if (*max_bytes_available == 0)
11140 return NULL;
11141
11142 /* Do double cast to prevent overflow from signed / unsigned conversion */
11143 size_t str_len= (size_t)(unsigned char)**buffer;
11144
11145 /*
11146 If the length encoded string has the length 0
11147 the total size of the string is only one byte long (the size byte)
11148 */
11149 if (str_len == 0)
11150 {
11151 ++*buffer;
11152 *string_length= 0;
11153 /*
11154 Return a pointer to the 0 character so the return value will be
11155 an empty string.
11156 */
11157 return *buffer-1;
11158 }
11159
11160 if (str_len >= *max_bytes_available)
11161 return NULL;
11162
11163 char *str= *buffer+1;
11164 *string_length= str_len;
11165 *max_bytes_available-= *string_length + 1;
11166 *buffer+= *string_length + 1;
11167 return str;
11168 }
11169 #endif // EMBEDDED LIBRARY
11170
11171
11172 /* the packet format is described in send_client_reply_packet() */
parse_client_handshake_packet(MPVIO_EXT * mpvio,uchar ** buff,ulong pkt_len)11173 static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
11174 uchar **buff, ulong pkt_len)
11175 {
11176 #ifndef EMBEDDED_LIBRARY
11177 NET *net= mpvio->net;
11178 char *end;
11179 bool packet_has_required_size= false;
11180 DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
11181
11182 uint charset_code= 0;
11183 end= (char *)net->read_pos;
11184 /*
11185 In order to safely scan a head for '\0' string terminators
11186 we must keep track of how many bytes remain in the allocated
11187 buffer or we might read past the end of the buffer.
11188 */
11189 size_t bytes_remaining_in_packet= pkt_len;
11190
11191 /*
11192 Peek ahead on the client capability packet and determine which version of
11193 the protocol should be used.
11194 */
11195 if (bytes_remaining_in_packet < 2)
11196 return packet_error;
11197
11198 mpvio->client_capabilities= uint2korr(end);
11199
11200 /*
11201 JConnector only sends server capabilities before starting SSL
11202 negotiation. The below code is patch for this.
11203 */
11204 if (bytes_remaining_in_packet == 4 &&
11205 mpvio->client_capabilities & CLIENT_SSL)
11206 {
11207 mpvio->client_capabilities= uint4korr(end);
11208 mpvio->max_client_packet_length= 0xfffff;
11209 charset_code= global_system_variables.character_set_client->number;
11210 goto skip_to_ssl;
11211 }
11212
11213 if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
11214 packet_has_required_size= bytes_remaining_in_packet >=
11215 AUTH_PACKET_HEADER_SIZE_PROTO_41;
11216 else
11217 packet_has_required_size= bytes_remaining_in_packet >=
11218 AUTH_PACKET_HEADER_SIZE_PROTO_40;
11219
11220 if (!packet_has_required_size)
11221 return packet_error;
11222
11223 if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
11224 {
11225 mpvio->client_capabilities= uint4korr(end);
11226 mpvio->max_client_packet_length= uint4korr(end + 4);
11227 charset_code= (uint)(uchar)*(end + 8);
11228 /*
11229 Skip 23 remaining filler bytes which have no particular meaning.
11230 */
11231 end+= AUTH_PACKET_HEADER_SIZE_PROTO_41;
11232 bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_41;
11233 }
11234 else
11235 {
11236 mpvio->client_capabilities= uint2korr(end);
11237 mpvio->max_client_packet_length= uint3korr(end + 2);
11238 end+= AUTH_PACKET_HEADER_SIZE_PROTO_40;
11239 bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_40;
11240 /**
11241 Old clients didn't have their own charset. Instead the assumption
11242 was that they used what ever the server used.
11243 */
11244 charset_code= global_system_variables.character_set_client->number;
11245 }
11246
11247 skip_to_ssl:
11248 #if defined(HAVE_OPENSSL)
11249 DBUG_PRINT("info", ("client capabilities: %lu", mpvio->client_capabilities));
11250
11251 /*
11252 If client requested SSL then we must stop parsing, try to switch to SSL,
11253 and wait for the client to send a new handshake packet.
11254 The client isn't expected to send any more bytes until SSL is initialized.
11255 */
11256 if (mpvio->client_capabilities & CLIENT_SSL)
11257 {
11258 unsigned long errptr;
11259 #if !defined(DBUG_OFF)
11260 uint ssl_charset_code= 0;
11261 #endif
11262
11263 /* Do the SSL layering. */
11264 if (!ssl_acceptor_fd)
11265 return packet_error;
11266
11267 DBUG_PRINT("info", ("IO layer change in progress..."));
11268 if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr))
11269 {
11270 DBUG_PRINT("error", ("Failed to accept new SSL connection"));
11271 return packet_error;
11272 }
11273
11274 DBUG_PRINT("info", ("Reading user information over SSL layer"));
11275 if ((pkt_len= my_net_read(net)) == packet_error)
11276 {
11277 DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
11278 pkt_len));
11279 return packet_error;
11280 }
11281 /* mark vio as encrypted */
11282 mpvio->vio_is_encrypted= 1;
11283
11284 /*
11285 A new packet was read and the statistics reflecting the remaining bytes
11286 in the packet must be updated.
11287 */
11288 bytes_remaining_in_packet= pkt_len;
11289
11290 /*
11291 After the SSL handshake is performed the client resends the handshake
11292 packet but because of legacy reasons we chose not to parse the packet
11293 fields a second time and instead only assert the length of the packet.
11294 */
11295 if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
11296 {
11297 packet_has_required_size= bytes_remaining_in_packet >=
11298 AUTH_PACKET_HEADER_SIZE_PROTO_41;
11299 #if !defined(DBUG_OFF)
11300 ssl_charset_code= (uint)(uchar)*((char *)net->read_pos + 8);
11301 DBUG_PRINT("info", ("client_character_set: %u", ssl_charset_code));
11302 #endif
11303 end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_41;
11304 bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_41;
11305 }
11306 else
11307 {
11308 packet_has_required_size= bytes_remaining_in_packet >=
11309 AUTH_PACKET_HEADER_SIZE_PROTO_40;
11310 end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_40;
11311 bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_40;
11312 #if !defined(DBUG_OFF)
11313 /**
11314 Old clients didn't have their own charset. Instead the assumption
11315 was that they used what ever the server used.
11316 */
11317 ssl_charset_code= global_system_variables.character_set_client->number;
11318 #endif
11319 }
11320 DBUG_ASSERT(charset_code == ssl_charset_code);
11321 if (!packet_has_required_size)
11322 return packet_error;
11323 }
11324 #endif /* HAVE_OPENSSL */
11325
11326 DBUG_PRINT("info", ("client_character_set: %u", charset_code));
11327 if (mpvio->charset_adapter->init_client_charset(charset_code))
11328 return packet_error;
11329
11330 if ((mpvio->client_capabilities & CLIENT_TRANSACTIONS) &&
11331 opt_using_transactions)
11332 net->return_status= mpvio->server_status;
11333
11334 /*
11335 The 4.0 and 4.1 versions of the protocol differ on how strings
11336 are terminated. In the 4.0 version, if a string is at the end
11337 of the packet, the string is not null terminated. Do not assume
11338 that the returned string is always null terminated.
11339 */
11340 get_proto_string_func_t get_string;
11341
11342 if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
11343 get_string= get_41_protocol_string;
11344 else
11345 get_string= get_40_protocol_string;
11346
11347 /*
11348 When the ability to change default plugin require that the initial password
11349 field can be of arbitrary size. However, the 41 client-server protocol limits
11350 the length of the auth-data-field sent from client to server to 255 bytes
11351 (CLIENT_SECURE_CONNECTION). The solution is to change the type of the field
11352 to a true length encoded string and indicate the protocol change with a new
11353 client capability flag: CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA.
11354 */
11355 get_proto_string_func_t get_length_encoded_string;
11356
11357 if (mpvio->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA)
11358 get_length_encoded_string= get_56_lenc_string;
11359 else
11360 get_length_encoded_string= get_41_lenc_string;
11361
11362 /*
11363 The CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA capability depends on the
11364 CLIENT_SECURE_CONNECTION. Refuse any connection which have the first but
11365 not the latter.
11366 */
11367 if ((mpvio->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) &&
11368 !(mpvio->client_capabilities & CLIENT_SECURE_CONNECTION))
11369 return packet_error;
11370
11371 /*
11372 In order to safely scan a head for '\0' string terminators
11373 we must keep track of how many bytes remain in the allocated
11374 buffer or we might read past the end of the buffer.
11375 */
11376 bytes_remaining_in_packet= pkt_len - (end - (char *)net->read_pos);
11377
11378 size_t user_len;
11379 char *user= get_string(&end, &bytes_remaining_in_packet, &user_len);
11380 if (user == NULL)
11381 return packet_error;
11382
11383 /*
11384 Old clients send a null-terminated string as password; new clients send
11385 the size (1 byte) + string (not null-terminated). Hence in case of empty
11386 password both send '\0'.
11387 */
11388 size_t passwd_len= 0;
11389 char *passwd= NULL;
11390
11391 if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
11392 {
11393 /*
11394 Get the password field.
11395 */
11396 passwd= get_length_encoded_string(&end, &bytes_remaining_in_packet,
11397 &passwd_len);
11398 }
11399 else
11400 {
11401 /*
11402 Old passwords are zero terminated strings.
11403 */
11404 passwd= get_string(&end, &bytes_remaining_in_packet, &passwd_len);
11405 }
11406
11407 if (passwd == NULL)
11408 return packet_error;
11409
11410 size_t db_len= 0;
11411 char *db= NULL;
11412
11413 if (mpvio->client_capabilities & CLIENT_CONNECT_WITH_DB)
11414 {
11415 db= get_string(&end, &bytes_remaining_in_packet, &db_len);
11416 if (db == NULL)
11417 return packet_error;
11418 }
11419
11420 /*
11421 Set the default for the password supplied flag for non-existing users
11422 as the default plugin (native passsword authentication) would do it
11423 for compatibility reasons.
11424 */
11425 if (passwd_len)
11426 mpvio->auth_info.password_used= PASSWORD_USED_YES;
11427
11428 size_t client_plugin_len= 0;
11429 char *client_plugin= get_string(&end, &bytes_remaining_in_packet,
11430 &client_plugin_len);
11431 if (client_plugin == NULL)
11432 client_plugin= &empty_c_string[0];
11433
11434 if ((mpvio->client_capabilities & CLIENT_CONNECT_ATTRS) &&
11435 read_client_connect_attrs(&end, &bytes_remaining_in_packet,
11436 mpvio->charset_adapter->charset()))
11437 return packet_error;
11438
11439 char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
11440 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
11441 uint dummy_errors;
11442
11443
11444 /*
11445 Copy and convert the user and database names to the character set used
11446 by the server. Since 4.1 all database names are stored in UTF-8. Also,
11447 ensure that the names are properly null-terminated as this is relied
11448 upon later.
11449 */
11450 if (db)
11451 {
11452 db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
11453 db, db_len, mpvio->charset_adapter->charset(),
11454 &dummy_errors);
11455 db_buff[db_len]= '\0';
11456 db= db_buff;
11457 }
11458
11459 user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
11460 system_charset_info, user, user_len,
11461 mpvio->charset_adapter->charset(),
11462 &dummy_errors);
11463 user_buff[user_len]= '\0';
11464 user= user_buff;
11465
11466 /* If username starts and ends in "'", chop them off */
11467 if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
11468 {
11469 user[user_len - 1]= 0;
11470 user++;
11471 user_len-= 2;
11472 }
11473
11474 if (make_lex_string_root(mpvio->mem_root,
11475 &mpvio->db, db, db_len, 0) == 0)
11476 return packet_error; /* The error is set by make_lex_string(). */
11477 if (mpvio->auth_info.user_name)
11478 my_free(mpvio->auth_info.user_name);
11479 if (!(mpvio->auth_info.user_name= my_strndup(user, user_len, MYF(MY_WME))))
11480 return packet_error; /* The error is set by my_strdup(). */
11481 mpvio->auth_info.user_name_length= user_len;
11482
11483 if (!initialized)
11484 {
11485 // if mysqld's been started with --skip-grant-tables option
11486 mpvio->status= MPVIO_EXT::SUCCESS;
11487 return packet_error;
11488 }
11489
11490 if (find_mpvio_user(mpvio))
11491 return packet_error;
11492
11493 if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
11494 {
11495 /*
11496 An old client is connecting
11497 */
11498 if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
11499 client_plugin= native_password_plugin_name.str;
11500 else
11501 {
11502 /*
11503 A really old client is connecting
11504 */
11505 client_plugin= old_password_plugin_name.str;
11506 /*
11507 For a passwordless accounts we use native_password_plugin.
11508 But when an old 4.0 client connects to it, we change it to
11509 old_password_plugin, otherwise MySQL will think that server
11510 and client plugins don't match.
11511 */
11512 if (mpvio->acl_user->salt_len == 0)
11513 mpvio->acl_user_plugin= old_password_plugin_name;
11514 }
11515 }
11516
11517 /*
11518 if the acl_user needs a different plugin to authenticate
11519 (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
11520 we need to restart the authentication in the server.
11521 But perhaps the client has already used the correct plugin -
11522 in that case the authentication on the client may not need to be
11523 restarted and a server auth plugin will read the data that the client
11524 has just send. Cache them to return in the next server_mpvio_read_packet().
11525 */
11526 if (my_strcasecmp(system_charset_info, mpvio->acl_user_plugin.str,
11527 plugin_name(mpvio->plugin)->str) != 0)
11528 {
11529 mpvio->cached_client_reply.pkt= passwd;
11530 mpvio->cached_client_reply.pkt_len= passwd_len;
11531 mpvio->cached_client_reply.plugin= client_plugin;
11532 mpvio->status= MPVIO_EXT::RESTART;
11533 return packet_error;
11534 }
11535
11536 /*
11537 ok, we don't need to restart the authentication on the server.
11538 but if the client used the wrong plugin, we need to restart
11539 the authentication on the client. Do it here, the server plugin
11540 doesn't need to know.
11541 */
11542 const char *client_auth_plugin=
11543 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
11544
11545 if (client_auth_plugin &&
11546 my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
11547 {
11548 mpvio->cached_client_reply.plugin= client_plugin;
11549 if (send_plugin_request_packet(mpvio,
11550 (uchar*) mpvio->cached_server_packet.pkt,
11551 mpvio->cached_server_packet.pkt_len))
11552 return packet_error;
11553
11554 passwd_len= my_net_read(mpvio->net);
11555 passwd = (char*) mpvio->net->read_pos;
11556 }
11557
11558 *buff= (uchar*) passwd;
11559 return passwd_len;
11560 #else
11561 return 0;
11562 #endif
11563 }
11564
11565
11566 /**
11567 Make sure that when sending plugin supplied data to the client they
11568 are not considered a special out-of-band command, like e.g.
11569 \255 (error) or \254 (change user request packet) or \0 (OK).
11570 To avoid this the server will send all plugin data packets "wrapped"
11571 in a command \1.
11572 Note that the client will continue sending its replies unrwapped.
11573 */
11574
11575 static inline int
wrap_plguin_data_into_proper_command(NET * net,const uchar * packet,int packet_len)11576 wrap_plguin_data_into_proper_command(NET *net,
11577 const uchar *packet, int packet_len)
11578 {
11579 return net_write_command(net, 1, (uchar *) "", 0, packet, packet_len);
11580 }
11581
11582
11583 /**
11584 vio->write_packet() callback method for server authentication plugins
11585
11586 This function is called by a server authentication plugin, when it wants
11587 to send data to the client.
11588
11589 It transparently wraps the data into a handshake packet,
11590 and handles plugin negotiation with the client. If necessary,
11591 it escapes the plugin data, if it starts with a mysql protocol packet byte.
11592 */
server_mpvio_write_packet(MYSQL_PLUGIN_VIO * param,const uchar * packet,int packet_len)11593 static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
11594 const uchar *packet, int packet_len)
11595 {
11596 MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
11597 int res;
11598
11599 DBUG_ENTER("server_mpvio_write_packet");
11600 /*
11601 Reset cached_client_reply if not an old client doing mysql_change_user,
11602 as this is where the password from COM_CHANGE_USER is stored.
11603 */
11604 if (!((!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)) &&
11605 mpvio->status == MPVIO_EXT::RESTART &&
11606 mpvio->cached_client_reply.plugin ==
11607 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin
11608 ))
11609 mpvio->cached_client_reply.pkt= 0;
11610 /* for the 1st packet we wrap plugin data into the handshake packet */
11611 if (mpvio->packets_written == 0)
11612 res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
11613 else if (mpvio->status == MPVIO_EXT::RESTART)
11614 res= send_plugin_request_packet(mpvio, packet, packet_len);
11615 else
11616 res= wrap_plguin_data_into_proper_command(mpvio->net, packet, packet_len);
11617 mpvio->packets_written++;
11618 DBUG_RETURN(res);
11619 }
11620
11621 /**
11622 vio->read_packet() callback method for server authentication plugins
11623
11624 This function is called by a server authentication plugin, when it wants
11625 to read data from the client.
11626
11627 It transparently extracts the client plugin data, if embedded into
11628 a client authentication handshake packet, and handles plugin negotiation
11629 with the client, if necessary.
11630
11631 RETURN
11632 -1 Protocol failure
11633 >= 0 Success and also the packet length
11634 */
server_mpvio_read_packet(MYSQL_PLUGIN_VIO * param,uchar ** buf)11635 static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
11636 {
11637 MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
11638 ulong pkt_len;
11639
11640 DBUG_ENTER("server_mpvio_read_packet");
11641 if (mpvio->packets_written == 0)
11642 {
11643 /*
11644 plugin wants to read the data without sending anything first.
11645 send an empty packet to force a server handshake packet to be sent
11646 */
11647 if (mpvio->write_packet(mpvio, 0, 0))
11648 pkt_len= packet_error;
11649 else
11650 pkt_len= my_net_read(mpvio->net);
11651 }
11652 else if (mpvio->cached_client_reply.pkt)
11653 {
11654 DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
11655 DBUG_ASSERT(mpvio->packets_read > 0);
11656 /*
11657 if the have the data cached from the last server_mpvio_read_packet
11658 (which can be the case if it's a restarted authentication)
11659 and a client has used the correct plugin, then we can return the
11660 cached data straight away and avoid one round trip.
11661 */
11662 const char *client_auth_plugin=
11663 ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
11664 if (client_auth_plugin == 0 ||
11665 my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
11666 client_auth_plugin) == 0)
11667 {
11668 mpvio->status= MPVIO_EXT::FAILURE;
11669 *buf= (uchar*) mpvio->cached_client_reply.pkt;
11670 mpvio->cached_client_reply.pkt= 0;
11671 mpvio->packets_read++;
11672 DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
11673 }
11674
11675 /* older clients don't support change of client plugin request */
11676 if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
11677 {
11678 mpvio->status= MPVIO_EXT::FAILURE;
11679 pkt_len= packet_error;
11680 goto err;
11681 }
11682
11683 /*
11684 But if the client has used the wrong plugin, the cached data are
11685 useless. Furthermore, we have to send a "change plugin" request
11686 to the client.
11687 */
11688 if (mpvio->write_packet(mpvio, 0, 0))
11689 pkt_len= packet_error;
11690 else
11691 pkt_len= my_net_read(mpvio->net);
11692 }
11693 else
11694 pkt_len= my_net_read(mpvio->net);
11695
11696 if (pkt_len == packet_error)
11697 goto err;
11698
11699 mpvio->packets_read++;
11700
11701 /*
11702 the 1st packet has the plugin data wrapped into the client authentication
11703 handshake packet
11704 */
11705 if (mpvio->packets_read == 1)
11706 {
11707 pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
11708 if (pkt_len == packet_error)
11709 goto err;
11710 }
11711 else
11712 *buf= mpvio->net->read_pos;
11713
11714 DBUG_RETURN((int)pkt_len);
11715
11716 err:
11717 if (mpvio->status == MPVIO_EXT::FAILURE)
11718 {
11719 my_error(ER_HANDSHAKE_ERROR, MYF(0));
11720 }
11721 DBUG_RETURN(-1);
11722 }
11723
11724 /**
11725 fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
11726 connection
11727 */
server_mpvio_info(MYSQL_PLUGIN_VIO * vio,MYSQL_PLUGIN_VIO_INFO * info)11728 static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
11729 MYSQL_PLUGIN_VIO_INFO *info)
11730 {
11731 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
11732 mpvio_info(mpvio->net->vio, info);
11733 }
11734
11735 #ifndef NO_EMBEDDED_ACCESS_CHECKS
acl_check_ssl(THD * thd,const ACL_USER * acl_user)11736 static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
11737 {
11738 #if defined(HAVE_OPENSSL)
11739 Vio *vio= thd->net.vio;
11740 SSL *ssl= (SSL *) vio->ssl_arg;
11741 X509 *cert;
11742 #endif
11743
11744 /*
11745 At this point we know that user is allowed to connect
11746 from given host by given username/password pair. Now
11747 we check if SSL is required, if user is using SSL and
11748 if X509 certificate attributes are OK
11749 */
11750 switch (acl_user->ssl_type) {
11751 case SSL_TYPE_NOT_SPECIFIED: // Impossible
11752 case SSL_TYPE_NONE: // SSL is not required
11753 return 0;
11754 #if defined(HAVE_OPENSSL)
11755 case SSL_TYPE_ANY: // Any kind of SSL is ok
11756 return vio_type(vio) != VIO_TYPE_SSL;
11757 case SSL_TYPE_X509: /* Client should have any valid certificate. */
11758 /*
11759 Connections with non-valid certificates are dropped already
11760 in sslaccept() anyway, so we do not check validity here.
11761
11762 We need to check for absence of SSL because without SSL
11763 we should reject connection.
11764 */
11765 if (vio_type(vio) == VIO_TYPE_SSL &&
11766 SSL_get_verify_result(ssl) == X509_V_OK &&
11767 (cert= SSL_get_peer_certificate(ssl)))
11768 {
11769 X509_free(cert);
11770 return 0;
11771 }
11772 return 1;
11773 case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
11774 /* If a cipher name is specified, we compare it to actual cipher in use. */
11775 if (vio_type(vio) != VIO_TYPE_SSL ||
11776 SSL_get_verify_result(ssl) != X509_V_OK)
11777 return 1;
11778 if (acl_user->ssl_cipher)
11779 {
11780 DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
11781 acl_user->ssl_cipher, SSL_get_cipher(ssl)));
11782 if (strcmp(acl_user->ssl_cipher, SSL_get_cipher(ssl)))
11783 {
11784 if (log_warnings)
11785 sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
11786 acl_user->ssl_cipher, SSL_get_cipher(ssl));
11787 return 1;
11788 }
11789 }
11790 /* Prepare certificate (if exists) */
11791 if (!(cert= SSL_get_peer_certificate(ssl)))
11792 return 1;
11793 /* If X509 issuer is specified, we check it... */
11794 if (acl_user->x509_issuer)
11795 {
11796 char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
11797 DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
11798 acl_user->x509_issuer, ptr));
11799 if (strcmp(acl_user->x509_issuer, ptr))
11800 {
11801 if (log_warnings)
11802 sql_print_information("X509 issuer mismatch: should be '%s' "
11803 "but is '%s'", acl_user->x509_issuer, ptr);
11804 OPENSSL_free(ptr);
11805 X509_free(cert);
11806 return 1;
11807 }
11808 OPENSSL_free(ptr);
11809 }
11810 /* X509 subject is specified, we check it .. */
11811 if (acl_user->x509_subject)
11812 {
11813 char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
11814 DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
11815 acl_user->x509_subject, ptr));
11816 if (strcmp(acl_user->x509_subject, ptr))
11817 {
11818 if (log_warnings)
11819 sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
11820 acl_user->x509_subject, ptr);
11821 OPENSSL_free(ptr);
11822 X509_free(cert);
11823 return 1;
11824 }
11825 OPENSSL_free(ptr);
11826 }
11827 X509_free(cert);
11828 return 0;
11829 #else /* HAVE_OPENSSL */
11830 default:
11831 /*
11832 If we don't have SSL but SSL is required for this user the
11833 authentication should fail.
11834 */
11835 return 1;
11836 #endif /* HAVE_OPENSSL */
11837 }
11838 return 1;
11839 }
11840 #endif
11841
11842
do_auth_once(THD * thd,LEX_STRING * auth_plugin_name,MPVIO_EXT * mpvio)11843 static int do_auth_once(THD *thd, LEX_STRING *auth_plugin_name,
11844 MPVIO_EXT *mpvio)
11845 {
11846 DBUG_ENTER("do_auth_once");
11847 int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
11848 bool unlock_plugin= false;
11849 plugin_ref plugin= NULL;
11850
11851 if (auth_plugin_name->str == native_password_plugin_name.str)
11852 plugin= native_password_plugin;
11853 #ifndef EMBEDDED_LIBRARY
11854 else
11855 if (auth_plugin_name->str == old_password_plugin_name.str)
11856 plugin= old_password_plugin;
11857 else
11858 {
11859 if (auth_plugin_name->length == 0)
11860 {
11861 auth_plugin_name->str= default_auth_plugin_name.str;
11862 auth_plugin_name->length= default_auth_plugin_name.length;
11863 }
11864 if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
11865 MYSQL_AUTHENTICATION_PLUGIN)))
11866 unlock_plugin= true;
11867 }
11868 #endif
11869
11870
11871 mpvio->plugin= plugin;
11872 old_status= mpvio->status;
11873
11874 if (plugin)
11875 {
11876 st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
11877 res= auth->authenticate_user(mpvio, &mpvio->auth_info);
11878
11879 if (unlock_plugin)
11880 plugin_unlock(thd, plugin);
11881 }
11882 else
11883 {
11884 /* Server cannot load the required plugin. */
11885 Host_errors errors;
11886 errors.m_no_auth_plugin= 1;
11887 inc_host_errors(mpvio->ip, &errors);
11888 my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
11889 res= CR_ERROR;
11890 }
11891
11892 /*
11893 If the status was MPVIO_EXT::RESTART before the authenticate_user() call
11894 it can never be MPVIO_EXT::RESTART after the call, because any call
11895 to write_packet() or read_packet() will reset the status.
11896
11897 But (!) if a plugin never called a read_packet() or write_packet(), the
11898 status will stay unchanged. We'll fix it, by resetting the status here.
11899 */
11900 if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART)
11901 mpvio->status= MPVIO_EXT::FAILURE; // reset to the default
11902
11903 DBUG_RETURN(res);
11904 }
11905
11906
11907 static void
server_mpvio_initialize(THD * thd,MPVIO_EXT * mpvio,Thd_charset_adapter * charset_adapter)11908 server_mpvio_initialize(THD *thd, MPVIO_EXT *mpvio,
11909 Thd_charset_adapter *charset_adapter)
11910 {
11911 memset(mpvio, 0, sizeof(MPVIO_EXT));
11912 mpvio->read_packet= server_mpvio_read_packet;
11913 mpvio->write_packet= server_mpvio_write_packet;
11914 mpvio->info= server_mpvio_info;
11915 mpvio->auth_info.host_or_ip= thd->security_ctx->host_or_ip;
11916 mpvio->auth_info.host_or_ip_length=
11917 (unsigned int) strlen(thd->security_ctx->host_or_ip);
11918 mpvio->auth_info.user_name= NULL;
11919 mpvio->auth_info.user_name_length= 0;
11920 #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
11921 if (thd->net.vio && thd->net.vio->ssl_arg)
11922 mpvio->vio_is_encrypted= 1;
11923 else
11924 #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
11925 mpvio->vio_is_encrypted= 0;
11926 mpvio->status= MPVIO_EXT::FAILURE;
11927
11928 mpvio->client_capabilities= thd->client_capabilities;
11929 mpvio->mem_root= thd->mem_root;
11930 mpvio->scramble= thd->scramble;
11931 mpvio->rand= &thd->rand;
11932 mpvio->thread_id= thd->thread_id;
11933 mpvio->server_status= &thd->server_status;
11934 mpvio->net= &thd->net;
11935 mpvio->ip= (char *) thd->security_ctx->get_ip()->ptr();
11936 mpvio->host= (char *) thd->security_ctx->get_host()->ptr();
11937 mpvio->charset_adapter= charset_adapter;
11938 }
11939
11940
11941 static void
server_mpvio_update_thd(THD * thd,MPVIO_EXT * mpvio)11942 server_mpvio_update_thd(THD *thd, MPVIO_EXT *mpvio)
11943 {
11944 thd->client_capabilities= mpvio->client_capabilities;
11945 thd->max_client_packet_length= mpvio->max_client_packet_length;
11946 if (mpvio->client_capabilities & CLIENT_INTERACTIVE)
11947 thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
11948 thd->security_ctx->user= mpvio->auth_info.user_name;
11949 if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
11950 thd->variables.sql_mode|= MODE_IGNORE_SPACE;
11951 }
11952
11953 /**
11954 Assign priv_user and priv_host fields of the Security_context.
11955
11956 @param sctx Security context, which priv_user and priv_host fields are
11957 updated.
11958 @param user Authenticated user data.
11959 */
11960 inline void
assign_priv_user_host(Security_context * sctx,const ACL_USER * user)11961 assign_priv_user_host(Security_context *sctx, const ACL_USER *user)
11962 {
11963 if (user->user)
11964 strmake(sctx->priv_user, user->user, USERNAME_LENGTH - 1);
11965 else
11966 *sctx->priv_user= 0;
11967
11968 if (user->host.get_host())
11969 strmake(sctx->priv_host, user->host.get_host(), MAX_HOSTNAME - 1);
11970 else
11971 *sctx->priv_host= 0;
11972 }
11973
11974 /**
11975 Perform the handshake, authorize the client and update thd sctx variables.
11976
11977 @param thd thread handle
11978 @param com_change_user_pkt_len size of the COM_CHANGE_USER packet
11979 (without the first, command, byte) or 0
11980 if it's not a COM_CHANGE_USER (that is, if
11981 it's a new connection)
11982
11983 @retval 0 success, thd is updated.
11984 @retval 1 error
11985 */
11986 int
acl_authenticate(THD * thd,uint com_change_user_pkt_len)11987 acl_authenticate(THD *thd, uint com_change_user_pkt_len)
11988 {
11989 int res= CR_OK;
11990 MPVIO_EXT mpvio;
11991 Thd_charset_adapter charset_adapter(thd);
11992
11993 LEX_STRING auth_plugin_name= default_auth_plugin_name;
11994 enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
11995 : COM_CONNECT;
11996
11997 DBUG_ENTER("acl_authenticate");
11998 compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
11999
12000 server_mpvio_initialize(thd, &mpvio, &charset_adapter);
12001
12002 DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
12003
12004 /*
12005 Clear thd->db as it points to something, that will be freed when
12006 connection is closed. We don't want to accidentally free a wrong
12007 pointer if connect failed.
12008 */
12009 thd->reset_db(NULL, 0);
12010
12011 if (command == COM_CHANGE_USER)
12012 {
12013 mpvio.packets_written++; // pretend that a server handshake packet was sent
12014 mpvio.packets_read++; // take COM_CHANGE_USER packet into account
12015
12016 /* Clear variables that are allocated */
12017 thd->set_user_connect(NULL);
12018
12019 if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
12020 {
12021 if (!thd->is_error())
12022 login_failed_error(&mpvio, mpvio.auth_info.password_used);
12023 server_mpvio_update_thd(thd, &mpvio);
12024 DBUG_RETURN(1);
12025 }
12026
12027 DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
12028 mpvio.status == MPVIO_EXT::SUCCESS);
12029 }
12030 else
12031 {
12032 /* mark the thd as having no scramble yet */
12033 mpvio.scramble[SCRAMBLE_LENGTH]= 1;
12034
12035 /*
12036 perform the first authentication attempt, with the default plugin.
12037 This sends the server handshake packet, reads the client reply
12038 with a user name, and performs the authentication if everyone has used
12039 the correct plugin.
12040 */
12041
12042 res= do_auth_once(thd, &auth_plugin_name, &mpvio);
12043 }
12044
12045 /*
12046 retry the authentication, if - after receiving the user name -
12047 we found that we need to switch to a non-default plugin
12048 */
12049 if (mpvio.status == MPVIO_EXT::RESTART)
12050 {
12051 DBUG_ASSERT(mpvio.acl_user);
12052 DBUG_ASSERT(command == COM_CHANGE_USER ||
12053 my_strcasecmp(system_charset_info, auth_plugin_name.str,
12054 mpvio.acl_user->plugin.str));
12055 auth_plugin_name= mpvio.acl_user->plugin;
12056 res= do_auth_once(thd, &auth_plugin_name, &mpvio);
12057 if (res <= CR_OK)
12058 {
12059 if (auth_plugin_name.str == native_password_plugin_name.str)
12060 thd->variables.old_passwords= 0;
12061 if (auth_plugin_name.str == old_password_plugin_name.str)
12062 thd->variables.old_passwords= 1;
12063 if (auth_plugin_name.str == sha256_password_plugin_name.str)
12064 thd->variables.old_passwords= 2;
12065 }
12066 }
12067
12068 server_mpvio_update_thd(thd, &mpvio);
12069
12070 Security_context *sctx= thd->security_ctx;
12071 const ACL_USER *acl_user= mpvio.acl_user;
12072
12073 thd->password= mpvio.auth_info.password_used; // remember for error messages
12074
12075 /*
12076 Log the command here so that the user can check the log
12077 for the tried logins and also to detect break-in attempts.
12078
12079 if sctx->user is unset it's protocol failure, bad packet.
12080 */
12081 if (mpvio.auth_info.user_name)
12082 {
12083 if (strcmp(mpvio.auth_info.authenticated_as, mpvio.auth_info.user_name))
12084 {
12085 general_log_print(thd, command, "%s@%s as %s on %s",
12086 mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
12087 mpvio.auth_info.authenticated_as,
12088 mpvio.db.str ? mpvio.db.str : (char*) "");
12089 }
12090 else
12091 general_log_print(thd, command, (char*) "%s@%s on %s",
12092 mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
12093 mpvio.db.str ? mpvio.db.str : (char*) "");
12094 }
12095
12096 if (res == CR_OK && !mpvio.can_authenticate())
12097 {
12098 res= CR_ERROR;
12099 }
12100
12101 if (mpvio.can_authenticate())
12102 assign_priv_user_host(sctx, acl_user);
12103
12104 if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
12105 {
12106 Host_errors errors;
12107 DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
12108 switch (res)
12109 {
12110 case CR_AUTH_PLUGIN_ERROR:
12111 errors.m_auth_plugin= 1;
12112 break;
12113 case CR_AUTH_HANDSHAKE:
12114 errors.m_handshake= 1;
12115 break;
12116 case CR_AUTH_USER_CREDENTIALS:
12117 errors.m_authentication= 1;
12118 break;
12119 case CR_ERROR:
12120 default:
12121 /* Unknown of unspecified auth plugin error. */
12122 errors.m_auth_plugin= 1;
12123 break;
12124 }
12125 inc_host_errors(mpvio.ip, &errors);
12126 if (!thd->is_error())
12127 login_failed_error(&mpvio, mpvio.auth_info.password_used);
12128 DBUG_RETURN (1);
12129 }
12130
12131 sctx->proxy_user[0]= 0;
12132
12133 if (initialized) // if not --skip-grant-tables
12134 {
12135 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12136 bool is_proxy_user= FALSE;
12137 const char *auth_user = acl_user->user ? acl_user->user : "";
12138 ACL_PROXY_USER *proxy_user;
12139 /* check if the user is allowed to proxy as another user */
12140 proxy_user= acl_find_proxy_user(auth_user, sctx->get_host()->ptr(),
12141 sctx->get_ip()->ptr(),
12142 mpvio.auth_info.authenticated_as,
12143 &is_proxy_user);
12144 if (is_proxy_user)
12145 {
12146 ACL_USER *acl_proxy_user;
12147
12148 /* we need to find the proxy user, but there was none */
12149 if (!proxy_user)
12150 {
12151 Host_errors errors;
12152 errors.m_proxy_user= 1;
12153 inc_host_errors(mpvio.ip, &errors);
12154 if (!thd->is_error())
12155 login_failed_error(&mpvio, mpvio.auth_info.password_used);
12156 DBUG_RETURN(1);
12157 }
12158
12159 my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
12160 "'%s'@'%s'", auth_user,
12161 acl_user->host.get_host() ? acl_user->host.get_host() : "");
12162
12163 /* we're proxying : find the proxy user definition */
12164 mysql_mutex_lock(&acl_cache->lock);
12165 acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ?
12166 proxy_user->get_proxied_host() : "",
12167 mpvio.auth_info.authenticated_as, TRUE);
12168 if (!acl_proxy_user)
12169 {
12170 Host_errors errors;
12171 errors.m_proxy_user_acl= 1;
12172 inc_host_errors(mpvio.ip, &errors);
12173 if (!thd->is_error())
12174 login_failed_error(&mpvio, mpvio.auth_info.password_used);
12175 mysql_mutex_unlock(&acl_cache->lock);
12176 DBUG_RETURN(1);
12177 }
12178
12179 if (acl_is_utility_user(acl_proxy_user->user,
12180 acl_proxy_user->host.get_host(), NULL))
12181 {
12182 if (!thd->is_error())
12183 login_failed_error(&mpvio, mpvio.auth_info.password_used);
12184 mysql_mutex_unlock(&acl_cache->lock);
12185 DBUG_RETURN(1);
12186 }
12187
12188 acl_user= acl_proxy_user->copy(thd->mem_root);
12189 DBUG_PRINT("info", ("User %s is a PROXY and will assume a PROXIED"
12190 " identity %s", auth_user, acl_user->user));
12191 mysql_mutex_unlock(&acl_cache->lock);
12192 }
12193 #endif
12194
12195 sctx->master_access= acl_user->access;
12196 assign_priv_user_host(sctx, acl_user);
12197
12198 #ifndef NO_EMBEDDED_ACCESS_CHECKS
12199 /*
12200 OK. Let's check the SSL. Historically it was checked after the password,
12201 as an additional layer, not instead of the password
12202 (in which case it would've been a plugin too).
12203 */
12204 if (acl_check_ssl(thd, acl_user))
12205 {
12206 Host_errors errors;
12207 errors.m_ssl= 1;
12208 inc_host_errors(mpvio.ip, &errors);
12209 if (!thd->is_error())
12210 login_failed_error(&mpvio, thd->password);
12211 DBUG_RETURN(1);
12212 }
12213
12214 if (unlikely(mpvio.acl_user && mpvio.acl_user->password_expired
12215 && !(mpvio.client_capabilities & CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS)
12216 && disconnect_on_expired_password))
12217 {
12218 /*
12219 Clients that don't signal password expiration support
12220 get a connect error.
12221 */
12222 Host_errors errors;
12223
12224 my_error(ER_MUST_CHANGE_PASSWORD_LOGIN, MYF(0));
12225 general_log_print(thd, COM_CONNECT, ER(ER_MUST_CHANGE_PASSWORD_LOGIN));
12226 if (log_warnings > 1)
12227 sql_print_warning("%s", ER(ER_MUST_CHANGE_PASSWORD_LOGIN));
12228
12229 errors.m_authentication= 1;
12230 inc_host_errors(mpvio.ip, &errors);
12231 DBUG_RETURN(1);
12232 }
12233
12234 /* Don't allow the user to connect if he has done too many queries */
12235 if ((acl_user->user_resource.questions || acl_user->user_resource.updates ||
12236 acl_user->user_resource.conn_per_hour ||
12237 acl_user->user_resource.user_conn ||
12238 global_system_variables.max_user_connections) &&
12239 get_or_create_user_conn(thd,
12240 (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
12241 (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
12242 &acl_user->user_resource))
12243 DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
12244
12245 /*
12246 We are copying the connected user's password expired flag to the security
12247 context.
12248 This allows proxy user to execute queries even if proxied user password
12249 expires.
12250 */
12251 sctx->password_expired= mpvio.acl_user->password_expired;
12252 #endif
12253 }
12254 else
12255 sctx->skip_grants();
12256
12257 const USER_CONN *uc;
12258 if ((uc= thd->get_user_connect()) &&
12259 (uc->user_resources.conn_per_hour || uc->user_resources.user_conn ||
12260 global_system_variables.max_user_connections) &&
12261 check_for_max_user_connections(thd, uc))
12262 {
12263 DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
12264 }
12265
12266 DBUG_PRINT("info",
12267 ("Capabilities: %lu packet_length: %ld Host: '%s' "
12268 "Login user: '%s' Priv_user: '%s' Using password: %s "
12269 "Access: %lu db: '%s'",
12270 thd->client_capabilities, thd->max_client_packet_length,
12271 sctx->host_or_ip, sctx->user, sctx->priv_user,
12272 thd->password ? "yes": "no",
12273 sctx->master_access, mpvio.db.str));
12274
12275 if (command == COM_CONNECT &&
12276 !(thd->main_security_ctx.master_access & SUPER_ACL))
12277 {
12278 mysql_mutex_lock(&LOCK_connection_count);
12279 bool count_ok= (thd->scheduler != extra_thread_scheduler)
12280 ? (connection_count <= max_connections)
12281 : (extra_connection_count <= extra_max_connections);
12282 mysql_mutex_unlock(&LOCK_connection_count);
12283 if (!count_ok)
12284 { // too many connections
12285 release_user_connection(thd);
12286 statistic_increment(connection_errors_max_connection, &LOCK_status);
12287 my_error(ER_CON_COUNT_ERROR, MYF(0));
12288 if (log_warnings)
12289 {
12290 sql_print_warning("%s", ER_DEFAULT(ER_CON_COUNT_ERROR));
12291 }
12292 DBUG_RETURN(1);
12293 }
12294 }
12295
12296 /*
12297 This is the default access rights for the current database. It's
12298 set to 0 here because we don't have an active database yet (and we
12299 may not have an active database to set.
12300 */
12301 sctx->db_access=0;
12302
12303 /* Change a database if necessary */
12304 if (mpvio.db.length)
12305 {
12306 if (mysql_change_db(thd, &mpvio.db, FALSE))
12307 {
12308 /* mysql_change_db() has pushed the error message. */
12309 release_user_connection(thd);
12310 Host_errors errors;
12311 errors.m_default_database= 1;
12312 inc_host_errors(mpvio.ip, &errors);
12313 DBUG_RETURN(1);
12314 }
12315 }
12316
12317 if (mpvio.auth_info.external_user[0])
12318 sctx->set_external_user(my_strdup(mpvio.auth_info.external_user, MYF(0)));
12319
12320
12321 if (res == CR_OK_HANDSHAKE_COMPLETE)
12322 thd->get_stmt_da()->disable_status();
12323 else
12324 my_ok(thd);
12325
12326 #ifdef HAVE_PSI_THREAD_INTERFACE
12327 PSI_THREAD_CALL(set_thread_user_host)
12328 (thd->main_security_ctx.user, strlen(thd->main_security_ctx.user),
12329 thd->main_security_ctx.host_or_ip, strlen(thd->main_security_ctx.host_or_ip));
12330 #endif
12331
12332 /* Ready to handle queries */
12333 DBUG_RETURN(0);
12334 }
12335
12336 /**
12337 MySQL Server Password Authentication Plugin
12338
12339 In the MySQL authentication protocol:
12340 1. the server sends the random scramble to the client
12341 2. client sends the encrypted password back to the server
12342 3. the server checks the password.
12343 */
native_password_authenticate(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)12344 static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
12345 MYSQL_SERVER_AUTH_INFO *info)
12346 {
12347 uchar *pkt;
12348 int pkt_len;
12349 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
12350
12351 DBUG_ENTER("native_password_authenticate");
12352
12353 /* generate the scramble, or reuse the old one */
12354 if (mpvio->scramble[SCRAMBLE_LENGTH])
12355 create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
12356
12357 /* send it to the client */
12358 if (mpvio->write_packet(mpvio, (uchar*) mpvio->scramble, SCRAMBLE_LENGTH + 1))
12359 DBUG_RETURN(CR_AUTH_HANDSHAKE);
12360
12361 /* reply and authenticate */
12362
12363 /*
12364 <digression>
12365 This is more complex than it looks.
12366
12367 The plugin (we) may be called right after the client was connected -
12368 and will need to send a scramble, read reply, authenticate.
12369
12370 Or the plugin may be called after another plugin has sent a scramble,
12371 and read the reply. If the client has used the correct client-plugin,
12372 we won't need to read anything here from the client, the client
12373 has already sent a reply with everything we need for authentication.
12374
12375 Or the plugin may be called after another plugin has sent a scramble,
12376 and read the reply, but the client has used the wrong client-plugin.
12377 We'll need to sent a "switch to another plugin" packet to the
12378 client and read the reply. "Use the short scramble" packet is a special
12379 case of "switch to another plugin" packet.
12380
12381 Or, perhaps, the plugin may be called after another plugin has
12382 done the handshake but did not send a useful scramble. We'll need
12383 to send a scramble (and perhaps a "switch to another plugin" packet)
12384 and read the reply.
12385
12386 Besides, a client may be an old one, that doesn't understand plugins.
12387 Or doesn't even understand 4.0 scramble.
12388
12389 And we want to keep the same protocol on the wire unless non-native
12390 plugins are involved.
12391
12392 Anyway, it still looks simple from a plugin point of view:
12393 "send the scramble, read the reply and authenticate"
12394 All the magic is transparently handled by the server.
12395 </digression>
12396 */
12397
12398 /* read the reply with the encrypted password */
12399 if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
12400 DBUG_RETURN(CR_AUTH_HANDSHAKE);
12401 DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
12402
12403 #ifdef NO_EMBEDDED_ACCESS_CHECKS
12404 DBUG_RETURN(CR_OK);
12405 #endif
12406
12407 DBUG_EXECUTE_IF("native_password_bad_reply",
12408 {
12409 pkt_len= 12;
12410 }
12411 );
12412
12413 if (pkt_len == 0) /* no password */
12414 DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK);
12415
12416 info->password_used= PASSWORD_USED_YES;
12417 if (pkt_len == SCRAMBLE_LENGTH)
12418 {
12419 if (!mpvio->acl_user->salt_len)
12420 DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
12421
12422 DBUG_RETURN(check_scramble(pkt, mpvio->scramble, mpvio->acl_user->salt) ?
12423 CR_AUTH_USER_CREDENTIALS : CR_OK);
12424 }
12425
12426 my_error(ER_HANDSHAKE_ERROR, MYF(0));
12427 DBUG_RETURN(CR_AUTH_HANDSHAKE);
12428 }
12429
old_password_authenticate(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)12430 static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
12431 MYSQL_SERVER_AUTH_INFO *info)
12432 {
12433 uchar *pkt;
12434 int pkt_len;
12435 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
12436
12437 /* generate the scramble, or reuse the old one */
12438 if (mpvio->scramble[SCRAMBLE_LENGTH])
12439 create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
12440
12441 /* send it to the client */
12442 if (mpvio->write_packet(mpvio, (uchar*) mpvio->scramble, SCRAMBLE_LENGTH + 1))
12443 return CR_AUTH_HANDSHAKE;
12444
12445 /* read the reply and authenticate */
12446 if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
12447 return CR_AUTH_HANDSHAKE;
12448
12449 #ifdef NO_EMBEDDED_ACCESS_CHECKS
12450 return CR_OK;
12451 #endif
12452
12453 /*
12454 legacy: if switch_from_long_to_short_scramble,
12455 the password is sent \0-terminated, the pkt_len is always 9 bytes.
12456 We need to figure out the correct scramble length here.
12457 */
12458 if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
12459 pkt_len= strnlen((char*)pkt, pkt_len);
12460
12461 if (pkt_len == 0) /* no password */
12462 return mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK;
12463
12464 if (secure_auth(mpvio))
12465 return CR_AUTH_HANDSHAKE;
12466
12467 info->password_used= PASSWORD_USED_YES;
12468
12469 if (pkt_len == SCRAMBLE_LENGTH_323)
12470 {
12471 if (!mpvio->acl_user->salt_len)
12472 return CR_AUTH_USER_CREDENTIALS;
12473
12474 return check_scramble_323(pkt, mpvio->scramble,
12475 (ulong *) mpvio->acl_user->salt) ?
12476 CR_AUTH_USER_CREDENTIALS : CR_OK;
12477 }
12478
12479 my_error(ER_HANDSHAKE_ERROR, MYF(0));
12480 return CR_AUTH_HANDSHAKE;
12481 }
12482
12483
12484 /**
12485 Interface for querying the MYSQL_PUBLIC_VIO about encryption state.
12486
12487 */
12488
my_vio_is_encrypted(MYSQL_PLUGIN_VIO * vio)12489 int my_vio_is_encrypted(MYSQL_PLUGIN_VIO *vio)
12490 {
12491 MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
12492 return (mpvio->vio_is_encrypted);
12493 }
12494
12495 #if defined(HAVE_OPENSSL)
12496 #define MAX_CIPHER_LENGTH 1024
12497 #define AUTH_DEFAULT_RSA_PRIVATE_KEY "private_key.pem"
12498 #define AUTH_DEFAULT_RSA_PUBLIC_KEY "public_key.pem"
12499
12500 char *auth_rsa_private_key_path;
12501 char *auth_rsa_public_key_path;
12502
12503 class Rsa_authentication_keys
12504 {
12505 private:
12506 RSA *m_public_key;
12507 RSA *m_private_key;
12508 int m_cipher_len;
12509 char *m_pem_public_key;
12510
12511 /**
12512 @brief Set key file path
12513
12514 @param key[in] Points to either auth_rsa_private_key_path or
12515 auth_rsa_public_key_path.
12516 @param key_file_path[out] Stores value of actual key file path.
12517
12518 */
get_key_file_path(char * key,String * key_file_path)12519 void get_key_file_path(char *key, String *key_file_path)
12520 {
12521 /*
12522 If a fully qualified path is entered use that, else assume the keys are
12523 stored in the data directory.
12524 */
12525 if (strchr(key, FN_LIBCHAR) != NULL ||
12526 strchr(key, FN_LIBCHAR2) != NULL)
12527 key_file_path->set_quick(key, strlen(key), system_charset_info);
12528 else
12529 {
12530 key_file_path->append(mysql_real_data_home, strlen(mysql_real_data_home));
12531 if ((*key_file_path)[key_file_path->length()] != FN_LIBCHAR)
12532 key_file_path->append(FN_LIBCHAR);
12533 key_file_path->append(key);
12534 }
12535 }
12536
12537 /**
12538 @brief Read a key file and store its value in RSA structure
12539
12540 @param key_ptr[out] Address of pointer to RSA. This is set to
12541 point to a non null value if key is correctly
12542 read.
12543 @param is_priv_key[in] Whether we are reading private key or public
12544 key.
12545 @param key_text_buffer[out] To store key file content of public key.
12546
12547 @return Error status
12548 @retval false Success : Either both keys are read or none
12549 are.
12550 @retval true Failure : An appropriate error is raised.
12551 */
read_key_file(RSA ** key_ptr,bool is_priv_key,char ** key_text_buffer)12552 bool read_key_file(RSA **key_ptr, bool is_priv_key, char **key_text_buffer)
12553 {
12554 String key_file_path;
12555 char *key;
12556 const char *key_type;
12557 FILE *key_file= NULL;
12558
12559 key= is_priv_key ? auth_rsa_private_key_path : auth_rsa_public_key_path;
12560 key_type= is_priv_key ? "private" : "public";
12561 *key_ptr= NULL;
12562
12563 get_key_file_path(key, &key_file_path);
12564
12565 /*
12566 Check for existance of private key/public key file.
12567 */
12568 if ((key_file= fopen(key_file_path.c_ptr(), "r")) == NULL)
12569 {
12570 sql_print_information("RSA %s key file not found: %s."
12571 " Some authentication plugins will not work.",
12572 key_type, key_file_path.c_ptr());
12573 }
12574 else
12575 {
12576 *key_ptr= is_priv_key ? PEM_read_RSAPrivateKey(key_file, 0, 0, 0) :
12577 PEM_read_RSA_PUBKEY(key_file, 0, 0, 0);
12578
12579 if (!(*key_ptr))
12580 {
12581 char error_buf[MYSQL_ERRMSG_SIZE];
12582 ERR_error_string_n(ERR_get_error(), error_buf, MYSQL_ERRMSG_SIZE);
12583 sql_print_error("Failure to parse RSA %s key (file exists): %s:"
12584 " %s", key_type, key_file_path.c_ptr(), error_buf);
12585
12586 /*
12587 Call ERR_clear_error() just in case there are more than 1 entry in the
12588 OpenSSL thread's error queue.
12589 */
12590 ERR_clear_error();
12591
12592 return true;
12593 }
12594
12595 /* For public key, read key file content into a char buffer. */
12596 if (!is_priv_key)
12597 {
12598 int filesize;
12599 fseek(key_file, 0, SEEK_END);
12600 filesize= ftell(key_file);
12601 fseek(key_file, 0, SEEK_SET);
12602 *key_text_buffer= new char[filesize+1];
12603 const size_t read_size = fread(*key_text_buffer, filesize, 1, key_file);
12604 if (read_size != 1)
12605 {
12606 delete[] key_text_buffer;
12607 key_text_buffer= NULL;
12608 return true;
12609 }
12610 (*key_text_buffer)[filesize]= '\0';
12611 }
12612 fclose(key_file);
12613 }
12614 return false;
12615 }
12616
12617 public:
Rsa_authentication_keys()12618 Rsa_authentication_keys()
12619 {
12620 m_cipher_len= 0;
12621 m_private_key= 0;
12622 m_public_key= 0;
12623 m_pem_public_key= 0;
12624 }
12625
~Rsa_authentication_keys()12626 ~Rsa_authentication_keys()
12627 {
12628 }
12629
free_memory()12630 void free_memory()
12631 {
12632 if (m_private_key)
12633 RSA_free(m_private_key);
12634
12635 if (m_public_key)
12636 {
12637 RSA_free(m_public_key);
12638 m_cipher_len= 0;
12639 }
12640
12641 if (m_pem_public_key)
12642 delete [] m_pem_public_key;
12643 }
12644
allocate_pem_buffer(size_t buffer_len)12645 void *allocate_pem_buffer(size_t buffer_len)
12646 {
12647 m_pem_public_key= new char[buffer_len];
12648 return m_pem_public_key;
12649 }
12650
get_private_key()12651 RSA *get_private_key()
12652 {
12653 return m_private_key;
12654 }
12655
get_public_key()12656 RSA *get_public_key()
12657 {
12658 return m_public_key;
12659 }
12660
get_cipher_length()12661 int get_cipher_length()
12662 {
12663 return (m_cipher_len= RSA_size(m_public_key));
12664 }
12665
12666 /**
12667 @brief Read RSA private key and public key from file and store them
12668 in m_private_key and m_public_key. Also, read public key in
12669 text format and store it in m_pem_public_key.
12670
12671 @return Error status
12672 @retval false Success : Either both keys are read or none are.
12673 @retval true Failure : An appropriate error is raised.
12674 */
read_rsa_keys()12675 bool read_rsa_keys()
12676 {
12677 RSA *rsa_private_key_ptr= NULL;
12678 RSA *rsa_public_key_ptr= NULL;
12679 char *pub_key_buff= NULL;
12680
12681 if ((strlen(auth_rsa_private_key_path) == 0) &&
12682 (strlen(auth_rsa_public_key_path) == 0))
12683 {
12684 sql_print_information("RSA key files not found."
12685 " Some authentication plugins will not work.");
12686 return false;
12687 }
12688
12689 /*
12690 Read private key in RSA format.
12691 */
12692 if (read_key_file(&rsa_private_key_ptr, true, NULL))
12693 return true;
12694
12695 /*
12696 Read public key in RSA format.
12697 */
12698 if (read_key_file(&rsa_public_key_ptr, false, &pub_key_buff))
12699 {
12700 if (rsa_private_key_ptr)
12701 RSA_free(rsa_private_key_ptr);
12702 return true;
12703 }
12704
12705 /*
12706 If both key files are read successfully then assign values to following
12707 members of the class
12708 1. m_pem_public_key
12709 2. m_private_key
12710 3. m_public_key
12711
12712 Else clean up.
12713 */
12714 if (rsa_private_key_ptr && rsa_public_key_ptr)
12715 {
12716 int buff_len= strlen(pub_key_buff);
12717 char *pem_file_buffer= (char *)allocate_pem_buffer(buff_len + 1);
12718 strncpy(pem_file_buffer, pub_key_buff, buff_len);
12719 pem_file_buffer[buff_len]= '\0';
12720
12721 m_private_key= rsa_private_key_ptr;
12722 m_public_key= rsa_public_key_ptr;
12723
12724 delete [] pub_key_buff;
12725 }
12726 else
12727 {
12728 if (rsa_private_key_ptr)
12729 RSA_free(rsa_private_key_ptr);
12730
12731 if (rsa_public_key_ptr)
12732 {
12733 delete [] pub_key_buff;
12734 RSA_free(rsa_public_key_ptr);
12735 }
12736 }
12737 return false;
12738 }
12739
get_public_key_as_pem(void)12740 const char *get_public_key_as_pem(void)
12741 {
12742 return m_pem_public_key;
12743 }
12744
12745 };
12746
12747 static Rsa_authentication_keys g_rsa_keys;
12748
12749 /**
12750
12751 */
show_rsa_public_key(THD * thd,SHOW_VAR * var,char * buff)12752 int show_rsa_public_key(THD *thd, SHOW_VAR *var, char *buff)
12753 {
12754 var->type= SHOW_CHAR;
12755 var->value= const_cast<char *>(g_rsa_keys.get_public_key_as_pem());
12756
12757 return 0;
12758 }
12759
deinit_rsa_keys(void)12760 void deinit_rsa_keys(void)
12761 {
12762 g_rsa_keys.free_memory();
12763 }
12764
12765 // Wraps a FILE handle, to ensure we always close it when returning.
12766 class FileCloser
12767 {
12768 FILE *m_file;
12769 public:
FileCloser(FILE * to_be_closed)12770 FileCloser(FILE *to_be_closed) : m_file(to_be_closed) {}
~FileCloser()12771 ~FileCloser()
12772 {
12773 if (m_file != NULL)
12774 fclose(m_file);
12775 }
12776 };
12777
12778 /**
12779 Loads the RSA key pair from disk and store them in a global variable.
12780
12781 @see init_ssl()
12782
12783 @return Error code
12784 @retval false Success
12785 @retval true Error
12786 */
12787
init_rsa_keys(void)12788 bool init_rsa_keys(void)
12789 {
12790 return (g_rsa_keys.read_rsa_keys());
12791 }
12792
12793 static MYSQL_PLUGIN plugin_info_ptr;
12794
init_sha256_password_handler(MYSQL_PLUGIN plugin_ref)12795 int init_sha256_password_handler(MYSQL_PLUGIN plugin_ref)
12796 {
12797 plugin_info_ptr= plugin_ref;
12798 return 0;
12799 }
12800
12801 /**
12802
12803 @param vio Virtual input-, output interface
12804 @param info[out] Connection information
12805
12806 Authenticate the user by recieving a RSA or TLS encrypted password and
12807 calculate a hash digest which should correspond to the user record digest
12808
12809 RSA keys are assumed to be pre-generated and supplied when server starts. If
12810 the client hasn't got a public key it can request one.
12811
12812 TLS certificates and keys are assumed to be pre-generated and supplied when
12813 server starts.
12814
12815 */
12816
sha256_password_authenticate(MYSQL_PLUGIN_VIO * vio,MYSQL_SERVER_AUTH_INFO * info)12817 static int sha256_password_authenticate(MYSQL_PLUGIN_VIO *vio,
12818 MYSQL_SERVER_AUTH_INFO *info)
12819 {
12820 uchar *pkt;
12821 int pkt_len;
12822 char *user_salt_begin;
12823 char *user_salt_end;
12824 char scramble[SCRAMBLE_LENGTH + 1];
12825 char stage2[CRYPT_MAX_PASSWORD_SIZE + 1];
12826 String scramble_response_packet;
12827 int cipher_length= 0;
12828 unsigned char plain_text[MAX_CIPHER_LENGTH];
12829 RSA *private_key= NULL;
12830 RSA *public_key= NULL;
12831
12832 DBUG_ENTER("sha256_password_authenticate");
12833
12834 generate_user_salt(scramble, SCRAMBLE_LENGTH + 1);
12835
12836 /*
12837 Note: The nonce is split into 8 + 12 bytes according to
12838 http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeV10
12839 Native authentication sent 20 bytes + '\0' character = 21 bytes.
12840 This plugin must do the same to stay consistent with historical behavior
12841 if it is set to operate as a default plugin.
12842 */
12843 scramble[SCRAMBLE_LENGTH] = '\0';
12844 if (vio->write_packet(vio, (unsigned char *) scramble, SCRAMBLE_LENGTH + 1))
12845 DBUG_RETURN(CR_ERROR);
12846
12847 /*
12848 After the call to read_packet() the user name will appear in
12849 mpvio->acl_user and info will contain current data.
12850 */
12851 if ((pkt_len= vio->read_packet(vio, &pkt)) == -1)
12852 DBUG_RETURN(CR_ERROR);
12853
12854 /*
12855 If first packet is a 0 byte then the client isn't sending any password
12856 else the client will send a password.
12857
12858 The original intention was that the password is a string[NUL] but this
12859 never got enforced properly so now we have to accept that an empty packet
12860 is a blank password, thus the check for pkt_len == 0 has to be made too.
12861 */
12862 if ((pkt_len == 0 || pkt_len == 1) && *pkt == 0)
12863 {
12864 info->password_used= PASSWORD_USED_NO;
12865 /*
12866 Send OK signal; the authentication might still be rejected based on
12867 host mask.
12868 */
12869 if (info->auth_string_length == 0)
12870 DBUG_RETURN(CR_OK);
12871 else
12872 DBUG_RETURN(CR_ERROR);
12873 }
12874 else
12875 info->password_used= PASSWORD_USED_YES;
12876
12877 if (!my_vio_is_encrypted(vio))
12878 {
12879 /*
12880 Since a password is being used it must be encrypted by RSA since no
12881 other encryption is being active.
12882 */
12883 private_key= g_rsa_keys.get_private_key();
12884 public_key= g_rsa_keys.get_public_key();
12885
12886 /*
12887 Without the keys encryption isn't possible.
12888 */
12889 if (private_key == NULL || public_key == NULL)
12890 {
12891 my_plugin_log_message(&plugin_info_ptr, MY_ERROR_LEVEL,
12892 "Authentication requires either RSA keys or SSL encryption");
12893 DBUG_RETURN(CR_ERROR);
12894 }
12895
12896
12897 if ((cipher_length= g_rsa_keys.get_cipher_length()) > MAX_CIPHER_LENGTH)
12898 {
12899 my_plugin_log_message(&plugin_info_ptr, MY_ERROR_LEVEL,
12900 "RSA key cipher length of %u is too long. Max value is %u.",
12901 g_rsa_keys.get_cipher_length(), MAX_CIPHER_LENGTH);
12902 DBUG_RETURN(CR_ERROR);
12903 }
12904
12905 /*
12906 Client sent a "public key request"-packet ?
12907 If the first packet is 1 then the client will require a public key before
12908 encrypting the password.
12909 */
12910 if (pkt_len == 1 && *pkt == 1)
12911 {
12912 uint pem_length= strlen(g_rsa_keys.get_public_key_as_pem());
12913 if (vio->write_packet(vio,
12914 (unsigned char *)g_rsa_keys.get_public_key_as_pem(),
12915 pem_length))
12916 DBUG_RETURN(CR_ERROR);
12917 /* Get the encrypted response from the client */
12918 if ((pkt_len= vio->read_packet(vio, &pkt)) == -1)
12919 DBUG_RETURN(CR_ERROR);
12920 }
12921
12922 /*
12923 The packet will contain the cipher used. The length of the packet
12924 must correspond to the expected cipher length.
12925 */
12926 if (pkt_len != cipher_length)
12927 DBUG_RETURN(CR_ERROR);
12928
12929 /* Decrypt password */
12930 RSA_private_decrypt(cipher_length, pkt, plain_text, private_key,
12931 RSA_PKCS1_OAEP_PADDING);
12932
12933 plain_text[cipher_length]= '\0'; // safety
12934 xor_string((char *) plain_text, cipher_length,
12935 (char *) scramble, SCRAMBLE_LENGTH);
12936
12937 /*
12938 Set packet pointers and length for the hash digest function below
12939 */
12940 pkt= plain_text;
12941 pkt_len= strlen((char *) plain_text) + 1; // include \0 intentionally.
12942
12943 if (pkt_len == 1)
12944 DBUG_RETURN(CR_ERROR);
12945 } // if(!my_vio_is_encrypter())
12946
12947 if (pkt_len > SHA256_PASSWORD_MAX_PASSWORD_LENGTH + 1)
12948 DBUG_RETURN(CR_ERROR);
12949
12950 /* A password was sent to an account without a password */
12951 if (info->auth_string_length == 0)
12952 DBUG_RETURN(CR_ERROR);
12953
12954 /*
12955 Fetch user authentication_string and extract the password salt
12956 */
12957 user_salt_begin= (char *) info->auth_string;
12958 user_salt_end= (char *) (info->auth_string + info->auth_string_length);
12959 if (extract_user_salt(&user_salt_begin, &user_salt_end) != CRYPT_SALT_LENGTH)
12960 {
12961 /* User salt is not correct */
12962 my_plugin_log_message(&plugin_info_ptr, MY_ERROR_LEVEL,
12963 "Password salt for user '%s' is corrupt.",
12964 info->user_name);
12965 DBUG_RETURN(CR_ERROR);
12966 }
12967
12968 /* Create hash digest */
12969 my_crypt_genhash(stage2,
12970 CRYPT_MAX_PASSWORD_SIZE,
12971 (char *) pkt,
12972 pkt_len-1,
12973 (char *) user_salt_begin,
12974 (const char **) 0);
12975
12976 /* Compare the newly created hash digest with the password record */
12977 int result= memcmp(info->auth_string,
12978 stage2,
12979 info->auth_string_length);
12980
12981 if (result == 0)
12982 DBUG_RETURN(CR_OK);
12983
12984 DBUG_RETURN(CR_ERROR);
12985 }
12986
12987 static MYSQL_SYSVAR_STR(private_key_path, auth_rsa_private_key_path,
12988 PLUGIN_VAR_READONLY,
12989 "A fully qualified path to the private RSA key used for authentication",
12990 NULL, NULL, AUTH_DEFAULT_RSA_PRIVATE_KEY);
12991 static MYSQL_SYSVAR_STR(public_key_path, auth_rsa_public_key_path,
12992 PLUGIN_VAR_READONLY,
12993 "A fully qualified path to the public RSA key used for authentication",
12994 NULL, NULL, AUTH_DEFAULT_RSA_PUBLIC_KEY);
12995
12996 static struct st_mysql_sys_var* sha256_password_sysvars[]= {
12997 MYSQL_SYSVAR(private_key_path),
12998 MYSQL_SYSVAR(public_key_path),
12999 0
13000 };
13001 #endif // HAVE_OPENSSL
13002
13003 static struct st_mysql_auth native_password_handler=
13004 {
13005 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
13006 native_password_plugin_name.str,
13007 native_password_authenticate
13008 };
13009
13010 static struct st_mysql_auth old_password_handler=
13011 {
13012 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
13013 old_password_plugin_name.str,
13014 old_password_authenticate
13015 };
13016
13017 #if defined(HAVE_OPENSSL)
13018 static struct st_mysql_auth sha256_password_handler=
13019 {
13020 MYSQL_AUTHENTICATION_INTERFACE_VERSION,
13021 sha256_password_plugin_name.str,
13022 sha256_password_authenticate
13023 };
13024 #endif
13025
mysql_declare_plugin(mysql_password)13026 mysql_declare_plugin(mysql_password)
13027 {
13028 MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
13029 &native_password_handler, /* type descriptor */
13030 native_password_plugin_name.str, /* Name */
13031 "R.J.Silk, Sergei Golubchik", /* Author */
13032 "Native MySQL authentication", /* Description */
13033 PLUGIN_LICENSE_GPL, /* License */
13034 NULL, /* Init function */
13035 NULL, /* Deinit function */
13036 0x0100, /* Version (1.0) */
13037 NULL, /* status variables */
13038 NULL, /* system variables */
13039 NULL, /* config options */
13040 0, /* flags */
13041 },
13042 {
13043 MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
13044 &old_password_handler, /* type descriptor */
13045 old_password_plugin_name.str, /* Name */
13046 "R.J.Silk, Sergei Golubchik", /* Author */
13047 "Old MySQL-4.0 authentication", /* Description */
13048 PLUGIN_LICENSE_GPL, /* License */
13049 NULL, /* Init function */
13050 NULL, /* Deinit function */
13051 0x0100, /* Version (1.0) */
13052 NULL, /* status variables */
13053 NULL, /* system variables */
13054 NULL, /* config options */
13055 0, /* flags */
13056 }
13057 #if defined(HAVE_OPENSSL)
13058 ,
13059 {
13060 MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
13061 &sha256_password_handler, /* type descriptor */
13062 sha256_password_plugin_name.str, /* Name */
13063 "Oracle", /* Author */
13064 "SHA256 password authentication", /* Description */
13065 PLUGIN_LICENSE_GPL, /* License */
13066 &init_sha256_password_handler, /* Init function */
13067 NULL, /* Deinit function */
13068 0x0100, /* Version (1.0) */
13069 NULL, /* status variables */
13070 sha256_password_sysvars, /* system variables */
13071 NULL, /* config options */
13072 0 /* flags */
13073 }
13074 #endif
13075 mysql_declare_plugin_end;
13076
13077 /*
13078 PASSWORD_VALIDATION_CODE, invoking appropriate plugin to validate
13079 the password strength.
13080 */
13081
13082 /* for validate_password_strength SQL function */
check_password_strength(String * password)13083 int check_password_strength(String *password)
13084 {
13085 int res= 0;
13086 DBUG_ASSERT(password != NULL);
13087 plugin_ref plugin= my_plugin_lock_by_name(0, &validate_password_plugin_name,
13088 MYSQL_VALIDATE_PASSWORD_PLUGIN);
13089 if (plugin)
13090 {
13091 st_mysql_validate_password *password_strength=
13092 (st_mysql_validate_password *) plugin_decl(plugin)->info;
13093
13094 res= password_strength->get_password_strength(password);
13095 plugin_unlock(0, plugin);
13096 }
13097 return(res);
13098 }
13099
13100 /* called when new user is created or exsisting password is changed */
check_password_policy(String * password)13101 int check_password_policy(String *password)
13102 {
13103 plugin_ref plugin;
13104 String empty_string;
13105
13106 if (!password)
13107 password= &empty_string;
13108
13109 plugin= my_plugin_lock_by_name(0, &validate_password_plugin_name,
13110 MYSQL_VALIDATE_PASSWORD_PLUGIN);
13111 if (plugin)
13112 {
13113 st_mysql_validate_password *password_validate=
13114 (st_mysql_validate_password *) plugin_decl(plugin)->info;
13115
13116 if (!password_validate->validate_password(password))
13117 {
13118 my_error(ER_NOT_VALID_PASSWORD, MYF(0));
13119 plugin_unlock(0, plugin);
13120 return (1);
13121 }
13122 plugin_unlock(0, plugin);
13123 }
13124 return (0);
13125 }
13126
13127 #ifndef NO_EMBEDDED_ACCESS_CHECKS
13128 my_bool validate_user_plugins= TRUE;
13129
13130 /**
13131 Iterate over the user records and check for irregularities.
13132 Currently this includes :
13133 - checking if the plugin referenced is present.
13134 - if there's sha256 users and there's neither SSL nor RSA configured
13135 */
13136 static void
validate_user_plugin_records()13137 validate_user_plugin_records()
13138 {
13139 DBUG_ENTER("validate_user_plugin_records");
13140 if (!validate_user_plugins)
13141 DBUG_VOID_RETURN;
13142
13143 lock_plugin_data();
13144 for (uint i=0 ; i < acl_users.elements ; i++)
13145 {
13146 struct st_plugin_int *plugin;
13147 ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
13148
13149 if (acl_user->plugin.length)
13150 {
13151 /* rule 1 : plugin does exit */
13152 if (!auth_plugin_is_built_in(acl_user->plugin.str))
13153 {
13154 plugin= plugin_find_by_type(&acl_user->plugin,
13155 MYSQL_AUTHENTICATION_PLUGIN);
13156
13157 if (!plugin)
13158 {
13159 sql_print_warning("The plugin '%.*s' used to authenticate "
13160 "user '%s'@'%.*s' is not loaded."
13161 " Nobody can currently login using this account.",
13162 (int) acl_user->plugin.length, acl_user->plugin.str,
13163 acl_user->user,
13164 acl_user->host.get_host_len(),
13165 acl_user->host.get_host());
13166 }
13167 }
13168 if (acl_user->plugin.str == sha256_password_plugin_name.str &&
13169 (!g_rsa_keys.get_private_key() || !g_rsa_keys.get_public_key()) &&
13170 !ssl_acceptor_fd)
13171 {
13172 sql_print_warning("The plugin '%s' is used to authenticate "
13173 "user '%s'@'%.*s', "
13174 "but neither SSL nor RSA keys are "
13175 "configured. "
13176 "Nobody can currently login using this account.",
13177 sha256_password_plugin_name.str,
13178 acl_user->user,
13179 acl_user->host.get_host_len(),
13180 acl_user->host.get_host());
13181 }
13182 }
13183 }
13184 unlock_plugin_data();
13185 DBUG_VOID_RETURN;
13186 }
13187
13188 #endif // NO_EMBEDDED_ACCESS_CHECKS
13189
13190