1 | /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. |
2 | Copyright (c) 2009, 2018, MariaDB |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ |
16 | |
17 | |
18 | /* |
19 | The privileges are saved in the following tables: |
20 | mysql/user ; super user who are allowed to do almost anything |
21 | mysql/host ; host privileges. This is used if host is empty in mysql/db. |
22 | mysql/db ; database privileges / user |
23 | |
24 | data in tables is sorted according to how many not-wild-cards there is |
25 | in the relevant fields. Empty strings comes last. |
26 | */ |
27 | |
28 | #include "mariadb.h" /* NO_EMBEDDED_ACCESS_CHECKS */ |
29 | #include "sql_priv.h" |
30 | #include "sql_acl.h" // MYSQL_DB_FIELD_COUNT, ACL_ACCESS |
31 | #include "sql_base.h" // close_mysql_tables |
32 | #include "key.h" // key_copy, key_cmp_if_same, key_restore |
33 | #include "sql_show.h" // append_identifier |
34 | #include "sql_table.h" // write_bin_log |
35 | #include "hash_filo.h" |
36 | #include "sql_parse.h" // check_access |
37 | #include "sql_view.h" // VIEW_ANY_ACL |
38 | #include "records.h" // READ_RECORD, read_record_info, |
39 | // init_read_record, end_read_record |
40 | #include "rpl_filter.h" // rpl_filter |
41 | #include "rpl_rli.h" |
42 | #include <m_ctype.h> |
43 | #include <stdarg.h> |
44 | #include "sp_head.h" |
45 | #include "sp.h" |
46 | #include "transaction.h" |
47 | #include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT |
48 | #include <sql_common.h> |
49 | #include <mysql/plugin_auth.h> |
50 | #include <mysql/plugin_password_validation.h> |
51 | #include "sql_connect.h" |
52 | #include "hostname.h" |
53 | #include "sql_db.h" |
54 | #include "sql_array.h" |
55 | #include "sql_hset.h" |
56 | #include "password.h" |
57 | |
58 | #include "sql_plugin_compat.h" |
59 | |
60 | bool mysql_user_table_is_in_short_password_format= false; |
61 | |
62 | static LEX_CSTRING native_password_plugin_name= { |
63 | STRING_WITH_LEN("mysql_native_password" ) |
64 | }; |
65 | |
66 | static LEX_CSTRING old_password_plugin_name= { |
67 | STRING_WITH_LEN("mysql_old_password" ) |
68 | }; |
69 | |
70 | /// @todo make it configurable |
71 | LEX_CSTRING *default_auth_plugin_name= &native_password_plugin_name; |
72 | |
73 | /* |
74 | Wildcard host, matches any hostname |
75 | */ |
76 | LEX_CSTRING host_not_specified= { STRING_WITH_LEN("%" ) }; |
77 | |
78 | /* |
79 | Constants, used in the SHOW GRANTS command. |
80 | Their actual string values are irrelevant, they're always compared |
81 | as pointers to these string constants. |
82 | */ |
83 | LEX_CSTRING current_user= { STRING_WITH_LEN("*current_user" ) }; |
84 | LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role" ) }; |
85 | LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role" ) }; |
86 | |
87 | |
88 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
89 | static plugin_ref old_password_plugin; |
90 | #endif |
91 | static plugin_ref native_password_plugin; |
92 | |
93 | /* Classes */ |
94 | |
95 | struct acl_host_and_ip |
96 | { |
97 | char *hostname; |
98 | long ip, ip_mask; // Used with masked ip:s |
99 | }; |
100 | |
101 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
102 | static bool compare_hostname(const acl_host_and_ip *, const char *, const char *); |
103 | #else |
104 | #define compare_hostname(X,Y,Z) 0 |
105 | #endif |
106 | |
107 | class ACL_ACCESS { |
108 | public: |
109 | ulong sort; |
110 | ulong access; |
111 | }; |
112 | |
113 | /* ACL_HOST is used if no host is specified */ |
114 | |
115 | class ACL_HOST :public ACL_ACCESS |
116 | { |
117 | public: |
118 | acl_host_and_ip host; |
119 | char *db; |
120 | }; |
121 | |
122 | class ACL_USER_BASE :public ACL_ACCESS |
123 | { |
124 | |
125 | public: |
126 | static void *operator new(size_t size, MEM_ROOT *mem_root) |
127 | { return (void*) alloc_root(mem_root, size); } |
128 | static void operator delete(void *, MEM_ROOT *){} |
129 | uchar flags; // field used to store various state information |
130 | LEX_CSTRING user; |
131 | /* list to hold references to granted roles (ACL_ROLE instances) */ |
132 | DYNAMIC_ARRAY role_grants; |
133 | }; |
134 | |
135 | class ACL_USER :public ACL_USER_BASE |
136 | { |
137 | public: |
138 | acl_host_and_ip host; |
139 | size_t hostname_length; |
140 | USER_RESOURCES user_resource; |
141 | uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form |
142 | uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1 |
143 | enum SSL_type ssl_type; |
144 | const char *ssl_cipher, *x509_issuer, *x509_subject; |
145 | LEX_CSTRING plugin; |
146 | LEX_CSTRING auth_string; |
147 | LEX_CSTRING default_rolename; |
148 | |
149 | ACL_USER *copy(MEM_ROOT *root) |
150 | { |
151 | ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER)); |
152 | if (!dst) |
153 | return 0; |
154 | *dst= *this; |
155 | dst->user.str= safe_strdup_root(root, user.str); |
156 | dst->user.length= user.length; |
157 | dst->ssl_cipher= safe_strdup_root(root, ssl_cipher); |
158 | dst->x509_issuer= safe_strdup_root(root, x509_issuer); |
159 | dst->x509_subject= safe_strdup_root(root, x509_subject); |
160 | if (plugin.str == native_password_plugin_name.str || |
161 | plugin.str == old_password_plugin_name.str) |
162 | dst->plugin= plugin; |
163 | else |
164 | dst->plugin.str= strmake_root(root, plugin.str, plugin.length); |
165 | dst->auth_string.str= safe_strdup_root(root, auth_string.str); |
166 | dst->host.hostname= safe_strdup_root(root, host.hostname); |
167 | dst->default_rolename.str= safe_strdup_root(root, default_rolename.str); |
168 | dst->default_rolename.length= default_rolename.length; |
169 | bzero(&dst->role_grants, sizeof(role_grants)); |
170 | return dst; |
171 | } |
172 | |
173 | int cmp(const char *user2, const char *host2) |
174 | { |
175 | CHARSET_INFO *cs= system_charset_info; |
176 | int res; |
177 | res= strcmp(safe_str(user.str), safe_str(user2)); |
178 | if (!res) |
179 | res= my_strcasecmp(cs, host.hostname, host2); |
180 | return res; |
181 | } |
182 | |
183 | bool eq(const char *user2, const char *host2) { return !cmp(user2, host2); } |
184 | |
185 | bool wild_eq(const char *user2, const char *host2, const char *ip2) |
186 | { |
187 | if (strcmp(safe_str(user.str), safe_str(user2))) |
188 | return false; |
189 | |
190 | return compare_hostname(&host, host2, ip2 ? ip2 : host2); |
191 | } |
192 | }; |
193 | |
194 | class ACL_ROLE :public ACL_USER_BASE |
195 | { |
196 | public: |
197 | /* |
198 | In case of granting a role to a role, the access bits are merged together |
199 | via a bit OR operation and placed in the ACL_USER::access field. |
200 | |
201 | When rebuilding role_grants via the rebuild_role_grant function, |
202 | the ACL_USER::access field needs to be reset first. The field |
203 | initial_role_access holds initial grants, as granted directly to the role |
204 | */ |
205 | ulong initial_role_access; |
206 | /* |
207 | In subgraph traversal, when we need to traverse only a part of the graph |
208 | (e.g. all direct and indirect grantees of a role X), the counter holds the |
209 | number of affected neighbour nodes. |
210 | See also propagate_role_grants() |
211 | */ |
212 | uint counter; |
213 | DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted |
214 | |
215 | ACL_ROLE(ACL_USER * user, MEM_ROOT *mem); |
216 | ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *mem); |
217 | |
218 | }; |
219 | |
220 | class ACL_DB :public ACL_ACCESS |
221 | { |
222 | public: |
223 | acl_host_and_ip host; |
224 | const char *user,*db; |
225 | ulong initial_access; /* access bits present in the table */ |
226 | }; |
227 | |
228 | #ifndef DBUG_OFF |
229 | /* status variables, only visible in SHOW STATUS after -#d,role_merge_stats */ |
230 | ulong role_global_merges= 0, role_db_merges= 0, role_table_merges= 0, |
231 | role_column_merges= 0, role_routine_merges= 0; |
232 | #endif |
233 | |
234 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
235 | static bool fix_and_copy_user(LEX_USER *to, LEX_USER *from, THD *thd); |
236 | static void update_hostname(acl_host_and_ip *host, const char *hostname); |
237 | static ulong get_sort(uint count,...); |
238 | static bool show_proxy_grants (THD *, const char *, const char *, |
239 | char *, size_t); |
240 | static bool show_role_grants(THD *, const char *, const char *, |
241 | ACL_USER_BASE *, char *, size_t); |
242 | static bool show_global_privileges(THD *, ACL_USER_BASE *, |
243 | bool, char *, size_t); |
244 | static bool show_database_privileges(THD *, const char *, const char *, |
245 | char *, size_t); |
246 | static bool show_table_and_column_privileges(THD *, const char *, const char *, |
247 | char *, size_t); |
248 | static int show_routine_grants(THD *, const char *, const char *, |
249 | const Sp_handler *sph, char *, int); |
250 | |
251 | class Grant_tables; |
252 | class User_table; |
253 | class Proxies_priv_table; |
254 | |
255 | class ACL_PROXY_USER :public ACL_ACCESS |
256 | { |
257 | acl_host_and_ip host; |
258 | const char *user; |
259 | acl_host_and_ip proxied_host; |
260 | const char *proxied_user; |
261 | bool with_grant; |
262 | |
263 | typedef enum { |
264 | MYSQL_PROXIES_PRIV_HOST, |
265 | MYSQL_PROXIES_PRIV_USER, |
266 | MYSQL_PROXIES_PRIV_PROXIED_HOST, |
267 | MYSQL_PROXIES_PRIV_PROXIED_USER, |
268 | MYSQL_PROXIES_PRIV_WITH_GRANT, |
269 | MYSQL_PROXIES_PRIV_GRANTOR, |
270 | MYSQL_PROXIES_PRIV_TIMESTAMP } proxy_table_fields; |
271 | public: |
272 | ACL_PROXY_USER () {}; |
273 | |
274 | void init(const char *host_arg, const char *user_arg, |
275 | const char *proxied_host_arg, const char *proxied_user_arg, |
276 | bool with_grant_arg) |
277 | { |
278 | user= (user_arg && *user_arg) ? user_arg : NULL; |
279 | update_hostname (&host, (host_arg && *host_arg) ? host_arg : NULL); |
280 | proxied_user= (proxied_user_arg && *proxied_user_arg) ? |
281 | proxied_user_arg : NULL; |
282 | update_hostname (&proxied_host, |
283 | (proxied_host_arg && *proxied_host_arg) ? |
284 | proxied_host_arg : NULL); |
285 | with_grant= with_grant_arg; |
286 | sort= get_sort(4, host.hostname, user, proxied_host.hostname, proxied_user); |
287 | } |
288 | |
289 | void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg, |
290 | const char *proxied_host_arg, const char *proxied_user_arg, |
291 | bool with_grant_arg) |
292 | { |
293 | init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL, |
294 | (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL, |
295 | (proxied_host_arg && *proxied_host_arg) ? |
296 | strdup_root (mem, proxied_host_arg) : NULL, |
297 | (proxied_user_arg && *proxied_user_arg) ? |
298 | strdup_root (mem, proxied_user_arg) : NULL, |
299 | with_grant_arg); |
300 | } |
301 | |
302 | void init(const Proxies_priv_table& proxies_priv_table, MEM_ROOT *mem); |
303 | |
304 | bool get_with_grant() { return with_grant; } |
305 | const char *get_user() { return user; } |
306 | const char *get_host() { return host.hostname; } |
307 | const char *get_proxied_user() { return proxied_user; } |
308 | const char *get_proxied_host() { return proxied_host.hostname; } |
309 | void set_user(MEM_ROOT *mem, const char *user_arg) |
310 | { |
311 | user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL; |
312 | } |
313 | void set_host(MEM_ROOT *mem, const char *host_arg) |
314 | { |
315 | update_hostname(&host, safe_strdup_root(mem, host_arg)); |
316 | } |
317 | |
318 | bool check_validity(bool check_no_resolve) |
319 | { |
320 | if (check_no_resolve && |
321 | (hostname_requires_resolving(host.hostname) || |
322 | hostname_requires_resolving(proxied_host.hostname))) |
323 | { |
324 | sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' " |
325 | "ignored in --skip-name-resolve mode." , |
326 | safe_str(proxied_user), |
327 | safe_str(proxied_host.hostname), |
328 | safe_str(user), |
329 | safe_str(host.hostname)); |
330 | return TRUE; |
331 | } |
332 | return FALSE; |
333 | } |
334 | |
335 | bool matches(const char *host_arg, const char *user_arg, const char *ip_arg, |
336 | const char *proxied_user_arg) |
337 | { |
338 | DBUG_ENTER("ACL_PROXY_USER::matches" ); |
339 | DBUG_PRINT("info" , ("compare_hostname(%s,%s,%s) &&" |
340 | "compare_hostname(%s,%s,%s) &&" |
341 | "wild_compare (%s,%s) &&" |
342 | "wild_compare (%s,%s)" , |
343 | host.hostname, host_arg, ip_arg, proxied_host.hostname, |
344 | host_arg, ip_arg, user_arg, user, |
345 | proxied_user_arg, proxied_user)); |
346 | DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) && |
347 | compare_hostname(&proxied_host, host_arg, ip_arg) && |
348 | (!user || |
349 | (user_arg && !wild_compare(user_arg, user, TRUE))) && |
350 | (!proxied_user || |
351 | (proxied_user && !wild_compare(proxied_user_arg, |
352 | proxied_user, TRUE)))); |
353 | } |
354 | |
355 | |
356 | inline static bool auth_element_equals(const char *a, const char *b) |
357 | { |
358 | return (a == b || (a != NULL && b != NULL && !strcmp(a,b))); |
359 | } |
360 | |
361 | |
362 | bool pk_equals(ACL_PROXY_USER *grant) |
363 | { |
364 | DBUG_ENTER("pk_equals" ); |
365 | DBUG_PRINT("info" , ("strcmp(%s,%s) &&" |
366 | "strcmp(%s,%s) &&" |
367 | "wild_compare (%s,%s) &&" |
368 | "wild_compare (%s,%s)" , |
369 | user, grant->user, proxied_user, grant->proxied_user, |
370 | host.hostname, grant->host.hostname, |
371 | proxied_host.hostname, grant->proxied_host.hostname)); |
372 | |
373 | bool res= auth_element_equals(user, grant->user) && |
374 | auth_element_equals(proxied_user, grant->proxied_user) && |
375 | auth_element_equals(host.hostname, grant->host.hostname) && |
376 | auth_element_equals(proxied_host.hostname, |
377 | grant->proxied_host.hostname); |
378 | DBUG_RETURN(res); |
379 | } |
380 | |
381 | |
382 | bool granted_on(const char *host_arg, const char *user_arg) |
383 | { |
384 | return (((!user && (!user_arg || !user_arg[0])) || |
385 | (user && user_arg && !strcmp(user, user_arg))) && |
386 | ((!host.hostname && (!host_arg || !host_arg[0])) || |
387 | (host.hostname && host_arg && !strcmp(host.hostname, host_arg)))); |
388 | } |
389 | |
390 | |
391 | void print_grant(String *str) |
392 | { |
393 | str->append(STRING_WITH_LEN("GRANT PROXY ON '" )); |
394 | if (proxied_user) |
395 | str->append(proxied_user, strlen(proxied_user)); |
396 | str->append(STRING_WITH_LEN("'@'" )); |
397 | if (proxied_host.hostname) |
398 | str->append(proxied_host.hostname, strlen(proxied_host.hostname)); |
399 | str->append(STRING_WITH_LEN("' TO '" )); |
400 | if (user) |
401 | str->append(user, strlen(user)); |
402 | str->append(STRING_WITH_LEN("'@'" )); |
403 | if (host.hostname) |
404 | str->append(host.hostname, strlen(host.hostname)); |
405 | str->append(STRING_WITH_LEN("'" )); |
406 | if (with_grant) |
407 | str->append(STRING_WITH_LEN(" WITH GRANT OPTION" )); |
408 | } |
409 | |
410 | void set_data(ACL_PROXY_USER *grant) |
411 | { |
412 | with_grant= grant->with_grant; |
413 | } |
414 | |
415 | static int store_pk(TABLE *table, |
416 | const LEX_CSTRING *host, |
417 | const LEX_CSTRING *user, |
418 | const LEX_CSTRING *proxied_host, |
419 | const LEX_CSTRING *proxied_user) |
420 | { |
421 | DBUG_ENTER("ACL_PROXY_USER::store_pk" ); |
422 | DBUG_PRINT("info" , ("host=%s, user=%s, proxied_host=%s, proxied_user=%s" , |
423 | host->str, user->str, |
424 | proxied_host->str, proxied_user->str)); |
425 | if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str, |
426 | host->length, |
427 | system_charset_info)) |
428 | DBUG_RETURN(TRUE); |
429 | if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str, |
430 | user->length, |
431 | system_charset_info)) |
432 | DBUG_RETURN(TRUE); |
433 | if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str, |
434 | proxied_host->length, |
435 | system_charset_info)) |
436 | DBUG_RETURN(TRUE); |
437 | if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str, |
438 | proxied_user->length, |
439 | system_charset_info)) |
440 | DBUG_RETURN(TRUE); |
441 | |
442 | DBUG_RETURN(FALSE); |
443 | } |
444 | |
445 | static int store_data_record(TABLE *table, |
446 | const LEX_CSTRING *host, |
447 | const LEX_CSTRING *user, |
448 | const LEX_CSTRING *proxied_host, |
449 | const LEX_CSTRING *proxied_user, |
450 | bool with_grant, |
451 | const char *grantor) |
452 | { |
453 | DBUG_ENTER("ACL_PROXY_USER::store_pk" ); |
454 | if (store_pk(table, host, user, proxied_host, proxied_user)) |
455 | DBUG_RETURN(TRUE); |
456 | DBUG_PRINT("info" , ("with_grant=%s" , with_grant ? "TRUE" : "FALSE" )); |
457 | if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0, |
458 | TRUE)) |
459 | DBUG_RETURN(TRUE); |
460 | if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor, |
461 | strlen(grantor), |
462 | system_charset_info)) |
463 | DBUG_RETURN(TRUE); |
464 | |
465 | DBUG_RETURN(FALSE); |
466 | } |
467 | }; |
468 | |
469 | #define FIRST_NON_YN_FIELD 26 |
470 | |
471 | class acl_entry :public hash_filo_element |
472 | { |
473 | public: |
474 | ulong access; |
475 | uint16 length; |
476 | char key[1]; // Key will be stored here |
477 | }; |
478 | |
479 | |
480 | static uchar* acl_entry_get_key(acl_entry *entry, size_t *length, |
481 | my_bool not_used __attribute__((unused))) |
482 | { |
483 | *length=(uint) entry->length; |
484 | return (uchar*) entry->key; |
485 | } |
486 | |
487 | static uchar* acl_role_get_key(ACL_ROLE *entry, size_t *length, |
488 | my_bool not_used __attribute__((unused))) |
489 | { |
490 | *length=(uint) entry->user.length; |
491 | return (uchar*) entry->user.str; |
492 | } |
493 | |
494 | struct ROLE_GRANT_PAIR : public Sql_alloc |
495 | { |
496 | char *u_uname; |
497 | char *u_hname; |
498 | char *r_uname; |
499 | LEX_STRING hashkey; |
500 | bool with_admin; |
501 | |
502 | bool init(MEM_ROOT *mem, const char *username, const char *hostname, |
503 | const char *rolename, bool with_admin_option); |
504 | }; |
505 | |
506 | static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length, |
507 | my_bool not_used __attribute__((unused))) |
508 | { |
509 | *length=(uint) entry->hashkey.length; |
510 | return (uchar*) entry->hashkey.str; |
511 | } |
512 | |
513 | bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, const char *username, |
514 | const char *hostname, const char *rolename, |
515 | bool with_admin_option) |
516 | { |
517 | size_t uname_l = safe_strlen(username); |
518 | size_t hname_l = safe_strlen(hostname); |
519 | size_t rname_l = safe_strlen(rolename); |
520 | /* |
521 | Create a buffer that holds all 3 NULL terminated strings in succession |
522 | To save memory space, the same buffer is used as the hashkey |
523 | */ |
524 | size_t bufflen = uname_l + hname_l + rname_l + 3; //add the '\0' aswell |
525 | char *buff= (char *)alloc_root(mem, bufflen); |
526 | if (!buff) |
527 | return true; |
528 | |
529 | /* |
530 | Offsets in the buffer for all 3 strings |
531 | */ |
532 | char *username_pos= buff; |
533 | char *hostname_pos= buff + uname_l + 1; |
534 | char *rolename_pos= buff + uname_l + hname_l + 2; |
535 | |
536 | if (username) //prevent undefined behaviour |
537 | memcpy(username_pos, username, uname_l); |
538 | username_pos[uname_l]= '\0'; //#1 string terminator |
539 | u_uname= username_pos; |
540 | |
541 | if (hostname) //prevent undefined behaviour |
542 | memcpy(hostname_pos, hostname, hname_l); |
543 | hostname_pos[hname_l]= '\0'; //#2 string terminator |
544 | u_hname= hostname_pos; |
545 | |
546 | if (rolename) //prevent undefined behaviour |
547 | memcpy(rolename_pos, rolename, rname_l); |
548 | rolename_pos[rname_l]= '\0'; //#3 string terminator |
549 | r_uname= rolename_pos; |
550 | |
551 | hashkey.str = buff; |
552 | hashkey.length = bufflen; |
553 | |
554 | with_admin= with_admin_option; |
555 | |
556 | return false; |
557 | } |
558 | |
559 | #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3) |
560 | #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \ |
561 | 1 + USERNAME_LENGTH + 1) |
562 | |
563 | #if defined(HAVE_OPENSSL) |
564 | /* |
565 | Without SSL the handshake consists of one packet. This packet |
566 | has both client capabilities and scrambled password. |
567 | With SSL the handshake might consist of two packets. If the first |
568 | packet (client capabilities) has CLIENT_SSL flag set, we have to |
569 | switch to SSL and read the second packet. The scrambled password |
570 | is in the second packet and client_capabilities field will be ignored. |
571 | Maybe it is better to accept flags other than CLIENT_SSL from the |
572 | second packet? |
573 | */ |
574 | #define SSL_HANDSHAKE_SIZE 2 |
575 | #define MIN_HANDSHAKE_SIZE 2 |
576 | #else |
577 | #define MIN_HANDSHAKE_SIZE 6 |
578 | #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ |
579 | #define NORMAL_HANDSHAKE_SIZE 6 |
580 | |
581 | #define ROLE_ASSIGN_COLUMN_IDX 44 |
582 | #define DEFAULT_ROLE_COLUMN_IDX 45 |
583 | #define MAX_STATEMENT_TIME_COLUMN_IDX 46 |
584 | |
585 | /* various flags valid for ACL_USER */ |
586 | #define IS_ROLE (1L << 0) |
587 | /* Flag to mark that a ROLE is on the recursive DEPTH_FIRST_SEARCH stack */ |
588 | #define ROLE_ON_STACK (1L << 1) |
589 | /* |
590 | Flag to mark that a ROLE and all it's neighbours have |
591 | been visited |
592 | */ |
593 | #define ROLE_EXPLORED (1L << 2) |
594 | /* Flag to mark that on_node was already called for this role */ |
595 | #define ROLE_OPENED (1L << 3) |
596 | |
597 | static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users; |
598 | static HASH acl_roles; |
599 | /* |
600 | An hash containing mappings user <--> role |
601 | |
602 | A hash is used so as to make updates quickly |
603 | The hashkey used represents all the entries combined |
604 | */ |
605 | static HASH acl_roles_mappings; |
606 | static MEM_ROOT acl_memroot, grant_memroot; |
607 | static bool initialized=0; |
608 | static bool allow_all_hosts=1; |
609 | static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash; |
610 | static HASH package_spec_priv_hash, package_body_priv_hash; |
611 | static DYNAMIC_ARRAY acl_wild_hosts; |
612 | static Hash_filo<acl_entry> *acl_cache; |
613 | static uint grant_version=0; /* Version of priv tables. incremented by acl_load */ |
614 | static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0); |
615 | static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b); |
616 | static ulong get_sort(uint count,...); |
617 | static void init_check_host(void); |
618 | static void rebuild_check_host(void); |
619 | static void rebuild_role_grants(void); |
620 | static ACL_USER *find_user_exact(const char *host, const char *user); |
621 | static ACL_USER *find_user_wild(const char *host, const char *user, const char *ip= 0); |
622 | static ACL_ROLE *find_acl_role(const char *user); |
623 | static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, const LEX_CSTRING *h, const LEX_CSTRING *r); |
624 | static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host); |
625 | static bool update_user_table(THD *, const User_table &, const char *, const char *, const |
626 | char *, size_t new_password_len); |
627 | static bool acl_load(THD *thd, const Grant_tables& grant_tables); |
628 | static inline void get_grantor(THD *thd, char* grantor); |
629 | static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname); |
630 | static bool get_YN_as_bool(Field *field); |
631 | |
632 | #define ROLE_CYCLE_FOUND 2 |
633 | static int traverse_role_graph_up(ACL_ROLE *, void *, |
634 | int (*) (ACL_ROLE *, void *), |
635 | int (*) (ACL_ROLE *, ACL_ROLE *, void *)); |
636 | |
637 | static int traverse_role_graph_down(ACL_USER_BASE *, void *, |
638 | int (*) (ACL_USER_BASE *, void *), |
639 | int (*) (ACL_USER_BASE *, ACL_ROLE *, void *)); |
640 | |
641 | |
642 | HASH *Sp_handler_procedure::get_priv_hash() const |
643 | { |
644 | return &proc_priv_hash; |
645 | } |
646 | |
647 | |
648 | HASH *Sp_handler_function::get_priv_hash() const |
649 | { |
650 | return &func_priv_hash; |
651 | } |
652 | |
653 | |
654 | HASH *Sp_handler_package_spec::get_priv_hash() const |
655 | { |
656 | return &package_spec_priv_hash; |
657 | } |
658 | |
659 | |
660 | HASH *Sp_handler_package_body::get_priv_hash() const |
661 | { |
662 | return &package_body_priv_hash; |
663 | } |
664 | |
665 | |
666 | /* |
667 | Enumeration of ACL/GRANT tables in the mysql database |
668 | */ |
669 | enum enum_acl_tables |
670 | { |
671 | USER_TABLE, |
672 | DB_TABLE, |
673 | TABLES_PRIV_TABLE, |
674 | COLUMNS_PRIV_TABLE, |
675 | #define FIRST_OPTIONAL_TABLE HOST_TABLE |
676 | HOST_TABLE, |
677 | PROCS_PRIV_TABLE, |
678 | PROXIES_PRIV_TABLE, |
679 | ROLES_MAPPING_TABLE, |
680 | TABLES_MAX // <== always the last |
681 | }; |
682 | // bits for open_grant_tables |
683 | static const int Table_user= 1 << USER_TABLE; |
684 | static const int Table_db= 1 << DB_TABLE; |
685 | static const int Table_tables_priv= 1 << TABLES_PRIV_TABLE; |
686 | static const int Table_columns_priv= 1 << COLUMNS_PRIV_TABLE; |
687 | static const int Table_host= 1 << HOST_TABLE; |
688 | static const int Table_procs_priv= 1 << PROCS_PRIV_TABLE; |
689 | static const int Table_proxies_priv= 1 << PROXIES_PRIV_TABLE; |
690 | static const int Table_roles_mapping= 1 << ROLES_MAPPING_TABLE; |
691 | |
692 | /** |
693 | Base class representing a generic grant table from the mysql database. |
694 | |
695 | The potential tables that this class can represent are: |
696 | user, db, columns_priv, tables_priv, host, procs_priv, proxies_priv, |
697 | roles_mapping |
698 | |
699 | Objects belonging to this parent class can only be constructed by the |
700 | Grants_table class. This ensures the correct initialization of the objects. |
701 | */ |
702 | class Grant_table_base |
703 | { |
704 | public: |
705 | /* Number of fields for this Grant Table. */ |
706 | uint num_fields() const { return tl.table->s->fields; } |
707 | /* Check if the table exists after an attempt to open it was made. |
708 | Some tables, such as the host table in MySQL 5.6.7+ are missing. */ |
709 | bool table_exists() const { return tl.table; }; |
710 | /* Initializes the READ_RECORD structure provided as a parameter |
711 | to read through the whole table, with all columns available. Cleaning up |
712 | is the caller's job. */ |
713 | bool init_read_record(READ_RECORD* info, THD* thd) const |
714 | { |
715 | DBUG_ASSERT(tl.table); |
716 | bool result= ::init_read_record(info, thd, tl.table, NULL, NULL, 1, |
717 | true, false); |
718 | if (!result) |
719 | tl.table->use_all_columns(); |
720 | return result; |
721 | } |
722 | |
723 | /* Return the number of privilege columns for this table. */ |
724 | uint num_privileges() const { return num_privilege_cols; } |
725 | /* Return a privilege column by index. */ |
726 | Field* priv_field(uint privilege_idx) const |
727 | { |
728 | DBUG_ASSERT(privilege_idx < num_privileges()); |
729 | return tl.table->field[start_privilege_column + privilege_idx]; |
730 | } |
731 | |
732 | /* Fetch the privileges from the table as a set of bits. The first column |
733 | is represented by the first bit in the result, the second column by the |
734 | second bit, etc. */ |
735 | ulong get_access() const |
736 | { |
737 | return get_access(start_privilege_column, |
738 | start_privilege_column + num_privileges() - 1); |
739 | } |
740 | |
741 | /* Return the underlying TABLE handle. */ |
742 | TABLE* table() const |
743 | { |
744 | return tl.table; |
745 | } |
746 | |
747 | /** Check if the table was opened, issue an error otherwise. */ |
748 | int no_such_table() const |
749 | { |
750 | if (table_exists()) |
751 | return 0; |
752 | |
753 | my_error(ER_NO_SUCH_TABLE, MYF(0), tl.db.str, tl.alias.str); |
754 | return 1; |
755 | } |
756 | |
757 | |
758 | protected: |
759 | friend class Grant_tables; |
760 | |
761 | Grant_table_base() : start_privilege_column(0), num_privilege_cols(0) |
762 | { |
763 | bzero(&tl, sizeof(tl)); |
764 | }; |
765 | |
766 | /* Initialization sequence common for all grant tables. This should be called |
767 | after all table-specific initialization is performed. */ |
768 | void init(enum thr_lock_type lock_type, bool is_optional) |
769 | { |
770 | tl.open_type= OT_BASE_ONLY; |
771 | if (lock_type >= TL_WRITE_ALLOW_WRITE) |
772 | tl.updating= 1; |
773 | if (is_optional) |
774 | tl.open_strategy= TABLE_LIST::OPEN_IF_EXISTS; |
775 | } |
776 | |
777 | /* |
778 | Get all access bits from table between start_field and end_field indices. |
779 | |
780 | IMPLEMENTATION |
781 | The record should be already read in table->record[0]. All privileges |
782 | are specified as an ENUM(Y,N). |
783 | |
784 | SYNOPSIS |
785 | get_access() |
786 | start_field_idx The field index at which the first privilege |
787 | specification begins. |
788 | end_field_idx The field index at which the last privilege |
789 | specification is located. |
790 | |
791 | RETURN VALUE |
792 | privilege mask |
793 | */ |
794 | ulong get_access(uint start_field_idx, uint end_field_idx) const |
795 | { |
796 | ulong access_bits= 0, bit= 1; |
797 | for (uint i = start_field_idx; i <= end_field_idx; i++, bit<<=1) |
798 | { |
799 | if (get_YN_as_bool(tl.table->field[i])) |
800 | access_bits|= bit; |
801 | } |
802 | return access_bits; |
803 | } |
804 | |
805 | /* Compute how many privilege columns this table has. This method |
806 | can only be called after the table has been opened. |
807 | |
808 | IMPLEMENTATION |
809 | A privilege column is of type enum('Y', 'N'). Privilege columns are |
810 | expected to be one after another. |
811 | */ |
812 | void compute_num_privilege_cols() |
813 | { |
814 | if (!table_exists()) // Table does not exist or not opened. |
815 | return; |
816 | |
817 | num_privilege_cols= 0; |
818 | for (uint i= 0; i < num_fields(); i++) |
819 | { |
820 | Field *field= tl.table->field[i]; |
821 | if (num_privilege_cols > 0 && field->real_type() != MYSQL_TYPE_ENUM) |
822 | return; |
823 | if (field->real_type() == MYSQL_TYPE_ENUM && |
824 | static_cast<Field_enum*>(field)->typelib->count == 2) |
825 | { |
826 | num_privilege_cols++; |
827 | if (num_privilege_cols == 1) |
828 | start_privilege_column= i; |
829 | } |
830 | } |
831 | } |
832 | |
833 | /* The index at which privilege columns start. */ |
834 | uint start_privilege_column; |
835 | /* The number of privilege columns in the table. */ |
836 | uint num_privilege_cols; |
837 | |
838 | TABLE_LIST tl; |
839 | }; |
840 | |
841 | class User_table: public Grant_table_base |
842 | { |
843 | public: |
844 | /* Field getters return NULL if the column is not present in the table. |
845 | This is consistent only if the table is in a supported version. We do |
846 | not guard against corrupt tables. (yet) */ |
847 | Field* host() const |
848 | { return get_field(0); } |
849 | Field* user() const |
850 | { return get_field(1); } |
851 | Field* password() const |
852 | { return have_password() ? NULL : tl.table->field[2]; } |
853 | /* Columns after privilege columns. */ |
854 | Field* ssl_type() const |
855 | { return get_field(start_privilege_column + num_privileges()); } |
856 | Field* ssl_cipher() const |
857 | { return get_field(start_privilege_column + num_privileges() + 1); } |
858 | Field* x509_issuer() const |
859 | { return get_field(start_privilege_column + num_privileges() + 2); } |
860 | Field* x509_subject() const |
861 | { return get_field(start_privilege_column + num_privileges() + 3); } |
862 | Field* max_questions() const |
863 | { return get_field(start_privilege_column + num_privileges() + 4); } |
864 | Field* max_updates() const |
865 | { return get_field(start_privilege_column + num_privileges() + 5); } |
866 | Field* max_connections() const |
867 | { return get_field(start_privilege_column + num_privileges() + 6); } |
868 | Field* max_user_connections() const |
869 | { return get_field(start_privilege_column + num_privileges() + 7); } |
870 | Field* plugin() const |
871 | { return get_field(start_privilege_column + num_privileges() + 8); } |
872 | Field* authentication_string() const |
873 | { return get_field(start_privilege_column + num_privileges() + 9); } |
874 | Field* password_expired() const |
875 | { return get_field(start_privilege_column + num_privileges() + 10); } |
876 | Field* is_role() const |
877 | { return get_field(start_privilege_column + num_privileges() + 11); } |
878 | Field* default_role() const |
879 | { return get_field(start_privilege_column + num_privileges() + 12); } |
880 | Field* max_statement_time() const |
881 | { return get_field(start_privilege_column + num_privileges() + 13); } |
882 | |
883 | /* |
884 | Check if a user entry in the user table is marked as being a role entry |
885 | |
886 | IMPLEMENTATION |
887 | Access the coresponding column and check the coresponding ENUM of the form |
888 | ENUM('N', 'Y') |
889 | |
890 | SYNOPSIS |
891 | check_is_role() |
892 | form an open table to read the entry from. |
893 | The record should be already read in table->record[0] |
894 | |
895 | RETURN VALUE |
896 | TRUE if the user is marked as a role |
897 | FALSE otherwise |
898 | */ |
899 | bool check_is_role() const |
900 | { |
901 | /* Table version does not support roles */ |
902 | if (!is_role()) |
903 | return false; |
904 | |
905 | return get_YN_as_bool(is_role()); |
906 | } |
907 | |
908 | |
909 | private: |
910 | friend class Grant_tables; |
911 | |
912 | /* Only Grant_tables can instantiate this class. */ |
913 | User_table() {}; |
914 | |
915 | void init(enum thr_lock_type lock_type) |
916 | { |
917 | /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ |
918 | tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, lock_type); |
919 | Grant_table_base::init(lock_type, false); |
920 | } |
921 | |
922 | /* The user table is a bit different compared to the other Grant tables. |
923 | Usually, we only add columns to the grant tables when adding functionality. |
924 | This makes it easy to test which version of the table we are using, by |
925 | just looking at the number of fields present in the table. |
926 | |
927 | In MySQL 5.7.6 the Password column was removed. We need to guard for that. |
928 | The field-fetching methods for the User table return NULL if the field |
929 | doesn't exist. This simplifies checking of table "version", as we don't |
930 | have to make use of num_fields() any more. |
931 | */ |
932 | inline Field* get_field(uint field_num) const |
933 | { |
934 | if (field_num >= num_fields()) |
935 | return NULL; |
936 | |
937 | return tl.table->field[field_num]; |
938 | } |
939 | |
940 | /* Normally password column is the third column in the table. If privileges |
941 | start on the third column instead, we are missing the password column. |
942 | This means we are using a MySQL 5.7.6+ data directory. */ |
943 | bool have_password() const { return start_privilege_column == 2; } |
944 | |
945 | }; |
946 | |
947 | class Db_table: public Grant_table_base |
948 | { |
949 | public: |
950 | Field* host() const { return tl.table->field[0]; } |
951 | Field* db() const { return tl.table->field[1]; } |
952 | Field* user() const { return tl.table->field[2]; } |
953 | |
954 | private: |
955 | friend class Grant_tables; |
956 | |
957 | Db_table() {}; |
958 | |
959 | void init(enum thr_lock_type lock_type) |
960 | { |
961 | /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ |
962 | tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_DB_NAME, NULL, lock_type); |
963 | Grant_table_base::init(lock_type, false); |
964 | } |
965 | }; |
966 | |
967 | class Tables_priv_table: public Grant_table_base |
968 | { |
969 | public: |
970 | Field* host() const { return tl.table->field[0]; } |
971 | Field* db() const { return tl.table->field[1]; } |
972 | Field* user() const { return tl.table->field[2]; } |
973 | Field* table_name() const { return tl.table->field[3]; } |
974 | Field* grantor() const { return tl.table->field[4]; } |
975 | Field* timestamp() const { return tl.table->field[5]; } |
976 | Field* table_priv() const { return tl.table->field[6]; } |
977 | Field* column_priv() const { return tl.table->field[7]; } |
978 | |
979 | private: |
980 | friend class Grant_tables; |
981 | |
982 | Tables_priv_table() {}; |
983 | |
984 | void init(enum thr_lock_type lock_type, Grant_table_base *next_table= NULL) |
985 | { |
986 | /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ |
987 | LEX_CSTRING MYSQL_TABLES_PRIV_NAME={STRING_WITH_LEN("tables_priv" ) }; |
988 | tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_TABLES_PRIV_NAME, NULL, lock_type); |
989 | Grant_table_base::init(lock_type, false); |
990 | } |
991 | }; |
992 | |
993 | class Columns_priv_table: public Grant_table_base |
994 | { |
995 | public: |
996 | Field* host() const { return tl.table->field[0]; } |
997 | Field* db() const { return tl.table->field[1]; } |
998 | Field* user() const { return tl.table->field[2]; } |
999 | Field* table_name() const { return tl.table->field[3]; } |
1000 | Field* column_name() const { return tl.table->field[4]; } |
1001 | Field* timestamp() const { return tl.table->field[5]; } |
1002 | Field* column_priv() const { return tl.table->field[6]; } |
1003 | |
1004 | private: |
1005 | friend class Grant_tables; |
1006 | |
1007 | Columns_priv_table() {}; |
1008 | |
1009 | void init(enum thr_lock_type lock_type) |
1010 | { |
1011 | /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ |
1012 | LEX_CSTRING MYSQL_COLUMNS_PRIV_NAME={ STRING_WITH_LEN("columns_priv" ) }; |
1013 | tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_COLUMNS_PRIV_NAME, NULL, lock_type); |
1014 | Grant_table_base::init(lock_type, false); |
1015 | } |
1016 | }; |
1017 | |
1018 | class Host_table: public Grant_table_base |
1019 | { |
1020 | public: |
1021 | Field* host() const { return tl.table->field[0]; } |
1022 | Field* db() const { return tl.table->field[1]; } |
1023 | |
1024 | private: |
1025 | friend class Grant_tables; |
1026 | |
1027 | Host_table() {} |
1028 | |
1029 | void init(enum thr_lock_type lock_type) |
1030 | { |
1031 | /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ |
1032 | LEX_CSTRING MYSQL_HOST_NAME={STRING_WITH_LEN("host" ) }; |
1033 | tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HOST_NAME, NULL, lock_type); |
1034 | Grant_table_base::init(lock_type, true); |
1035 | } |
1036 | }; |
1037 | |
1038 | class Procs_priv_table: public Grant_table_base |
1039 | { |
1040 | public: |
1041 | Field* host() const { return tl.table->field[0]; } |
1042 | Field* db() const { return tl.table->field[1]; } |
1043 | Field* user() const { return tl.table->field[2]; } |
1044 | Field* routine_name() const { return tl.table->field[3]; } |
1045 | Field* routine_type() const { return tl.table->field[4]; } |
1046 | Field* grantor() const { return tl.table->field[5]; } |
1047 | Field* proc_priv() const { return tl.table->field[6]; } |
1048 | Field* timestamp() const { return tl.table->field[7]; } |
1049 | |
1050 | private: |
1051 | friend class Grant_tables; |
1052 | |
1053 | Procs_priv_table() {} |
1054 | |
1055 | void init(enum thr_lock_type lock_type) |
1056 | { |
1057 | /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ |
1058 | LEX_CSTRING MYSQL_PROCS_PRIV_NAME={STRING_WITH_LEN("procs_priv" ) }; |
1059 | tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROCS_PRIV_NAME, NULL, lock_type); |
1060 | Grant_table_base::init(lock_type, true); |
1061 | } |
1062 | }; |
1063 | |
1064 | class Proxies_priv_table: public Grant_table_base |
1065 | { |
1066 | public: |
1067 | Field* host() const { return tl.table->field[0]; } |
1068 | Field* user() const { return tl.table->field[1]; } |
1069 | Field* proxied_host() const { return tl.table->field[2]; } |
1070 | Field* proxied_user() const { return tl.table->field[3]; } |
1071 | Field* with_grant() const { return tl.table->field[4]; } |
1072 | Field* grantor() const { return tl.table->field[5]; } |
1073 | Field* timestamp() const { return tl.table->field[6]; } |
1074 | |
1075 | private: |
1076 | friend class Grant_tables; |
1077 | |
1078 | Proxies_priv_table() {} |
1079 | |
1080 | void init(enum thr_lock_type lock_type) |
1081 | { |
1082 | /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ |
1083 | LEX_CSTRING MYSQL_PROXIES_PRIV_NAME={STRING_WITH_LEN("proxies_priv" ) }; |
1084 | tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PROXIES_PRIV_NAME, NULL, lock_type); |
1085 | Grant_table_base::init(lock_type, true); |
1086 | } |
1087 | }; |
1088 | |
1089 | class Roles_mapping_table: public Grant_table_base |
1090 | { |
1091 | public: |
1092 | Field* host() const { return tl.table->field[0]; } |
1093 | Field* user() const { return tl.table->field[1]; } |
1094 | Field* role() const { return tl.table->field[2]; } |
1095 | Field* admin_option() const { return tl.table->field[3]; } |
1096 | |
1097 | private: |
1098 | friend class Grant_tables; |
1099 | |
1100 | Roles_mapping_table() {} |
1101 | |
1102 | void init(enum thr_lock_type lock_type) |
1103 | { |
1104 | /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ |
1105 | LEX_CSTRING MYSQL_ROLES_MAPPING_NAME={STRING_WITH_LEN("roles_mapping" ) }; |
1106 | tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_ROLES_MAPPING_NAME, NULL, lock_type); |
1107 | Grant_table_base::init(lock_type, true); |
1108 | } |
1109 | }; |
1110 | |
1111 | /** |
1112 | Class that represents a collection of grant tables. |
1113 | */ |
1114 | class Grant_tables |
1115 | { |
1116 | public: |
1117 | /* When constructing the Grant_tables object, we initialize only |
1118 | the tables which are going to be opened. |
1119 | @param which_tables Bitmap of which tables to open. |
1120 | @param lock_type Lock type to use when opening tables. |
1121 | */ |
1122 | Grant_tables(int which_tables, enum thr_lock_type lock_type) |
1123 | { |
1124 | DBUG_ENTER("Grant_tables::Grant_tables" ); |
1125 | DBUG_PRINT("info" , ("which_tables: %x, lock_type: %u" , |
1126 | which_tables, lock_type)); |
1127 | DBUG_ASSERT(which_tables); /* At least one table must be opened. */ |
1128 | Grant_table_base* prev= NULL; |
1129 | /* We start from the last table, Table_roles_mapping, such that |
1130 | the first one in the linked list is Table_user. */ |
1131 | if (which_tables & Table_roles_mapping) |
1132 | { |
1133 | m_roles_mapping_table.init(lock_type); |
1134 | prev= &m_roles_mapping_table; |
1135 | } |
1136 | if (which_tables & Table_proxies_priv) |
1137 | { |
1138 | m_proxies_priv_table.init(lock_type); |
1139 | link_tables(&m_proxies_priv_table, prev); |
1140 | prev= &m_proxies_priv_table; |
1141 | } |
1142 | if (which_tables & Table_procs_priv) |
1143 | { |
1144 | m_procs_priv_table.init(lock_type); |
1145 | link_tables(&m_procs_priv_table, prev); |
1146 | prev= &m_procs_priv_table; |
1147 | } |
1148 | if (which_tables & Table_host) |
1149 | { |
1150 | m_host_table.init(lock_type); |
1151 | link_tables(&m_host_table, prev); |
1152 | prev= &m_host_table; |
1153 | } |
1154 | if (which_tables & Table_columns_priv) |
1155 | { |
1156 | m_columns_priv_table.init(lock_type); |
1157 | link_tables(&m_columns_priv_table, prev); |
1158 | prev= &m_columns_priv_table; |
1159 | } |
1160 | if (which_tables & Table_tables_priv) |
1161 | { |
1162 | m_tables_priv_table.init(lock_type); |
1163 | link_tables(&m_tables_priv_table, prev); |
1164 | prev= &m_tables_priv_table; |
1165 | } |
1166 | if (which_tables & Table_db) |
1167 | { |
1168 | m_db_table.init(lock_type); |
1169 | link_tables(&m_db_table, prev); |
1170 | prev= &m_db_table; |
1171 | } |
1172 | if (which_tables & Table_user) |
1173 | { |
1174 | m_user_table.init(lock_type); |
1175 | link_tables(&m_user_table, prev); |
1176 | prev= &m_user_table; |
1177 | } |
1178 | |
1179 | first_table_in_list= prev; |
1180 | DBUG_VOID_RETURN; |
1181 | } |
1182 | |
1183 | /* Before any operation is possible on grant tables, they must be opened. |
1184 | This opens the tables according to the lock type specified during |
1185 | construction. |
1186 | |
1187 | @retval 1 replication filters matched. Abort the operation, |
1188 | but return OK (!) |
1189 | @retval 0 tables were opened successfully |
1190 | @retval -1 error, tables could not be opened |
1191 | */ |
1192 | int open_and_lock(THD *thd) |
1193 | { |
1194 | DBUG_ENTER("Grant_tables::open_and_lock" ); |
1195 | DBUG_ASSERT(first_table_in_list); |
1196 | #ifdef HAVE_REPLICATION |
1197 | if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE && |
1198 | thd->slave_thread && !thd->spcont) |
1199 | { |
1200 | /* |
1201 | GRANT and REVOKE are applied the slave in/exclusion rules as they are |
1202 | some kind of updates to the mysql.% tables. |
1203 | */ |
1204 | Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; |
1205 | if (rpl_filter->is_on() && |
1206 | !rpl_filter->tables_ok(0, &first_table_in_list->tl)) |
1207 | DBUG_RETURN(1); |
1208 | } |
1209 | #endif |
1210 | if (open_and_lock_tables(thd, &first_table_in_list->tl, FALSE, |
1211 | MYSQL_LOCK_IGNORE_TIMEOUT)) |
1212 | DBUG_RETURN(-1); |
1213 | |
1214 | /* |
1215 | We can read privilege tables even when !initialized. |
1216 | This can be acl_load() - server startup or FLUSH PRIVILEGES |
1217 | */ |
1218 | if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE && |
1219 | !initialized) |
1220 | { |
1221 | my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables" ); |
1222 | DBUG_RETURN(-1); |
1223 | } |
1224 | |
1225 | /* The privilge columns vary based on MariaDB version. Figure out |
1226 | how many we have after we've opened the table. */ |
1227 | m_user_table.compute_num_privilege_cols(); |
1228 | m_db_table.compute_num_privilege_cols(); |
1229 | m_tables_priv_table.compute_num_privilege_cols(); |
1230 | m_columns_priv_table.compute_num_privilege_cols(); |
1231 | m_host_table.compute_num_privilege_cols(); |
1232 | m_procs_priv_table.compute_num_privilege_cols(); |
1233 | m_proxies_priv_table.compute_num_privilege_cols(); |
1234 | m_roles_mapping_table.compute_num_privilege_cols(); |
1235 | DBUG_RETURN(0); |
1236 | } |
1237 | |
1238 | inline const User_table& user_table() const |
1239 | { |
1240 | return m_user_table; |
1241 | } |
1242 | |
1243 | inline const Db_table& db_table() const |
1244 | { |
1245 | return m_db_table; |
1246 | } |
1247 | |
1248 | |
1249 | inline const Tables_priv_table& tables_priv_table() const |
1250 | { |
1251 | return m_tables_priv_table; |
1252 | } |
1253 | |
1254 | inline const Columns_priv_table& columns_priv_table() const |
1255 | { |
1256 | return m_columns_priv_table; |
1257 | } |
1258 | |
1259 | inline const Host_table& host_table() const |
1260 | { |
1261 | return m_host_table; |
1262 | } |
1263 | |
1264 | inline const Procs_priv_table& procs_priv_table() const |
1265 | { |
1266 | return m_procs_priv_table; |
1267 | } |
1268 | |
1269 | inline const Proxies_priv_table& proxies_priv_table() const |
1270 | { |
1271 | return m_proxies_priv_table; |
1272 | } |
1273 | |
1274 | inline const Roles_mapping_table& roles_mapping_table() const |
1275 | { |
1276 | return m_roles_mapping_table; |
1277 | } |
1278 | |
1279 | private: |
1280 | User_table m_user_table; |
1281 | Db_table m_db_table; |
1282 | Tables_priv_table m_tables_priv_table; |
1283 | Columns_priv_table m_columns_priv_table; |
1284 | Host_table m_host_table; |
1285 | Procs_priv_table m_procs_priv_table; |
1286 | Proxies_priv_table m_proxies_priv_table; |
1287 | Roles_mapping_table m_roles_mapping_table; |
1288 | |
1289 | /* The grant tables are set-up in a linked list. We keep the head of it. */ |
1290 | Grant_table_base *first_table_in_list; |
1291 | /** |
1292 | Chain two grant tables' TABLE_LIST members. |
1293 | */ |
1294 | static void link_tables(Grant_table_base *from, Grant_table_base *to) |
1295 | { |
1296 | DBUG_ASSERT(from); |
1297 | if (to) |
1298 | from->tl.next_local= from->tl.next_global= &to->tl; |
1299 | else |
1300 | from->tl.next_local= from->tl.next_global= NULL; |
1301 | } |
1302 | }; |
1303 | |
1304 | |
1305 | void ACL_PROXY_USER::init(const Proxies_priv_table& proxies_priv_table, |
1306 | MEM_ROOT *mem) |
1307 | { |
1308 | init(get_field(mem, proxies_priv_table.host()), |
1309 | get_field(mem, proxies_priv_table.user()), |
1310 | get_field(mem, proxies_priv_table.proxied_host()), |
1311 | get_field(mem, proxies_priv_table.proxied_user()), |
1312 | proxies_priv_table.with_grant()->val_int() != 0); |
1313 | } |
1314 | |
1315 | |
1316 | |
1317 | /* |
1318 | Enumeration of various ACL's and Hashes used in handle_grant_struct() |
1319 | */ |
1320 | enum enum_acl_lists |
1321 | { |
1322 | USER_ACL= 0, |
1323 | ROLE_ACL, |
1324 | DB_ACL, |
1325 | COLUMN_PRIVILEGES_HASH, |
1326 | PROC_PRIVILEGES_HASH, |
1327 | FUNC_PRIVILEGES_HASH, |
1328 | PACKAGE_SPEC_PRIVILEGES_HASH, |
1329 | PACKAGE_BODY_PRIVILEGES_HASH, |
1330 | PROXY_USERS_ACL, |
1331 | ROLES_MAPPINGS_HASH |
1332 | }; |
1333 | |
1334 | ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) : counter(0) |
1335 | { |
1336 | |
1337 | access= user->access; |
1338 | /* set initial role access the same as the table row privileges */ |
1339 | initial_role_access= user->access; |
1340 | this->user.str= safe_strdup_root(root, user->user.str); |
1341 | this->user.length= user->user.length; |
1342 | bzero(&role_grants, sizeof(role_grants)); |
1343 | bzero(&parent_grantee, sizeof(parent_grantee)); |
1344 | flags= IS_ROLE; |
1345 | } |
1346 | |
1347 | ACL_ROLE::ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *root) : |
1348 | initial_role_access(privileges), counter(0) |
1349 | { |
1350 | this->access= initial_role_access; |
1351 | this->user.str= safe_strdup_root(root, rolename); |
1352 | this->user.length= strlen(rolename); |
1353 | bzero(&role_grants, sizeof(role_grants)); |
1354 | bzero(&parent_grantee, sizeof(parent_grantee)); |
1355 | flags= IS_ROLE; |
1356 | } |
1357 | |
1358 | |
1359 | static bool is_invalid_role_name(const char *str) |
1360 | { |
1361 | if (*str && strcasecmp(str, "PUBLIC" ) && strcasecmp(str, "NONE" )) |
1362 | return false; |
1363 | |
1364 | my_error(ER_INVALID_ROLE, MYF(0), str); |
1365 | return true; |
1366 | } |
1367 | |
1368 | |
1369 | static void free_acl_user(ACL_USER *user) |
1370 | { |
1371 | delete_dynamic(&(user->role_grants)); |
1372 | } |
1373 | |
1374 | static void free_acl_role(ACL_ROLE *role) |
1375 | { |
1376 | delete_dynamic(&(role->role_grants)); |
1377 | delete_dynamic(&(role->parent_grantee)); |
1378 | } |
1379 | |
1380 | static my_bool check_if_exists(THD *, plugin_ref, void *) |
1381 | { |
1382 | return TRUE; |
1383 | } |
1384 | |
1385 | static bool has_validation_plugins() |
1386 | { |
1387 | return plugin_foreach(NULL, check_if_exists, |
1388 | MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL); |
1389 | } |
1390 | |
1391 | struct validation_data { LEX_CSTRING *user, *password; }; |
1392 | |
1393 | static my_bool do_validate(THD *, plugin_ref plugin, void *arg) |
1394 | { |
1395 | struct validation_data *data= (struct validation_data *)arg; |
1396 | struct st_mariadb_password_validation *handler= |
1397 | (st_mariadb_password_validation *)plugin_decl(plugin)->info; |
1398 | return handler->validate_password(data->user, data->password); |
1399 | } |
1400 | |
1401 | |
1402 | static bool validate_password(LEX_USER *user, THD *thd) |
1403 | { |
1404 | if (user->pwtext.length || !user->pwhash.length) |
1405 | { |
1406 | struct validation_data data= { &user->user, |
1407 | user->pwtext.str ? &user->pwtext : |
1408 | const_cast<LEX_CSTRING *>(&empty_clex_str) }; |
1409 | if (plugin_foreach(NULL, do_validate, |
1410 | MariaDB_PASSWORD_VALIDATION_PLUGIN, &data)) |
1411 | { |
1412 | my_error(ER_NOT_VALID_PASSWORD, MYF(0)); |
1413 | return true; |
1414 | } |
1415 | } |
1416 | else |
1417 | { |
1418 | if (!thd->slave_thread && |
1419 | strict_password_validation && has_validation_plugins()) |
1420 | { |
1421 | my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--strict-password-validation" ); |
1422 | return true; |
1423 | } |
1424 | } |
1425 | return false; |
1426 | } |
1427 | |
1428 | /** |
1429 | Convert scrambled password to binary form, according to scramble type, |
1430 | Binary form is stored in user.salt. |
1431 | |
1432 | @param acl_user The object where to store the salt |
1433 | @param password The password hash containing the salt |
1434 | @param password_len The length of the password hash |
1435 | |
1436 | Despite the name of the function it is used when loading ACLs from disk |
1437 | to store the password hash in the ACL_USER object. |
1438 | */ |
1439 | |
1440 | static void |
1441 | set_user_salt(ACL_USER *acl_user, const char *password, size_t password_len) |
1442 | { |
1443 | if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH) |
1444 | { |
1445 | get_salt_from_password(acl_user->salt, password); |
1446 | acl_user->salt_len= SCRAMBLE_LENGTH; |
1447 | } |
1448 | else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) |
1449 | { |
1450 | get_salt_from_password_323((ulong *) acl_user->salt, password); |
1451 | acl_user->salt_len= SCRAMBLE_LENGTH_323; |
1452 | } |
1453 | else |
1454 | acl_user->salt_len= 0; |
1455 | } |
1456 | |
1457 | static const char *fix_plugin_ptr(const char *name) |
1458 | { |
1459 | if (my_strcasecmp(system_charset_info, name, |
1460 | native_password_plugin_name.str) == 0) |
1461 | return native_password_plugin_name.str; |
1462 | else |
1463 | if (my_strcasecmp(system_charset_info, name, |
1464 | old_password_plugin_name.str) == 0) |
1465 | return old_password_plugin_name.str; |
1466 | else |
1467 | return name; |
1468 | } |
1469 | |
1470 | /** |
1471 | Fix ACL::plugin pointer to point to a hard-coded string, if appropriate |
1472 | |
1473 | Make sure that if ACL_USER's plugin is a built-in, then it points |
1474 | to a hard coded string, not to an allocated copy. Run-time, for |
1475 | authentication, we want to be able to detect built-ins by comparing |
1476 | pointers, not strings. |
1477 | |
1478 | Additionally - update the salt if the plugin is built-in. |
1479 | |
1480 | @retval 0 the pointers were fixed |
1481 | @retval 1 this ACL_USER uses a not built-in plugin |
1482 | */ |
1483 | static bool fix_user_plugin_ptr(ACL_USER *user) |
1484 | { |
1485 | if (lex_string_eq(&user->plugin, &native_password_plugin_name)) |
1486 | user->plugin= native_password_plugin_name; |
1487 | else |
1488 | if (lex_string_eq(&user->plugin, &old_password_plugin_name)) |
1489 | user->plugin= old_password_plugin_name; |
1490 | else |
1491 | return true; |
1492 | |
1493 | if (user->auth_string.length) |
1494 | set_user_salt(user, user->auth_string.str, user->auth_string.length); |
1495 | return false; |
1496 | } |
1497 | |
1498 | |
1499 | /* |
1500 | Validates the password, calculates password hash, transforms |
1501 | equivalent LEX_USER representations. |
1502 | |
1503 | Upon entering this function: |
1504 | |
1505 | - if user->plugin is specified, user->auth is the plugin auth data. |
1506 | - if user->plugin is mysql_native_password or mysql_old_password, |
1507 | user->auth is the password hash, and LEX_USER is transformed |
1508 | to match the next case (that is, user->plugin is cleared). |
1509 | - if user->plugin is NOT specified, built-in auth is assumed, that is |
1510 | mysql_native_password or mysql_old_password. In that case, |
1511 | user->pwhash is the password hash. And user->pwtext is the original |
1512 | plain-text password. Either one can be set or both. |
1513 | |
1514 | Upon exiting this function: |
1515 | |
1516 | - user->pwtext is left untouched |
1517 | - user->pwhash is the password hash, as the mysql.user.password column |
1518 | - user->plugin is the plugin name, as the mysql.user.plugin column |
1519 | - user->auth is the plugin auth data, as the mysql.user.authentication_string column |
1520 | */ |
1521 | static bool fix_lex_user(THD *thd, LEX_USER *user) |
1522 | { |
1523 | size_t check_length; |
1524 | |
1525 | DBUG_ASSERT(user->plugin.length || !user->auth.length); |
1526 | DBUG_ASSERT(!(user->plugin.length && (user->pwtext.length || user->pwhash.length))); |
1527 | |
1528 | if (lex_string_eq(&user->plugin, &native_password_plugin_name)) |
1529 | check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; |
1530 | else |
1531 | if (lex_string_eq(&user->plugin, &old_password_plugin_name)) |
1532 | check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; |
1533 | else |
1534 | if (user->plugin.length) |
1535 | return false; // nothing else to do |
1536 | else if (thd->variables.old_passwords == 1 || |
1537 | user->pwhash.length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) |
1538 | check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; |
1539 | else |
1540 | check_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; |
1541 | |
1542 | if (user->plugin.length) |
1543 | { |
1544 | user->pwhash= user->auth; |
1545 | user->plugin= empty_clex_str; |
1546 | user->auth= empty_clex_str; |
1547 | } |
1548 | |
1549 | if (user->pwhash.length && user->pwhash.length != check_length) |
1550 | { |
1551 | my_error(ER_PASSWD_LENGTH, MYF(0), (int) check_length); |
1552 | return true; |
1553 | } |
1554 | |
1555 | if (user->pwtext.length && !user->pwhash.length) |
1556 | { |
1557 | size_t scramble_length; |
1558 | void (*make_scramble)(char *, const char *, size_t); |
1559 | |
1560 | if (thd->variables.old_passwords == 1) |
1561 | { |
1562 | scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; |
1563 | make_scramble= my_make_scrambled_password_323; |
1564 | } |
1565 | else |
1566 | { |
1567 | scramble_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; |
1568 | make_scramble= my_make_scrambled_password; |
1569 | } |
1570 | |
1571 | Query_arena *arena, backup; |
1572 | arena= thd->activate_stmt_arena_if_needed(&backup); |
1573 | char *buff= (char *) thd->alloc(scramble_length + 1); |
1574 | if (arena) |
1575 | thd->restore_active_arena(arena, &backup); |
1576 | |
1577 | if (buff == NULL) |
1578 | return true; |
1579 | make_scramble(buff, user->pwtext.str, user->pwtext.length); |
1580 | user->pwhash.str= buff; |
1581 | user->pwhash.length= scramble_length; |
1582 | } |
1583 | |
1584 | return false; |
1585 | } |
1586 | |
1587 | |
1588 | static bool get_YN_as_bool(Field *field) |
1589 | { |
1590 | char buff[2]; |
1591 | String res(buff,sizeof(buff),&my_charset_latin1); |
1592 | field->val_str(&res); |
1593 | return res[0] == 'Y' || res[0] == 'y'; |
1594 | } |
1595 | |
1596 | |
1597 | /* |
1598 | Initialize structures responsible for user/db-level privilege checking and |
1599 | load privilege information for them from tables in the 'mysql' database. |
1600 | |
1601 | SYNOPSIS |
1602 | acl_init() |
1603 | dont_read_acl_tables TRUE if we want to skip loading data from |
1604 | privilege tables and disable privilege checking. |
1605 | |
1606 | NOTES |
1607 | This function is mostly responsible for preparatory steps, main work |
1608 | on initialization and grants loading is done in acl_reload(). |
1609 | |
1610 | RETURN VALUES |
1611 | 0 ok |
1612 | 1 Could not initialize grant's |
1613 | */ |
1614 | |
1615 | bool acl_init(bool dont_read_acl_tables) |
1616 | { |
1617 | THD *thd; |
1618 | bool return_val; |
1619 | DBUG_ENTER("acl_init" ); |
1620 | |
1621 | acl_cache= new Hash_filo<acl_entry>(ACL_CACHE_SIZE, 0, 0, |
1622 | (my_hash_get_key) acl_entry_get_key, |
1623 | (my_hash_free_key) free, |
1624 | &my_charset_utf8_bin); |
1625 | |
1626 | /* |
1627 | cache built-in native authentication plugins, |
1628 | to avoid hash searches and a global mutex lock on every connect |
1629 | */ |
1630 | native_password_plugin= my_plugin_lock_by_name(0, |
1631 | &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN); |
1632 | old_password_plugin= my_plugin_lock_by_name(0, |
1633 | &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN); |
1634 | |
1635 | if (!native_password_plugin || !old_password_plugin) |
1636 | DBUG_RETURN(1); |
1637 | |
1638 | if (dont_read_acl_tables) |
1639 | { |
1640 | DBUG_RETURN(0); /* purecov: tested */ |
1641 | } |
1642 | |
1643 | /* |
1644 | To be able to run this from boot, we allocate a temporary THD |
1645 | */ |
1646 | if (!(thd=new THD(0))) |
1647 | DBUG_RETURN(1); /* purecov: inspected */ |
1648 | thd->thread_stack= (char*) &thd; |
1649 | thd->store_globals(); |
1650 | /* |
1651 | It is safe to call acl_reload() since acl_* arrays and hashes which |
1652 | will be freed there are global static objects and thus are initialized |
1653 | by zeros at startup. |
1654 | */ |
1655 | return_val= acl_reload(thd); |
1656 | delete thd; |
1657 | DBUG_RETURN(return_val); |
1658 | } |
1659 | |
1660 | /** |
1661 | Choose from either native or old password plugins when assigning a password |
1662 | */ |
1663 | |
1664 | static bool set_user_plugin (ACL_USER *user, size_t password_len) |
1665 | { |
1666 | switch (password_len) |
1667 | { |
1668 | case 0: /* no password */ |
1669 | case SCRAMBLED_PASSWORD_CHAR_LENGTH: |
1670 | user->plugin= native_password_plugin_name; |
1671 | return FALSE; |
1672 | case SCRAMBLED_PASSWORD_CHAR_LENGTH_323: |
1673 | user->plugin= old_password_plugin_name; |
1674 | return FALSE; |
1675 | default: |
1676 | sql_print_warning("Found invalid password for user: '%s@%s'; " |
1677 | "Ignoring user" , safe_str(user->user.str), |
1678 | safe_str(user->host.hostname)); |
1679 | return TRUE; |
1680 | } |
1681 | } |
1682 | |
1683 | |
1684 | /* |
1685 | Initialize structures responsible for user/db-level privilege checking |
1686 | and load information about grants from open privilege tables. |
1687 | |
1688 | SYNOPSIS |
1689 | acl_load() |
1690 | thd Current thread |
1691 | tables List containing open "mysql.host", "mysql.user", |
1692 | "mysql.db", "mysql.proxies_priv" and "mysql.roles_mapping" |
1693 | tables. |
1694 | |
1695 | RETURN VALUES |
1696 | FALSE Success |
1697 | TRUE Error |
1698 | */ |
1699 | |
1700 | static bool acl_load(THD *thd, const Grant_tables& tables) |
1701 | { |
1702 | READ_RECORD read_record_info; |
1703 | bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; |
1704 | char tmp_name[SAFE_NAME_LEN+1]; |
1705 | int password_length; |
1706 | Sql_mode_save old_mode_save(thd); |
1707 | DBUG_ENTER("acl_load" ); |
1708 | |
1709 | thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; |
1710 | |
1711 | grant_version++; /* Privileges updated */ |
1712 | |
1713 | const Host_table& host_table= tables.host_table(); |
1714 | init_sql_alloc(&acl_memroot, "ACL" , ACL_ALLOC_BLOCK_SIZE, 0, MYF(0)); |
1715 | if (host_table.table_exists()) // "host" table may not exist (e.g. in MySQL 5.6.7+) |
1716 | { |
1717 | if (host_table.init_read_record(&read_record_info, thd)) |
1718 | DBUG_RETURN(true); |
1719 | while (!(read_record_info.read_record())) |
1720 | { |
1721 | ACL_HOST host; |
1722 | update_hostname(&host.host, get_field(&acl_memroot, host_table.host())); |
1723 | host.db= get_field(&acl_memroot, host_table.db()); |
1724 | if (lower_case_table_names && host.db) |
1725 | { |
1726 | /* |
1727 | convert db to lower case and give a warning if the db wasn't |
1728 | already in lower case |
1729 | */ |
1730 | char *end = strnmov(tmp_name, host.db, sizeof(tmp_name)); |
1731 | if (end >= tmp_name + sizeof(tmp_name)) |
1732 | { |
1733 | sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), host.db); |
1734 | continue; |
1735 | } |
1736 | my_casedn_str(files_charset_info, host.db); |
1737 | if (strcmp(host.db, tmp_name) != 0) |
1738 | sql_print_warning("'host' entry '%s|%s' had database in mixed " |
1739 | "case that has been forced to lowercase because " |
1740 | "lower_case_table_names is set. It will not be " |
1741 | "possible to remove this privilege using REVOKE." , |
1742 | host.host.hostname, host.db); |
1743 | } |
1744 | host.access= host_table.get_access(); |
1745 | host.access= fix_rights_for_db(host.access); |
1746 | host.sort= get_sort(2, host.host.hostname, host.db); |
1747 | if (check_no_resolve && hostname_requires_resolving(host.host.hostname)) |
1748 | { |
1749 | sql_print_warning("'host' entry '%s|%s' " |
1750 | "ignored in --skip-name-resolve mode." , |
1751 | safe_str(host.host.hostname), |
1752 | safe_str(host.db)); |
1753 | continue; |
1754 | } |
1755 | #ifndef TO_BE_REMOVED |
1756 | if (host_table.num_fields() == 8) |
1757 | { // Without grant |
1758 | if (host.access & CREATE_ACL) |
1759 | host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL; |
1760 | } |
1761 | #endif |
1762 | (void) push_dynamic(&acl_hosts,(uchar*) &host); |
1763 | } |
1764 | my_qsort((uchar*) dynamic_element(&acl_hosts, 0, ACL_HOST*), |
1765 | acl_hosts.elements, sizeof(ACL_HOST),(qsort_cmp) acl_compare); |
1766 | end_read_record(&read_record_info); |
1767 | } |
1768 | freeze_size(&acl_hosts); |
1769 | |
1770 | const User_table& user_table= tables.user_table(); |
1771 | if (user_table.init_read_record(&read_record_info, thd)) |
1772 | DBUG_RETURN(true); |
1773 | |
1774 | username_char_length= MY_MIN(user_table.user()->char_length(), |
1775 | USERNAME_CHAR_LENGTH); |
1776 | if (user_table.password()) // Password column might be missing. (MySQL 5.7.6+) |
1777 | { |
1778 | password_length= user_table.password()->field_length / |
1779 | user_table.password()->charset()->mbmaxlen; |
1780 | if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323) |
1781 | { |
1782 | sql_print_error("Fatal error: mysql.user table is damaged or in " |
1783 | "unsupported 3.20 format." ); |
1784 | DBUG_RETURN(TRUE); |
1785 | } |
1786 | |
1787 | DBUG_PRINT("info" ,("user table fields: %d, password length: %d" , |
1788 | user_table.num_fields(), password_length)); |
1789 | |
1790 | mysql_mutex_lock(&LOCK_global_system_variables); |
1791 | if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH) |
1792 | { |
1793 | if (opt_secure_auth) |
1794 | { |
1795 | mysql_mutex_unlock(&LOCK_global_system_variables); |
1796 | sql_print_error("Fatal error: mysql.user table is in old format, " |
1797 | "but server started with --secure-auth option." ); |
1798 | DBUG_RETURN(TRUE); |
1799 | } |
1800 | mysql_user_table_is_in_short_password_format= true; |
1801 | if (global_system_variables.old_passwords) |
1802 | mysql_mutex_unlock(&LOCK_global_system_variables); |
1803 | else |
1804 | { |
1805 | extern sys_var *Sys_old_passwords_ptr; |
1806 | Sys_old_passwords_ptr->value_origin= sys_var::AUTO; |
1807 | global_system_variables.old_passwords= 1; |
1808 | mysql_mutex_unlock(&LOCK_global_system_variables); |
1809 | sql_print_warning("mysql.user table is not updated to new password format; " |
1810 | "Disabling new password usage until " |
1811 | "mysql_fix_privilege_tables is run" ); |
1812 | } |
1813 | thd->variables.old_passwords= 1; |
1814 | } |
1815 | else |
1816 | { |
1817 | mysql_user_table_is_in_short_password_format= false; |
1818 | mysql_mutex_unlock(&LOCK_global_system_variables); |
1819 | } |
1820 | } |
1821 | |
1822 | allow_all_hosts=0; |
1823 | while (!(read_record_info.read_record())) |
1824 | { |
1825 | ACL_USER user; |
1826 | bool is_role= FALSE; |
1827 | bzero(&user, sizeof(user)); |
1828 | update_hostname(&user.host, get_field(&acl_memroot, user_table.host())); |
1829 | char *username= get_field(&acl_memroot, user_table.user()); |
1830 | user.user.str= username; |
1831 | user.user.length= safe_strlen(username); |
1832 | |
1833 | /* |
1834 | If the user entry is a role, skip password and hostname checks |
1835 | A user can not log in with a role so some checks are not necessary |
1836 | */ |
1837 | is_role= user_table.check_is_role(); |
1838 | |
1839 | if (is_role && is_invalid_role_name(username)) |
1840 | { |
1841 | thd->clear_error(); // the warning is still issued |
1842 | continue; |
1843 | } |
1844 | |
1845 | if (!is_role && check_no_resolve && |
1846 | hostname_requires_resolving(user.host.hostname)) |
1847 | { |
1848 | sql_print_warning("'user' entry '%s@%s' " |
1849 | "ignored in --skip-name-resolve mode." , |
1850 | safe_str(user.user.str), |
1851 | safe_str(user.host.hostname)); |
1852 | continue; |
1853 | } |
1854 | |
1855 | char *password= const_cast<char*>("" ); |
1856 | if (user_table.password()) |
1857 | password= get_field(&acl_memroot, user_table.password()); |
1858 | size_t password_len= safe_strlen(password); |
1859 | user.auth_string.str= safe_str(password); |
1860 | user.auth_string.length= password_len; |
1861 | set_user_salt(&user, password, password_len); |
1862 | |
1863 | if (!is_role && set_user_plugin(&user, password_len)) |
1864 | continue; |
1865 | |
1866 | { |
1867 | user.access= user_table.get_access() & GLOBAL_ACLS; |
1868 | /* |
1869 | if it is pre 5.0.1 privilege table then map CREATE privilege on |
1870 | CREATE VIEW & SHOW VIEW privileges |
1871 | */ |
1872 | if (user_table.num_fields() <= 31 && (user.access & CREATE_ACL)) |
1873 | user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL); |
1874 | |
1875 | /* |
1876 | if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on |
1877 | CREATE PROCEDURE & ALTER PROCEDURE privileges |
1878 | */ |
1879 | if (user_table.num_fields() <= 33 && (user.access & CREATE_ACL)) |
1880 | user.access|= CREATE_PROC_ACL; |
1881 | if (user_table.num_fields() <= 33 && (user.access & ALTER_ACL)) |
1882 | user.access|= ALTER_PROC_ACL; |
1883 | |
1884 | /* |
1885 | pre 5.0.3 did not have CREATE_USER_ACL |
1886 | */ |
1887 | if (user_table.num_fields() <= 36 && (user.access & GRANT_ACL)) |
1888 | user.access|= CREATE_USER_ACL; |
1889 | |
1890 | |
1891 | /* |
1892 | if it is pre 5.1.6 privilege table then map CREATE privilege on |
1893 | CREATE|ALTER|DROP|EXECUTE EVENT |
1894 | */ |
1895 | if (user_table.num_fields() <= 37 && (user.access & SUPER_ACL)) |
1896 | user.access|= EVENT_ACL; |
1897 | |
1898 | /* |
1899 | if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE. |
1900 | */ |
1901 | if (user_table.num_fields() <= 38 && (user.access & SUPER_ACL)) |
1902 | user.access|= TRIGGER_ACL; |
1903 | |
1904 | if (user_table.num_fields() <= 46 && (user.access & DELETE_ACL)) |
1905 | user.access|= DELETE_HISTORY_ACL; |
1906 | |
1907 | user.sort= get_sort(2, user.host.hostname, user.user.str); |
1908 | user.hostname_length= safe_strlen(user.host.hostname); |
1909 | user.user_resource.user_conn= 0; |
1910 | user.user_resource.max_statement_time= 0.0; |
1911 | |
1912 | /* Starting from 4.0.2 we have more fields */ |
1913 | if (user_table.ssl_type()) |
1914 | { |
1915 | char *ssl_type=get_field(thd->mem_root, user_table.ssl_type()); |
1916 | if (!ssl_type) |
1917 | user.ssl_type=SSL_TYPE_NONE; |
1918 | else if (!strcmp(ssl_type, "ANY" )) |
1919 | user.ssl_type=SSL_TYPE_ANY; |
1920 | else if (!strcmp(ssl_type, "X509" )) |
1921 | user.ssl_type=SSL_TYPE_X509; |
1922 | else /* !strcmp(ssl_type, "SPECIFIED") */ |
1923 | user.ssl_type=SSL_TYPE_SPECIFIED; |
1924 | |
1925 | user.ssl_cipher= get_field(&acl_memroot, user_table.ssl_cipher()); |
1926 | user.x509_issuer= get_field(&acl_memroot, user_table.x509_issuer()); |
1927 | user.x509_subject= get_field(&acl_memroot, user_table.x509_subject()); |
1928 | |
1929 | char *ptr = get_field(thd->mem_root, user_table.max_questions()); |
1930 | user.user_resource.questions=ptr ? atoi(ptr) : 0; |
1931 | ptr = get_field(thd->mem_root, user_table.max_updates()); |
1932 | user.user_resource.updates=ptr ? atoi(ptr) : 0; |
1933 | ptr = get_field(thd->mem_root, user_table.max_connections()); |
1934 | user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0; |
1935 | if (user.user_resource.questions || user.user_resource.updates || |
1936 | user.user_resource.conn_per_hour) |
1937 | mqh_used=1; |
1938 | |
1939 | if (user_table.max_user_connections()) |
1940 | { |
1941 | /* Starting from 5.0.3 we have max_user_connections field */ |
1942 | ptr= get_field(thd->mem_root, user_table.max_user_connections()); |
1943 | user.user_resource.user_conn= ptr ? atoi(ptr) : 0; |
1944 | } |
1945 | |
1946 | if (!is_role && user_table.plugin()) |
1947 | { |
1948 | /* We may have plugin & auth_String fields */ |
1949 | char *tmpstr= get_field(&acl_memroot, user_table.plugin()); |
1950 | if (tmpstr) |
1951 | { |
1952 | user.plugin.str= tmpstr; |
1953 | user.plugin.length= strlen(user.plugin.str); |
1954 | user.auth_string.str= |
1955 | safe_str(get_field(&acl_memroot, |
1956 | user_table.authentication_string())); |
1957 | user.auth_string.length= strlen(user.auth_string.str); |
1958 | |
1959 | if (user.auth_string.length && password_len) |
1960 | { |
1961 | sql_print_warning("'user' entry '%s@%s' has both a password " |
1962 | "and an authentication plugin specified. The " |
1963 | "password will be ignored." , |
1964 | safe_str(user.user.str), |
1965 | safe_str(user.host.hostname)); |
1966 | } |
1967 | |
1968 | fix_user_plugin_ptr(&user); |
1969 | } |
1970 | } |
1971 | |
1972 | if (user_table.max_statement_time()) |
1973 | { |
1974 | /* Starting from 10.1.1 we can have max_statement_time */ |
1975 | ptr= get_field(thd->mem_root, |
1976 | user_table.max_statement_time()); |
1977 | user.user_resource.max_statement_time= ptr ? atof(ptr) : 0.0; |
1978 | } |
1979 | } |
1980 | else |
1981 | { |
1982 | user.ssl_type=SSL_TYPE_NONE; |
1983 | #ifndef TO_BE_REMOVED |
1984 | if (user_table.num_fields() <= 13) |
1985 | { // Without grant |
1986 | if (user.access & CREATE_ACL) |
1987 | user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; |
1988 | } |
1989 | /* Convert old privileges */ |
1990 | user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL; |
1991 | if (user.access & FILE_ACL) |
1992 | user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL; |
1993 | if (user.access & PROCESS_ACL) |
1994 | user.access|= SUPER_ACL | EXECUTE_ACL; |
1995 | #endif |
1996 | } |
1997 | |
1998 | (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *), |
1999 | 8, 8, MYF(0)); |
2000 | |
2001 | /* check default role, if any */ |
2002 | if (!is_role && user_table.default_role()) |
2003 | { |
2004 | user.default_rolename.str= |
2005 | get_field(&acl_memroot, user_table.default_role()); |
2006 | user.default_rolename.length= safe_strlen(user.default_rolename.str); |
2007 | } |
2008 | |
2009 | if (is_role) |
2010 | { |
2011 | DBUG_PRINT("info" , ("Found role %s" , user.user.str)); |
2012 | ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot); |
2013 | entry->role_grants = user.role_grants; |
2014 | (void) my_init_dynamic_array(&entry->parent_grantee, |
2015 | sizeof(ACL_USER_BASE *), 8, 8, MYF(0)); |
2016 | my_hash_insert(&acl_roles, (uchar *)entry); |
2017 | |
2018 | continue; |
2019 | } |
2020 | else |
2021 | { |
2022 | DBUG_PRINT("info" , ("Found user %s" , user.user.str)); |
2023 | (void) push_dynamic(&acl_users,(uchar*) &user); |
2024 | } |
2025 | if (!user.host.hostname || |
2026 | (user.host.hostname[0] == wild_many && !user.host.hostname[1])) |
2027 | allow_all_hosts=1; // Anyone can connect |
2028 | } |
2029 | } |
2030 | my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, |
2031 | sizeof(ACL_USER),(qsort_cmp) acl_compare); |
2032 | end_read_record(&read_record_info); |
2033 | freeze_size(&acl_users); |
2034 | |
2035 | const Db_table& db_table= tables.db_table(); |
2036 | if (db_table.init_read_record(&read_record_info, thd)) |
2037 | DBUG_RETURN(TRUE); |
2038 | while (!(read_record_info.read_record())) |
2039 | { |
2040 | ACL_DB db; |
2041 | char *db_name; |
2042 | db.user=get_field(&acl_memroot, db_table.user()); |
2043 | const char *hostname= get_field(&acl_memroot, db_table.host()); |
2044 | if (!hostname && find_acl_role(db.user)) |
2045 | hostname= "" ; |
2046 | update_hostname(&db.host, hostname); |
2047 | db.db= db_name= get_field(&acl_memroot, db_table.db()); |
2048 | if (!db.db) |
2049 | { |
2050 | sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped" ); |
2051 | continue; |
2052 | } |
2053 | if (check_no_resolve && hostname_requires_resolving(db.host.hostname)) |
2054 | { |
2055 | sql_print_warning("'db' entry '%s %s@%s' " |
2056 | "ignored in --skip-name-resolve mode." , |
2057 | db.db, safe_str(db.user), safe_str(db.host.hostname)); |
2058 | continue; |
2059 | } |
2060 | db.access= db_table.get_access(); |
2061 | db.access=fix_rights_for_db(db.access); |
2062 | db.initial_access= db.access; |
2063 | if (lower_case_table_names) |
2064 | { |
2065 | /* |
2066 | convert db to lower case and give a warning if the db wasn't |
2067 | already in lower case |
2068 | */ |
2069 | char *end = strnmov(tmp_name, db.db, sizeof(tmp_name)); |
2070 | if (end >= tmp_name + sizeof(tmp_name)) |
2071 | { |
2072 | sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), db.db); |
2073 | continue; |
2074 | } |
2075 | my_casedn_str(files_charset_info, db_name); |
2076 | if (strcmp(db_name, tmp_name) != 0) |
2077 | { |
2078 | sql_print_warning("'db' entry '%s %s@%s' had database in mixed " |
2079 | "case that has been forced to lowercase because " |
2080 | "lower_case_table_names is set. It will not be " |
2081 | "possible to remove this privilege using REVOKE." , |
2082 | db.db, safe_str(db.user), safe_str(db.host.hostname)); |
2083 | } |
2084 | } |
2085 | db.sort=get_sort(3,db.host.hostname,db.db,db.user); |
2086 | #ifndef TO_BE_REMOVED |
2087 | if (db_table.num_fields() <= 9) |
2088 | { // Without grant |
2089 | if (db.access & CREATE_ACL) |
2090 | db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; |
2091 | } |
2092 | #endif |
2093 | (void) push_dynamic(&acl_dbs,(uchar*) &db); |
2094 | } |
2095 | my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements, |
2096 | sizeof(ACL_DB),(qsort_cmp) acl_compare); |
2097 | end_read_record(&read_record_info); |
2098 | freeze_size(&acl_dbs); |
2099 | |
2100 | const Proxies_priv_table& proxies_priv_table= tables.proxies_priv_table(); |
2101 | if (proxies_priv_table.table_exists()) |
2102 | { |
2103 | if (proxies_priv_table.init_read_record(&read_record_info, thd)) |
2104 | DBUG_RETURN(TRUE); |
2105 | while (!(read_record_info.read_record())) |
2106 | { |
2107 | ACL_PROXY_USER proxy; |
2108 | proxy.init(proxies_priv_table, &acl_memroot); |
2109 | if (proxy.check_validity(check_no_resolve)) |
2110 | continue; |
2111 | if (push_dynamic(&acl_proxy_users, (uchar*) &proxy)) |
2112 | DBUG_RETURN(TRUE); |
2113 | } |
2114 | my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*), |
2115 | acl_proxy_users.elements, |
2116 | sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare); |
2117 | end_read_record(&read_record_info); |
2118 | } |
2119 | else |
2120 | { |
2121 | sql_print_error("Missing system table mysql.proxies_priv; " |
2122 | "please run mysql_upgrade to create it" ); |
2123 | } |
2124 | freeze_size(&acl_proxy_users); |
2125 | |
2126 | const Roles_mapping_table& roles_mapping_table= tables.roles_mapping_table(); |
2127 | if (roles_mapping_table.table_exists()) |
2128 | { |
2129 | if (roles_mapping_table.init_read_record(&read_record_info, thd)) |
2130 | DBUG_RETURN(TRUE); |
2131 | |
2132 | MEM_ROOT temp_root; |
2133 | init_alloc_root(&temp_root, "ACL_tmp" , ACL_ALLOC_BLOCK_SIZE, 0, MYF(0)); |
2134 | while (!(read_record_info.read_record())) |
2135 | { |
2136 | char *hostname= safe_str(get_field(&temp_root, roles_mapping_table.host())); |
2137 | char *username= safe_str(get_field(&temp_root, roles_mapping_table.user())); |
2138 | char *rolename= safe_str(get_field(&temp_root, roles_mapping_table.role())); |
2139 | bool with_grant_option= get_YN_as_bool(roles_mapping_table.admin_option()); |
2140 | |
2141 | if (add_role_user_mapping(username, hostname, rolename)) { |
2142 | sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'" , |
2143 | username, hostname, rolename); |
2144 | continue; |
2145 | } |
2146 | |
2147 | ROLE_GRANT_PAIR *mapping= new (&acl_memroot) ROLE_GRANT_PAIR; |
2148 | |
2149 | if (mapping->init(&acl_memroot, username, hostname, rolename, with_grant_option)) |
2150 | continue; |
2151 | |
2152 | my_hash_insert(&acl_roles_mappings, (uchar*) mapping); |
2153 | } |
2154 | |
2155 | free_root(&temp_root, MYF(0)); |
2156 | end_read_record(&read_record_info); |
2157 | } |
2158 | else |
2159 | { |
2160 | sql_print_error("Missing system table mysql.roles_mapping; " |
2161 | "please run mysql_upgrade to create it" ); |
2162 | } |
2163 | |
2164 | init_check_host(); |
2165 | |
2166 | initialized=1; |
2167 | DBUG_RETURN(FALSE); |
2168 | } |
2169 | |
2170 | |
2171 | void acl_free(bool end) |
2172 | { |
2173 | my_hash_free(&acl_roles); |
2174 | free_root(&acl_memroot,MYF(0)); |
2175 | delete_dynamic(&acl_hosts); |
2176 | delete_dynamic_with_callback(&acl_users, (FREE_FUNC) free_acl_user); |
2177 | delete_dynamic(&acl_dbs); |
2178 | delete_dynamic(&acl_wild_hosts); |
2179 | delete_dynamic(&acl_proxy_users); |
2180 | my_hash_free(&acl_check_hosts); |
2181 | my_hash_free(&acl_roles_mappings); |
2182 | if (!end) |
2183 | acl_cache->clear(1); /* purecov: inspected */ |
2184 | else |
2185 | { |
2186 | plugin_unlock(0, native_password_plugin); |
2187 | plugin_unlock(0, old_password_plugin); |
2188 | delete acl_cache; |
2189 | acl_cache=0; |
2190 | } |
2191 | } |
2192 | |
2193 | |
2194 | /* |
2195 | Forget current user/db-level privileges and read new privileges |
2196 | from the privilege tables. |
2197 | |
2198 | SYNOPSIS |
2199 | acl_reload() |
2200 | thd Current thread |
2201 | |
2202 | NOTE |
2203 | All tables of calling thread which were open and locked by LOCK TABLES |
2204 | statement will be unlocked and closed. |
2205 | This function is also used for initialization of structures responsible |
2206 | for user/db-level privilege checking. |
2207 | |
2208 | RETURN VALUE |
2209 | FALSE Success |
2210 | TRUE Failure |
2211 | */ |
2212 | |
2213 | bool acl_reload(THD *thd) |
2214 | { |
2215 | DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users; |
2216 | HASH old_acl_roles, old_acl_roles_mappings; |
2217 | MEM_ROOT old_mem; |
2218 | int result; |
2219 | DBUG_ENTER("acl_reload" ); |
2220 | |
2221 | Grant_tables tables(Table_host | Table_user | Table_db | Table_proxies_priv | |
2222 | Table_roles_mapping, TL_READ); |
2223 | /* |
2224 | To avoid deadlocks we should obtain table locks before |
2225 | obtaining acl_cache->lock mutex. |
2226 | */ |
2227 | if (unlikely((result= tables.open_and_lock(thd)))) |
2228 | { |
2229 | DBUG_ASSERT(result <= 0); |
2230 | /* |
2231 | Execution might have been interrupted; only print the error message |
2232 | if an error condition has been raised. |
2233 | */ |
2234 | if (thd->get_stmt_da()->is_error()) |
2235 | sql_print_error("Fatal error: Can't open and lock privilege tables: %s" , |
2236 | thd->get_stmt_da()->message()); |
2237 | goto end; |
2238 | } |
2239 | |
2240 | acl_cache->clear(0); |
2241 | mysql_mutex_lock(&acl_cache->lock); |
2242 | |
2243 | old_acl_hosts= acl_hosts; |
2244 | old_acl_users= acl_users; |
2245 | old_acl_roles= acl_roles; |
2246 | old_acl_roles_mappings= acl_roles_mappings; |
2247 | old_acl_proxy_users= acl_proxy_users; |
2248 | old_acl_dbs= acl_dbs; |
2249 | my_init_dynamic_array(&acl_hosts, sizeof(ACL_HOST), 20, 50, MYF(0)); |
2250 | my_init_dynamic_array(&acl_users, sizeof(ACL_USER), 50, 100, MYF(0)); |
2251 | my_init_dynamic_array(&acl_dbs, sizeof(ACL_DB), 50, 100, MYF(0)); |
2252 | my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), 50, 100, MYF(0)); |
2253 | my_hash_init2(&acl_roles,50, &my_charset_utf8_bin, |
2254 | 0, 0, 0, (my_hash_get_key) acl_role_get_key, 0, |
2255 | (void (*)(void *))free_acl_role, 0); |
2256 | my_hash_init2(&acl_roles_mappings, 50, &my_charset_utf8_bin, 0, 0, 0, |
2257 | (my_hash_get_key) acl_role_map_get_key, 0, 0, 0); |
2258 | old_mem= acl_memroot; |
2259 | delete_dynamic(&acl_wild_hosts); |
2260 | my_hash_free(&acl_check_hosts); |
2261 | |
2262 | if ((result= acl_load(thd, tables))) |
2263 | { // Error. Revert to old list |
2264 | DBUG_PRINT("error" ,("Reverting to old privileges" )); |
2265 | acl_free(); /* purecov: inspected */ |
2266 | acl_hosts= old_acl_hosts; |
2267 | acl_users= old_acl_users; |
2268 | acl_roles= old_acl_roles; |
2269 | acl_roles_mappings= old_acl_roles_mappings; |
2270 | acl_proxy_users= old_acl_proxy_users; |
2271 | acl_dbs= old_acl_dbs; |
2272 | acl_memroot= old_mem; |
2273 | init_check_host(); |
2274 | } |
2275 | else |
2276 | { |
2277 | my_hash_free(&old_acl_roles); |
2278 | free_root(&old_mem,MYF(0)); |
2279 | delete_dynamic(&old_acl_hosts); |
2280 | delete_dynamic_with_callback(&old_acl_users, (FREE_FUNC) free_acl_user); |
2281 | delete_dynamic(&old_acl_proxy_users); |
2282 | delete_dynamic(&old_acl_dbs); |
2283 | my_hash_free(&old_acl_roles_mappings); |
2284 | } |
2285 | mysql_mutex_unlock(&acl_cache->lock); |
2286 | end: |
2287 | close_mysql_tables(thd); |
2288 | DBUG_RETURN(result); |
2289 | } |
2290 | |
2291 | /* |
2292 | Get all access bits from table after fieldnr |
2293 | |
2294 | IMPLEMENTATION |
2295 | We know that the access privileges ends when there is no more fields |
2296 | or the field is not an enum with two elements. |
2297 | |
2298 | SYNOPSIS |
2299 | get_access() |
2300 | form an open table to read privileges from. |
2301 | The record should be already read in table->record[0] |
2302 | fieldnr number of the first privilege (that is ENUM('N','Y') field |
2303 | next_field on return - number of the field next to the last ENUM |
2304 | (unless next_field == 0) |
2305 | |
2306 | RETURN VALUE |
2307 | privilege mask |
2308 | */ |
2309 | |
2310 | static ulong get_access(TABLE *form, uint fieldnr, uint *next_field) |
2311 | { |
2312 | ulong access_bits=0,bit; |
2313 | char buff[2]; |
2314 | String res(buff,sizeof(buff),&my_charset_latin1); |
2315 | Field **pos; |
2316 | |
2317 | for (pos=form->field+fieldnr, bit=1; |
2318 | *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM && |
2319 | ((Field_enum*) (*pos))->typelib->count == 2 ; |
2320 | pos++, fieldnr++, bit<<=1) |
2321 | { |
2322 | if (get_YN_as_bool(*pos)) |
2323 | access_bits|= bit; |
2324 | } |
2325 | if (next_field) |
2326 | *next_field=fieldnr; |
2327 | return access_bits; |
2328 | } |
2329 | |
2330 | |
2331 | /* |
2332 | Return a number which, if sorted 'desc', puts strings in this order: |
2333 | no wildcards |
2334 | wildcards |
2335 | empty string |
2336 | */ |
2337 | |
2338 | static ulong get_sort(uint count,...) |
2339 | { |
2340 | va_list args; |
2341 | va_start(args,count); |
2342 | ulong sort=0; |
2343 | |
2344 | /* Should not use this function with more than 4 arguments for compare. */ |
2345 | DBUG_ASSERT(count <= 4); |
2346 | |
2347 | while (count--) |
2348 | { |
2349 | char *start, *str= va_arg(args,char*); |
2350 | uint chars= 0; |
2351 | uint wild_pos= 0; /* first wildcard position */ |
2352 | |
2353 | if ((start= str)) |
2354 | { |
2355 | for (; *str ; str++) |
2356 | { |
2357 | if (*str == wild_prefix && str[1]) |
2358 | str++; |
2359 | else if (*str == wild_many || *str == wild_one) |
2360 | { |
2361 | wild_pos= (uint) (str - start) + 1; |
2362 | break; |
2363 | } |
2364 | chars= 128; // Marker that chars existed |
2365 | } |
2366 | } |
2367 | sort= (sort << 8) + (wild_pos ? MY_MIN(wild_pos, 127U) : chars); |
2368 | } |
2369 | va_end(args); |
2370 | return sort; |
2371 | } |
2372 | |
2373 | |
2374 | static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) |
2375 | { |
2376 | if (a->sort > b->sort) |
2377 | return -1; |
2378 | if (a->sort < b->sort) |
2379 | return 1; |
2380 | return 0; |
2381 | } |
2382 | |
2383 | |
2384 | /* |
2385 | Gets user credentials without authentication and resource limit checks. |
2386 | |
2387 | SYNOPSIS |
2388 | acl_getroot() |
2389 | sctx Context which should be initialized |
2390 | user user name |
2391 | host host name |
2392 | ip IP |
2393 | db current data base name |
2394 | |
2395 | RETURN |
2396 | FALSE OK |
2397 | TRUE Error |
2398 | */ |
2399 | |
2400 | bool acl_getroot(Security_context *sctx, const char *user, const char *host, |
2401 | const char *ip, const char *db) |
2402 | { |
2403 | int res= 1; |
2404 | uint i; |
2405 | ACL_USER *acl_user= 0; |
2406 | DBUG_ENTER("acl_getroot" ); |
2407 | |
2408 | DBUG_PRINT("enter" , ("Host: '%s', Ip: '%s', User: '%s', db: '%s'" , |
2409 | host, ip, user, db)); |
2410 | sctx->user= user; |
2411 | sctx->host= host; |
2412 | sctx->ip= ip; |
2413 | sctx->host_or_ip= host ? host : (safe_str(ip)); |
2414 | |
2415 | if (!initialized) |
2416 | { |
2417 | /* |
2418 | here if mysqld's been started with --skip-grant-tables option. |
2419 | */ |
2420 | sctx->skip_grants(); |
2421 | DBUG_RETURN(FALSE); |
2422 | } |
2423 | |
2424 | mysql_mutex_lock(&acl_cache->lock); |
2425 | |
2426 | sctx->master_access= 0; |
2427 | sctx->db_access= 0; |
2428 | *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0; |
2429 | |
2430 | if (host[0]) // User, not Role |
2431 | { |
2432 | acl_user= find_user_wild(host, user, ip); |
2433 | |
2434 | if (acl_user) |
2435 | { |
2436 | res= 0; |
2437 | for (i=0 ; i < acl_dbs.elements ; i++) |
2438 | { |
2439 | ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); |
2440 | if (!acl_db->user || |
2441 | (user && user[0] && !strcmp(user, acl_db->user))) |
2442 | { |
2443 | if (compare_hostname(&acl_db->host, host, ip)) |
2444 | { |
2445 | if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0))) |
2446 | { |
2447 | sctx->db_access= acl_db->access; |
2448 | break; |
2449 | } |
2450 | } |
2451 | } |
2452 | } |
2453 | sctx->master_access= acl_user->access; |
2454 | |
2455 | if (acl_user->user.str) |
2456 | strmake_buf(sctx->priv_user, user); |
2457 | |
2458 | if (acl_user->host.hostname) |
2459 | strmake_buf(sctx->priv_host, acl_user->host.hostname); |
2460 | } |
2461 | } |
2462 | else // Role, not User |
2463 | { |
2464 | ACL_ROLE *acl_role= find_acl_role(user); |
2465 | if (acl_role) |
2466 | { |
2467 | res= 0; |
2468 | for (i=0 ; i < acl_dbs.elements ; i++) |
2469 | { |
2470 | ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*); |
2471 | if (!acl_db->user || |
2472 | (user && user[0] && !strcmp(user, acl_db->user))) |
2473 | { |
2474 | if (compare_hostname(&acl_db->host, "" , "" )) |
2475 | { |
2476 | if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0))) |
2477 | { |
2478 | sctx->db_access= acl_db->access; |
2479 | break; |
2480 | } |
2481 | } |
2482 | } |
2483 | } |
2484 | sctx->master_access= acl_role->access; |
2485 | |
2486 | if (acl_role->user.str) |
2487 | strmake_buf(sctx->priv_role, user); |
2488 | } |
2489 | } |
2490 | |
2491 | mysql_mutex_unlock(&acl_cache->lock); |
2492 | DBUG_RETURN(res); |
2493 | } |
2494 | |
2495 | static int check_user_can_set_role(const char *user, const char *host, |
2496 | const char *ip, const char *rolename, ulonglong *access) |
2497 | { |
2498 | ACL_ROLE *role; |
2499 | ACL_USER_BASE *acl_user_base; |
2500 | ACL_USER *UNINIT_VAR(acl_user); |
2501 | bool is_granted= FALSE; |
2502 | int result= 0; |
2503 | |
2504 | /* clear role privileges */ |
2505 | mysql_mutex_lock(&acl_cache->lock); |
2506 | |
2507 | if (!strcasecmp(rolename, "NONE" )) |
2508 | { |
2509 | /* have to clear the privileges */ |
2510 | /* get the current user */ |
2511 | acl_user= find_user_wild(host, user, ip); |
2512 | if (acl_user == NULL) |
2513 | { |
2514 | my_error(ER_INVALID_CURRENT_USER, MYF(0)); |
2515 | result= -1; |
2516 | } |
2517 | else if (access) |
2518 | *access= acl_user->access; |
2519 | |
2520 | goto end; |
2521 | } |
2522 | |
2523 | role= find_acl_role(rolename); |
2524 | |
2525 | /* According to SQL standard, the same error message must be presented */ |
2526 | if (role == NULL) { |
2527 | my_error(ER_INVALID_ROLE, MYF(0), rolename); |
2528 | result= -1; |
2529 | goto end; |
2530 | } |
2531 | |
2532 | for (uint i=0 ; i < role->parent_grantee.elements ; i++) |
2533 | { |
2534 | acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**)); |
2535 | if (acl_user_base->flags & IS_ROLE) |
2536 | continue; |
2537 | |
2538 | acl_user= (ACL_USER *)acl_user_base; |
2539 | if (acl_user->wild_eq(user, host, ip)) |
2540 | { |
2541 | is_granted= TRUE; |
2542 | break; |
2543 | } |
2544 | } |
2545 | |
2546 | /* According to SQL standard, the same error message must be presented */ |
2547 | if (!is_granted) |
2548 | { |
2549 | my_error(ER_INVALID_ROLE, MYF(0), rolename); |
2550 | result= 1; |
2551 | goto end; |
2552 | } |
2553 | |
2554 | if (access) |
2555 | { |
2556 | *access = acl_user->access | role->access; |
2557 | } |
2558 | end: |
2559 | mysql_mutex_unlock(&acl_cache->lock); |
2560 | return result; |
2561 | |
2562 | } |
2563 | |
2564 | int acl_check_setrole(THD *thd, const char *rolename, ulonglong *access) |
2565 | { |
2566 | /* Yes! priv_user@host. Don't ask why - that's what check_access() does. */ |
2567 | return check_user_can_set_role(thd->security_ctx->priv_user, |
2568 | thd->security_ctx->host, thd->security_ctx->ip, rolename, access); |
2569 | } |
2570 | |
2571 | |
2572 | int acl_setrole(THD *thd, const char *rolename, ulonglong access) |
2573 | { |
2574 | /* merge the privileges */ |
2575 | Security_context *sctx= thd->security_ctx; |
2576 | sctx->master_access= static_cast<ulong>(access); |
2577 | if (thd->db.str) |
2578 | sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db.str, FALSE); |
2579 | |
2580 | if (!strcasecmp(rolename, "NONE" )) |
2581 | { |
2582 | thd->security_ctx->priv_role[0]= 0; |
2583 | } |
2584 | else |
2585 | { |
2586 | if (thd->db.str) |
2587 | sctx->db_access|= acl_get("" , "" , rolename, thd->db.str, FALSE); |
2588 | /* mark the current role */ |
2589 | strmake_buf(thd->security_ctx->priv_role, rolename); |
2590 | } |
2591 | return 0; |
2592 | } |
2593 | |
2594 | static uchar* check_get_key(ACL_USER *buff, size_t *length, |
2595 | my_bool not_used __attribute__((unused))) |
2596 | { |
2597 | *length=buff->hostname_length; |
2598 | return (uchar*) buff->host.hostname; |
2599 | } |
2600 | |
2601 | |
2602 | static void acl_update_role(const char *rolename, ulong privileges) |
2603 | { |
2604 | ACL_ROLE *role= find_acl_role(rolename); |
2605 | if (role) |
2606 | role->initial_role_access= role->access= privileges; |
2607 | } |
2608 | |
2609 | |
2610 | static void acl_update_user(const char *user, const char *host, |
2611 | const char *password, size_t password_len, |
2612 | enum SSL_type ssl_type, |
2613 | const char *ssl_cipher, |
2614 | const char *x509_issuer, |
2615 | const char *x509_subject, |
2616 | USER_RESOURCES *mqh, |
2617 | ulong privileges, |
2618 | const LEX_CSTRING *plugin, |
2619 | const LEX_CSTRING *auth) |
2620 | { |
2621 | mysql_mutex_assert_owner(&acl_cache->lock); |
2622 | |
2623 | for (uint i=0 ; i < acl_users.elements ; i++) |
2624 | { |
2625 | ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); |
2626 | if (acl_user->eq(user, host)) |
2627 | { |
2628 | if (plugin->str[0]) |
2629 | { |
2630 | acl_user->plugin= *plugin; |
2631 | acl_user->auth_string.str= auth->str ? |
2632 | strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("" ); |
2633 | acl_user->auth_string.length= auth->length; |
2634 | if (fix_user_plugin_ptr(acl_user)) |
2635 | acl_user->plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length); |
2636 | } |
2637 | else |
2638 | if (password[0]) |
2639 | { |
2640 | acl_user->auth_string.str= strmake_root(&acl_memroot, password, password_len); |
2641 | acl_user->auth_string.length= password_len; |
2642 | set_user_salt(acl_user, password, password_len); |
2643 | set_user_plugin(acl_user, password_len); |
2644 | } |
2645 | acl_user->access=privileges; |
2646 | if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR) |
2647 | acl_user->user_resource.questions=mqh->questions; |
2648 | if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR) |
2649 | acl_user->user_resource.updates=mqh->updates; |
2650 | if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR) |
2651 | acl_user->user_resource.conn_per_hour= mqh->conn_per_hour; |
2652 | if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS) |
2653 | acl_user->user_resource.user_conn= mqh->user_conn; |
2654 | if (mqh->specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME) |
2655 | acl_user->user_resource.max_statement_time= mqh->max_statement_time; |
2656 | if (ssl_type != SSL_TYPE_NOT_SPECIFIED) |
2657 | { |
2658 | acl_user->ssl_type= ssl_type; |
2659 | acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) : |
2660 | 0); |
2661 | acl_user->x509_issuer= (x509_issuer ? strdup_root(&acl_memroot,x509_issuer) : |
2662 | 0); |
2663 | acl_user->x509_subject= (x509_subject ? |
2664 | strdup_root(&acl_memroot,x509_subject) : 0); |
2665 | } |
2666 | /* search complete: */ |
2667 | break; |
2668 | } |
2669 | } |
2670 | } |
2671 | |
2672 | |
2673 | static void acl_insert_role(const char *rolename, ulong privileges) |
2674 | { |
2675 | ACL_ROLE *entry; |
2676 | |
2677 | mysql_mutex_assert_owner(&acl_cache->lock); |
2678 | entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot); |
2679 | (void) my_init_dynamic_array(&entry->parent_grantee, |
2680 | sizeof(ACL_USER_BASE *), 8, 8, MYF(0)); |
2681 | (void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *), |
2682 | 8, 8, MYF(0)); |
2683 | |
2684 | my_hash_insert(&acl_roles, (uchar *)entry); |
2685 | } |
2686 | |
2687 | |
2688 | static void acl_insert_user(const char *user, const char *host, |
2689 | const char *password, size_t password_len, |
2690 | enum SSL_type ssl_type, |
2691 | const char *ssl_cipher, |
2692 | const char *x509_issuer, |
2693 | const char *x509_subject, |
2694 | USER_RESOURCES *mqh, |
2695 | ulong privileges, |
2696 | const LEX_CSTRING *plugin, |
2697 | const LEX_CSTRING *auth) |
2698 | { |
2699 | ACL_USER acl_user; |
2700 | |
2701 | mysql_mutex_assert_owner(&acl_cache->lock); |
2702 | |
2703 | bzero(&acl_user, sizeof(acl_user)); |
2704 | acl_user.user.str=*user ? strdup_root(&acl_memroot,user) : 0; |
2705 | acl_user.user.length= strlen(user); |
2706 | update_hostname(&acl_user.host, safe_strdup_root(&acl_memroot, host)); |
2707 | if (plugin->str[0]) |
2708 | { |
2709 | acl_user.plugin= *plugin; |
2710 | acl_user.auth_string.str= auth->str ? |
2711 | strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("" ); |
2712 | acl_user.auth_string.length= auth->length; |
2713 | if (fix_user_plugin_ptr(&acl_user)) |
2714 | acl_user.plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length); |
2715 | } |
2716 | else |
2717 | { |
2718 | acl_user.auth_string.str= strmake_root(&acl_memroot, password, password_len); |
2719 | acl_user.auth_string.length= password_len; |
2720 | set_user_salt(&acl_user, password, password_len); |
2721 | set_user_plugin(&acl_user, password_len); |
2722 | } |
2723 | |
2724 | acl_user.flags= 0; |
2725 | acl_user.access=privileges; |
2726 | acl_user.user_resource = *mqh; |
2727 | acl_user.sort=get_sort(2, acl_user.host.hostname, acl_user.user.str); |
2728 | acl_user.hostname_length=(uint) strlen(host); |
2729 | acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ? |
2730 | ssl_type : SSL_TYPE_NONE); |
2731 | acl_user.ssl_cipher= ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) : 0; |
2732 | acl_user.x509_issuer= x509_issuer ? strdup_root(&acl_memroot,x509_issuer) : 0; |
2733 | acl_user.x509_subject=x509_subject ? strdup_root(&acl_memroot,x509_subject) : 0; |
2734 | (void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *), |
2735 | 8, 8, MYF(0)); |
2736 | |
2737 | (void) push_dynamic(&acl_users,(uchar*) &acl_user); |
2738 | if (!acl_user.host.hostname || |
2739 | (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1])) |
2740 | allow_all_hosts=1; // Anyone can connect /* purecov: tested */ |
2741 | my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, |
2742 | sizeof(ACL_USER),(qsort_cmp) acl_compare); |
2743 | |
2744 | /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ |
2745 | rebuild_check_host(); |
2746 | |
2747 | /* |
2748 | Rebuild every user's role_grants since 'acl_users' has been sorted |
2749 | and old pointers to ACL_USER elements are no longer valid |
2750 | */ |
2751 | rebuild_role_grants(); |
2752 | } |
2753 | |
2754 | |
2755 | static bool acl_update_db(const char *user, const char *host, const char *db, |
2756 | ulong privileges) |
2757 | { |
2758 | mysql_mutex_assert_owner(&acl_cache->lock); |
2759 | |
2760 | bool updated= false; |
2761 | |
2762 | for (uint i=0 ; i < acl_dbs.elements ; i++) |
2763 | { |
2764 | ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*); |
2765 | if ((!acl_db->user && !user[0]) || |
2766 | (acl_db->user && |
2767 | !strcmp(user,acl_db->user))) |
2768 | { |
2769 | if ((!acl_db->host.hostname && !host[0]) || |
2770 | (acl_db->host.hostname && |
2771 | !strcmp(host, acl_db->host.hostname))) |
2772 | { |
2773 | if ((!acl_db->db && !db[0]) || |
2774 | (acl_db->db && !strcmp(db,acl_db->db))) |
2775 | |
2776 | { |
2777 | if (privileges) |
2778 | { |
2779 | acl_db->access= privileges; |
2780 | acl_db->initial_access= acl_db->access; |
2781 | } |
2782 | else |
2783 | delete_dynamic_element(&acl_dbs,i); |
2784 | updated= true; |
2785 | } |
2786 | } |
2787 | } |
2788 | } |
2789 | |
2790 | return updated; |
2791 | } |
2792 | |
2793 | |
2794 | /* |
2795 | Insert a user/db/host combination into the global acl_cache |
2796 | |
2797 | SYNOPSIS |
2798 | acl_insert_db() |
2799 | user User name |
2800 | host Host name |
2801 | db Database name |
2802 | privileges Bitmap of privileges |
2803 | |
2804 | NOTES |
2805 | acl_cache->lock must be locked when calling this |
2806 | */ |
2807 | |
2808 | static void acl_insert_db(const char *user, const char *host, const char *db, |
2809 | ulong privileges) |
2810 | { |
2811 | ACL_DB acl_db; |
2812 | mysql_mutex_assert_owner(&acl_cache->lock); |
2813 | acl_db.user=strdup_root(&acl_memroot,user); |
2814 | update_hostname(&acl_db.host, safe_strdup_root(&acl_memroot, host)); |
2815 | acl_db.db=strdup_root(&acl_memroot,db); |
2816 | acl_db.initial_access= acl_db.access= privileges; |
2817 | acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user); |
2818 | (void) push_dynamic(&acl_dbs,(uchar*) &acl_db); |
2819 | my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements, |
2820 | sizeof(ACL_DB),(qsort_cmp) acl_compare); |
2821 | } |
2822 | |
2823 | |
2824 | /* |
2825 | Get privilege for a host, user and db combination |
2826 | |
2827 | as db_is_pattern changes the semantics of comparison, |
2828 | acl_cache is not used if db_is_pattern is set. |
2829 | */ |
2830 | |
2831 | ulong acl_get(const char *host, const char *ip, |
2832 | const char *user, const char *db, my_bool db_is_pattern) |
2833 | { |
2834 | ulong host_access= ~(ulong)0, db_access= 0; |
2835 | uint i; |
2836 | size_t key_length; |
2837 | char key[ACL_KEY_LENGTH],*tmp_db,*end; |
2838 | acl_entry *entry; |
2839 | DBUG_ENTER("acl_get" ); |
2840 | |
2841 | tmp_db= strmov(strmov(key, safe_str(ip)) + 1, user) + 1; |
2842 | end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db); |
2843 | |
2844 | if (end >= key + sizeof(key)) // db name was truncated |
2845 | DBUG_RETURN(0); // no privileges for an invalid db name |
2846 | |
2847 | if (lower_case_table_names) |
2848 | { |
2849 | my_casedn_str(files_charset_info, tmp_db); |
2850 | db=tmp_db; |
2851 | } |
2852 | key_length= (size_t) (end-key); |
2853 | |
2854 | mysql_mutex_lock(&acl_cache->lock); |
2855 | if (!db_is_pattern && (entry=acl_cache->search((uchar*) key, key_length))) |
2856 | { |
2857 | db_access=entry->access; |
2858 | mysql_mutex_unlock(&acl_cache->lock); |
2859 | DBUG_PRINT("exit" , ("access: 0x%lx" , db_access)); |
2860 | DBUG_RETURN(db_access); |
2861 | } |
2862 | |
2863 | /* |
2864 | Check if there are some access rights for database and user |
2865 | */ |
2866 | for (i=0 ; i < acl_dbs.elements ; i++) |
2867 | { |
2868 | ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*); |
2869 | if (!acl_db->user || !strcmp(user,acl_db->user)) |
2870 | { |
2871 | if (compare_hostname(&acl_db->host,host,ip)) |
2872 | { |
2873 | if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern)) |
2874 | { |
2875 | db_access=acl_db->access; |
2876 | if (acl_db->host.hostname) |
2877 | goto exit; // Fully specified. Take it |
2878 | /* the host table is not used for roles */ |
2879 | if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user)) |
2880 | goto exit; |
2881 | break; /* purecov: tested */ |
2882 | } |
2883 | } |
2884 | } |
2885 | } |
2886 | if (!db_access) |
2887 | goto exit; // Can't be better |
2888 | |
2889 | /* |
2890 | No host specified for user. Get hostdata from host table |
2891 | */ |
2892 | host_access=0; // Host must be found |
2893 | for (i=0 ; i < acl_hosts.elements ; i++) |
2894 | { |
2895 | ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*); |
2896 | if (compare_hostname(&acl_host->host,host,ip)) |
2897 | { |
2898 | if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern)) |
2899 | { |
2900 | host_access=acl_host->access; // Fully specified. Take it |
2901 | break; |
2902 | } |
2903 | } |
2904 | } |
2905 | exit: |
2906 | /* Save entry in cache for quick retrieval */ |
2907 | if (!db_is_pattern && |
2908 | (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length))) |
2909 | { |
2910 | entry->access=(db_access & host_access); |
2911 | DBUG_ASSERT(key_length < 0xffff); |
2912 | entry->length=(uint16)key_length; |
2913 | memcpy((uchar*) entry->key,key,key_length); |
2914 | acl_cache->add(entry); |
2915 | } |
2916 | mysql_mutex_unlock(&acl_cache->lock); |
2917 | DBUG_PRINT("exit" , ("access: 0x%lx" , db_access & host_access)); |
2918 | DBUG_RETURN(db_access & host_access); |
2919 | } |
2920 | |
2921 | /* |
2922 | Check if there are any possible matching entries for this host |
2923 | |
2924 | NOTES |
2925 | All host names without wild cards are stored in a hash table, |
2926 | entries with wildcards are stored in a dynamic array |
2927 | */ |
2928 | |
2929 | static void init_check_host(void) |
2930 | { |
2931 | DBUG_ENTER("init_check_host" ); |
2932 | (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip), |
2933 | acl_users.elements, 1, MYF(0)); |
2934 | (void) my_hash_init(&acl_check_hosts,system_charset_info, |
2935 | acl_users.elements, 0, 0, |
2936 | (my_hash_get_key) check_get_key, 0, 0); |
2937 | if (!allow_all_hosts) |
2938 | { |
2939 | for (uint i=0 ; i < acl_users.elements ; i++) |
2940 | { |
2941 | ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); |
2942 | if (strchr(acl_user->host.hostname,wild_many) || |
2943 | strchr(acl_user->host.hostname,wild_one) || |
2944 | acl_user->host.ip_mask) |
2945 | { // Has wildcard |
2946 | uint j; |
2947 | for (j=0 ; j < acl_wild_hosts.elements ; j++) |
2948 | { // Check if host already exists |
2949 | acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j, |
2950 | acl_host_and_ip *); |
2951 | if (!my_strcasecmp(system_charset_info, |
2952 | acl_user->host.hostname, acl->hostname)) |
2953 | break; // already stored |
2954 | } |
2955 | if (j == acl_wild_hosts.elements) // If new |
2956 | (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host); |
2957 | } |
2958 | else if (!my_hash_search(&acl_check_hosts,(uchar*) |
2959 | acl_user->host.hostname, |
2960 | strlen(acl_user->host.hostname))) |
2961 | { |
2962 | if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user)) |
2963 | { // End of memory |
2964 | allow_all_hosts=1; // Should never happen |
2965 | DBUG_VOID_RETURN; |
2966 | } |
2967 | } |
2968 | } |
2969 | } |
2970 | freeze_size(&acl_wild_hosts); |
2971 | freeze_size(&acl_check_hosts.array); |
2972 | DBUG_VOID_RETURN; |
2973 | } |
2974 | |
2975 | |
2976 | /* |
2977 | Rebuild lists used for checking of allowed hosts |
2978 | |
2979 | We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding, |
2980 | dropping or renaming user, since they contain pointers to elements of |
2981 | 'acl_user' array, which are invalidated by drop operation, and use |
2982 | ACL_USER::host::hostname as a key, which is changed by rename. |
2983 | */ |
2984 | static void rebuild_check_host(void) |
2985 | { |
2986 | delete_dynamic(&acl_wild_hosts); |
2987 | my_hash_free(&acl_check_hosts); |
2988 | init_check_host(); |
2989 | } |
2990 | |
2991 | /* |
2992 | Reset a role role_grants dynamic array. |
2993 | Also, the role's access bits are reset to the ones present in the table. |
2994 | */ |
2995 | static my_bool acl_role_reset_role_arrays(void *ptr, |
2996 | void * not_used __attribute__((unused))) |
2997 | { |
2998 | ACL_ROLE *role= (ACL_ROLE *)ptr; |
2999 | reset_dynamic(&role->role_grants); |
3000 | reset_dynamic(&role->parent_grantee); |
3001 | role->counter= 0; |
3002 | return 0; |
3003 | } |
3004 | |
3005 | /* |
3006 | Add a the coresponding pointers present in the mapping to the entries in |
3007 | acl_users and acl_roles |
3008 | */ |
3009 | static bool add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role) |
3010 | { |
3011 | return push_dynamic(&grantee->role_grants, (uchar*) &role) |
3012 | || push_dynamic(&role->parent_grantee, (uchar*) &grantee); |
3013 | |
3014 | } |
3015 | |
3016 | /* |
3017 | Revert the last add_role_user_mapping() action |
3018 | */ |
3019 | static void undo_add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role) |
3020 | { |
3021 | void *pop __attribute__((unused)); |
3022 | |
3023 | pop= pop_dynamic(&grantee->role_grants); |
3024 | DBUG_ASSERT(role == *(ACL_ROLE**)pop); |
3025 | |
3026 | pop= pop_dynamic(&role->parent_grantee); |
3027 | DBUG_ASSERT(grantee == *(ACL_USER_BASE**)pop); |
3028 | } |
3029 | |
3030 | /* |
3031 | this helper is used when building role_grants and parent_grantee arrays |
3032 | from scratch. |
3033 | |
3034 | this happens either on initial loading of data from tables, in acl_load(). |
3035 | or in rebuild_role_grants after acl_role_reset_role_arrays(). |
3036 | */ |
3037 | static bool add_role_user_mapping(const char *uname, const char *hname, |
3038 | const char *rname) |
3039 | { |
3040 | ACL_USER_BASE *grantee= find_acl_user_base(uname, hname); |
3041 | ACL_ROLE *role= find_acl_role(rname); |
3042 | |
3043 | if (grantee == NULL || role == NULL) |
3044 | return 1; |
3045 | |
3046 | /* |
3047 | because all arrays are rebuilt completely, and counters were also reset, |
3048 | we can increment them here, and after the rebuild all counters will |
3049 | have correct values (equal to the number of roles granted). |
3050 | */ |
3051 | if (grantee->flags & IS_ROLE) |
3052 | ((ACL_ROLE*)grantee)->counter++; |
3053 | return add_role_user_mapping(grantee, role); |
3054 | } |
3055 | |
3056 | /* |
3057 | This helper function is used to removes roles and grantees |
3058 | from the corresponding cross-reference arrays. see remove_role_user_mapping(). |
3059 | as such, it asserts that an element to delete is present in the array, |
3060 | and is present only once. |
3061 | */ |
3062 | static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr) |
3063 | { |
3064 | bool found __attribute__((unused))= false; |
3065 | for (uint i= 0; i < array->elements; i++) |
3066 | { |
3067 | if (ptr == *dynamic_element(array, i, void**)) |
3068 | { |
3069 | DBUG_ASSERT(!found); |
3070 | delete_dynamic_element(array, i); |
3071 | IF_DBUG_ASSERT(found= true, break); |
3072 | } |
3073 | } |
3074 | DBUG_ASSERT(found); |
3075 | } |
3076 | |
3077 | static void remove_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role, |
3078 | int grantee_idx=-1, int role_idx=-1) |
3079 | { |
3080 | remove_ptr_from_dynarray(&grantee->role_grants, role); |
3081 | remove_ptr_from_dynarray(&role->parent_grantee, grantee); |
3082 | } |
3083 | |
3084 | |
3085 | static my_bool add_role_user_mapping_action(void *ptr, void *unused __attribute__((unused))) |
3086 | { |
3087 | ROLE_GRANT_PAIR *pair= (ROLE_GRANT_PAIR*)ptr; |
3088 | bool status __attribute__((unused)); |
3089 | status= add_role_user_mapping(pair->u_uname, pair->u_hname, pair->r_uname); |
3090 | /* |
3091 | The invariant chosen is that acl_roles_mappings should _always_ |
3092 | only contain valid entries, referencing correct user and role grants. |
3093 | If add_role_user_mapping detects an invalid entry, it will not add |
3094 | the mapping into the ACL_USER::role_grants array. |
3095 | */ |
3096 | DBUG_ASSERT(status == 0); |
3097 | return 0; |
3098 | } |
3099 | |
3100 | |
3101 | /* |
3102 | Rebuild the role grants every time the acl_users is modified |
3103 | |
3104 | The role grants in the ACL_USER class need to be rebuilt, as they contain |
3105 | pointers to elements of the acl_users array. |
3106 | */ |
3107 | |
3108 | static void rebuild_role_grants(void) |
3109 | { |
3110 | DBUG_ENTER("rebuild_role_grants" ); |
3111 | /* |
3112 | Reset every user's and role's role_grants array |
3113 | */ |
3114 | for (uint i=0; i < acl_users.elements; i++) { |
3115 | ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *); |
3116 | reset_dynamic(&user->role_grants); |
3117 | } |
3118 | my_hash_iterate(&acl_roles, acl_role_reset_role_arrays, NULL); |
3119 | |
3120 | /* Rebuild the direct links between users and roles in ACL_USER::role_grants */ |
3121 | my_hash_iterate(&acl_roles_mappings, add_role_user_mapping_action, NULL); |
3122 | |
3123 | DBUG_VOID_RETURN; |
3124 | } |
3125 | |
3126 | |
3127 | /* Return true if there is no users that can match the given host */ |
3128 | bool acl_check_host(const char *host, const char *ip) |
3129 | { |
3130 | if (allow_all_hosts) |
3131 | return 0; |
3132 | mysql_mutex_lock(&acl_cache->lock); |
3133 | |
3134 | if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) || |
3135 | (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip)))) |
3136 | { |
3137 | mysql_mutex_unlock(&acl_cache->lock); |
3138 | return 0; // Found host |
3139 | } |
3140 | for (uint i=0 ; i < acl_wild_hosts.elements ; i++) |
3141 | { |
3142 | acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*); |
3143 | if (compare_hostname(acl, host, ip)) |
3144 | { |
3145 | mysql_mutex_unlock(&acl_cache->lock); |
3146 | return 0; // Host ok |
3147 | } |
3148 | } |
3149 | mysql_mutex_unlock(&acl_cache->lock); |
3150 | if (ip != NULL) |
3151 | { |
3152 | /* Increment HOST_CACHE.COUNT_HOST_ACL_ERRORS. */ |
3153 | Host_errors errors; |
3154 | errors.m_host_acl= 1; |
3155 | inc_host_errors(ip, &errors); |
3156 | } |
3157 | return 1; // Host is not allowed |
3158 | } |
3159 | |
3160 | /** |
3161 | Check if the user is allowed to alter the mysql.user table |
3162 | |
3163 | @param thd THD |
3164 | @param host Hostname for the user |
3165 | @param user User name |
3166 | |
3167 | @return Error status |
3168 | @retval 0 OK |
3169 | @retval 1 Error |
3170 | */ |
3171 | |
3172 | static int check_alter_user(THD *thd, const char *host, const char *user) |
3173 | { |
3174 | int error = 1; |
3175 | if (!initialized) |
3176 | { |
3177 | my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables" ); |
3178 | goto end; |
3179 | } |
3180 | |
3181 | if (IF_WSREP((!WSREP(thd) || !thd->wsrep_applier), 1) && |
3182 | !thd->slave_thread && !thd->security_ctx->priv_user[0] && |
3183 | !in_bootstrap) |
3184 | { |
3185 | my_message(ER_PASSWORD_ANONYMOUS_USER, |
3186 | ER_THD(thd, ER_PASSWORD_ANONYMOUS_USER), |
3187 | MYF(0)); |
3188 | goto end; |
3189 | } |
3190 | if (!host) // Role |
3191 | { |
3192 | my_error(ER_PASSWORD_NO_MATCH, MYF(0)); |
3193 | goto end; |
3194 | } |
3195 | |
3196 | if (!thd->slave_thread && |
3197 | IF_WSREP((!WSREP(thd) || !thd->wsrep_applier),1) && |
3198 | (strcmp(thd->security_ctx->priv_user, user) || |
3199 | my_strcasecmp(system_charset_info, host, |
3200 | thd->security_ctx->priv_host))) |
3201 | { |
3202 | if (check_access(thd, UPDATE_ACL, "mysql" , NULL, NULL, 1, 0)) |
3203 | goto end; |
3204 | } |
3205 | |
3206 | error = 0; |
3207 | |
3208 | end: |
3209 | return error; |
3210 | } |
3211 | /** |
3212 | Check if the user is allowed to change password |
3213 | |
3214 | @param thd THD |
3215 | @param user User, hostname, new password or password hash |
3216 | |
3217 | @return Error status |
3218 | @retval 0 OK |
3219 | @retval 1 ERROR; In this case the error is sent to the client. |
3220 | */ |
3221 | |
3222 | bool check_change_password(THD *thd, LEX_USER *user) |
3223 | { |
3224 | LEX_USER *real_user= get_current_user(thd, user); |
3225 | |
3226 | if (fix_and_copy_user(real_user, user, thd) || |
3227 | validate_password(real_user, thd)) |
3228 | return true; |
3229 | |
3230 | *user= *real_user; |
3231 | |
3232 | return check_alter_user(thd, user->host.str, user->user.str); |
3233 | } |
3234 | |
3235 | |
3236 | /** |
3237 | Change a password for a user. |
3238 | |
3239 | @param thd THD |
3240 | @param user User, hostname, new password hash |
3241 | |
3242 | @return Error code |
3243 | @retval 0 ok |
3244 | @retval 1 ERROR; In this case the error is sent to the client. |
3245 | */ |
3246 | bool change_password(THD *thd, LEX_USER *user) |
3247 | { |
3248 | Grant_tables tables(Table_user, TL_WRITE); |
3249 | /* Buffer should be extended when password length is extended. */ |
3250 | char buff[512]; |
3251 | ulong query_length= 0; |
3252 | enum_binlog_format save_binlog_format; |
3253 | int result=0; |
3254 | const CSET_STRING query_save __attribute__((unused)) = thd->query_string; |
3255 | DBUG_ENTER("change_password" ); |
3256 | DBUG_PRINT("enter" ,("host: '%s' user: '%s' new_password: '%s'" , |
3257 | user->host.str, user->user.str, user->pwhash.str)); |
3258 | DBUG_ASSERT(user->host.str != 0); // Ensured by parent |
3259 | |
3260 | /* |
3261 | This statement will be replicated as a statement, even when using |
3262 | row-based replication. The flag will be reset at the end of the |
3263 | statement. |
3264 | This has to be handled here as it's called by set_var.cc, which is |
3265 | not automaticly handled by sql_parse.cc |
3266 | */ |
3267 | save_binlog_format= thd->set_current_stmt_binlog_format_stmt(); |
3268 | |
3269 | if (mysql_bin_log.is_open() || |
3270 | (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))) |
3271 | { |
3272 | query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'" , |
3273 | safe_str(user->user.str), safe_str(user->host.str), |
3274 | safe_str(user->pwhash.str)); |
3275 | } |
3276 | |
3277 | if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)) |
3278 | { |
3279 | thd->set_query(buff, query_length, system_charset_info); |
3280 | WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user" , NULL); |
3281 | } |
3282 | |
3283 | if ((result= tables.open_and_lock(thd))) |
3284 | DBUG_RETURN(result != 1); |
3285 | |
3286 | result= 1; |
3287 | |
3288 | mysql_mutex_lock(&acl_cache->lock); |
3289 | ACL_USER *acl_user; |
3290 | if (!(acl_user= find_user_exact(user->host.str, user->user.str))) |
3291 | { |
3292 | mysql_mutex_unlock(&acl_cache->lock); |
3293 | my_message(ER_PASSWORD_NO_MATCH, |
3294 | ER_THD(thd, ER_PASSWORD_NO_MATCH), MYF(0)); |
3295 | goto end; |
3296 | } |
3297 | |
3298 | /* update loaded acl entry: */ |
3299 | if (acl_user->plugin.str == native_password_plugin_name.str || |
3300 | acl_user->plugin.str == old_password_plugin_name.str) |
3301 | { |
3302 | acl_user->auth_string.str= strmake_root(&acl_memroot, user->pwhash.str, user->pwhash.length); |
3303 | acl_user->auth_string.length= user->pwhash.length; |
3304 | set_user_salt(acl_user, user->pwhash.str, user->pwhash.length); |
3305 | |
3306 | set_user_plugin(acl_user, user->pwhash.length); |
3307 | } |
3308 | else |
3309 | push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, |
3310 | ER_SET_PASSWORD_AUTH_PLUGIN, |
3311 | ER_THD(thd, ER_SET_PASSWORD_AUTH_PLUGIN)); |
3312 | |
3313 | if (update_user_table(thd, tables.user_table(), |
3314 | safe_str(acl_user->host.hostname), |
3315 | safe_str(acl_user->user.str), |
3316 | user->pwhash.str, user->pwhash.length)) |
3317 | { |
3318 | mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */ |
3319 | goto end; |
3320 | } |
3321 | |
3322 | acl_cache->clear(1); // Clear locked hostname cache |
3323 | mysql_mutex_unlock(&acl_cache->lock); |
3324 | result= 0; |
3325 | if (mysql_bin_log.is_open()) |
3326 | { |
3327 | DBUG_ASSERT(query_length); |
3328 | thd->clear_error(); |
3329 | result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length, |
3330 | FALSE, FALSE, FALSE, 0); |
3331 | } |
3332 | end: |
3333 | close_mysql_tables(thd); |
3334 | |
3335 | #ifdef WITH_WSREP |
3336 | error: // this label is used in WSREP_TO_ISOLATION_BEGIN |
3337 | if (WSREP(thd) && !thd->wsrep_applier) |
3338 | { |
3339 | WSREP_TO_ISOLATION_END; |
3340 | |
3341 | thd->set_query(query_save); |
3342 | thd->wsrep_exec_mode = LOCAL_STATE; |
3343 | } |
3344 | #endif /* WITH_WSREP */ |
3345 | thd->restore_stmt_binlog_format(save_binlog_format); |
3346 | |
3347 | DBUG_RETURN(result); |
3348 | } |
3349 | |
3350 | int acl_check_set_default_role(THD *thd, const char *host, const char *user) |
3351 | { |
3352 | return check_alter_user(thd, host, user); |
3353 | } |
3354 | |
3355 | int acl_set_default_role(THD *thd, const char *host, const char *user, |
3356 | const char *rolename) |
3357 | { |
3358 | Grant_tables tables(Table_user, TL_WRITE); |
3359 | char user_key[MAX_KEY_LENGTH]; |
3360 | int result= 1; |
3361 | int error; |
3362 | ulong query_length= 0; |
3363 | bool clear_role= FALSE; |
3364 | char buff[512]; |
3365 | enum_binlog_format save_binlog_format; |
3366 | const CSET_STRING query_save __attribute__((unused)) = thd->query_string; |
3367 | |
3368 | DBUG_ENTER("acl_set_default_role" ); |
3369 | DBUG_PRINT("enter" ,("host: '%s' user: '%s' rolename: '%s'" , |
3370 | safe_str(user), safe_str(host), safe_str(rolename))); |
3371 | |
3372 | if (rolename == current_role.str) { |
3373 | if (!thd->security_ctx->priv_role[0]) |
3374 | rolename= "NONE" ; |
3375 | else |
3376 | rolename= thd->security_ctx->priv_role; |
3377 | } |
3378 | |
3379 | if (check_user_can_set_role(user, host, host, rolename, NULL)) |
3380 | DBUG_RETURN(result); |
3381 | |
3382 | if (!strcasecmp(rolename, "NONE" )) |
3383 | clear_role= TRUE; |
3384 | |
3385 | if (mysql_bin_log.is_open() || |
3386 | (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0))) |
3387 | { |
3388 | query_length= |
3389 | sprintf(buff,"SET DEFAULT ROLE '%-.120s' FOR '%-.120s'@'%-.120s'" , |
3390 | safe_str(rolename), safe_str(user), safe_str(host)); |
3391 | } |
3392 | |
3393 | /* |
3394 | This statement will be replicated as a statement, even when using |
3395 | row-based replication. The flag will be reset at the end of the |
3396 | statement. |
3397 | This has to be handled here as it's called by set_var.cc, which is |
3398 | not automaticly handled by sql_parse.cc |
3399 | */ |
3400 | save_binlog_format= thd->set_current_stmt_binlog_format_stmt(); |
3401 | |
3402 | if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)) |
3403 | { |
3404 | thd->set_query(buff, query_length, system_charset_info); |
3405 | WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user" , NULL); |
3406 | } |
3407 | |
3408 | /* |
3409 | Extra block due to WSREP_TO_ISOLATION_BEGIN using goto. |
3410 | TODO(cvicentiu) Should move this block out in a new function. |
3411 | */ |
3412 | { |
3413 | if ((result= tables.open_and_lock(thd))) |
3414 | DBUG_RETURN(result != 1); |
3415 | |
3416 | const User_table& user_table= tables.user_table(); |
3417 | TABLE *table= user_table.table(); |
3418 | |
3419 | result= 1; |
3420 | |
3421 | mysql_mutex_lock(&acl_cache->lock); |
3422 | ACL_USER *acl_user; |
3423 | if (!(acl_user= find_user_exact(host, user))) |
3424 | { |
3425 | mysql_mutex_unlock(&acl_cache->lock); |
3426 | my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH), |
3427 | MYF(0)); |
3428 | goto end; |
3429 | } |
3430 | |
3431 | if (!clear_role) |
3432 | { |
3433 | /* set new default_rolename */ |
3434 | acl_user->default_rolename.str= safe_strdup_root(&acl_memroot, rolename); |
3435 | acl_user->default_rolename.length= strlen(rolename); |
3436 | } |
3437 | else |
3438 | { |
3439 | /* clear the default_rolename */ |
3440 | acl_user->default_rolename.str = NULL; |
3441 | acl_user->default_rolename.length = 0; |
3442 | } |
3443 | |
3444 | /* update the mysql.user table with the new default role */ |
3445 | tables.user_table().table()->use_all_columns(); |
3446 | if (!tables.user_table().default_role()) |
3447 | { |
3448 | my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0), |
3449 | table->alias.c_ptr(), DEFAULT_ROLE_COLUMN_IDX + 1, |
3450 | tables.user_table().num_fields(), |
3451 | static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID); |
3452 | mysql_mutex_unlock(&acl_cache->lock); |
3453 | goto end; |
3454 | } |
3455 | user_table.host()->store(host,(uint) strlen(host), system_charset_info); |
3456 | user_table.user()->store(user,(uint) strlen(user), system_charset_info); |
3457 | key_copy((uchar *) user_key, table->record[0], table->key_info, |
3458 | table->key_info->key_length); |
3459 | |
3460 | if (table->file->ha_index_read_idx_map(table->record[0], 0, |
3461 | (uchar *) user_key, HA_WHOLE_KEY, |
3462 | HA_READ_KEY_EXACT)) |
3463 | { |
3464 | mysql_mutex_unlock(&acl_cache->lock); |
3465 | my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH), |
3466 | MYF(0)); |
3467 | goto end; |
3468 | } |
3469 | store_record(table, record[1]); |
3470 | user_table.default_role()->store(acl_user->default_rolename.str, |
3471 | acl_user->default_rolename.length, |
3472 | system_charset_info); |
3473 | if (unlikely(error= table->file->ha_update_row(table->record[1], |
3474 | table->record[0])) && |
3475 | error != HA_ERR_RECORD_IS_THE_SAME) |
3476 | { |
3477 | mysql_mutex_unlock(&acl_cache->lock); |
3478 | table->file->print_error(error,MYF(0)); /* purecov: deadcode */ |
3479 | goto end; |
3480 | } |
3481 | |
3482 | acl_cache->clear(1); |
3483 | mysql_mutex_unlock(&acl_cache->lock); |
3484 | result= 0; |
3485 | if (mysql_bin_log.is_open()) |
3486 | { |
3487 | DBUG_ASSERT(query_length); |
3488 | thd->clear_error(); |
3489 | result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length, |
3490 | FALSE, FALSE, FALSE, 0); |
3491 | } |
3492 | end: |
3493 | close_mysql_tables(thd); |
3494 | } |
3495 | |
3496 | #ifdef WITH_WSREP |
3497 | error: // this label is used in WSREP_TO_ISOLATION_END |
3498 | if (WSREP(thd) && !thd->wsrep_applier) |
3499 | { |
3500 | WSREP_TO_ISOLATION_END; |
3501 | |
3502 | thd->set_query(query_save); |
3503 | thd->wsrep_exec_mode = LOCAL_STATE; |
3504 | } |
3505 | #endif /* WITH_WSREP */ |
3506 | |
3507 | thd->restore_stmt_binlog_format(save_binlog_format); |
3508 | |
3509 | DBUG_RETURN(result); |
3510 | } |
3511 | |
3512 | |
3513 | /* |
3514 | Find user in ACL |
3515 | |
3516 | SYNOPSIS |
3517 | is_acl_user() |
3518 | host host name |
3519 | user user name |
3520 | |
3521 | RETURN |
3522 | FALSE user not fond |
3523 | TRUE there is such user |
3524 | */ |
3525 | |
3526 | bool is_acl_user(const char *host, const char *user) |
3527 | { |
3528 | bool res; |
3529 | |
3530 | /* --skip-grants */ |
3531 | if (!initialized) |
3532 | return TRUE; |
3533 | |
3534 | mysql_mutex_lock(&acl_cache->lock); |
3535 | |
3536 | if (*host) // User |
3537 | res= find_user_exact(host, user) != NULL; |
3538 | else // Role |
3539 | res= find_acl_role(user) != NULL; |
3540 | |
3541 | mysql_mutex_unlock(&acl_cache->lock); |
3542 | return res; |
3543 | } |
3544 | |
3545 | |
3546 | /* |
3547 | unlike find_user_exact and find_user_wild, |
3548 | this function finds anonymous users too, it's when a |
3549 | user is not empty, but priv_user (acl_user->user) is empty. |
3550 | */ |
3551 | static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip) |
3552 | { |
3553 | ACL_USER *result= NULL; |
3554 | mysql_mutex_assert_owner(&acl_cache->lock); |
3555 | for (uint i=0; i < acl_users.elements; i++) |
3556 | { |
3557 | ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*); |
3558 | if ((!acl_user_tmp->user.str || |
3559 | !strcmp(user, acl_user_tmp->user.str)) && |
3560 | compare_hostname(&acl_user_tmp->host, host, ip)) |
3561 | { |
3562 | result= acl_user_tmp; |
3563 | break; |
3564 | } |
3565 | } |
3566 | return result; |
3567 | } |
3568 | |
3569 | |
3570 | /* |
3571 | Find first entry that matches the specified user@host pair |
3572 | */ |
3573 | static ACL_USER * find_user_exact(const char *host, const char *user) |
3574 | { |
3575 | mysql_mutex_assert_owner(&acl_cache->lock); |
3576 | |
3577 | for (uint i=0 ; i < acl_users.elements ; i++) |
3578 | { |
3579 | ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); |
3580 | if (acl_user->eq(user, host)) |
3581 | return acl_user; |
3582 | } |
3583 | return 0; |
3584 | } |
3585 | |
3586 | /* |
3587 | Find first entry that matches the specified user@host pair |
3588 | */ |
3589 | static ACL_USER * find_user_wild(const char *host, const char *user, const char *ip) |
3590 | { |
3591 | mysql_mutex_assert_owner(&acl_cache->lock); |
3592 | |
3593 | for (uint i=0 ; i < acl_users.elements ; i++) |
3594 | { |
3595 | ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); |
3596 | if (acl_user->wild_eq(user, host, ip)) |
3597 | return acl_user; |
3598 | } |
3599 | return 0; |
3600 | } |
3601 | |
3602 | /* |
3603 | Find a role with the specified name |
3604 | */ |
3605 | static ACL_ROLE *find_acl_role(const char *role) |
3606 | { |
3607 | DBUG_ENTER("find_acl_role" ); |
3608 | DBUG_PRINT("enter" ,("role: '%s'" , role)); |
3609 | DBUG_PRINT("info" , ("Hash elements: %ld" , acl_roles.records)); |
3610 | |
3611 | mysql_mutex_assert_owner(&acl_cache->lock); |
3612 | |
3613 | ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role, |
3614 | safe_strlen(role)); |
3615 | DBUG_RETURN(r); |
3616 | } |
3617 | |
3618 | |
3619 | static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host) |
3620 | { |
3621 | if (*host) |
3622 | return find_user_exact(host, user); |
3623 | |
3624 | return find_acl_role(user); |
3625 | } |
3626 | |
3627 | |
3628 | /* |
3629 | Comparing of hostnames |
3630 | |
3631 | NOTES |
3632 | A hostname may be of type: |
3633 | hostname (May include wildcards); monty.pp.sci.fi |
3634 | ip (May include wildcards); 192.168.0.0 |
3635 | ip/netmask 192.168.0.0/255.255.255.0 |
3636 | |
3637 | A net mask of 0.0.0.0 is not allowed. |
3638 | */ |
3639 | |
3640 | static const char *calc_ip(const char *ip, long *val, char end) |
3641 | { |
3642 | long ip_val,tmp; |
3643 | if (!(ip=str2int(ip,10,0,255,&ip_val)) || *ip != '.') |
3644 | return 0; |
3645 | ip_val<<=24; |
3646 | if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.') |
3647 | return 0; |
3648 | ip_val+=tmp<<16; |
3649 | if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.') |
3650 | return 0; |
3651 | ip_val+=tmp<<8; |
3652 | if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != end) |
3653 | return 0; |
3654 | *val=ip_val+tmp; |
3655 | return ip; |
3656 | } |
3657 | |
3658 | |
3659 | static void update_hostname(acl_host_and_ip *host, const char *hostname) |
3660 | { |
3661 | // fix historical undocumented convention that empty host is the same as '%' |
3662 | hostname=const_cast<char*>(hostname ? hostname : host_not_specified.str); |
3663 | host->hostname=(char*) hostname; // This will not be modified! |
3664 | if (!(hostname= calc_ip(hostname,&host->ip,'/')) || |
3665 | !(hostname= calc_ip(hostname+1,&host->ip_mask,'\0'))) |
3666 | { |
3667 | host->ip= host->ip_mask=0; // Not a masked ip |
3668 | } |
3669 | } |
3670 | |
3671 | |
3672 | static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, |
3673 | const char *ip) |
3674 | { |
3675 | long tmp; |
3676 | if (host->ip_mask && ip && calc_ip(ip,&tmp,'\0')) |
3677 | { |
3678 | return (tmp & host->ip_mask) == host->ip; |
3679 | } |
3680 | return (!host->hostname || |
3681 | (hostname && !wild_case_compare(system_charset_info, |
3682 | hostname, host->hostname)) || |
3683 | (ip && !wild_compare(ip, host->hostname, 0))); |
3684 | } |
3685 | |
3686 | /** |
3687 | Check if the given host name needs to be resolved or not. |
3688 | Host name has to be resolved if it actually contains *name*. |
3689 | |
3690 | For example: |
3691 | 192.168.1.1 --> FALSE |
3692 | 192.168.1.0/255.255.255.0 --> FALSE |
3693 | % --> FALSE |
3694 | 192.168.1.% --> FALSE |
3695 | AB% --> FALSE |
3696 | |
3697 | AAAAFFFF --> TRUE (Hostname) |
3698 | AAAA:FFFF:1234:5678 --> FALSE |
3699 | ::1 --> FALSE |
3700 | |
3701 | This function does not check if the given string is a valid host name or |
3702 | not. It assumes that the argument is a valid host name. |
3703 | |
3704 | @param hostname the string to check. |
3705 | |
3706 | @return a flag telling if the argument needs to be resolved or not. |
3707 | @retval TRUE the argument is a host name and needs to be resolved. |
3708 | @retval FALSE the argument is either an IP address, or a patter and |
3709 | should not be resolved. |
3710 | */ |
3711 | |
3712 | bool hostname_requires_resolving(const char *hostname) |
3713 | { |
3714 | if (!hostname) |
3715 | return FALSE; |
3716 | |
3717 | /* Check if hostname is the localhost. */ |
3718 | |
3719 | size_t hostname_len= strlen(hostname); |
3720 | size_t localhost_len= strlen(my_localhost); |
3721 | |
3722 | if (hostname == my_localhost || |
3723 | (hostname_len == localhost_len && |
3724 | !my_strnncoll(system_charset_info, |
3725 | (const uchar *) hostname, hostname_len, |
3726 | (const uchar *) my_localhost, strlen(my_localhost)))) |
3727 | { |
3728 | return FALSE; |
3729 | } |
3730 | |
3731 | /* |
3732 | If the string contains any of {':', '%', '_', '/'}, it is definitely |
3733 | not a host name: |
3734 | - ':' means that the string is an IPv6 address; |
3735 | - '%' or '_' means that the string is a pattern; |
3736 | - '/' means that the string is an IPv4 network address; |
3737 | */ |
3738 | |
3739 | for (const char *p= hostname; *p; ++p) |
3740 | { |
3741 | switch (*p) { |
3742 | case ':': |
3743 | case '%': |
3744 | case '_': |
3745 | case '/': |
3746 | return FALSE; |
3747 | } |
3748 | } |
3749 | |
3750 | /* |
3751 | Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address |
3752 | (12.34.56.78). The assumption is that if the string contains only |
3753 | digits and dots, it is an IPv4 address. Otherwise -- a host name. |
3754 | */ |
3755 | |
3756 | for (const char *p= hostname; *p; ++p) |
3757 | { |
3758 | if (*p != '.' && !my_isdigit(&my_charset_latin1, *p)) |
3759 | return TRUE; /* a "letter" has been found. */ |
3760 | } |
3761 | |
3762 | return FALSE; /* all characters are either dots or digits. */ |
3763 | } |
3764 | |
3765 | |
3766 | void set_authentication_plugin_from_password(const User_table& user_table, |
3767 | const char* password, size_t password_length) |
3768 | { |
3769 | if (password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH) |
3770 | { |
3771 | user_table.plugin()->store(native_password_plugin_name.str, |
3772 | native_password_plugin_name.length, |
3773 | system_charset_info); |
3774 | } |
3775 | else |
3776 | { |
3777 | DBUG_ASSERT(password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323); |
3778 | user_table.plugin()->store(old_password_plugin_name.str, |
3779 | old_password_plugin_name.length, |
3780 | system_charset_info); |
3781 | } |
3782 | user_table.authentication_string()->store(password, |
3783 | password_length, |
3784 | system_charset_info); |
3785 | } |
3786 | /** |
3787 | Update record for user in mysql.user privilege table with new password. |
3788 | |
3789 | @param thd THD |
3790 | @param table Pointer to TABLE object for open mysql.user table |
3791 | @param host Hostname |
3792 | @param user Username |
3793 | @param new_password New password hash |
3794 | @param new_password_len Length of new password hash |
3795 | |
3796 | @see change_password |
3797 | */ |
3798 | |
3799 | static bool update_user_table(THD *thd, const User_table& user_table, |
3800 | const char *host, const char *user, |
3801 | const char *new_password, size_t new_password_len) |
3802 | { |
3803 | char user_key[MAX_KEY_LENGTH]; |
3804 | int error; |
3805 | DBUG_ENTER("update_user_table" ); |
3806 | DBUG_PRINT("enter" ,("user: %s host: %s" ,user,host)); |
3807 | |
3808 | TABLE *table= user_table.table(); |
3809 | table->use_all_columns(); |
3810 | user_table.host()->store(host,(uint) strlen(host), system_charset_info); |
3811 | user_table.user()->store(user,(uint) strlen(user), system_charset_info); |
3812 | key_copy((uchar *) user_key, table->record[0], table->key_info, |
3813 | table->key_info->key_length); |
3814 | |
3815 | if (table->file->ha_index_read_idx_map(table->record[0], 0, |
3816 | (uchar *) user_key, HA_WHOLE_KEY, |
3817 | HA_READ_KEY_EXACT)) |
3818 | { |
3819 | my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH), |
3820 | MYF(0)); /* purecov: deadcode */ |
3821 | DBUG_RETURN(1); /* purecov: deadcode */ |
3822 | } |
3823 | store_record(table,record[1]); |
3824 | /* If the password column is missing, we use the |
3825 | authentication_string column. */ |
3826 | if (user_table.password()) |
3827 | user_table.password()->store(new_password, new_password_len, system_charset_info); |
3828 | else |
3829 | set_authentication_plugin_from_password(user_table, new_password, |
3830 | new_password_len); |
3831 | |
3832 | |
3833 | if (unlikely(error= table->file->ha_update_row(table->record[1], |
3834 | table->record[0])) && |
3835 | error != HA_ERR_RECORD_IS_THE_SAME) |
3836 | { |
3837 | table->file->print_error(error,MYF(0)); /* purecov: deadcode */ |
3838 | DBUG_RETURN(1); |
3839 | } |
3840 | DBUG_RETURN(0); |
3841 | } |
3842 | |
3843 | |
3844 | /* |
3845 | Return 1 if we are allowed to create new users |
3846 | the logic here is: INSERT_ACL is sufficient. |
3847 | It's also a requirement in opt_safe_user_create, |
3848 | otherwise CREATE_USER_ACL is enough. |
3849 | */ |
3850 | |
3851 | static bool test_if_create_new_users(THD *thd) |
3852 | { |
3853 | Security_context *sctx= thd->security_ctx; |
3854 | bool create_new_users= MY_TEST(sctx->master_access & INSERT_ACL) || |
3855 | (!opt_safe_user_create && |
3856 | MY_TEST(sctx->master_access & CREATE_USER_ACL)); |
3857 | if (!create_new_users) |
3858 | { |
3859 | TABLE_LIST tl; |
3860 | ulong db_access; |
3861 | tl.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_USER_NAME, NULL, TL_WRITE); |
3862 | create_new_users= 1; |
3863 | |
3864 | db_access=acl_get(sctx->host, sctx->ip, |
3865 | sctx->priv_user, tl.db.str, 0); |
3866 | if (sctx->priv_role[0]) |
3867 | db_access|= acl_get("" , "" , sctx->priv_role, tl.db.str, 0); |
3868 | if (!(db_access & INSERT_ACL)) |
3869 | { |
3870 | if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE)) |
3871 | create_new_users=0; |
3872 | } |
3873 | } |
3874 | return create_new_users; |
3875 | } |
3876 | |
3877 | |
3878 | /**************************************************************************** |
3879 | Handle GRANT commands |
3880 | ****************************************************************************/ |
3881 | |
3882 | static int replace_user_table(THD *thd, const User_table &user_table, |
3883 | LEX_USER &combo, |
3884 | ulong rights, bool revoke_grant, |
3885 | bool can_create_user, bool no_auto_create) |
3886 | { |
3887 | int error = -1; |
3888 | bool old_row_exists=0; |
3889 | char what= (revoke_grant) ? 'N' : 'Y'; |
3890 | uchar user_key[MAX_KEY_LENGTH]; |
3891 | bool handle_as_role= combo.is_role(); |
3892 | LEX *lex= thd->lex; |
3893 | TABLE *table= user_table.table(); |
3894 | DBUG_ENTER("replace_user_table" ); |
3895 | |
3896 | mysql_mutex_assert_owner(&acl_cache->lock); |
3897 | |
3898 | if (combo.pwhash.str && combo.pwhash.str[0]) |
3899 | { |
3900 | if (combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH && |
3901 | combo.pwhash.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323) |
3902 | { |
3903 | DBUG_ASSERT(0); |
3904 | my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH); |
3905 | DBUG_RETURN(-1); |
3906 | } |
3907 | } |
3908 | else |
3909 | combo.pwhash= empty_clex_str; |
3910 | |
3911 | /* if the user table is not up to date, we can't handle role updates */ |
3912 | if (!user_table.is_role() && handle_as_role) |
3913 | { |
3914 | my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0), |
3915 | "user" , ROLE_ASSIGN_COLUMN_IDX + 1, user_table.num_fields(), |
3916 | static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID); |
3917 | DBUG_RETURN(-1); |
3918 | } |
3919 | |
3920 | table->use_all_columns(); |
3921 | user_table.host()->store(combo.host.str,combo.host.length, |
3922 | system_charset_info); |
3923 | user_table.user()->store(combo.user.str,combo.user.length, |
3924 | system_charset_info); |
3925 | key_copy(user_key, table->record[0], table->key_info, |
3926 | table->key_info->key_length); |
3927 | |
3928 | if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key, |
3929 | HA_WHOLE_KEY, |
3930 | HA_READ_KEY_EXACT)) |
3931 | { |
3932 | /* what == 'N' means revoke */ |
3933 | if (what == 'N') |
3934 | { |
3935 | my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str); |
3936 | goto end; |
3937 | } |
3938 | /* |
3939 | There are four options which affect the process of creation of |
3940 | a new user (mysqld option --safe-create-user, 'insert' privilege |
3941 | on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and |
3942 | SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule |
3943 | how it should work. |
3944 | if (safe-user-create && ! INSERT_priv) => reject |
3945 | else if (identified_by) => create |
3946 | else if (no_auto_create_user) => reject |
3947 | else create |
3948 | |
3949 | see also test_if_create_new_users() |
3950 | */ |
3951 | else if (!combo.pwhash.length && !combo.plugin.length && no_auto_create) |
3952 | { |
3953 | my_error(ER_PASSWORD_NO_MATCH, MYF(0)); |
3954 | goto end; |
3955 | } |
3956 | else if (!can_create_user) |
3957 | { |
3958 | my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0)); |
3959 | goto end; |
3960 | } |
3961 | else if (combo.plugin.str[0]) |
3962 | { |
3963 | if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN)) |
3964 | { |
3965 | my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str); |
3966 | goto end; |
3967 | } |
3968 | } |
3969 | |
3970 | old_row_exists = 0; |
3971 | restore_record(table,s->default_values); |
3972 | user_table.host()->store(combo.host.str,combo.host.length, |
3973 | system_charset_info); |
3974 | user_table.user()->store(combo.user.str,combo.user.length, |
3975 | system_charset_info); |
3976 | } |
3977 | else |