1
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18
The privileges are saved in the following tables:
19
mysql/user ; super user who are allowed to do almost anything
20
mysql/host ; host privileges. This is used if host is empty in mysql/db.
21
mysql/db ; database privileges / user
23
data in tables is sorted according to how many not-wild-cards there is
24
in the relevant fields. Empty strings comes last.
27
#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
29
#include "sql_acl.h" // MYSQL_DB_FIELD_COUNT, ACL_ACCESS
30
#include "sql_base.h" // close_mysql_tables
31
#include "key.h" // key_copy, key_cmp_if_same, key_restore
32
#include "sql_show.h" // append_identifier
33
#include "sql_table.h" // build_table_filename
34
#include "hash_filo.h"
35
#include "sql_parse.h" // check_access
36
#include "sql_view.h" // VIEW_ANY_ACL
37
#include "records.h" // READ_RECORD, read_record_info,
38
// init_read_record, end_read_record
39
#include "rpl_filter.h" // rpl_filter
44
#include "transaction.h"
45
#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
46
#include "records.h" // init_read_record, end_read_record
47
#include <sql_common.h>
48
#include <mysql/plugin_auth.h>
49
#include "sql_connect.h"
53
bool mysql_user_table_is_in_short_password_format= false;
56
TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
58
{ C_STRING_WITH_LEN("Host") },
59
{ C_STRING_WITH_LEN("char(60)") },
63
{ C_STRING_WITH_LEN("Db") },
64
{ C_STRING_WITH_LEN("char(64)") },
68
{ C_STRING_WITH_LEN("User") },
69
{ C_STRING_WITH_LEN("char(16)") },
73
{ C_STRING_WITH_LEN("Select_priv") },
74
{ C_STRING_WITH_LEN("enum('N','Y')") },
75
{ C_STRING_WITH_LEN("utf8") }
78
{ C_STRING_WITH_LEN("Insert_priv") },
79
{ C_STRING_WITH_LEN("enum('N','Y')") },
80
{ C_STRING_WITH_LEN("utf8") }
83
{ C_STRING_WITH_LEN("Update_priv") },
84
{ C_STRING_WITH_LEN("enum('N','Y')") },
85
{ C_STRING_WITH_LEN("utf8") }
88
{ C_STRING_WITH_LEN("Delete_priv") },
89
{ C_STRING_WITH_LEN("enum('N','Y')") },
90
{ C_STRING_WITH_LEN("utf8") }
93
{ C_STRING_WITH_LEN("Create_priv") },
94
{ C_STRING_WITH_LEN("enum('N','Y')") },
95
{ C_STRING_WITH_LEN("utf8") }
98
{ C_STRING_WITH_LEN("Drop_priv") },
99
{ C_STRING_WITH_LEN("enum('N','Y')") },
100
{ C_STRING_WITH_LEN("utf8") }
103
{ C_STRING_WITH_LEN("Grant_priv") },
104
{ C_STRING_WITH_LEN("enum('N','Y')") },
105
{ C_STRING_WITH_LEN("utf8") }
108
{ C_STRING_WITH_LEN("References_priv") },
109
{ C_STRING_WITH_LEN("enum('N','Y')") },
110
{ C_STRING_WITH_LEN("utf8") }
113
{ C_STRING_WITH_LEN("Index_priv") },
114
{ C_STRING_WITH_LEN("enum('N','Y')") },
115
{ C_STRING_WITH_LEN("utf8") }
118
{ C_STRING_WITH_LEN("Alter_priv") },
119
{ C_STRING_WITH_LEN("enum('N','Y')") },
120
{ C_STRING_WITH_LEN("utf8") }
123
{ C_STRING_WITH_LEN("Create_tmp_table_priv") },
124
{ C_STRING_WITH_LEN("enum('N','Y')") },
125
{ C_STRING_WITH_LEN("utf8") }
128
{ C_STRING_WITH_LEN("Lock_tables_priv") },
129
{ C_STRING_WITH_LEN("enum('N','Y')") },
130
{ C_STRING_WITH_LEN("utf8") }
133
{ C_STRING_WITH_LEN("Create_view_priv") },
134
{ C_STRING_WITH_LEN("enum('N','Y')") },
135
{ C_STRING_WITH_LEN("utf8") }
138
{ C_STRING_WITH_LEN("Show_view_priv") },
139
{ C_STRING_WITH_LEN("enum('N','Y')") },
140
{ C_STRING_WITH_LEN("utf8") }
143
{ C_STRING_WITH_LEN("Create_routine_priv") },
144
{ C_STRING_WITH_LEN("enum('N','Y')") },
145
{ C_STRING_WITH_LEN("utf8") }
148
{ C_STRING_WITH_LEN("Alter_routine_priv") },
149
{ C_STRING_WITH_LEN("enum('N','Y')") },
150
{ C_STRING_WITH_LEN("utf8") }
153
{ C_STRING_WITH_LEN("Execute_priv") },
154
{ C_STRING_WITH_LEN("enum('N','Y')") },
155
{ C_STRING_WITH_LEN("utf8") }
158
{ C_STRING_WITH_LEN("Event_priv") },
159
{ C_STRING_WITH_LEN("enum('N','Y')") },
160
{ C_STRING_WITH_LEN("utf8") }
163
{ C_STRING_WITH_LEN("Trigger_priv") },
164
{ C_STRING_WITH_LEN("enum('N','Y')") },
165
{ C_STRING_WITH_LEN("utf8") }
169
const TABLE_FIELD_DEF
170
mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields};
172
static LEX_STRING native_password_plugin_name= {
173
C_STRING_WITH_LEN("mysql_native_password")
176
static LEX_STRING old_password_plugin_name= {
177
C_STRING_WITH_LEN("mysql_old_password")
180
/// @todo make it configurable
181
LEX_STRING *default_auth_plugin_name= &native_password_plugin_name;
183
#ifndef NO_EMBEDDED_ACCESS_CHECKS
184
static plugin_ref old_password_plugin;
186
static plugin_ref native_password_plugin;
190
struct acl_host_and_ip
193
long ip, ip_mask; // Used with masked ip:s
202
/* ACL_HOST is used if no host is specified */
204
class ACL_HOST :public ACL_ACCESS
207
acl_host_and_ip host;
211
class ACL_USER :public ACL_ACCESS
214
acl_host_and_ip host;
215
uint hostname_length;
216
USER_RESOURCES user_resource;
218
uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form
219
uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
220
enum SSL_type ssl_type;
221
const char *ssl_cipher, *x509_issuer, *x509_subject;
223
LEX_STRING auth_string;
225
ACL_USER *copy(MEM_ROOT *root)
227
ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
231
dst->user= safe_strdup_root(root, user);
232
dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
233
dst->x509_issuer= safe_strdup_root(root, x509_issuer);
234
dst->x509_subject= safe_strdup_root(root, x509_subject);
235
if (plugin.str == native_password_plugin_name.str ||
236
plugin.str == old_password_plugin_name.str)
239
dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
240
dst->auth_string.str= safe_strdup_root(root, auth_string.str);
241
dst->host.hostname= safe_strdup_root(root, host.hostname);
246
class ACL_DB :public ACL_ACCESS
249
acl_host_and_ip host;
254
#ifndef NO_EMBEDDED_ACCESS_CHECKS
255
static void update_hostname(acl_host_and_ip *host, const char *hostname);
256
static ulong get_sort(uint count,...);
257
static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
259
static bool show_proxy_grants (THD *thd, LEX_USER *user,
260
char *buff, size_t buffsize);
262
class ACL_PROXY_USER :public ACL_ACCESS
264
acl_host_and_ip host;
266
acl_host_and_ip proxied_host;
267
const char *proxied_user;
271
MYSQL_PROXIES_PRIV_HOST,
272
MYSQL_PROXIES_PRIV_USER,
273
MYSQL_PROXIES_PRIV_PROXIED_HOST,
274
MYSQL_PROXIES_PRIV_PROXIED_USER,
275
MYSQL_PROXIES_PRIV_WITH_GRANT,
276
MYSQL_PROXIES_PRIV_GRANTOR,
277
MYSQL_PROXIES_PRIV_TIMESTAMP } old_acl_proxy_users;
279
ACL_PROXY_USER () {};
281
void init(const char *host_arg, const char *user_arg,
282
const char *proxied_host_arg, const char *proxied_user_arg,
285
user= (user_arg && *user_arg) ? user_arg : NULL;
286
update_hostname (&host,
287
(host_arg && *host_arg) ? host_arg : NULL);
288
proxied_user= (proxied_user_arg && *proxied_user_arg) ?
289
proxied_user_arg : NULL;
290
update_hostname (&proxied_host,
291
(proxied_host_arg && *proxied_host_arg) ?
292
proxied_host_arg : NULL);
293
with_grant= with_grant_arg;
294
sort= get_sort(4, host.hostname, user,
295
proxied_host.hostname, proxied_user);
298
void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
299
const char *proxied_host_arg, const char *proxied_user_arg,
302
init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
303
(user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
304
(proxied_host_arg && *proxied_host_arg) ?
305
strdup_root (mem, proxied_host_arg) : NULL,
306
(proxied_user_arg && *proxied_user_arg) ?
307
strdup_root (mem, proxied_user_arg) : NULL,
311
void init(TABLE *table, MEM_ROOT *mem)
313
init (get_field(mem, table->field[MYSQL_PROXIES_PRIV_HOST]),
314
get_field(mem, table->field[MYSQL_PROXIES_PRIV_USER]),
315
get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]),
316
get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]),
317
table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->val_int() != 0);
320
bool get_with_grant() { return with_grant; }
321
const char *get_user() { return user; }
322
const char *get_host() { return host.hostname; }
323
const char *get_proxied_user() { return proxied_user; }
324
const char *get_proxied_host() { return proxied_host.hostname; }
325
void set_user(MEM_ROOT *mem, const char *user_arg)
327
user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
329
void set_host(MEM_ROOT *mem, const char *host_arg)
331
update_hostname(&host,
332
(host_arg && *host_arg) ?
333
strdup_root(mem, host_arg) : NULL);
336
bool check_validity(bool check_no_resolve)
338
if (check_no_resolve &&
339
(hostname_requires_resolving(host.hostname) ||
340
hostname_requires_resolving(proxied_host.hostname)))
342
sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
343
"ignored in --skip-name-resolve mode.",
344
proxied_user ? proxied_user : "",
345
proxied_host.hostname ? proxied_host.hostname : "",
347
host.hostname ? host.hostname : "");
353
bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
354
const char *proxied_user_arg)
356
DBUG_ENTER("ACL_PROXY_USER::matches");
357
DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
358
"compare_hostname(%s,%s,%s) &&"
359
"wild_compare (%s,%s) &&"
360
"wild_compare (%s,%s)",
361
host.hostname ? host.hostname : "<NULL>",
362
host_arg ? host_arg : "<NULL>",
363
ip_arg ? ip_arg : "<NULL>",
364
proxied_host.hostname ? proxied_host.hostname : "<NULL>",
365
host_arg ? host_arg : "<NULL>",
366
ip_arg ? ip_arg : "<NULL>",
367
user_arg ? user_arg : "<NULL>",
368
user ? user : "<NULL>",
369
proxied_user_arg ? proxied_user_arg : "<NULL>",
370
proxied_user ? proxied_user : "<NULL>"));
371
DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
372
compare_hostname(&proxied_host, host_arg, ip_arg) &&
374
(user_arg && !wild_compare(user_arg, user, TRUE))) &&
376
(proxied_user && !wild_compare(proxied_user_arg,
377
proxied_user, TRUE))));
381
inline static bool auth_element_equals(const char *a, const char *b)
383
return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
387
bool pk_equals(ACL_PROXY_USER *grant)
389
DBUG_ENTER("pk_equals");
390
DBUG_PRINT("info", ("strcmp(%s,%s) &&"
392
"wild_compare (%s,%s) &&"
393
"wild_compare (%s,%s)",
394
user ? user : "<NULL>",
395
grant->user ? grant->user : "<NULL>",
396
proxied_user ? proxied_user : "<NULL>",
397
grant->proxied_user ? grant->proxied_user : "<NULL>",
398
host.hostname ? host.hostname : "<NULL>",
399
grant->host.hostname ? grant->host.hostname : "<NULL>",
400
proxied_host.hostname ? proxied_host.hostname : "<NULL>",
401
grant->proxied_host.hostname ?
402
grant->proxied_host.hostname : "<NULL>"));
404
DBUG_RETURN(auth_element_equals(user, grant->user) &&
405
auth_element_equals(proxied_user, grant->proxied_user) &&
406
auth_element_equals(host.hostname, grant->host.hostname) &&
407
auth_element_equals(proxied_host.hostname,
408
grant->proxied_host.hostname));
412
bool granted_on(const char *host_arg, const char *user_arg)
414
return (((!user && (!user_arg || !user_arg[0])) ||
415
(user && user_arg && !strcmp(user, user_arg))) &&
416
((!host.hostname && (!host_arg || !host_arg[0])) ||
417
(host.hostname && host_arg && !strcmp(host.hostname, host_arg))));
421
void print_grant(String *str)
423
str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
425
str->append(proxied_user, strlen(proxied_user));
426
str->append(STRING_WITH_LEN("'@'"));
427
if (proxied_host.hostname)
428
str->append(proxied_host.hostname, strlen(proxied_host.hostname));
429
str->append(STRING_WITH_LEN("' TO '"));
431
str->append(user, strlen(user));
432
str->append(STRING_WITH_LEN("'@'"));
434
str->append(host.hostname, strlen(host.hostname));
435
str->append(STRING_WITH_LEN("'"));
437
str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
440
void set_data(ACL_PROXY_USER *grant)
442
with_grant= grant->with_grant;
445
static int store_pk(TABLE *table,
446
const LEX_STRING *host,
447
const LEX_STRING *user,
448
const LEX_STRING *proxied_host,
449
const LEX_STRING *proxied_user)
451
DBUG_ENTER("ACL_PROXY_USER::store_pk");
452
DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
453
host->str ? host->str : "<NULL>",
454
user->str ? user->str : "<NULL>",
455
proxied_host->str ? proxied_host->str : "<NULL>",
456
proxied_user->str ? proxied_user->str : "<NULL>"));
457
if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
459
system_charset_info))
461
if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
463
system_charset_info))
465
if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str,
466
proxied_host->length,
467
system_charset_info))
469
if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str,
470
proxied_user->length,
471
system_charset_info))
477
static int store_data_record(TABLE *table,
478
const LEX_STRING *host,
479
const LEX_STRING *user,
480
const LEX_STRING *proxied_host,
481
const LEX_STRING *proxied_user,
485
DBUG_ENTER("ACL_PROXY_USER::store_pk");
486
if (store_pk(table, host, user, proxied_host, proxied_user))
488
DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
489
if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
492
if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
494
system_charset_info))
501
#define FIRST_NON_YN_FIELD 26
503
class acl_entry :public hash_filo_element
508
char key[1]; // Key will be stored here
512
static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
513
my_bool not_used __attribute__((unused)))
515
*length=(uint) entry->length;
516
return (uchar*) entry->key;
519
#define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
520
#define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
521
1 + USERNAME_LENGTH + 1)
523
/** Size of the header fields of an authentication packet. */
524
#define AUTH_PACKET_HEADER_SIZE_PROTO_41 32
525
#define AUTH_PACKET_HEADER_SIZE_PROTO_40 5
527
static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users;
528
static MEM_ROOT mem, memex;
529
static bool initialized=0;
530
static bool allow_all_hosts=1;
531
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
532
static DYNAMIC_ARRAY acl_wild_hosts;
533
static hash_filo *acl_cache;
534
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
535
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
536
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
537
static ulong get_sort(uint count,...);
538
static void init_check_host(void);
539
static void rebuild_check_host(void);
540
static ACL_USER *find_acl_user(const char *host, const char *user,
542
static bool update_user_table(THD *thd, TABLE *table,
543
const char *host, const char *user,
544
const char *new_password, uint new_password_len);
545
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
546
static my_bool grant_load(THD *thd, TABLE_LIST *tables);
547
static inline void get_grantor(THD *thd, char* grantor);
550
Convert scrambled password to binary form, according to scramble type,
551
Binary form is stored in user.salt.
556
set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
558
if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
560
get_salt_from_password(acl_user->salt, password);
561
acl_user->salt_len= SCRAMBLE_LENGTH;
563
else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
565
get_salt_from_password_323((ulong *) acl_user->salt, password);
566
acl_user->salt_len= SCRAMBLE_LENGTH_323;
569
acl_user->salt_len= 0;
573
Initialize structures responsible for user/db-level privilege checking and
574
load privilege information for them from tables in the 'mysql' database.
578
dont_read_acl_tables TRUE if we want to skip loading data from
579
privilege tables and disable privilege checking.
582
This function is mostly responsible for preparatory steps, main work
583
on initialization and grants loading is done in acl_reload().
587
1 Could not initialize grant's
590
my_bool acl_init(bool dont_read_acl_tables)
594
DBUG_ENTER("acl_init");
596
acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
597
(my_hash_get_key) acl_entry_get_key,
598
(my_hash_free_key) free,
599
&my_charset_utf8_bin);
602
cache built-in native authentication plugins,
603
to avoid hash searches and a global mutex lock on every connect
605
native_password_plugin= my_plugin_lock_by_name(0,
606
&native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
607
old_password_plugin= my_plugin_lock_by_name(0,
608
&old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
610
if (!native_password_plugin || !old_password_plugin)
613
if (dont_read_acl_tables)
615
DBUG_RETURN(0); /* purecov: tested */
619
To be able to run this from boot, we allocate a temporary THD
622
DBUG_RETURN(1); /* purecov: inspected */
623
thd->thread_stack= (char*) &thd;
624
thd->store_globals();
626
It is safe to call acl_reload() since acl_* arrays and hashes which
627
will be freed there are global static objects and thus are initialized
630
return_val= acl_reload(thd);
632
/* Remember that we don't have a THD */
633
my_pthread_setspecific_ptr(THR_THD, 0);
634
DBUG_RETURN(return_val);
638
Choose from either native or old password plugins when assigning a password
642
set_user_plugin (ACL_USER *user, int password_len)
644
switch (password_len)
646
case 0: /* no password */
647
case SCRAMBLED_PASSWORD_CHAR_LENGTH:
648
user->plugin= native_password_plugin_name;
650
case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
651
user->plugin= old_password_plugin_name;
653
case 45: /* 4.1: to be removed */
654
sql_print_warning("Found 4.1.0 style password for user '%s@%s'. "
656
"You should change password for this user.",
657
user->user ? user->user : "",
658
user->host.hostname ? user->host.hostname : "");
661
sql_print_warning("Found invalid password for user: '%s@%s'; "
662
"Ignoring user", user->user ? user->user : "",
663
user->host.hostname ? user->host.hostname : "");
670
Initialize structures responsible for user/db-level privilege checking
671
and load information about grants from open privilege tables.
676
tables List containing open "mysql.host", "mysql.user" and
684
static my_bool acl_load(THD *thd, TABLE_LIST *tables)
687
READ_RECORD read_record_info;
688
my_bool return_val= TRUE;
689
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
690
char tmp_name[NAME_LEN+1];
692
ulong old_sql_mode= thd->variables.sql_mode;
693
DBUG_ENTER("acl_load");
695
thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
697
grant_version++; /* Privileges updated */
699
acl_cache->clear(1); // Clear locked hostname cache
701
init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
702
init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0,
704
table->use_all_columns();
705
(void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50);
706
while (!(read_record_info.read_record(&read_record_info)))
709
update_hostname(&host.host,get_field(&mem, table->field[0]));
710
host.db= get_field(&mem, table->field[1]);
711
if (lower_case_table_names && host.db)
714
convert db to lower case and give a warning if the db wasn't
715
already in lower case
717
(void) strmov(tmp_name, host.db);
718
my_casedn_str(files_charset_info, host.db);
719
if (strcmp(host.db, tmp_name) != 0)
720
sql_print_warning("'host' entry '%s|%s' had database in mixed "
721
"case that has been forced to lowercase because "
722
"lower_case_table_names is set. It will not be "
723
"possible to remove this privilege using REVOKE.",
724
host.host.hostname ? host.host.hostname : "",
725
host.db ? host.db : "");
727
host.access= get_access(table,2);
728
host.access= fix_rights_for_db(host.access);
729
host.sort= get_sort(2,host.host.hostname,host.db);
730
if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
732
sql_print_warning("'host' entry '%s|%s' "
733
"ignored in --skip-name-resolve mode.",
734
host.host.hostname ? host.host.hostname : "",
735
host.db ? host.db : "");
738
#ifndef TO_BE_REMOVED
739
if (table->s->fields == 8)
741
if (host.access & CREATE_ACL)
742
host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
745
(void) push_dynamic(&acl_hosts,(uchar*) &host);
747
my_qsort((uchar*) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
748
sizeof(ACL_HOST),(qsort_cmp) acl_compare);
749
end_read_record(&read_record_info);
750
freeze_size(&acl_hosts);
752
init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0,FALSE);
753
table->use_all_columns();
754
(void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100);
755
password_length= table->field[2]->field_length /
756
table->field[2]->charset()->mbmaxlen;
757
if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
759
sql_print_error("Fatal error: mysql.user table is damaged or in "
760
"unsupported 3.20 format.");
764
DBUG_PRINT("info",("user table fields: %d, password length: %d",
765
table->s->fields, password_length));
767
mysql_mutex_lock(&LOCK_global_system_variables);
768
if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
772
mysql_mutex_unlock(&LOCK_global_system_variables);
773
sql_print_error("Fatal error: mysql.user table is in old format, "
774
"but server started with --secure-auth option.");
777
mysql_user_table_is_in_short_password_format= true;
778
if (global_system_variables.old_passwords)
779
mysql_mutex_unlock(&LOCK_global_system_variables);
782
global_system_variables.old_passwords= 1;
783
mysql_mutex_unlock(&LOCK_global_system_variables);
784
sql_print_warning("mysql.user table is not updated to new password format; "
785
"Disabling new password usage until "
786
"mysql_fix_privilege_tables is run");
788
thd->variables.old_passwords= 1;
792
mysql_user_table_is_in_short_password_format= false;
793
mysql_mutex_unlock(&LOCK_global_system_variables);
797
while (!(read_record_info.read_record(&read_record_info)))
800
bzero(&user, sizeof(user));
801
update_hostname(&user.host, get_field(&mem, table->field[0]));
802
user.user= get_field(&mem, table->field[1]);
803
if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
805
sql_print_warning("'user' entry '%s@%s' "
806
"ignored in --skip-name-resolve mode.",
807
user.user ? user.user : "",
808
user.host.hostname ? user.host.hostname : "");
812
char *password= get_field(&mem, table->field[2]);
813
uint password_len= password ? strlen(password) : 0;
814
set_user_salt(&user, password, password_len);
816
if (set_user_plugin(&user, password_len))
821
user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
823
if it is pre 5.0.1 privilege table then map CREATE privilege on
824
CREATE VIEW & SHOW VIEW privileges
826
if (table->s->fields <= 31 && (user.access & CREATE_ACL))
827
user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
830
if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
831
CREATE PROCEDURE & ALTER PROCEDURE privileges
833
if (table->s->fields <= 33 && (user.access & CREATE_ACL))
834
user.access|= CREATE_PROC_ACL;
835
if (table->s->fields <= 33 && (user.access & ALTER_ACL))
836
user.access|= ALTER_PROC_ACL;
839
pre 5.0.3 did not have CREATE_USER_ACL
841
if (table->s->fields <= 36 && (user.access & GRANT_ACL))
842
user.access|= CREATE_USER_ACL;
846
if it is pre 5.1.6 privilege table then map CREATE privilege on
847
CREATE|ALTER|DROP|EXECUTE EVENT
849
if (table->s->fields <= 37 && (user.access & SUPER_ACL))
850
user.access|= EVENT_ACL;
853
if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
855
if (table->s->fields <= 38 && (user.access & SUPER_ACL))
856
user.access|= TRIGGER_ACL;
858
user.sort= get_sort(2,user.host.hostname,user.user);
859
user.hostname_length= (user.host.hostname ?
860
(uint) strlen(user.host.hostname) : 0);
862
/* Starting from 4.0.2 we have more fields */
863
if (table->s->fields >= 31)
865
char *ssl_type=get_field(thd->mem_root, table->field[next_field++]);
867
user.ssl_type=SSL_TYPE_NONE;
868
else if (!strcmp(ssl_type, "ANY"))
869
user.ssl_type=SSL_TYPE_ANY;
870
else if (!strcmp(ssl_type, "X509"))
871
user.ssl_type=SSL_TYPE_X509;
872
else /* !strcmp(ssl_type, "SPECIFIED") */
873
user.ssl_type=SSL_TYPE_SPECIFIED;
875
user.ssl_cipher= get_field(&mem, table->field[next_field++]);
876
user.x509_issuer= get_field(&mem, table->field[next_field++]);
877
user.x509_subject= get_field(&mem, table->field[next_field++]);
879
char *ptr = get_field(thd->mem_root, table->field[next_field++]);
880
user.user_resource.questions=ptr ? atoi(ptr) : 0;
881
ptr = get_field(thd->mem_root, table->field[next_field++]);
882
user.user_resource.updates=ptr ? atoi(ptr) : 0;
883
ptr = get_field(thd->mem_root, table->field[next_field++]);
884
user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
885
if (user.user_resource.questions || user.user_resource.updates ||
886
user.user_resource.conn_per_hour)
889
if (table->s->fields >= 36)
891
/* Starting from 5.0.3 we have max_user_connections field */
892
ptr= get_field(thd->mem_root, table->field[next_field++]);
893
user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
896
if (table->s->fields >= 41)
898
/* We may have plugin & auth_String fields */
899
char *tmpstr= get_field(&mem, table->field[next_field++]);
904
sql_print_warning("'user' entry '%s@%s' has both a password "
905
"and an authentication plugin specified. The "
906
"password will be ignored.",
907
user.user ? user.user : "",
908
user.host.hostname ? user.host.hostname : "");
910
if (my_strcasecmp(system_charset_info, tmpstr,
911
native_password_plugin_name.str) == 0)
912
user.plugin= native_password_plugin_name;
914
if (my_strcasecmp(system_charset_info, tmpstr,
915
old_password_plugin_name.str) == 0)
916
user.plugin= old_password_plugin_name;
919
user.plugin.str= tmpstr;
920
user.plugin.length= strlen(tmpstr);
922
user.auth_string.str= get_field(&mem, table->field[next_field++]);
923
if (!user.auth_string.str)
924
user.auth_string.str= const_cast<char*>("");
925
user.auth_string.length= strlen(user.auth_string.str);
931
user.ssl_type=SSL_TYPE_NONE;
932
#ifndef TO_BE_REMOVED
933
if (table->s->fields <= 13)
935
if (user.access & CREATE_ACL)
936
user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
938
/* Convert old privileges */
939
user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
940
if (user.access & FILE_ACL)
941
user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
942
if (user.access & PROCESS_ACL)
943
user.access|= SUPER_ACL | EXECUTE_ACL;
946
(void) push_dynamic(&acl_users,(uchar*) &user);
947
if (!user.host.hostname ||
948
(user.host.hostname[0] == wild_many && !user.host.hostname[1]))
949
allow_all_hosts=1; // Anyone can connect
952
my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
953
sizeof(ACL_USER),(qsort_cmp) acl_compare);
954
end_read_record(&read_record_info);
955
freeze_size(&acl_users);
957
init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0,FALSE);
958
table->use_all_columns();
959
(void) my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100);
960
while (!(read_record_info.read_record(&read_record_info)))
963
update_hostname(&db.host,get_field(&mem, table->field[MYSQL_DB_FIELD_HOST]));
964
db.db=get_field(&mem, table->field[MYSQL_DB_FIELD_DB]);
967
sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
970
db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]);
971
if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
973
sql_print_warning("'db' entry '%s %s@%s' "
974
"ignored in --skip-name-resolve mode.",
976
db.user ? db.user : "",
977
db.host.hostname ? db.host.hostname : "");
980
db.access=get_access(table,3);
981
db.access=fix_rights_for_db(db.access);
982
if (lower_case_table_names)
985
convert db to lower case and give a warning if the db wasn't
986
already in lower case
988
(void)strmov(tmp_name, db.db);
989
my_casedn_str(files_charset_info, db.db);
990
if (strcmp(db.db, tmp_name) != 0)
992
sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
993
"case that has been forced to lowercase because "
994
"lower_case_table_names is set. It will not be "
995
"possible to remove this privilege using REVOKE.",
997
db.user ? db.user : "",
998
db.host.hostname ? db.host.hostname : "");
1001
db.sort=get_sort(3,db.host.hostname,db.db,db.user);
1002
#ifndef TO_BE_REMOVED
1003
if (table->s->fields <= 9)
1005
if (db.access & CREATE_ACL)
1006
db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
1009
(void) push_dynamic(&acl_dbs,(uchar*) &db);
1011
my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
1012
sizeof(ACL_DB),(qsort_cmp) acl_compare);
1013
end_read_record(&read_record_info);
1014
freeze_size(&acl_dbs);
1016
(void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER),
1018
if (tables[3].table)
1020
init_read_record(&read_record_info, thd, table= tables[3].table, NULL, 1,
1022
table->use_all_columns();
1023
while (!(read_record_info.read_record(&read_record_info)))
1025
ACL_PROXY_USER proxy;
1026
proxy.init(table, &mem);
1027
if (proxy.check_validity(check_no_resolve))
1029
if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
1031
end_read_record(&read_record_info);
1035
my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
1036
acl_proxy_users.elements,
1037
sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
1038
end_read_record(&read_record_info);
1042
sql_print_error("Missing system table mysql.proxies_priv; "
1043
"please run mysql_upgrade to create it");
1045
freeze_size(&acl_proxy_users);
1053
thd->variables.sql_mode= old_sql_mode;
1054
DBUG_RETURN(return_val);
1058
void acl_free(bool end)
1060
free_root(&mem,MYF(0));
1061
delete_dynamic(&acl_hosts);
1062
delete_dynamic(&acl_users);
1063
delete_dynamic(&acl_dbs);
1064
delete_dynamic(&acl_wild_hosts);
1065
delete_dynamic(&acl_proxy_users);
1066
my_hash_free(&acl_check_hosts);
1067
plugin_unlock(0, native_password_plugin);
1068
plugin_unlock(0, old_password_plugin);
1070
acl_cache->clear(1); /* purecov: inspected */
1080
Forget current user/db-level privileges and read new privileges
1081
from the privilege tables.
1088
All tables of calling thread which were open and locked by LOCK TABLES
1089
statement will be unlocked and closed.
1090
This function is also used for initialization of structures responsible
1091
for user/db-level privilege checking.
1098
my_bool acl_reload(THD *thd)
1100
TABLE_LIST tables[4];
1101
DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users;
1103
bool old_initialized;
1104
my_bool return_val= TRUE;
1105
DBUG_ENTER("acl_reload");
1108
To avoid deadlocks we should obtain table locks before
1109
obtaining acl_cache->lock mutex.
1111
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
1112
C_STRING_WITH_LEN("host"), "host", TL_READ);
1113
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
1114
C_STRING_WITH_LEN("user"), "user", TL_READ);
1115
tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
1116
C_STRING_WITH_LEN("db"), "db", TL_READ);
1117
tables[3].init_one_table(C_STRING_WITH_LEN("mysql"),
1118
C_STRING_WITH_LEN("proxies_priv"),
1119
"proxies_priv", TL_READ);
1120
tables[0].next_local= tables[0].next_global= tables + 1;
1121
tables[1].next_local= tables[1].next_global= tables + 2;
1122
tables[2].next_local= tables[2].next_global= tables + 3;
1123
tables[0].open_type= tables[1].open_type= tables[2].open_type=
1124
tables[3].open_type= OT_BASE_ONLY;
1125
tables[3].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
1127
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
1130
Execution might have been interrupted; only print the error message
1131
if an error condition has been raised.
1133
if (thd->stmt_da->is_error())
1134
sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
1135
thd->stmt_da->message());
1139
if ((old_initialized=initialized))
1140
mysql_mutex_lock(&acl_cache->lock);
1142
old_acl_hosts= acl_hosts;
1143
old_acl_users= acl_users;
1144
old_acl_proxy_users= acl_proxy_users;
1145
old_acl_dbs= acl_dbs;
1147
delete_dynamic(&acl_wild_hosts);
1148
my_hash_free(&acl_check_hosts);
1150
if ((return_val= acl_load(thd, tables)))
1151
{ // Error. Revert to old list
1152
DBUG_PRINT("error",("Reverting to old privileges"));
1153
acl_free(); /* purecov: inspected */
1154
acl_hosts= old_acl_hosts;
1155
acl_users= old_acl_users;
1156
acl_proxy_users= old_acl_proxy_users;
1157
acl_dbs= old_acl_dbs;
1163
free_root(&old_mem,MYF(0));
1164
delete_dynamic(&old_acl_hosts);
1165
delete_dynamic(&old_acl_users);
1166
delete_dynamic(&old_acl_proxy_users);
1167
delete_dynamic(&old_acl_dbs);
1169
if (old_initialized)
1170
mysql_mutex_unlock(&acl_cache->lock);
1172
close_mysql_tables(thd);
1173
DBUG_RETURN(return_val);
1178
Get all access bits from table after fieldnr
1181
We know that the access privileges ends when there is no more fields
1182
or the field is not an enum with two elements.
1186
form an open table to read privileges from.
1187
The record should be already read in table->record[0]
1188
fieldnr number of the first privilege (that is ENUM('N','Y') field
1189
next_field on return - number of the field next to the last ENUM
1190
(unless next_field == 0)
1196
static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
1198
ulong access_bits=0,bit;
1200
String res(buff,sizeof(buff),&my_charset_latin1);
1203
for (pos=form->field+fieldnr, bit=1;
1204
*pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
1205
((Field_enum*) (*pos))->typelib->count == 2 ;
1206
pos++, fieldnr++, bit<<=1)
1208
(*pos)->val_str(&res);
1209
if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
1213
*next_field=fieldnr;
1219
Return a number which, if sorted 'desc', puts strings in this order:
1225
static ulong get_sort(uint count,...)
1228
va_start(args,count);
1231
/* Should not use this function with more than 4 arguments for compare. */
1232
DBUG_ASSERT(count <= 4);
1236
char *start, *str= va_arg(args,char*);
1238
uint wild_pos= 0; /* first wildcard position */
1242
for (; *str ; str++)
1244
if (*str == wild_prefix && str[1])
1246
else if (*str == wild_many || *str == wild_one)
1248
wild_pos= (uint) (str - start) + 1;
1251
chars= 128; // Marker that chars existed
1254
sort= (sort << 8) + (wild_pos ? min(wild_pos, 127) : chars);
1261
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
1263
if (a->sort > b->sort)
1265
if (a->sort < b->sort)
1272
Gets user credentials without authentication and resource limit checks.
1276
sctx Context which should be initialized
1280
db current data base name
1287
bool acl_getroot(Security_context *sctx, char *user, char *host,
1292
ACL_USER *acl_user= 0;
1293
DBUG_ENTER("acl_getroot");
1295
DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
1296
(host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
1297
user, (db ? db : "(NULL)")));
1301
sctx->host_or_ip= host ? host : (ip ? ip : "");
1306
here if mysqld's been started with --skip-grant-tables option.
1308
sctx->skip_grants();
1312
mysql_mutex_lock(&acl_cache->lock);
1314
sctx->master_access= 0;
1316
*sctx->priv_user= *sctx->priv_host= 0;
1319
Find acl entry in user database.
1320
This is specially tailored to suit the check we do for CALL of
1321
a stored procedure; user is set to what is actually a
1322
priv_user, which can be ''.
1324
for (i=0 ; i < acl_users.elements ; i++)
1326
ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
1327
if ((!acl_user_tmp->user && !user[0]) ||
1328
(acl_user_tmp->user && strcmp(user, acl_user_tmp->user) == 0))
1330
if (compare_hostname(&acl_user_tmp->host, host, ip))
1332
acl_user= acl_user_tmp;
1341
for (i=0 ; i < acl_dbs.elements ; i++)
1343
ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
1344
if (!acl_db->user ||
1345
(user && user[0] && !strcmp(user, acl_db->user)))
1347
if (compare_hostname(&acl_db->host, host, ip))
1349
if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
1351
sctx->db_access= acl_db->access;
1357
sctx->master_access= acl_user->access;
1360
strmake(sctx->priv_user, user, USERNAME_LENGTH);
1362
*sctx->priv_user= 0;
1364
if (acl_user->host.hostname)
1365
strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
1367
*sctx->priv_host= 0;
1369
mysql_mutex_unlock(&acl_cache->lock);
1373
static uchar* check_get_key(ACL_USER *buff, size_t *length,
1374
my_bool not_used __attribute__((unused)))
1376
*length=buff->hostname_length;
1377
return (uchar*) buff->host.hostname;
1381
static void acl_update_user(const char *user, const char *host,
1382
const char *password, uint password_len,
1383
enum SSL_type ssl_type,
1384
const char *ssl_cipher,
1385
const char *x509_issuer,
1386
const char *x509_subject,
1387
USER_RESOURCES *mqh,
1389
const LEX_STRING *plugin,
1390
const LEX_STRING *auth)
1392
mysql_mutex_assert_owner(&acl_cache->lock);
1394
for (uint i=0 ; i < acl_users.elements ; i++)
1396
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
1397
if ((!acl_user->user && !user[0]) ||
1398
(acl_user->user && !strcmp(user,acl_user->user)))
1400
if ((!acl_user->host.hostname && !host[0]) ||
1401
(acl_user->host.hostname &&
1402
!my_strcasecmp(system_charset_info, host, acl_user->host.hostname)))
1406
acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
1407
acl_user->plugin.length= plugin->length;
1408
acl_user->auth_string.str= auth->str ?
1409
strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
1410
acl_user->auth_string.length= auth->length;
1412
acl_user->access=privileges;
1413
if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1414
acl_user->user_resource.questions=mqh->questions;
1415
if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1416
acl_user->user_resource.updates=mqh->updates;
1417
if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
1418
acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
1419
if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
1420
acl_user->user_resource.user_conn= mqh->user_conn;
1421
if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
1423
acl_user->ssl_type= ssl_type;
1424
acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&mem,ssl_cipher) :
1426
acl_user->x509_issuer= (x509_issuer ? strdup_root(&mem,x509_issuer) :
1428
acl_user->x509_subject= (x509_subject ?
1429
strdup_root(&mem,x509_subject) : 0);
1432
set_user_salt(acl_user, password, password_len);
1433
/* search complete: */
1441
static void acl_insert_user(const char *user, const char *host,
1442
const char *password, uint password_len,
1443
enum SSL_type ssl_type,
1444
const char *ssl_cipher,
1445
const char *x509_issuer,
1446
const char *x509_subject,
1447
USER_RESOURCES *mqh,
1449
const LEX_STRING *plugin,
1450
const LEX_STRING *auth)
1454
mysql_mutex_assert_owner(&acl_cache->lock);
1456
acl_user.user=*user ? strdup_root(&mem,user) : 0;
1457
update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
1460
acl_user.plugin.str= strmake_root(&mem, plugin->str, plugin->length);
1461
acl_user.plugin.length= plugin->length;
1462
acl_user.auth_string.str= auth->str ?
1463
strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
1464
acl_user.auth_string.length= auth->length;
1468
acl_user.plugin= password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 ?
1469
old_password_plugin_name : native_password_plugin_name;
1470
acl_user.auth_string.str= const_cast<char*>("");
1471
acl_user.auth_string.length= 0;
1474
acl_user.access=privileges;
1475
acl_user.user_resource = *mqh;
1476
acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
1477
acl_user.hostname_length=(uint) strlen(host);
1478
acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ?
1479
ssl_type : SSL_TYPE_NONE);
1480
acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0;
1481
acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0;
1482
acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
1484
set_user_salt(&acl_user, password, password_len);
1486
(void) push_dynamic(&acl_users,(uchar*) &acl_user);
1487
if (!acl_user.host.hostname ||
1488
(acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
1489
allow_all_hosts=1; // Anyone can connect /* purecov: tested */
1490
my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
1491
sizeof(ACL_USER),(qsort_cmp) acl_compare);
1493
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
1494
rebuild_check_host();
1498
static void acl_update_db(const char *user, const char *host, const char *db,
1501
mysql_mutex_assert_owner(&acl_cache->lock);
1503
for (uint i=0 ; i < acl_dbs.elements ; i++)
1505
ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
1506
if ((!acl_db->user && !user[0]) ||
1508
!strcmp(user,acl_db->user)))
1510
if ((!acl_db->host.hostname && !host[0]) ||
1511
(acl_db->host.hostname &&
1512
!strcmp(host, acl_db->host.hostname)))
1514
if ((!acl_db->db && !db[0]) ||
1515
(acl_db->db && !strcmp(db,acl_db->db)))
1518
acl_db->access=privileges;
1520
delete_dynamic_element(&acl_dbs,i);
1529
Insert a user/db/host combination into the global acl_cache
1536
privileges Bitmap of privileges
1539
acl_cache->lock must be locked when calling this
1542
static void acl_insert_db(const char *user, const char *host, const char *db,
1546
mysql_mutex_assert_owner(&acl_cache->lock);
1547
acl_db.user=strdup_root(&mem,user);
1548
update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
1549
acl_db.db=strdup_root(&mem,db);
1550
acl_db.access=privileges;
1551
acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
1552
(void) push_dynamic(&acl_dbs,(uchar*) &acl_db);
1553
my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
1554
sizeof(ACL_DB),(qsort_cmp) acl_compare);
1560
Get privilege for a host, user and db combination
1562
as db_is_pattern changes the semantics of comparison,
1563
acl_cache is not used if db_is_pattern is set.
1566
ulong acl_get(const char *host, const char *ip,
1567
const char *user, const char *db, my_bool db_is_pattern)
1569
ulong host_access= ~(ulong)0, db_access= 0;
1572
char key[ACL_KEY_LENGTH],*tmp_db,*end;
1574
DBUG_ENTER("acl_get");
1576
mysql_mutex_lock(&acl_cache->lock);
1577
end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db);
1578
if (lower_case_table_names)
1580
my_casedn_str(files_charset_info, tmp_db);
1583
key_length= (size_t) (end-key);
1584
if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key,
1587
db_access=entry->access;
1588
mysql_mutex_unlock(&acl_cache->lock);
1589
DBUG_PRINT("exit", ("access: 0x%lx", db_access));
1590
DBUG_RETURN(db_access);
1594
Check if there are some access rights for database and user
1596
for (i=0 ; i < acl_dbs.elements ; i++)
1598
ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
1599
if (!acl_db->user || !strcmp(user,acl_db->user))
1601
if (compare_hostname(&acl_db->host,host,ip))
1603
if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
1605
db_access=acl_db->access;
1606
if (acl_db->host.hostname)
1607
goto exit; // Fully specified. Take it
1608
break; /* purecov: tested */
1614
goto exit; // Can't be better
1617
No host specified for user. Get hostdata from host table
1619
host_access=0; // Host must be found
1620
for (i=0 ; i < acl_hosts.elements ; i++)
1622
ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
1623
if (compare_hostname(&acl_host->host,host,ip))
1625
if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
1627
host_access=acl_host->access; // Fully specified. Take it
1633
/* Save entry in cache for quick retrieval */
1634
if (!db_is_pattern &&
1635
(entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
1637
entry->access=(db_access & host_access);
1638
entry->length=key_length;
1639
memcpy((uchar*) entry->key,key,key_length);
1640
acl_cache->add(entry);
1642
mysql_mutex_unlock(&acl_cache->lock);
1643
DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
1644
DBUG_RETURN(db_access & host_access);
1648
Check if there are any possible matching entries for this host
1651
All host names without wild cards are stored in a hash table,
1652
entries with wildcards are stored in a dynamic array
1655
static void init_check_host(void)
1657
DBUG_ENTER("init_check_host");
1658
(void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
1659
acl_users.elements,1);
1660
(void) my_hash_init(&acl_check_hosts,system_charset_info,
1661
acl_users.elements, 0, 0,
1662
(my_hash_get_key) check_get_key, 0, 0);
1663
if (!allow_all_hosts)
1665
for (uint i=0 ; i < acl_users.elements ; i++)
1667
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
1668
if (strchr(acl_user->host.hostname,wild_many) ||
1669
strchr(acl_user->host.hostname,wild_one) ||
1670
acl_user->host.ip_mask)
1673
for (j=0 ; j < acl_wild_hosts.elements ; j++)
1674
{ // Check if host already exists
1675
acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j,
1677
if (!my_strcasecmp(system_charset_info,
1678
acl_user->host.hostname, acl->hostname))
1679
break; // already stored
1681
if (j == acl_wild_hosts.elements) // If new
1682
(void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
1684
else if (!my_hash_search(&acl_check_hosts,(uchar*)
1685
acl_user->host.hostname,
1686
strlen(acl_user->host.hostname)))
1688
if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
1690
allow_all_hosts=1; // Should never happen
1696
freeze_size(&acl_wild_hosts);
1697
freeze_size(&acl_check_hosts.array);
1703
Rebuild lists used for checking of allowed hosts
1705
We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
1706
dropping or renaming user, since they contain pointers to elements of
1707
'acl_user' array, which are invalidated by drop operation, and use
1708
ACL_USER::host::hostname as a key, which is changed by rename.
1710
void rebuild_check_host(void)
1712
delete_dynamic(&acl_wild_hosts);
1713
my_hash_free(&acl_check_hosts);
1718
/* Return true if there is no users that can match the given host */
1720
bool acl_check_host(const char *host, const char *ip)
1722
if (allow_all_hosts)
1724
mysql_mutex_lock(&acl_cache->lock);
1726
if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
1727
(ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
1729
mysql_mutex_unlock(&acl_cache->lock);
1730
return 0; // Found host
1732
for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
1734
acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
1735
if (compare_hostname(acl, host, ip))
1737
mysql_mutex_unlock(&acl_cache->lock);
1738
return 0; // Host ok
1741
mysql_mutex_unlock(&acl_cache->lock);
1742
return 1; // Host is not allowed
1747
Check if the user is allowed to change password
1750
check_change_password()
1752
host hostname for the user
1754
new_password new password
1757
new_password cannot be NULL
1761
1 ERROR ; In this case the error is sent to the client.
1764
int check_change_password(THD *thd, const char *host, const char *user,
1765
char *new_password, uint new_password_len)
1769
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1772
if (!thd->slave_thread &&
1773
(strcmp(thd->security_ctx->user, user) ||
1774
my_strcasecmp(system_charset_info, host,
1775
thd->security_ctx->priv_host)))
1777
if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
1780
if (!thd->slave_thread && !thd->security_ctx->user[0])
1782
my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
1786
size_t len= strlen(new_password);
1787
if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
1788
len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1790
my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
1798
Change a password for a user
1805
new_password New password for host@user
1809
1 ERROR; In this case the error is sent to the client.
1812
bool change_password(THD *thd, const char *host, const char *user,
1817
/* Buffer should be extended when password length is extended. */
1820
bool save_binlog_row_based;
1821
uint new_password_len= (uint) strlen(new_password);
1823
DBUG_ENTER("change_password");
1824
DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
1825
host,user,new_password));
1826
DBUG_ASSERT(host != 0); // Ensured by parent
1828
if (check_change_password(thd, host, user, new_password, new_password_len))
1831
tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
1833
#ifdef HAVE_REPLICATION
1835
GRANT and REVOKE are applied the slave in/exclusion rules as they are
1836
some kind of updates to the mysql.% tables.
1838
if (thd->slave_thread && rpl_filter->is_on())
1841
The tables must be marked "updating" so that tables_ok() takes them into
1842
account in tests. It's ok to leave 'updating' set after tables_ok.
1845
/* Thanks to bzero, tables.next==0 */
1846
if (!(thd->spcont || rpl_filter->tables_ok(0, &tables)))
1850
if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
1854
This statement will be replicated as a statement, even when using
1855
row-based replication. The flag will be reset at the end of the
1858
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
1859
thd->clear_current_stmt_binlog_format_row();
1861
mysql_mutex_lock(&acl_cache->lock);
1863
if (!(acl_user= find_acl_user(host, user, TRUE)))
1865
mysql_mutex_unlock(&acl_cache->lock);
1866
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
1870
/* update loaded acl entry: */
1871
set_user_salt(acl_user, new_password, new_password_len);
1873
if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
1874
native_password_plugin_name.str) &&
1875
my_strcasecmp(system_charset_info, acl_user->plugin.str,
1876
old_password_plugin_name.str))
1877
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
1878
ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
1880
set_user_plugin(acl_user, new_password_len);
1882
if (update_user_table(thd, table,
1883
acl_user->host.hostname ? acl_user->host.hostname : "",
1884
acl_user->user ? acl_user->user : "",
1885
new_password, new_password_len))
1887
mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
1891
acl_cache->clear(1); // Clear locked hostname cache
1892
mysql_mutex_unlock(&acl_cache->lock);
1894
if (mysql_bin_log.is_open())
1896
query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
1897
acl_user->user ? acl_user->user : "",
1898
acl_user->host.hostname ? acl_user->host.hostname : "",
1901
result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
1902
FALSE, FALSE, FALSE, 0);
1905
close_mysql_tables(thd);
1907
/* Restore the state of binlog format */
1908
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
1909
if (save_binlog_row_based)
1910
thd->set_current_stmt_binlog_format_row();
1912
DBUG_RETURN(result);
1926
TRUE there are such user
1929
bool is_acl_user(const char *host, const char *user)
1937
mysql_mutex_lock(&acl_cache->lock);
1938
res= find_acl_user(host, user, TRUE) != NULL;
1939
mysql_mutex_unlock(&acl_cache->lock);
1945
Find first entry that matches the current user
1949
find_acl_user(const char *host, const char *user, my_bool exact)
1951
DBUG_ENTER("find_acl_user");
1952
DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user));
1954
mysql_mutex_assert_owner(&acl_cache->lock);
1956
for (uint i=0 ; i < acl_users.elements ; i++)
1958
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
1959
DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
1960
user, acl_user->user ? acl_user->user : "",
1962
acl_user->host.hostname ? acl_user->host.hostname :
1964
if ((!acl_user->user && !user[0]) ||
1965
(acl_user->user && !strcmp(user,acl_user->user)))
1967
if (exact ? !my_strcasecmp(system_charset_info, host,
1968
acl_user->host.hostname ?
1969
acl_user->host.hostname : "") :
1970
compare_hostname(&acl_user->host,host,host))
1972
DBUG_RETURN(acl_user);
1981
Comparing of hostnames
1984
A hostname may be of type:
1985
hostname (May include wildcards); monty.pp.sci.fi
1986
ip (May include wildcards); 192.168.0.0
1987
ip/netmask 192.168.0.0/255.255.255.0
1989
A net mask of 0.0.0.0 is not allowed.
1992
static const char *calc_ip(const char *ip, long *val, char end)
1995
if (!(ip=str2int(ip,10,0,255,&ip_val)) || *ip != '.')
1998
if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
2001
if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
2004
if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != end)
2011
static void update_hostname(acl_host_and_ip *host, const char *hostname)
2013
host->hostname=(char*) hostname; // This will not be modified!
2015
(!(hostname=calc_ip(hostname,&host->ip,'/')) ||
2016
!(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
2018
host->ip= host->ip_mask=0; // Not a masked ip
2023
static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
2027
if (host->ip_mask && ip && calc_ip(ip,&tmp,'\0'))
2029
return (tmp & host->ip_mask) == host->ip;
2031
return (!host->hostname ||
2032
(hostname && !wild_case_compare(system_charset_info,
2033
hostname, host->hostname)) ||
2034
(ip && !wild_compare(ip, host->hostname, 0)));
2038
Check if the given host name needs to be resolved or not.
2039
Host name has to be resolved if it actually contains *name*.
2042
192.168.1.1 --> FALSE
2043
192.168.1.0/255.255.255.0 --> FALSE
2045
192.168.1.% --> FALSE
2048
AAAAFFFF --> TRUE (Hostname)
2049
AAAA:FFFF:1234:5678 --> FALSE
2052
This function does not check if the given string is a valid host name or
2053
not. It assumes that the argument is a valid host name.
2055
@param hostname the string to check.
2057
@return a flag telling if the argument needs to be resolved or not.
2058
@retval TRUE the argument is a host name and needs to be resolved.
2059
@retval FALSE the argument is either an IP address, or a patter and
2060
should not be resolved.
2063
bool hostname_requires_resolving(const char *hostname)
2068
/* Check if hostname is the localhost. */
2070
size_t hostname_len= strlen(hostname);
2071
size_t localhost_len= strlen(my_localhost);
2073
if (hostname == my_localhost ||
2074
(hostname_len == localhost_len &&
2075
!my_strnncoll(system_charset_info,
2076
(const uchar *) hostname, hostname_len,
2077
(const uchar *) my_localhost, strlen(my_localhost))))
2083
If the string contains any of {':', '%', '_', '/'}, it is definitely
2085
- ':' means that the string is an IPv6 address;
2086
- '%' or '_' means that the string is a pattern;
2087
- '/' means that the string is an IPv4 network address;
2090
for (const char *p= hostname; *p; ++p)
2102
Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
2103
(12.34.56.78). The assumption is that if the string contains only
2104
digits and dots, it is an IPv4 address. Otherwise -- a host name.
2107
for (const char *p= hostname; *p; ++p)
2109
if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
2110
return TRUE; /* a "letter" has been found. */
2113
return FALSE; /* all characters are either dots or digits. */
2118
Update record for user in mysql.user privilege table with new password.
2123
table Pointer to TABLE object for open mysql.user table
2124
host/user Hostname/username pair identifying user for which
2125
new password should be set
2126
new_password New password
2127
new_password_len Length of new password
2130
static bool update_user_table(THD *thd, TABLE *table,
2131
const char *host, const char *user,
2132
const char *new_password, uint new_password_len)
2134
char user_key[MAX_KEY_LENGTH];
2136
DBUG_ENTER("update_user_table");
2137
DBUG_PRINT("enter",("user: %s host: %s",user,host));
2139
table->use_all_columns();
2140
table->field[0]->store(host,(uint) strlen(host), system_charset_info);
2141
table->field[1]->store(user,(uint) strlen(user), system_charset_info);
2142
key_copy((uchar *) user_key, table->record[0], table->key_info,
2143
table->key_info->key_length);
2145
if (table->file->index_read_idx_map(table->record[0], 0,
2146
(uchar *) user_key, HA_WHOLE_KEY,
2149
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
2150
MYF(0)); /* purecov: deadcode */
2151
DBUG_RETURN(1); /* purecov: deadcode */
2153
store_record(table,record[1]);
2154
table->field[2]->store(new_password, new_password_len, system_charset_info);
2155
if ((error=table->file->ha_update_row(table->record[1],table->record[0])) &&
2156
error != HA_ERR_RECORD_IS_THE_SAME)
2158
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
2166
Return 1 if we are allowed to create new users
2167
the logic here is: INSERT_ACL is sufficient.
2168
It's also a requirement in opt_safe_user_create,
2169
otherwise CREATE_USER_ACL is enough.
2172
static bool test_if_create_new_users(THD *thd)
2174
Security_context *sctx= thd->security_ctx;
2175
bool create_new_users= test(sctx->master_access & INSERT_ACL) ||
2176
(!opt_safe_user_create &&
2177
test(sctx->master_access & CREATE_USER_ACL));
2178
if (!create_new_users)
2182
tl.init_one_table(C_STRING_WITH_LEN("mysql"),
2183
C_STRING_WITH_LEN("user"), "user", TL_WRITE);
2184
create_new_users= 1;
2186
db_access=acl_get(sctx->host, sctx->ip,
2187
sctx->priv_user, tl.db, 0);
2188
if (!(db_access & INSERT_ACL))
2190
if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
2194
return create_new_users;
2198
/****************************************************************************
2199
Handle GRANT commands
2200
****************************************************************************/
2202
static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
2203
ulong rights, bool revoke_grant,
2204
bool can_create_user, bool no_auto_create)
2207
bool old_row_exists=0;
2208
const char *password= "";
2209
uint password_len= 0;
2210
char what= (revoke_grant) ? 'N' : 'Y';
2211
uchar user_key[MAX_KEY_LENGTH];
2213
DBUG_ENTER("replace_user_table");
2215
mysql_mutex_assert_owner(&acl_cache->lock);
2217
if (combo.password.str && combo.password.str[0])
2219
if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
2220
combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
2222
my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
2225
password_len= combo.password.length;
2226
password=combo.password.str;
2229
table->use_all_columns();
2230
table->field[0]->store(combo.host.str,combo.host.length,
2231
system_charset_info);
2232
table->field[1]->store(combo.user.str,combo.user.length,
2233
system_charset_info);
2234
key_copy(user_key, table->record[0], table->key_info,
2235
table->key_info->key_length);
2237
if (table->file->index_read_idx_map(table->record[0], 0, user_key,
2241
/* what == 'N' means revoke */
2244
my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
2248
There are four options which affect the process of creation of
2249
a new user (mysqld option --safe-create-user, 'insert' privilege
2250
on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
2251
SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
2253
if (safe-user-create && ! INSERT_priv) => reject
2254
else if (identified_by) => create
2255
else if (no_auto_create_user) => reject
2258
see also test_if_create_new_users()
2260
else if (!password_len && !combo.plugin.length && no_auto_create)
2262
my_error(ER_PASSWORD_NO_MATCH, MYF(0));
2265
else if (!can_create_user)
2267
my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
2270
else if (combo.plugin.str[0])
2272
if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN))
2274
my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str);
2280
restore_record(table,s->default_values);
2281
table->field[0]->store(combo.host.str,combo.host.length,
2282
system_charset_info);
2283
table->field[1]->store(combo.user.str,combo.user.length,
2284
system_charset_info);
2285
table->field[2]->store(password, password_len,
2286
system_charset_info);
2291
store_record(table,record[1]); // Save copy for update
2292
/* what == 'N' means revoke */
2293
if (combo.plugin.length && what != 'N')
2295
my_error(ER_GRANT_PLUGIN_USER_EXISTS, MYF(0),
2296
static_cast<int>(combo.user.length), combo.user.str);
2299
if (combo.password.str) // If password given
2300
table->field[2]->store(password, password_len, system_charset_info);
2301
else if (!rights && !revoke_grant &&
2302
lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
2303
!lex->mqh.specified_limits)
2309
/* Update table columns with new privileges */
2314
for (tmp_field= table->field+3, priv = SELECT_ACL;
2315
*tmp_field && (*tmp_field)->real_type() == MYSQL_TYPE_ENUM &&
2316
((Field_enum*) (*tmp_field))->typelib->count == 2 ;
2317
tmp_field++, priv <<= 1)
2319
if (priv & rights) // set requested privileges
2320
(*tmp_field)->store(&what, 1, &my_charset_latin1);
2322
rights= get_access(table, 3, &next_field);
2323
DBUG_PRINT("info",("table fields: %d",table->s->fields));
2324
if (table->s->fields >= 31) /* From 4.0.0 we have more fields */
2326
/* We write down SSL related ACL stuff */
2327
switch (lex->ssl_type) {
2329
table->field[next_field]->store(STRING_WITH_LEN("ANY"),
2330
&my_charset_latin1);
2331
table->field[next_field+1]->store("", 0, &my_charset_latin1);
2332
table->field[next_field+2]->store("", 0, &my_charset_latin1);
2333
table->field[next_field+3]->store("", 0, &my_charset_latin1);
2336
table->field[next_field]->store(STRING_WITH_LEN("X509"),
2337
&my_charset_latin1);
2338
table->field[next_field+1]->store("", 0, &my_charset_latin1);
2339
table->field[next_field+2]->store("", 0, &my_charset_latin1);
2340
table->field[next_field+3]->store("", 0, &my_charset_latin1);
2342
case SSL_TYPE_SPECIFIED:
2343
table->field[next_field]->store(STRING_WITH_LEN("SPECIFIED"),
2344
&my_charset_latin1);
2345
table->field[next_field+1]->store("", 0, &my_charset_latin1);
2346
table->field[next_field+2]->store("", 0, &my_charset_latin1);
2347
table->field[next_field+3]->store("", 0, &my_charset_latin1);
2348
if (lex->ssl_cipher)
2349
table->field[next_field+1]->store(lex->ssl_cipher,
2350
strlen(lex->ssl_cipher), system_charset_info);
2351
if (lex->x509_issuer)
2352
table->field[next_field+2]->store(lex->x509_issuer,
2353
strlen(lex->x509_issuer), system_charset_info);
2354
if (lex->x509_subject)
2355
table->field[next_field+3]->store(lex->x509_subject,
2356
strlen(lex->x509_subject), system_charset_info);
2358
case SSL_TYPE_NOT_SPECIFIED:
2361
table->field[next_field]->store("", 0, &my_charset_latin1);
2362
table->field[next_field+1]->store("", 0, &my_charset_latin1);
2363
table->field[next_field+2]->store("", 0, &my_charset_latin1);
2364
table->field[next_field+3]->store("", 0, &my_charset_latin1);
2369
USER_RESOURCES mqh= lex->mqh;
2370
if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
2371
table->field[next_field]->store((longlong) mqh.questions, TRUE);
2372
if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
2373
table->field[next_field+1]->store((longlong) mqh.updates, TRUE);
2374
if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
2375
table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE);
2376
if (table->s->fields >= 36 &&
2377
(mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
2378
table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE);
2379
mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
2382
if (combo.plugin.str[0])
2384
if (table->s->fields >= 41 && combo.plugin.str[0])
2386
table->field[next_field]->store(combo.plugin.str, combo.plugin.length,
2387
system_charset_info);
2388
table->field[next_field]->set_notnull();
2389
table->field[next_field + 1]->store(combo.auth.str, combo.auth.length,
2390
system_charset_info);
2391
table->field[next_field + 1]->set_notnull();
2395
my_error(ER_BAD_FIELD_ERROR, MYF(0), "plugin", "mysql.user");
2404
We should NEVER delete from the user table, as a uses can still
2405
use mysqld even if he doesn't have any privileges in the user table!
2407
if (cmp_record(table,record[1]))
2410
table->file->ha_update_row(table->record[1],table->record[0])) &&
2411
error != HA_ERR_RECORD_IS_THE_SAME)
2412
{ // This should never happen
2413
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
2414
error= -1; /* purecov: deadcode */
2415
goto end; /* purecov: deadcode */
2421
else if ((error=table->file->ha_write_row(table->record[0]))) // insert
2422
{ // This should never happen
2423
if (table->file->is_fatal_error(error, HA_CHECK_DUP))
2425
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
2426
error= -1; /* purecov: deadcode */
2427
goto end; /* purecov: deadcode */
2430
error=0; // Privileges granted / revoked
2435
acl_cache->clear(1); // Clear privilege cache
2437
acl_update_user(combo.user.str, combo.host.str,
2438
combo.password.str, password_len,
2448
acl_insert_user(combo.user.str, combo.host.str, password, password_len,
2463
change grants in the mysql.db table
2466
static int replace_db_table(TABLE *table, const char *db,
2467
const LEX_USER &combo,
2468
ulong rights, bool revoke_grant)
2471
ulong priv,store_rights;
2472
bool old_row_exists=0;
2474
char what= (revoke_grant) ? 'N' : 'Y';
2475
uchar user_key[MAX_KEY_LENGTH];
2476
DBUG_ENTER("replace_db_table");
2480
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2484
/* Check if there is such a user in user table in memory? */
2485
if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
2487
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
2491
table->use_all_columns();
2492
table->field[0]->store(combo.host.str,combo.host.length,
2493
system_charset_info);
2494
table->field[1]->store(db,(uint) strlen(db), system_charset_info);
2495
table->field[2]->store(combo.user.str,combo.user.length,
2496
system_charset_info);
2497
key_copy(user_key, table->record[0], table->key_info,
2498
table->key_info->key_length);
2500
if (table->file->index_read_idx_map(table->record[0],0, user_key,
2505
{ // no row, no revoke
2506
my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
2510
restore_record(table, s->default_values);
2511
table->field[0]->store(combo.host.str,combo.host.length,
2512
system_charset_info);
2513
table->field[1]->store(db,(uint) strlen(db), system_charset_info);
2514
table->field[2]->store(combo.user.str,combo.user.length,
2515
system_charset_info);
2520
store_record(table,record[1]);
2523
store_rights=get_rights_for_db(rights);
2524
for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
2526
if (priv & store_rights) // do it if priv is chosen
2527
table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
2529
rights=get_access(table,3);
2530
rights=fix_rights_for_db(rights);
2534
/* update old existing row */
2537
if ((error= table->file->ha_update_row(table->record[1],
2538
table->record[0])) &&
2539
error != HA_ERR_RECORD_IS_THE_SAME)
2540
goto table_error; /* purecov: deadcode */
2542
else /* must have been a revoke of all privileges */
2544
if ((error= table->file->ha_delete_row(table->record[1])))
2545
goto table_error; /* purecov: deadcode */
2548
else if (rights && (error= table->file->ha_write_row(table->record[0])))
2550
if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
2551
goto table_error; /* purecov: deadcode */
2554
acl_cache->clear(1); // Clear privilege cache
2556
acl_update_db(combo.user.str,combo.host.str,db,rights);
2559
acl_insert_db(combo.user.str,combo.host.str,db,rights);
2562
/* This could only happen if the grant tables got corrupted */
2564
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
2572
acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
2574
mysql_mutex_assert_owner(&acl_cache->lock);
2576
DBUG_ENTER("acl_update_proxy_user");
2577
for (uint i= 0; i < acl_proxy_users.elements; i++)
2579
ACL_PROXY_USER *acl_user=
2580
dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
2582
if (acl_user->pk_equals(new_value))
2586
DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
2587
delete_dynamic_element(&acl_proxy_users, i);
2591
DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
2592
acl_user->set_data(new_value);
2602
acl_insert_proxy_user(ACL_PROXY_USER *new_value)
2604
DBUG_ENTER("acl_insert_proxy_user");
2605
mysql_mutex_assert_owner(&acl_cache->lock);
2606
(void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
2607
my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
2608
acl_proxy_users.elements,
2609
sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
2615
replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
2616
const LEX_USER *proxied_user, bool with_grant_arg,
2619
bool old_row_exists= 0;
2621
uchar user_key[MAX_KEY_LENGTH];
2622
ACL_PROXY_USER new_grant;
2623
char grantor[USER_HOST_BUFF_SIZE];
2625
DBUG_ENTER("replace_proxies_priv_table");
2629
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2633
/* Check if there is such a user in user table in memory? */
2634
if (!find_acl_user(user->host.str,user->user.str, FALSE))
2636
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
2640
table->use_all_columns();
2641
ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
2642
&proxied_user->host, &proxied_user->user);
2644
key_copy(user_key, table->record[0], table->key_info,
2645
table->key_info->key_length);
2647
get_grantor(thd, grantor);
2649
table->file->ha_index_init(0, 1);
2650
if (table->file->index_read_map(table->record[0], user_key,
2654
DBUG_PRINT ("info", ("Row not found"));
2656
{ // no row, no revoke
2657
my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
2661
restore_record(table, s->default_values);
2662
ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
2663
&proxied_user->host,
2664
&proxied_user->user,
2670
DBUG_PRINT("info", ("Row found"));
2672
store_record(table, record[1]);
2677
/* update old existing row */
2680
if ((error= table->file->ha_update_row(table->record[1],
2681
table->record[0])) &&
2682
error != HA_ERR_RECORD_IS_THE_SAME)
2683
goto table_error; /* purecov: inspected */
2687
if ((error= table->file->ha_delete_row(table->record[1])))
2688
goto table_error; /* purecov: inspected */
2691
else if ((error= table->file->ha_write_row(table->record[0])))
2693
DBUG_PRINT("info", ("error inserting the row"));
2694
if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
2695
goto table_error; /* purecov: inspected */
2698
acl_cache->clear(1); // Clear privilege cache
2701
new_grant.init(user->host.str, user->user.str,
2702
proxied_user->host.str, proxied_user->user.str,
2704
acl_update_proxy_user(&new_grant, revoke_grant);
2708
new_grant.init(&mem, user->host.str, user->user.str,
2709
proxied_user->host.str, proxied_user->user.str,
2711
acl_insert_proxy_user(&new_grant);
2714
table->file->ha_index_end();
2717
/* This could only happen if the grant tables got corrupted */
2719
DBUG_PRINT("info", ("table error"));
2720
table->file->print_error(error, MYF(0)); /* purecov: inspected */
2723
DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
2724
table->file->ha_index_end();
2729
class GRANT_COLUMN :public Sql_alloc
2735
GRANT_COLUMN(String &c, ulong y) :rights (y)
2737
column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length());
2742
static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
2743
my_bool not_used __attribute__((unused)))
2745
*length=buff->key_length;
2746
return (uchar*) buff->column;
2750
class GRANT_NAME :public Sql_alloc
2753
acl_host_and_ip host;
2754
char *db, *user, *tname, *hash_key;
2758
GRANT_NAME(const char *h, const char *d,const char *u,
2759
const char *t, ulong p, bool is_routine);
2760
GRANT_NAME (TABLE *form, bool is_routine);
2761
virtual ~GRANT_NAME() {};
2762
virtual bool ok() { return privs != 0; }
2763
void set_user_details(const char *h, const char *d,
2764
const char *u, const char *t,
2769
class GRANT_TABLE :public GRANT_NAME
2775
GRANT_TABLE(const char *h, const char *d,const char *u,
2776
const char *t, ulong p, ulong c);
2777
GRANT_TABLE (TABLE *form, TABLE *col_privs);
2779
bool ok() { return privs != 0 || cols != 0; }
2783
void GRANT_NAME::set_user_details(const char *h, const char *d,
2784
const char *u, const char *t,
2787
/* Host given by user */
2788
update_hostname(&host, strdup_root(&memex, h));
2791
db= strdup_root(&memex, d);
2792
if (lower_case_table_names)
2793
my_casedn_str(files_charset_info, db);
2795
user = strdup_root(&memex,u);
2796
sort= get_sort(3,host.hostname,db,user);
2799
tname= strdup_root(&memex, t);
2800
if (lower_case_table_names || is_routine)
2801
my_casedn_str(files_charset_info, tname);
2803
key_length= strlen(d) + strlen(u)+ strlen(t)+3;
2804
hash_key= (char*) alloc_root(&memex,key_length);
2805
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
2808
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
2809
const char *t, ulong p, bool is_routine)
2810
:db(0), tname(0), privs(p)
2812
set_user_details(h, d, u, t, is_routine);
2815
GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
2816
const char *t, ulong p, ulong c)
2817
:GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
2819
(void) my_hash_init2(&hash_columns,4,system_charset_info,
2820
0,0,0, (my_hash_get_key) get_key_column,0,0);
2824
GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
2826
update_hostname(&host, get_field(&memex, form->field[0]));
2827
db= get_field(&memex,form->field[1]);
2828
user= get_field(&memex,form->field[2]);
2831
sort= get_sort(3, host.hostname, db, user);
2832
tname= get_field(&memex,form->field[3]);
2835
/* Wrong table row; Ignore it */
2837
return; /* purecov: inspected */
2839
if (lower_case_table_names)
2841
my_casedn_str(files_charset_info, db);
2843
if (lower_case_table_names || is_routine)
2845
my_casedn_str(files_charset_info, tname);
2847
key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
2848
hash_key= (char*) alloc_root(&memex, key_length);
2849
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
2850
privs = (ulong) form->field[6]->val_int();
2851
privs = fix_rights_for_table(privs);
2855
GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
2856
:GRANT_NAME(form, FALSE)
2858
uchar key[MAX_KEY_LENGTH];
2862
/* Wrong table row; Ignore it */
2863
my_hash_clear(&hash_columns); /* allow for destruction */
2867
cols= (ulong) form->field[7]->val_int();
2868
cols = fix_rights_for_column(cols);
2870
(void) my_hash_init2(&hash_columns,4,system_charset_info,
2871
0,0,0, (my_hash_get_key) get_key_column,0,0);
2874
uint key_prefix_len;
2875
KEY_PART_INFO *key_part= col_privs->key_info->key_part;
2876
col_privs->field[0]->store(host.hostname,
2877
host.hostname ? (uint) strlen(host.hostname) :
2879
system_charset_info);
2880
col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
2881
col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
2882
col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
2884
key_prefix_len= (key_part[0].store_length +
2885
key_part[1].store_length +
2886
key_part[2].store_length +
2887
key_part[3].store_length);
2888
key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
2889
col_privs->field[4]->store("",0, &my_charset_latin1);
2891
col_privs->file->ha_index_init(0, 1);
2892
if (col_privs->file->index_read_map(col_privs->record[0], (uchar*) key,
2893
(key_part_map)15, HA_READ_KEY_EXACT))
2895
cols = 0; /* purecov: deadcode */
2896
col_privs->file->ha_index_end();
2901
String *res,column_name;
2902
GRANT_COLUMN *mem_check;
2903
/* As column name is a string, we don't have to supply a buffer */
2904
res=col_privs->field[4]->val_str(&column_name);
2905
ulong priv= (ulong) col_privs->field[6]->val_int();
2906
if (!(mem_check = new GRANT_COLUMN(*res,
2907
fix_rights_for_column(priv))))
2909
/* Don't use this entry */
2910
privs = cols = 0; /* purecov: deadcode */
2911
return; /* purecov: deadcode */
2913
if (my_hash_insert(&hash_columns, (uchar *) mem_check))
2915
/* Invalidate this entry */
2919
} while (!col_privs->file->index_next(col_privs->record[0]) &&
2920
!key_cmp_if_same(col_privs,key,0,key_prefix_len));
2921
col_privs->file->ha_index_end();
2926
GRANT_TABLE::~GRANT_TABLE()
2928
my_hash_free(&hash_columns);
2932
static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
2933
my_bool not_used __attribute__((unused)))
2935
*length=buff->key_length;
2936
return (uchar*) buff->hash_key;
2940
void free_grant_table(GRANT_TABLE *grant_table)
2942
my_hash_free(&grant_table->hash_columns);
2946
/* Search after a matching grant. Prefer exact grants before not exact ones */
2948
static GRANT_NAME *name_hash_search(HASH *name_hash,
2949
const char *host,const char* ip,
2951
const char *user, const char *tname,
2952
bool exact, bool name_tolower)
2954
char helping [NAME_LEN*2+USERNAME_LENGTH+3], *name_ptr;
2956
GRANT_NAME *grant_name,*found=0;
2957
HASH_SEARCH_STATE state;
2959
name_ptr= strmov(strmov(helping, user) + 1, db) + 1;
2960
len = (uint) (strmov(name_ptr, tname) - helping) + 1;
2962
my_casedn_str(files_charset_info, name_ptr);
2963
for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
2966
grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
2971
if (!grant_name->host.hostname ||
2973
!my_strcasecmp(system_charset_info, host,
2974
grant_name->host.hostname)) ||
2975
(ip && !strcmp(ip, grant_name->host.hostname)))
2980
if (compare_hostname(&grant_name->host, host, ip) &&
2981
(!found || found->sort < grant_name->sort))
2982
found=grant_name; // Host ok
2990
routine_hash_search(const char *host, const char *ip, const char *db,
2991
const char *user, const char *tname, bool proc, bool exact)
2993
return (GRANT_TABLE*)
2994
name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
2995
host, ip, db, user, tname, exact, TRUE);
2999
inline GRANT_TABLE *
3000
table_hash_search(const char *host, const char *ip, const char *db,
3001
const char *user, const char *tname, bool exact)
3003
return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
3004
user, tname, exact, FALSE);
3008
inline GRANT_COLUMN *
3009
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
3011
return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
3012
(uchar*) cname, length);
3016
static int replace_column_table(GRANT_TABLE *g_t,
3017
TABLE *table, const LEX_USER &combo,
3018
List <LEX_COLUMN> &columns,
3019
const char *db, const char *table_name,
3020
ulong rights, bool revoke_grant)
3022
int error=0,result=0;
3023
uchar key[MAX_KEY_LENGTH];
3024
uint key_prefix_length;
3025
KEY_PART_INFO *key_part= table->key_info->key_part;
3026
DBUG_ENTER("replace_column_table");
3028
table->use_all_columns();
3029
table->field[0]->store(combo.host.str,combo.host.length,
3030
system_charset_info);
3031
table->field[1]->store(db,(uint) strlen(db),
3032
system_charset_info);
3033
table->field[2]->store(combo.user.str,combo.user.length,
3034
system_charset_info);
3035
table->field[3]->store(table_name,(uint) strlen(table_name),
3036
system_charset_info);
3038
/* Get length of 4 first key parts */
3039
key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
3040
key_part[2].store_length + key_part[3].store_length);
3041
key_copy(key, table->record[0], table->key_info, key_prefix_length);
3043
rights&= COL_ACLS; // Only ACL for columns
3045
/* first fix privileges for all columns in column list */
3047
List_iterator <LEX_COLUMN> iter(columns);
3048
class LEX_COLUMN *column;
3049
table->file->ha_index_init(0, 1);
3050
while ((column= iter++))
3052
ulong privileges= column->rights;
3053
bool old_row_exists=0;
3054
uchar user_key[MAX_KEY_LENGTH];
3056
key_restore(table->record[0],key,table->key_info,
3058
table->field[4]->store(column->column.ptr(), column->column.length(),
3059
system_charset_info);
3060
/* Get key for the first 4 columns */
3061
key_copy(user_key, table->record[0], table->key_info,
3062
table->key_info->key_length);
3064
if (table->file->index_read_map(table->record[0], user_key, HA_WHOLE_KEY,
3069
my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
3070
combo.user.str, combo.host.str,
3071
table_name); /* purecov: inspected */
3072
result= -1; /* purecov: inspected */
3073
continue; /* purecov: inspected */
3076
restore_record(table, s->default_values); // Get empty record
3077
key_restore(table->record[0],key,table->key_info,
3079
table->field[4]->store(column->column.ptr(),column->column.length(),
3080
system_charset_info);
3084
ulong tmp= (ulong) table->field[6]->val_int();
3085
tmp=fix_rights_for_column(tmp);
3088
privileges = tmp & ~(privileges | rights);
3092
store_record(table,record[1]); // copy original row
3095
table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
3099
GRANT_COLUMN *grant_column;
3101
error=table->file->ha_update_row(table->record[1],table->record[0]);
3103
error=table->file->ha_delete_row(table->record[1]);
3104
if (error && error != HA_ERR_RECORD_IS_THE_SAME)
3106
table->file->print_error(error,MYF(0)); /* purecov: inspected */
3107
result= -1; /* purecov: inspected */
3108
goto end; /* purecov: inspected */
3112
grant_column= column_hash_search(g_t, column->column.ptr(),
3113
column->column.length());
3114
if (grant_column) // Should always be true
3115
grant_column->rights= privileges; // Update hash
3119
GRANT_COLUMN *grant_column;
3120
if ((error=table->file->ha_write_row(table->record[0])))
3122
table->file->print_error(error,MYF(0)); /* purecov: inspected */
3123
result= -1; /* purecov: inspected */
3124
goto end; /* purecov: inspected */
3126
grant_column= new GRANT_COLUMN(column->column,privileges);
3127
if (my_hash_insert(&g_t->hash_columns,(uchar*) grant_column))
3136
If revoke of privileges on the table level, remove all such privileges
3142
uchar user_key[MAX_KEY_LENGTH];
3143
key_copy(user_key, table->record[0], table->key_info,
3146
if (table->file->index_read_map(table->record[0], user_key,
3151
/* Scan through all rows with the same host,db,user and table */
3154
ulong privileges = (ulong) table->field[6]->val_int();
3155
privileges=fix_rights_for_column(privileges);
3156
store_record(table,record[1]);
3158
if (privileges & rights) // is in this record the priv to be revoked ??
3160
GRANT_COLUMN *grant_column = NULL;
3161
char colum_name_buf[HOSTNAME_LENGTH+1];
3162
String column_name(colum_name_buf,sizeof(colum_name_buf),
3163
system_charset_info);
3165
privileges&= ~rights;
3166
table->field[6]->store((longlong)
3167
get_rights_for_column(privileges), TRUE);
3168
table->field[4]->val_str(&column_name);
3169
grant_column = column_hash_search(g_t,
3171
column_name.length());
3175
if ((tmp_error=table->file->ha_update_row(table->record[1],
3176
table->record[0])) &&
3177
tmp_error != HA_ERR_RECORD_IS_THE_SAME)
3178
{ /* purecov: deadcode */
3179
table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
3180
result= -1; /* purecov: deadcode */
3181
goto end; /* purecov: deadcode */
3184
grant_column->rights = privileges; // Update hash
3189
if ((tmp_error = table->file->ha_delete_row(table->record[1])))
3190
{ /* purecov: deadcode */
3191
table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
3192
result= -1; /* purecov: deadcode */
3193
goto end; /* purecov: deadcode */
3196
my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
3199
} while (!table->file->index_next(table->record[0]) &&
3200
!key_cmp_if_same(table, key, 0, key_prefix_length));
3204
table->file->ha_index_end();
3205
DBUG_RETURN(result);
3208
static inline void get_grantor(THD *thd, char *grantor)
3210
const char *user= thd->security_ctx->user;
3211
const char *host= thd->security_ctx->host_or_ip;
3213
#if defined(HAVE_REPLICATION)
3214
if (thd->slave_thread && thd->has_invoker())
3216
user= thd->get_invoker_user().str;
3217
host= thd->get_invoker_host().str;
3220
strxmov(grantor, user, "@", host, NullS);
3223
static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
3224
TABLE *table, const LEX_USER &combo,
3225
const char *db, const char *table_name,
3226
ulong rights, ulong col_rights,
3229
char grantor[USER_HOST_BUFF_SIZE];
3230
int old_row_exists = 1;
3232
ulong store_table_rights, store_col_rights;
3233
uchar user_key[MAX_KEY_LENGTH];
3234
DBUG_ENTER("replace_table_table");
3236
get_grantor(thd, grantor);
3238
The following should always succeed as new users are created before
3239
this function is called!
3241
if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
3243
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
3244
MYF(0)); /* purecov: deadcode */
3245
DBUG_RETURN(-1); /* purecov: deadcode */
3248
table->use_all_columns();
3249
restore_record(table, s->default_values); // Get empty record
3250
table->field[0]->store(combo.host.str,combo.host.length,
3251
system_charset_info);
3252
table->field[1]->store(db,(uint) strlen(db), system_charset_info);
3253
table->field[2]->store(combo.user.str,combo.user.length,
3254
system_charset_info);
3255
table->field[3]->store(table_name,(uint) strlen(table_name),
3256
system_charset_info);
3257
store_record(table,record[1]); // store at pos 1
3258
key_copy(user_key, table->record[0], table->key_info,
3259
table->key_info->key_length);
3261
if (table->file->index_read_idx_map(table->record[0], 0, user_key,
3266
The following should never happen as we first check the in memory
3267
grant tables for the user. There is however always a small change that
3268
the user has modified the grant tables directly.
3271
{ // no row, no revoke
3272
my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
3273
combo.user.str, combo.host.str,
3274
table_name); /* purecov: deadcode */
3275
DBUG_RETURN(-1); /* purecov: deadcode */
3278
restore_record(table,record[1]); // Get saved record
3281
store_table_rights= get_rights_for_table(rights);
3282
store_col_rights= get_rights_for_column(col_rights);
3286
store_record(table,record[1]);
3287
j = (ulong) table->field[6]->val_int();
3288
k = (ulong) table->field[7]->val_int();
3292
/* column rights are already fixed in mysql_table_grant */
3293
store_table_rights=j & ~store_table_rights;
3297
store_table_rights|= j;
3298
store_col_rights|= k;
3302
table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
3303
table->field[6]->store((longlong) store_table_rights, TRUE);
3304
table->field[7]->store((longlong) store_col_rights, TRUE);
3305
rights=fix_rights_for_table(store_table_rights);
3306
col_rights=fix_rights_for_column(store_col_rights);
3310
if (store_table_rights || store_col_rights)
3312
if ((error=table->file->ha_update_row(table->record[1],
3313
table->record[0])) &&
3314
error != HA_ERR_RECORD_IS_THE_SAME)
3315
goto table_error; /* purecov: deadcode */
3317
else if ((error = table->file->ha_delete_row(table->record[1])))
3318
goto table_error; /* purecov: deadcode */
3322
error=table->file->ha_write_row(table->record[0]);
3323
if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
3324
goto table_error; /* purecov: deadcode */
3327
if (rights | col_rights)
3329
grant_table->privs= rights;
3330
grant_table->cols= col_rights;
3334
my_hash_delete(&column_priv_hash,(uchar*) grant_table);
3338
/* This should never happen */
3340
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
3341
DBUG_RETURN(-1); /* purecov: deadcode */
3349
static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
3350
TABLE *table, const LEX_USER &combo,
3351
const char *db, const char *routine_name,
3352
bool is_proc, ulong rights, bool revoke_grant)
3354
char grantor[USER_HOST_BUFF_SIZE];
3355
int old_row_exists= 1;
3357
ulong store_proc_rights;
3358
DBUG_ENTER("replace_routine_table");
3362
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3366
get_grantor(thd, grantor);
3368
New users are created before this function is called.
3370
There may be some cases where a routine's definer is removed but the
3374
table->use_all_columns();
3375
restore_record(table, s->default_values); // Get empty record
3376
table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
3377
table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
3378
table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
3379
table->field[3]->store(routine_name,(uint) strlen(routine_name),
3380
&my_charset_latin1);
3381
table->field[4]->store((longlong)(is_proc ?
3382
TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION),
3384
store_record(table,record[1]); // store at pos 1
3386
if (table->file->index_read_idx_map(table->record[0], 0,
3387
(uchar*) table->field[0]->ptr,
3392
The following should never happen as we first check the in memory
3393
grant tables for the user. There is however always a small change that
3394
the user has modified the grant tables directly.
3397
{ // no row, no revoke
3398
my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
3399
combo.user.str, combo.host.str, routine_name);
3403
restore_record(table,record[1]); // Get saved record
3406
store_proc_rights= get_rights_for_procedure(rights);
3410
store_record(table,record[1]);
3411
j= (ulong) table->field[6]->val_int();
3415
/* column rights are already fixed in mysql_table_grant */
3416
store_proc_rights=j & ~store_proc_rights;
3420
store_proc_rights|= j;
3424
table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
3425
table->field[6]->store((longlong) store_proc_rights, TRUE);
3426
rights=fix_rights_for_procedure(store_proc_rights);
3430
if (store_proc_rights)
3432
if ((error=table->file->ha_update_row(table->record[1],
3433
table->record[0])) &&
3434
error != HA_ERR_RECORD_IS_THE_SAME)
3437
else if ((error= table->file->ha_delete_row(table->record[1])))
3442
error=table->file->ha_write_row(table->record[0]);
3443
if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
3449
grant_name->privs= rights;
3453
my_hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*)
3458
/* This should never happen */
3460
table->file->print_error(error,MYF(0));
3466
Store table level and column level grants in the privilege tables
3471
table_list List of tables to give grant
3472
user_list List of users to give grant
3473
columns List of columns to give grant
3474
rights Table level grant
3475
revoke_grant Set to 1 if this is a REVOKE command
3482
int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
3483
List <LEX_USER> &user_list,
3484
List <LEX_COLUMN> &columns, ulong rights,
3487
ulong column_priv= 0;
3488
List_iterator <LEX_USER> str_list (user_list);
3489
LEX_USER *Str, *tmp_Str;
3490
TABLE_LIST tables[3];
3491
bool create_new_users=0;
3492
char *db_name, *table_name;
3493
bool save_binlog_row_based;
3494
DBUG_ENTER("mysql_table_grant");
3498
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
3499
"--skip-grant-tables"); /* purecov: inspected */
3500
DBUG_RETURN(TRUE); /* purecov: inspected */
3502
if (rights & ~TABLE_ACLS)
3504
my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
3511
if (columns.elements)
3513
class LEX_COLUMN *column;
3514
List_iterator <LEX_COLUMN> column_iter(columns);
3516
if (open_normal_and_derived_tables(thd, table_list, 0))
3519
while ((column = column_iter++))
3521
uint unused_field_idx= NO_CACHED_FIELD_INDEX;
3523
Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
3524
column->column.length(),
3525
column->column.ptr(), NULL, NULL,
3527
&unused_field_idx, FALSE, &dummy);
3530
my_error(ER_BAD_FIELD_ERROR, MYF(0),
3531
column->column.c_ptr(), table_list->alias);
3534
if (f == (Field *)-1)
3536
column_priv|= column->rights;
3538
close_mysql_tables(thd);
3542
if (!(rights & CREATE_ACL))
3544
char buf[FN_REFLEN + 1];
3545
build_table_filename(buf, sizeof(buf) - 1, table_list->db,
3546
table_list->table_name, reg_ext, 0);
3547
fn_format(buf, buf, "", "", MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS |
3548
MY_RETURN_REAL_PATH | MY_APPEND_EXT);
3549
if (access(buf,F_OK))
3551
my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
3555
if (table_list->grant.want_privilege)
3558
get_privilege_desc(command, sizeof(command),
3559
table_list->grant.want_privilege);
3560
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
3561
command, thd->security_ctx->priv_user,
3562
thd->security_ctx->host_or_ip, table_list->alias);
3568
/* open the mysql.tables_priv and mysql.columns_priv tables */
3570
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
3571
C_STRING_WITH_LEN("user"), "user", TL_WRITE);
3572
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
3573
C_STRING_WITH_LEN("tables_priv"),
3574
"tables_priv", TL_WRITE);
3575
tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
3576
C_STRING_WITH_LEN("columns_priv"),
3577
"columns_priv", TL_WRITE);
3578
tables[0].next_local= tables[0].next_global= tables+1;
3579
/* Don't open column table if we don't need it ! */
3580
if (column_priv || (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
3581
tables[1].next_local= tables[1].next_global= tables+2;
3584
This statement will be replicated as a statement, even when using
3585
row-based replication. The flag will be reset at the end of the
3588
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
3589
thd->clear_current_stmt_binlog_format_row();
3591
#ifdef HAVE_REPLICATION
3593
GRANT and REVOKE are applied the slave in/exclusion rules as they are
3594
some kind of updates to the mysql.% tables.
3596
if (thd->slave_thread && rpl_filter->is_on())
3599
The tables must be marked "updating" so that tables_ok() takes them into
3602
tables[0].updating= tables[1].updating= tables[2].updating= 1;
3603
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
3605
/* Restore the state of binlog format */
3606
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3607
if (save_binlog_row_based)
3608
thd->set_current_stmt_binlog_format_row();
3615
The lock api is depending on the thd->lex variable which needs to be
3618
Query_tables_list backup;
3619
thd->lex->reset_n_backup_query_tables_list(&backup);
3621
Restore Query_tables_list::sql_command value, which was reset
3622
above, as the code writing query to the binary log assumes that
3623
this value corresponds to the statement being executed.
3625
thd->lex->sql_command= backup.sql_command;
3626
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
3627
{ // Should never happen
3628
/* Restore the state of binlog format */
3629
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3630
thd->lex->restore_backup_query_tables_list(&backup);
3631
if (save_binlog_row_based)
3632
thd->set_current_stmt_binlog_format_row();
3633
DBUG_RETURN(TRUE); /* purecov: deadcode */
3637
create_new_users= test_if_create_new_users(thd);
3639
mysql_rwlock_wrlock(&LOCK_grant);
3640
mysql_mutex_lock(&acl_cache->lock);
3641
MEM_ROOT *old_root= thd->mem_root;
3642
thd->mem_root= &memex;
3645
while ((tmp_Str = str_list++))
3648
GRANT_TABLE *grant_table;
3649
if (!(Str= get_current_user(thd, tmp_Str)))
3654
/* Create user if needed */
3655
error=replace_user_table(thd, tables[0].table, *Str,
3656
0, revoke_grant, create_new_users,
3657
test(thd->variables.sql_mode &
3658
MODE_NO_AUTO_CREATE_USER));
3661
result= TRUE; // Remember error
3662
continue; // Add next user
3665
db_name= table_list->get_db_name();
3666
table_name= table_list->get_table_name();
3668
/* Find/create cached table grant */
3669
grant_table= table_hash_search(Str->host.str, NullS, db_name,
3670
Str->user.str, table_name, 1);
3675
my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
3676
Str->user.str, Str->host.str, table_list->table_name);
3680
grant_table = new GRANT_TABLE (Str->host.str, db_name,
3681
Str->user.str, table_name,
3685
my_hash_insert(&column_priv_hash,(uchar*) grant_table))
3687
result= TRUE; /* purecov: deadcode */
3688
continue; /* purecov: deadcode */
3692
/* If revoke_grant, calculate the new column privilege for tables_priv */
3695
class LEX_COLUMN *column;
3696
List_iterator <LEX_COLUMN> column_iter(columns);
3697
GRANT_COLUMN *grant_column;
3699
/* Fix old grants */
3700
while ((column = column_iter++))
3702
grant_column = column_hash_search(grant_table,
3703
column->column.ptr(),
3704
column->column.length());
3706
grant_column->rights&= ~(column->rights | rights);
3708
/* scan trough all columns to get new column grant */
3710
for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
3712
grant_column= (GRANT_COLUMN*)
3713
my_hash_element(&grant_table->hash_columns, idx);
3714
grant_column->rights&= ~rights; // Fix other columns
3715
column_priv|= grant_column->rights;
3720
column_priv|= grant_table->cols;
3724
/* update table and columns */
3726
if (replace_table_table(thd, grant_table, tables[1].table, *Str,
3727
db_name, table_name,
3728
rights, column_priv, revoke_grant))
3730
/* Should only happen if table is crashed */
3731
result= TRUE; /* purecov: deadcode */
3733
else if (tables[2].table)
3735
if ((replace_column_table(grant_table, tables[2].table, *Str,
3737
db_name, table_name,
3738
rights, revoke_grant)))
3744
thd->mem_root= old_root;
3745
mysql_mutex_unlock(&acl_cache->lock);
3747
if (!result) /* success */
3749
result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
3752
mysql_rwlock_unlock(&LOCK_grant);
3754
if (!result) /* success */
3757
/* Tables are automatically closed */
3758
thd->lex->restore_backup_query_tables_list(&backup);
3759
/* Restore the state of binlog format */
3760
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3761
if (save_binlog_row_based)
3762
thd->set_current_stmt_binlog_format_row();
3763
DBUG_RETURN(result);
3768
Store routine level grants in the privilege tables
3770
@param thd Thread handle
3771
@param table_list List of routines to give grant
3772
@param is_proc Is this a list of procedures?
3773
@param user_list List of users to give grant
3774
@param rights Table level grant
3775
@param revoke_grant Is this is a REVOKE command?
3778
@retval FALSE Success.
3779
@retval TRUE An error occurred.
3782
bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
3783
List <LEX_USER> &user_list, ulong rights,
3784
bool revoke_grant, bool write_to_binlog)
3786
List_iterator <LEX_USER> str_list (user_list);
3787
LEX_USER *Str, *tmp_Str;
3788
TABLE_LIST tables[2];
3789
bool create_new_users=0, result=0;
3790
char *db_name, *table_name;
3791
bool save_binlog_row_based;
3792
DBUG_ENTER("mysql_routine_grant");
3796
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
3797
"--skip-grant-tables");
3800
if (rights & ~PROC_ACLS)
3802
my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
3809
if (sp_exist_routines(thd, table_list, is_proc))
3813
/* open the mysql.user and mysql.procs_priv tables */
3815
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
3816
C_STRING_WITH_LEN("user"), "user", TL_WRITE);
3817
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
3818
C_STRING_WITH_LEN("procs_priv"), "procs_priv", TL_WRITE);
3819
tables[0].next_local= tables[0].next_global= tables+1;
3822
This statement will be replicated as a statement, even when using
3823
row-based replication. The flag will be reset at the end of the
3826
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
3827
thd->clear_current_stmt_binlog_format_row();
3829
#ifdef HAVE_REPLICATION
3831
GRANT and REVOKE are applied the slave in/exclusion rules as they are
3832
some kind of updates to the mysql.% tables.
3834
if (thd->slave_thread && rpl_filter->is_on())
3837
The tables must be marked "updating" so that tables_ok() takes them into
3840
tables[0].updating= tables[1].updating= 1;
3841
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
3843
/* Restore the state of binlog format */
3844
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3845
if (save_binlog_row_based)
3846
thd->set_current_stmt_binlog_format_row();
3852
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
3853
{ // Should never happen
3854
/* Restore the state of binlog format */
3855
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3856
if (save_binlog_row_based)
3857
thd->set_current_stmt_binlog_format_row();
3862
create_new_users= test_if_create_new_users(thd);
3863
mysql_rwlock_wrlock(&LOCK_grant);
3864
mysql_mutex_lock(&acl_cache->lock);
3865
MEM_ROOT *old_root= thd->mem_root;
3866
thd->mem_root= &memex;
3868
DBUG_PRINT("info",("now time to iterate and add users"));
3870
while ((tmp_Str= str_list++))
3873
GRANT_NAME *grant_name;
3874
if (!(Str= get_current_user(thd, tmp_Str)))
3879
/* Create user if needed */
3880
error=replace_user_table(thd, tables[0].table, *Str,
3881
0, revoke_grant, create_new_users,
3882
test(thd->variables.sql_mode &
3883
MODE_NO_AUTO_CREATE_USER));
3886
result= TRUE; // Remember error
3887
continue; // Add next user
3890
db_name= table_list->db;
3891
table_name= table_list->table_name;
3893
grant_name= routine_hash_search(Str->host.str, NullS, db_name,
3894
Str->user.str, table_name, is_proc, 1);
3899
my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
3900
Str->user.str, Str->host.str, table_name);
3904
grant_name= new GRANT_NAME(Str->host.str, db_name,
3905
Str->user.str, table_name,
3908
my_hash_insert(is_proc ?
3909
&proc_priv_hash : &func_priv_hash,(uchar*) grant_name))
3916
if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
3917
db_name, table_name, is_proc, rights,
3924
thd->mem_root= old_root;
3925
mysql_mutex_unlock(&acl_cache->lock);
3927
if (write_to_binlog)
3929
if (write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
3933
mysql_rwlock_unlock(&LOCK_grant);
3934
/* Restore the state of binlog format */
3935
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3936
if (save_binlog_row_based)
3937
thd->set_current_stmt_binlog_format_row();
3939
/* Tables are automatically closed */
3940
DBUG_RETURN(result);
3944
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
3945
ulong rights, bool revoke_grant, bool is_proxy)
3947
List_iterator <LEX_USER> str_list (list);
3948
LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
3949
char tmp_db[NAME_LEN+1];
3950
bool create_new_users=0;
3951
TABLE_LIST tables[2];
3952
bool save_binlog_row_based;
3953
DBUG_ENTER("mysql_grant");
3956
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
3957
"--skip-grant-tables"); /* purecov: tested */
3958
DBUG_RETURN(TRUE); /* purecov: tested */
3961
if (lower_case_table_names && db)
3964
my_casedn_str(files_charset_info, tmp_db);
3971
proxied_user= str_list++;
3974
/* open the mysql.user and mysql.db or mysql.proxies_priv tables */
3975
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
3976
C_STRING_WITH_LEN("user"), "user", TL_WRITE);
3979
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
3980
C_STRING_WITH_LEN("proxies_priv"),
3984
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
3985
C_STRING_WITH_LEN("db"),
3988
tables[0].next_local= tables[0].next_global= tables+1;
3991
This statement will be replicated as a statement, even when using
3992
row-based replication. The flag will be reset at the end of the
3995
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
3996
thd->clear_current_stmt_binlog_format_row();
3998
#ifdef HAVE_REPLICATION
4000
GRANT and REVOKE are applied the slave in/exclusion rules as they are
4001
some kind of updates to the mysql.% tables.
4003
if (thd->slave_thread && rpl_filter->is_on())
4006
The tables must be marked "updating" so that tables_ok() takes them into
4009
tables[0].updating= tables[1].updating= 1;
4010
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
4012
/* Restore the state of binlog format */
4013
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4014
if (save_binlog_row_based)
4015
thd->set_current_stmt_binlog_format_row();
4021
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
4022
{ // This should never happen
4023
/* Restore the state of binlog format */
4024
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4025
if (save_binlog_row_based)
4026
thd->set_current_stmt_binlog_format_row();
4027
DBUG_RETURN(TRUE); /* purecov: deadcode */
4031
create_new_users= test_if_create_new_users(thd);
4033
/* go through users in user_list */
4034
mysql_rwlock_wrlock(&LOCK_grant);
4035
mysql_mutex_lock(&acl_cache->lock);
4039
while ((tmp_Str = str_list++))
4041
if (!(Str= get_current_user(thd, tmp_Str)))
4047
No User, but a password?
4048
They did GRANT ... TO CURRENT_USER() IDENTIFIED BY ... !
4049
Get the current user, and shallow-copy the new password to them!
4051
if (!tmp_Str->user.str && tmp_Str->password.str)
4052
Str->password= tmp_Str->password;
4053
if (replace_user_table(thd, tables[0].table, *Str,
4054
(!db ? rights : 0), revoke_grant, create_new_users,
4055
test(thd->variables.sql_mode &
4056
MODE_NO_AUTO_CREATE_USER)))
4060
ulong db_rights= rights & DB_ACLS;
4061
if (db_rights == rights)
4063
if (replace_db_table(tables[1].table, db, *Str, db_rights,
4069
my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
4075
if (replace_proxies_priv_table (thd, tables[1].table, Str, proxied_user,
4076
rights & GRANT_ACL ? TRUE : FALSE,
4081
mysql_mutex_unlock(&acl_cache->lock);
4085
result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
4088
mysql_rwlock_unlock(&LOCK_grant);
4092
/* Restore the state of binlog format */
4093
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4094
if (save_binlog_row_based)
4095
thd->set_current_stmt_binlog_format_row();
4097
DBUG_RETURN(result);
4101
/* Free grant array if possible */
4103
void grant_free(void)
4105
DBUG_ENTER("grant_free");
4106
my_hash_free(&column_priv_hash);
4107
my_hash_free(&proc_priv_hash);
4108
my_hash_free(&func_priv_hash);
4109
free_root(&memex,MYF(0));
4115
@brief Initialize structures responsible for table/column-level privilege
4116
checking and load information for them from tables in the 'mysql' database.
4118
@return Error status
4120
@retval 1 Could not initialize grant subsystem.
4123
my_bool grant_init()
4127
DBUG_ENTER("grant_init");
4129
if (!(thd= new THD))
4130
DBUG_RETURN(1); /* purecov: deadcode */
4131
thd->thread_stack= (char*) &thd;
4132
thd->store_globals();
4133
return_val= grant_reload(thd);
4135
/* Remember that we don't have a THD */
4136
my_pthread_setspecific_ptr(THR_THD, 0);
4137
DBUG_RETURN(return_val);
4142
@brief Helper function to grant_reload_procs_priv
4144
Reads the procs_priv table into memory hash.
4146
@param table A pointer to the procs_priv table structure.
4149
@see grant_reload_procs_priv
4152
@retval TRUE An error occurred
4153
@retval FALSE Success
4156
static my_bool grant_load_procs_priv(TABLE *p_table)
4158
MEM_ROOT *memex_ptr;
4159
my_bool return_val= 1;
4160
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
4161
MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
4163
DBUG_ENTER("grant_load_procs_priv");
4164
(void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
4165
0,0,0, (my_hash_get_key) get_grant_table,
4167
(void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
4168
0,0,0, (my_hash_get_key) get_grant_table,
4170
p_table->file->ha_index_init(0, 1);
4171
p_table->use_all_columns();
4173
if (!p_table->file->index_first(p_table->record[0]))
4176
my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
4179
GRANT_NAME *mem_check;
4181
if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE)))
4183
/* This could only happen if we are out memory */
4187
if (check_no_resolve)
4189
if (hostname_requires_resolving(mem_check->host.hostname))
4191
sql_print_warning("'procs_priv' entry '%s %s@%s' "
4192
"ignored in --skip-name-resolve mode.",
4193
mem_check->tname, mem_check->user,
4194
mem_check->host.hostname ?
4195
mem_check->host.hostname : "");
4199
if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
4201
hash= &proc_priv_hash;
4204
if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
4206
hash= &func_priv_hash;
4210
sql_print_warning("'procs_priv' entry '%s' "
4211
"ignored, bad routine type",
4216
mem_check->privs= fix_rights_for_procedure(mem_check->privs);
4217
if (! mem_check->ok())
4219
else if (my_hash_insert(hash, (uchar*) mem_check))
4225
while (!p_table->file->index_next(p_table->record[0]));
4231
p_table->file->ha_index_end();
4232
my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
4233
DBUG_RETURN(return_val);
4238
@brief Initialize structures responsible for table/column-level privilege
4239
checking and load information about grants from open privilege tables.
4241
@param thd Current thread
4242
@param tables List containing open "mysql.tables_priv" and
4243
"mysql.columns_priv" tables.
4248
@retval FALSE Success
4252
static my_bool grant_load(THD *thd, TABLE_LIST *tables)
4254
MEM_ROOT *memex_ptr;
4255
my_bool return_val= 1;
4256
TABLE *t_table= 0, *c_table= 0;
4257
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
4258
MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
4260
ulong old_sql_mode= thd->variables.sql_mode;
4261
DBUG_ENTER("grant_load");
4263
thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
4265
(void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
4266
0,0,0, (my_hash_get_key) get_grant_table,
4267
(my_hash_free_key) free_grant_table,0);
4269
t_table = tables[0].table;
4270
c_table = tables[1].table;
4271
t_table->file->ha_index_init(0, 1);
4272
t_table->use_all_columns();
4273
c_table->use_all_columns();
4275
if (!t_table->file->index_first(t_table->record[0]))
4278
my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
4281
GRANT_TABLE *mem_check;
4282
if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table)))
4284
/* This could only happen if we are out memory */
4288
if (check_no_resolve)
4290
if (hostname_requires_resolving(mem_check->host.hostname))
4292
sql_print_warning("'tables_priv' entry '%s %s@%s' "
4293
"ignored in --skip-name-resolve mode.",
4295
mem_check->user ? mem_check->user : "",
4296
mem_check->host.hostname ?
4297
mem_check->host.hostname : "");
4302
if (! mem_check->ok())
4304
else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
4310
while (!t_table->file->index_next(t_table->record[0]));
4313
return_val=0; // Return ok
4316
thd->variables.sql_mode= old_sql_mode;
4317
t_table->file->ha_index_end();
4318
my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
4319
DBUG_RETURN(return_val);
4324
@brief Helper function to grant_reload. Reloads procs_priv table is it
4327
@param thd A pointer to the thread handler object.
4332
@retval FALSE Success
4333
@retval TRUE An error has occurred.
4336
static my_bool grant_reload_procs_priv(THD *thd)
4338
HASH old_proc_priv_hash, old_func_priv_hash;
4340
my_bool return_val= FALSE;
4341
DBUG_ENTER("grant_reload_procs_priv");
4343
table.init_one_table("mysql", 5, "procs_priv",
4344
strlen("procs_priv"), "procs_priv",
4346
table.open_type= OT_BASE_ONLY;
4348
if (open_and_lock_tables(thd, &table, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
4351
mysql_rwlock_wrlock(&LOCK_grant);
4352
/* Save a copy of the current hash if we need to undo the grant load */
4353
old_proc_priv_hash= proc_priv_hash;
4354
old_func_priv_hash= func_priv_hash;
4356
if ((return_val= grant_load_procs_priv(table.table)))
4358
/* Error; Reverting to old hash */
4359
DBUG_PRINT("error",("Reverting to old privileges"));
4361
proc_priv_hash= old_proc_priv_hash;
4362
func_priv_hash= old_func_priv_hash;
4366
my_hash_free(&old_proc_priv_hash);
4367
my_hash_free(&old_func_priv_hash);
4369
mysql_rwlock_unlock(&LOCK_grant);
4371
close_mysql_tables(thd);
4372
DBUG_RETURN(return_val);
4377
@brief Reload information about table and column level privileges if possible
4379
@param thd Current thread
4381
Locked tables are checked by acl_reload() and doesn't have to be checked
4383
This function is also used for initialization of structures responsible
4384
for table/column-level privilege checking.
4387
@retval FALSE Success
4391
my_bool grant_reload(THD *thd)
4393
TABLE_LIST tables[2];
4394
HASH old_column_priv_hash;
4396
my_bool return_val= 1;
4397
DBUG_ENTER("grant_reload");
4399
/* Don't do anything if running with --skip-grant-tables */
4403
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
4404
C_STRING_WITH_LEN("tables_priv"),
4405
"tables_priv", TL_READ);
4406
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
4407
C_STRING_WITH_LEN("columns_priv"),
4408
"columns_priv", TL_READ);
4409
tables[0].next_local= tables[0].next_global= tables+1;
4410
tables[0].open_type= tables[1].open_type= OT_BASE_ONLY;
4413
To avoid deadlocks we should obtain table locks before
4414
obtaining LOCK_grant rwlock.
4416
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
4419
mysql_rwlock_wrlock(&LOCK_grant);
4420
old_column_priv_hash= column_priv_hash;
4423
Create a new memory pool but save the current memory pool to make an undo
4424
opertion possible in case of failure.
4427
init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
4429
if ((return_val= grant_load(thd, tables)))
4430
{ // Error. Revert to old hash
4431
DBUG_PRINT("error",("Reverting to old privileges"));
4432
grant_free(); /* purecov: deadcode */
4433
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
4434
memex= old_mem; /* purecov: deadcode */
4438
my_hash_free(&old_column_priv_hash);
4439
free_root(&old_mem,MYF(0));
4441
mysql_rwlock_unlock(&LOCK_grant);
4442
close_mysql_tables(thd);
4445
It is OK failing to load procs_priv table because we may be
4446
working with 4.1 privilege tables.
4448
if (grant_reload_procs_priv(thd))
4451
mysql_rwlock_wrlock(&LOCK_grant);
4453
mysql_rwlock_unlock(&LOCK_grant);
4456
DBUG_RETURN(return_val);
4461
@brief Check table level grants
4463
@param thd Thread handler
4464
@param want_access Bits of privileges user needs to have.
4465
@param tables List of tables to check. The user should have
4466
'want_access' to all tables in list.
4467
@param any_combination_will_do TRUE if it's enough to have any privilege for
4468
any combination of the table columns.
4469
@param number Check at most this number of tables.
4470
@param no_errors TRUE if no error should be sent directly to the client.
4472
If table->grant.want_privilege != 0 then the requested privileges where
4473
in the set of COL_ACLS but access was not granted on the table level. As
4474
a consequence an extra check of column privileges is required.
4476
Specifically if this function returns FALSE the user has some kind of
4477
privilege on a combination of columns in each table.
4479
This function is usually preceeded by check_access which establish the
4480
User-, Db- and Host access rights.
4483
@see check_table_access
4485
@note This functions assumes that either number of tables to be inspected
4486
by it is limited explicitly (i.e. is is not UINT_MAX) or table list
4487
used and thd->lex->query_tables_own_last value correspond to each
4488
other (the latter should be either 0 or point to next_global member
4489
of one of elements of this table list).
4491
@return Access status
4492
@retval FALSE Access granted; But column privileges might need to be
4494
@retval TRUE The user did not have the requested privileges on any of the
4499
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
4500
bool any_combination_will_do, uint number, bool no_errors)
4503
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
4504
Security_context *sctx= thd->security_ctx;
4506
ulong orig_want_access= want_access;
4507
DBUG_ENTER("check_grant");
4508
DBUG_ASSERT(number > 0);
4511
Walk through the list of tables that belong to the query and save the
4512
requested access (orig_want_privilege) to be able to use it when
4513
checking access rights to the underlying tables of a view. Our grant
4514
system gradually eliminates checked bits from want_privilege and thus
4515
after all checks are done we can no longer use it.
4516
The check that first_not_own_table is not reached is for the case when
4517
the given table list refers to the list for prelocking (contains tables
4518
of other queries). For simple queries first_not_own_table is 0.
4520
for (i= 0, tl= tables;
4521
i < number && tl != first_not_own_table;
4522
tl= tl->next_global, i++)
4525
Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
4526
It will be checked during making view.
4528
tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
4531
mysql_rwlock_rdlock(&LOCK_grant);
4533
tl && number-- && tl != first_not_own_table;
4534
tl= tl->next_global)
4536
sctx = test(tl->security_ctx) ? tl->security_ctx : thd->security_ctx;
4538
const ACL_internal_table_access *access=
4539
get_cached_table_access(&tl->grant.m_internal,
4541
tl->get_table_name());
4545
switch(access->check(orig_want_access, &tl->grant.privilege))
4547
case ACL_INTERNAL_ACCESS_GRANTED:
4550
- the information_schema does not subclass ACL_internal_table_access,
4551
there are no per table privilege checks for I_S,
4552
- the performance schema does use per tables checks, but at most
4553
returns 'CHECK_GRANT', and never 'ACCESS_GRANTED'.
4554
so this branch is not used.
4557
case ACL_INTERNAL_ACCESS_DENIED:
4559
case ACL_INTERNAL_ACCESS_CHECK_GRANT:
4564
want_access= orig_want_access;
4565
want_access&= ~sctx->master_access;
4569
if (!(~tl->grant.privilege & want_access) ||
4570
tl->is_anonymous_derived_table() || tl->schema_table)
4573
It is subquery in the FROM clause. VIEW set tl->derived after
4574
table opening, but this function always called before table opening.
4576
if (!tl->referencing_view)
4579
If it's a temporary table created for a subquery in the FROM
4580
clause, or an INFORMATION_SCHEMA table, drop the request for
4583
tl->grant.want_privilege= 0;
4587
GRANT_TABLE *grant_table= table_hash_search(sctx->host, sctx->ip,
4590
tl->get_table_name(),
4595
want_access &= ~tl->grant.privilege;
4596
goto err; // No grants
4600
For SHOW COLUMNS, SHOW INDEX it is enough to have some
4601
privileges on any column combination on the table.
4603
if (any_combination_will_do)
4606
tl->grant.grant_table= grant_table; // Remember for column test
4607
tl->grant.version= grant_version;
4608
tl->grant.privilege|= grant_table->privs;
4609
tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege);
4611
if (!(~tl->grant.privilege & want_access))
4614
if (want_access & ~(grant_table->cols | tl->grant.privilege))
4616
want_access &= ~(grant_table->cols | tl->grant.privilege);
4617
goto err; // impossible
4620
mysql_rwlock_unlock(&LOCK_grant);
4624
mysql_rwlock_unlock(&LOCK_grant);
4625
if (!no_errors) // Not a silent skip of table
4628
get_privilege_desc(command, sizeof(command), want_access);
4629
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
4633
tl ? tl->get_table_name() : "unknown");
4640
Check column rights in given security context
4643
check_grant_column()
4645
grant grant information structure
4647
table_name table name
4649
length column name length
4650
sctx security context
4657
bool check_grant_column(THD *thd, GRANT_INFO *grant,
4658
const char *db_name, const char *table_name,
4659
const char *name, uint length, Security_context *sctx)
4661
GRANT_TABLE *grant_table;
4662
GRANT_COLUMN *grant_column;
4663
ulong want_access= grant->want_privilege & ~grant->privilege;
4664
DBUG_ENTER("check_grant_column");
4665
DBUG_PRINT("enter", ("table: %s want_access: %lu", table_name, want_access));
4668
DBUG_RETURN(0); // Already checked
4670
mysql_rwlock_rdlock(&LOCK_grant);
4672
/* reload table if someone has modified any grants */
4674
if (grant->version != grant_version)
4677
table_hash_search(sctx->host, sctx->ip, db_name,
4679
table_name, 0); /* purecov: inspected */
4680
grant->version= grant_version; /* purecov: inspected */
4682
if (!(grant_table= grant->grant_table))
4683
goto err; /* purecov: deadcode */
4685
grant_column=column_hash_search(grant_table, name, length);
4686
if (grant_column && !(~grant_column->rights & want_access))
4688
mysql_rwlock_unlock(&LOCK_grant);
4693
mysql_rwlock_unlock(&LOCK_grant);
4695
get_privilege_desc(command, sizeof(command), want_access);
4696
my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
4707
Check the access right to a column depending on the type of table.
4710
check_column_grant_in_table_ref()
4712
table_ref table reference where to check the field
4713
name name of field to check
4714
length length of name
4717
Check the access rights to a column depending on the type of table
4718
reference where the column is checked. The function provides a
4719
generic interface to check column access rights that hides the
4720
heterogeneity of the column representation - whether it is a view
4721
or a stored table colum.
4728
bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
4729
const char *name, uint length)
4732
const char *db_name;
4733
const char *table_name;
4734
Security_context *sctx= test(table_ref->security_ctx) ?
4735
table_ref->security_ctx : thd->security_ctx;
4737
if (table_ref->view || table_ref->field_translation)
4739
/* View or derived information schema table. */
4741
grant= &(table_ref->grant);
4742
db_name= table_ref->view_db.str;
4743
table_name= table_ref->view_name.str;
4744
if (table_ref->belong_to_view &&
4745
thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
4747
view_privs= get_column_grant(thd, grant, db_name, table_name, name);
4748
if (view_privs & VIEW_ANY_ACL)
4750
table_ref->belong_to_view->allowed_show= TRUE;
4753
table_ref->belong_to_view->allowed_show= FALSE;
4754
my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
4760
/* Normal or temporary table. */
4761
TABLE *table= table_ref->table;
4762
grant= &(table->grant);
4763
db_name= table->s->db.str;
4764
table_name= table->s->table_name.str;
4767
if (grant->want_privilege)
4768
return check_grant_column(thd, grant, db_name, table_name, name,
4777
@brief check if a query can access a set of columns
4779
@param thd the current thread
4780
@param want_access_arg the privileges requested
4781
@param fields an iterator over the fields of a table reference.
4782
@return Operation status
4785
@details This function walks over the columns of a table reference
4786
The columns may originate from different tables, depending on the kind of
4787
table reference, e.g. join, view.
4788
For each table it will retrieve the grant information and will use it
4789
to check the required access privileges for the fields requested from it.
4791
bool check_grant_all_columns(THD *thd, ulong want_access_arg,
4792
Field_iterator_table_ref *fields)
4794
Security_context *sctx= thd->security_ctx;
4795
ulong want_access= want_access_arg;
4796
const char *table_name= NULL;
4798
const char* db_name;
4800
/* Initialized only to make gcc happy */
4801
GRANT_TABLE *grant_table= NULL;
4803
Flag that gets set if privilege checking has to be performed on column
4806
bool using_column_privileges= FALSE;
4808
mysql_rwlock_rdlock(&LOCK_grant);
4810
for (; !fields->end_of_fields(); fields->next())
4812
const char *field_name= fields->name();
4814
if (table_name != fields->get_table_name())
4816
table_name= fields->get_table_name();
4817
db_name= fields->get_db_name();
4818
grant= fields->grant();
4819
/* get a fresh one for each table */
4820
want_access= want_access_arg & ~grant->privilege;
4823
/* reload table if someone has modified any grants */
4824
if (grant->version != grant_version)
4827
table_hash_search(sctx->host, sctx->ip, db_name,
4829
table_name, 0); /* purecov: inspected */
4830
grant->version= grant_version; /* purecov: inspected */
4833
grant_table= grant->grant_table;
4834
DBUG_ASSERT (grant_table);
4840
GRANT_COLUMN *grant_column=
4841
column_hash_search(grant_table, field_name,
4842
(uint) strlen(field_name));
4844
using_column_privileges= TRUE;
4845
if (!grant_column || (~grant_column->rights & want_access))
4849
mysql_rwlock_unlock(&LOCK_grant);
4853
mysql_rwlock_unlock(&LOCK_grant);
4856
get_privilege_desc(command, sizeof(command), want_access);
4858
Do not give an error message listing a column name unless the user has
4859
privilege to see all columns.
4861
if (using_column_privileges)
4862
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
4863
command, sctx->priv_user,
4864
sctx->host_or_ip, table_name);
4866
my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
4876
static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
4878
Security_context *sctx= thd->security_ctx;
4880
for (uint idx= 0; idx < hash->records; ++idx)
4882
GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
4884
if (strcmp(item->user, sctx->priv_user) == 0 &&
4885
strcmp(item->db, db) == 0 &&
4886
compare_hostname(&item->host, sctx->host, sctx->ip))
4897
Check if a user has the right to access a database
4898
Access is accepted if he has a grant for any table/routine in the database
4899
Return 1 if access is denied
4902
bool check_grant_db(THD *thd,const char *db)
4904
Security_context *sctx= thd->security_ctx;
4905
char helping [NAME_LEN+USERNAME_LENGTH+2];
4909
len= (uint) (strmov(strmov(helping, sctx->priv_user) + 1, db) - helping) + 1;
4911
mysql_rwlock_rdlock(&LOCK_grant);
4913
for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
4915
GRANT_TABLE *grant_table= (GRANT_TABLE*)
4916
my_hash_element(&column_priv_hash,
4918
if (len < grant_table->key_length &&
4919
!memcmp(grant_table->hash_key,helping,len) &&
4920
compare_hostname(&grant_table->host, sctx->host, sctx->ip))
4922
error= FALSE; /* Found match. */
4928
error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
4929
check_grant_db_routine(thd, db, &func_priv_hash);
4931
mysql_rwlock_unlock(&LOCK_grant);
4937
/****************************************************************************
4938
Check routine level grants
4941
bool check_grant_routine()
4943
want_access Bits of privileges user needs to have
4944
procs List of routines to check. The user should have 'want_access'
4945
is_proc True if the list is all procedures, else functions
4946
no_errors If 0 then we write an error. The error is sent directly to
4951
1 Error: User did not have the requested privielges
4952
****************************************************************************/
4954
bool check_grant_routine(THD *thd, ulong want_access,
4955
TABLE_LIST *procs, bool is_proc, bool no_errors)
4958
Security_context *sctx= thd->security_ctx;
4959
char *user= sctx->priv_user;
4960
char *host= sctx->priv_host;
4961
DBUG_ENTER("check_grant_routine");
4963
want_access&= ~sctx->master_access;
4965
DBUG_RETURN(0); // ok
4967
mysql_rwlock_rdlock(&LOCK_grant);
4968
for (table= procs; table; table= table->next_global)
4970
GRANT_NAME *grant_proc;
4971
if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
4972
table->table_name, is_proc, 0)))
4973
table->grant.privilege|= grant_proc->privs;
4975
if (want_access & ~table->grant.privilege)
4977
want_access &= ~table->grant.privilege;
4981
mysql_rwlock_unlock(&LOCK_grant);
4984
mysql_rwlock_unlock(&LOCK_grant);
4988
const char *command="";
4990
strxmov(buff, table->db, ".", table->table_name, NullS);
4991
if (want_access & EXECUTE_ACL)
4993
else if (want_access & ALTER_PROC_ACL)
4994
command= "alter routine";
4995
else if (want_access & GRANT_ACL)
4997
my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
4998
command, user, host, table ? buff : "unknown");
5005
Check if routine has any of the
5006
routine level grants
5009
bool check_routine_level_acl()
5019
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
5022
bool no_routine_acl= 1;
5023
GRANT_NAME *grant_proc;
5024
Security_context *sctx= thd->security_ctx;
5025
mysql_rwlock_rdlock(&LOCK_grant);
5026
if ((grant_proc= routine_hash_search(sctx->priv_host,
5030
no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
5031
mysql_rwlock_unlock(&LOCK_grant);
5032
return no_routine_acl;
5036
/*****************************************************************************
5037
Functions to retrieve the grant for a table/column (for SHOW functions)
5038
*****************************************************************************/
5040
ulong get_table_grant(THD *thd, TABLE_LIST *table)
5043
Security_context *sctx= thd->security_ctx;
5044
const char *db = table->db ? table->db : thd->db;
5045
GRANT_TABLE *grant_table;
5047
mysql_rwlock_rdlock(&LOCK_grant);
5048
#ifdef EMBEDDED_LIBRARY
5051
grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
5052
table->table_name, 0);
5054
table->grant.grant_table=grant_table; // Remember for column test
5055
table->grant.version=grant_version;
5057
table->grant.privilege|= grant_table->privs;
5058
privilege= table->grant.privilege;
5059
mysql_rwlock_unlock(&LOCK_grant);
5065
Determine the access priviliges for a field.
5070
grant grants table descriptor
5071
db_name name of database that the field belongs to
5072
table_name name of table that the field belongs to
5073
field_name name of field
5076
The procedure may also modify: grant->grant_table and grant->version.
5079
The access priviliges for the field db_name.table_name.field_name
5082
ulong get_column_grant(THD *thd, GRANT_INFO *grant,
5083
const char *db_name, const char *table_name,
5084
const char *field_name)
5086
GRANT_TABLE *grant_table;
5087
GRANT_COLUMN *grant_column;
5090
mysql_rwlock_rdlock(&LOCK_grant);
5091
/* reload table if someone has modified any grants */
5092
if (grant->version != grant_version)
5094
Security_context *sctx= thd->security_ctx;
5096
table_hash_search(sctx->host, sctx->ip,
5097
db_name, sctx->priv_user,
5098
table_name, 0); /* purecov: inspected */
5099
grant->version= grant_version; /* purecov: inspected */
5102
if (!(grant_table= grant->grant_table))
5103
priv= grant->privilege;
5106
grant_column= column_hash_search(grant_table, field_name,
5107
(uint) strlen(field_name));
5109
priv= (grant->privilege | grant_table->privs);
5111
priv= (grant->privilege | grant_table->privs | grant_column->rights);
5113
mysql_rwlock_unlock(&LOCK_grant);
5118
/* Help function for mysql_show_grants */
5120
static void add_user_option(String *grant, ulong value, const char *name)
5124
char buff[22], *p; // just as in int2str
5126
grant->append(name, strlen(name));
5128
p=int10_to_str(value, buff, 10);
5129
grant->append(buff,p-buff);
5133
static const char *command_array[]=
5135
"SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
5136
"SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
5137
"ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
5138
"LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
5139
"CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
5140
"CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE"
5143
static uint command_lengths[]=
5145
6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
5146
14, 13, 11, 5, 7, 17
5150
static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
5151
const char *type, int typelen,
5152
char *buff, int buffsize);
5156
SHOW GRANTS; Send grants for a user to the client
5159
Send to client grant-like strings depicting user@host privileges
5162
bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
5170
Protocol *protocol= thd->protocol;
5171
DBUG_ENTER("mysql_show_grants");
5173
LINT_INIT(acl_user);
5176
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
5180
mysql_rwlock_rdlock(&LOCK_grant);
5181
mysql_mutex_lock(&acl_cache->lock);
5183
acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE);
5186
mysql_mutex_unlock(&acl_cache->lock);
5187
mysql_rwlock_unlock(&LOCK_grant);
5189
my_error(ER_NONEXISTING_GRANT, MYF(0),
5190
lex_user->user.str, lex_user->host.str);
5194
Item_string *field=new Item_string("",0,&my_charset_latin1);
5195
List<Item> field_list;
5197
field->max_length=1024;
5198
strxmov(buff,"Grants for ",lex_user->user.str,"@",
5199
lex_user->host.str,NullS);
5200
field_list.push_back(field);
5201
if (protocol->send_result_set_metadata(&field_list,
5202
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
5204
mysql_mutex_unlock(&acl_cache->lock);
5205
mysql_rwlock_unlock(&LOCK_grant);
5210
/* Add first global access grants */
5212
String global(buff,sizeof(buff),system_charset_info);
5214
global.append(STRING_WITH_LEN("GRANT "));
5216
want_access= acl_user->access;
5217
if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
5218
global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
5219
else if (!(want_access & ~GRANT_ACL))
5220
global.append(STRING_WITH_LEN("USAGE"));
5224
ulong j,test_access= want_access & ~GRANT_ACL;
5225
for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
5227
if (test_access & j)
5230
global.append(STRING_WITH_LEN(", "));
5232
global.append(command_array[counter],command_lengths[counter]);
5236
global.append (STRING_WITH_LEN(" ON *.* TO '"));
5237
global.append(lex_user->user.str, lex_user->user.length,
5238
system_charset_info);
5239
global.append (STRING_WITH_LEN("'@'"));
5240
global.append(lex_user->host.str,lex_user->host.length,
5241
system_charset_info);
5242
global.append ('\'');
5243
if (acl_user->salt_len)
5245
char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
5246
if (acl_user->salt_len == SCRAMBLE_LENGTH)
5247
make_password_from_salt(passwd_buff, acl_user->salt);
5249
make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt);
5250
global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
5251
global.append(passwd_buff);
5252
global.append('\'');
5254
/* "show grants" SSL related stuff */
5255
if (acl_user->ssl_type == SSL_TYPE_ANY)
5256
global.append(STRING_WITH_LEN(" REQUIRE SSL"));
5257
else if (acl_user->ssl_type == SSL_TYPE_X509)
5258
global.append(STRING_WITH_LEN(" REQUIRE X509"));
5259
else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
5261
int ssl_options = 0;
5262
global.append(STRING_WITH_LEN(" REQUIRE "));
5263
if (acl_user->x509_issuer)
5266
global.append(STRING_WITH_LEN("ISSUER \'"));
5267
global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
5268
global.append('\'');
5270
if (acl_user->x509_subject)
5274
global.append(STRING_WITH_LEN("SUBJECT \'"));
5275
global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
5276
system_charset_info);
5277
global.append('\'');
5279
if (acl_user->ssl_cipher)
5283
global.append(STRING_WITH_LEN("CIPHER '"));
5284
global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
5285
system_charset_info);
5286
global.append('\'');
5289
if ((want_access & GRANT_ACL) ||
5290
(acl_user->user_resource.questions ||
5291
acl_user->user_resource.updates ||
5292
acl_user->user_resource.conn_per_hour ||
5293
acl_user->user_resource.user_conn))
5295
global.append(STRING_WITH_LEN(" WITH"));
5296
if (want_access & GRANT_ACL)
5297
global.append(STRING_WITH_LEN(" GRANT OPTION"));
5298
add_user_option(&global, acl_user->user_resource.questions,
5299
"MAX_QUERIES_PER_HOUR");
5300
add_user_option(&global, acl_user->user_resource.updates,
5301
"MAX_UPDATES_PER_HOUR");
5302
add_user_option(&global, acl_user->user_resource.conn_per_hour,
5303
"MAX_CONNECTIONS_PER_HOUR");
5304
add_user_option(&global, acl_user->user_resource.user_conn,
5305
"MAX_USER_CONNECTIONS");
5307
protocol->prepare_for_resend();
5308
protocol->store(global.ptr(),global.length(),global.charset());
5309
if (protocol->write())
5316
/* Add database access */
5317
for (counter=0 ; counter < acl_dbs.elements ; counter++)
5319
const char *user, *host;
5321
acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
5322
if (!(user=acl_db->user))
5324
if (!(host=acl_db->host.hostname))
5328
We do not make SHOW GRANTS case-sensitive here (like REVOKE),
5329
but make it case-insensitive because that's the way they are
5330
actually applied, and showing fewer privileges than are applied
5331
would be wrong from a security point of view.
5334
if (!strcmp(lex_user->user.str,user) &&
5335
!my_strcasecmp(system_charset_info, lex_user->host.str, host))
5337
want_access=acl_db->access;
5340
String db(buff,sizeof(buff),system_charset_info);
5342
db.append(STRING_WITH_LEN("GRANT "));
5344
if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
5345
db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
5346
else if (!(want_access & ~GRANT_ACL))
5347
db.append(STRING_WITH_LEN("USAGE"));
5351
ulong j,test_access= want_access & ~GRANT_ACL;
5352
for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
5354
if (test_access & j)
5357
db.append(STRING_WITH_LEN(", "));
5359
db.append(command_array[cnt],command_lengths[cnt]);
5363
db.append (STRING_WITH_LEN(" ON "));
5364
append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
5365
db.append (STRING_WITH_LEN(".* TO '"));
5366
db.append(lex_user->user.str, lex_user->user.length,
5367
system_charset_info);
5368
db.append (STRING_WITH_LEN("'@'"));
5369
// host and lex_user->host are equal except for case
5370
db.append(host, strlen(host), system_charset_info);
5372
if (want_access & GRANT_ACL)
5373
db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
5374
protocol->prepare_for_resend();
5375
protocol->store(db.ptr(),db.length(),db.charset());
5376
if (protocol->write())
5385
/* Add table & column access */
5386
for (index=0 ; index < column_priv_hash.records ; index++)
5388
const char *user, *host;
5389
GRANT_TABLE *grant_table= (GRANT_TABLE*)
5390
my_hash_element(&column_priv_hash, index);
5392
if (!(user=grant_table->user))
5394
if (!(host= grant_table->host.hostname))
5398
We do not make SHOW GRANTS case-sensitive here (like REVOKE),
5399
but make it case-insensitive because that's the way they are
5400
actually applied, and showing fewer privileges than are applied
5401
would be wrong from a security point of view.
5404
if (!strcmp(lex_user->user.str,user) &&
5405
!my_strcasecmp(system_charset_info, lex_user->host.str, host))
5407
ulong table_access= grant_table->privs;
5408
if ((table_access | grant_table->cols) != 0)
5410
String global(buff, sizeof(buff), system_charset_info);
5411
ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;
5414
global.append(STRING_WITH_LEN("GRANT "));
5416
if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
5417
global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
5418
else if (!test_access)
5419
global.append(STRING_WITH_LEN("USAGE"));
5422
/* Add specific column access */
5426
for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
5428
if (test_access & j)
5431
global.append(STRING_WITH_LEN(", "));
5433
global.append(command_array[counter],command_lengths[counter]);
5435
if (grant_table->cols)
5438
for (uint col_index=0 ;
5439
col_index < grant_table->hash_columns.records ;
5442
GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
5443
my_hash_element(&grant_table->hash_columns,col_index);
5444
if (grant_column->rights & j)
5450
If we have a duplicated table level privilege, we
5451
must write the access privilege name again.
5453
if (table_access & j)
5455
global.append(STRING_WITH_LEN(", "));
5456
global.append(command_array[counter],
5457
command_lengths[counter]);
5459
global.append(STRING_WITH_LEN(" ("));
5462
global.append(STRING_WITH_LEN(", "));
5463
global.append(grant_column->column,
5464
grant_column->key_length,
5465
system_charset_info);
5474
global.append(STRING_WITH_LEN(" ON "));
5475
append_identifier(thd, &global, grant_table->db,
5476
strlen(grant_table->db));
5478
append_identifier(thd, &global, grant_table->tname,
5479
strlen(grant_table->tname));
5480
global.append(STRING_WITH_LEN(" TO '"));
5481
global.append(lex_user->user.str, lex_user->user.length,
5482
system_charset_info);
5483
global.append(STRING_WITH_LEN("'@'"));
5484
// host and lex_user->host are equal except for case
5485
global.append(host, strlen(host), system_charset_info);
5486
global.append('\'');
5487
if (table_access & GRANT_ACL)
5488
global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
5489
protocol->prepare_for_resend();
5490
protocol->store(global.ptr(),global.length(),global.charset());
5491
if (protocol->write())
5500
if (show_routine_grants(thd, lex_user, &proc_priv_hash,
5501
STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
5507
if (show_routine_grants(thd, lex_user, &func_priv_hash,
5508
STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
5514
if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
5521
mysql_mutex_unlock(&acl_cache->lock);
5522
mysql_rwlock_unlock(&LOCK_grant);
5528
static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
5529
const char *type, int typelen,
5530
char *buff, int buffsize)
5532
uint counter, index;
5534
Protocol *protocol= thd->protocol;
5535
/* Add routine access */
5536
for (index=0 ; index < hash->records ; index++)
5538
const char *user, *host;
5539
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
5541
if (!(user=grant_proc->user))
5543
if (!(host= grant_proc->host.hostname))
5547
We do not make SHOW GRANTS case-sensitive here (like REVOKE),
5548
but make it case-insensitive because that's the way they are
5549
actually applied, and showing fewer privileges than are applied
5550
would be wrong from a security point of view.
5553
if (!strcmp(lex_user->user.str,user) &&
5554
!my_strcasecmp(system_charset_info, lex_user->host.str, host))
5556
ulong proc_access= grant_proc->privs;
5557
if (proc_access != 0)
5559
String global(buff, buffsize, system_charset_info);
5560
ulong test_access= proc_access & ~GRANT_ACL;
5563
global.append(STRING_WITH_LEN("GRANT "));
5566
global.append(STRING_WITH_LEN("USAGE"));
5569
/* Add specific procedure access */
5573
for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
5575
if (test_access & j)
5578
global.append(STRING_WITH_LEN(", "));
5580
global.append(command_array[counter],command_lengths[counter]);
5584
global.append(STRING_WITH_LEN(" ON "));
5585
global.append(type,typelen);
5587
append_identifier(thd, &global, grant_proc->db,
5588
strlen(grant_proc->db));
5590
append_identifier(thd, &global, grant_proc->tname,
5591
strlen(grant_proc->tname));
5592
global.append(STRING_WITH_LEN(" TO '"));
5593
global.append(lex_user->user.str, lex_user->user.length,
5594
system_charset_info);
5595
global.append(STRING_WITH_LEN("'@'"));
5596
// host and lex_user->host are equal except for case
5597
global.append(host, strlen(host), system_charset_info);
5598
global.append('\'');
5599
if (proc_access & GRANT_ACL)
5600
global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
5601
protocol->prepare_for_resend();
5602
protocol->store(global.ptr(),global.length(),global.charset());
5603
if (protocol->write())
5615
Make a clear-text version of the requested privilege.
5618
void get_privilege_desc(char *to, uint max_length, ulong access)
5622
DBUG_ASSERT(max_length >= 30); // For end ',' removal
5626
max_length--; // Reserve place for end-zero
5627
for (pos=0 ; access ; pos++, access>>=1)
5630
command_lengths[pos] + (uint) (to-start) < max_length)
5632
to= strmov(to, command_array[pos]);
5636
to--; // Remove end ','
5642
void get_mqh(const char *user, const char *host, USER_CONN *uc)
5646
mysql_mutex_lock(&acl_cache->lock);
5648
if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
5649
uc->user_resources= acl_user->user_resource;
5651
bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
5653
mysql_mutex_unlock(&acl_cache->lock);
5657
Open the grant tables.
5661
thd The current thread.
5662
tables (out) The 4 elements array for the opened tables.
5665
Tables are numbered as follows:
5672
1 Skip GRANT handling during replication.
5677
#define GRANT_TABLES 6
5678
int open_grant_tables(THD *thd, TABLE_LIST *tables)
5680
DBUG_ENTER("open_grant_tables");
5684
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
5688
tables->init_one_table(C_STRING_WITH_LEN("mysql"),
5689
C_STRING_WITH_LEN("user"), "user", TL_WRITE);
5690
(tables+1)->init_one_table(C_STRING_WITH_LEN("mysql"),
5691
C_STRING_WITH_LEN("db"), "db", TL_WRITE);
5692
(tables+2)->init_one_table(C_STRING_WITH_LEN("mysql"),
5693
C_STRING_WITH_LEN("tables_priv"),
5694
"tables_priv", TL_WRITE);
5695
(tables+3)->init_one_table(C_STRING_WITH_LEN("mysql"),
5696
C_STRING_WITH_LEN("columns_priv"),
5697
"columns_priv", TL_WRITE);
5698
(tables+4)->init_one_table(C_STRING_WITH_LEN("mysql"),
5699
C_STRING_WITH_LEN("procs_priv"),
5700
"procs_priv", TL_WRITE);
5701
(tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"),
5702
C_STRING_WITH_LEN("proxies_priv"),
5703
"proxies_priv", TL_WRITE);
5704
tables[5].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
5706
tables->next_local= tables->next_global= tables + 1;
5707
(tables+1)->next_local= (tables+1)->next_global= tables + 2;
5708
(tables+2)->next_local= (tables+2)->next_global= tables + 3;
5709
(tables+3)->next_local= (tables+3)->next_global= tables + 4;
5710
(tables+4)->next_local= (tables+4)->next_global= tables + 5;
5712
#ifdef HAVE_REPLICATION
5714
GRANT and REVOKE are applied the slave in/exclusion rules as they are
5715
some kind of updates to the mysql.% tables.
5717
if (thd->slave_thread && rpl_filter->is_on())
5720
The tables must be marked "updating" so that tables_ok() takes them into
5723
tables[0].updating= tables[1].updating= tables[2].updating=
5724
tables[3].updating= tables[4].updating= tables[5].updating= 1;
5725
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
5727
tables[0].updating= tables[1].updating= tables[2].updating=
5728
tables[3].updating= tables[4].updating= tables[5].updating= 0;
5732
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5733
{ // This should never happen
5740
ACL_USER *check_acl_user(LEX_USER *user_name,
5741
uint *acl_acl_userdx)
5743
ACL_USER *acl_user= 0;
5746
mysql_mutex_assert_owner(&acl_cache->lock);
5748
for (counter= 0 ; counter < acl_users.elements ; counter++)
5750
const char *user,*host;
5751
acl_user= dynamic_element(&acl_users, counter, ACL_USER*);
5752
if (!(user=acl_user->user))
5754
if (!(host=acl_user->host.hostname))
5756
if (!strcmp(user_name->user.str,user) &&
5757
!my_strcasecmp(system_charset_info, user_name->host.str, host))
5760
if (counter == acl_users.elements)
5763
*acl_acl_userdx= counter;
5768
Modify a privilege table.
5771
modify_grant_table()
5772
table The table to modify.
5773
host_field The host name field.
5774
user_field The user name field.
5775
user_to The new name for the user if to be renamed,
5779
Update user/host in the current record if user_to is not NULL.
5780
Delete the current record if user_to is NULL.
5787
static int modify_grant_table(TABLE *table, Field *host_field,
5788
Field *user_field, LEX_USER *user_to)
5791
DBUG_ENTER("modify_grant_table");
5796
store_record(table, record[1]);
5797
host_field->store(user_to->host.str, user_to->host.length,
5798
system_charset_info);
5799
user_field->store(user_to->user.str, user_to->user.length,
5800
system_charset_info);
5801
if ((error= table->file->ha_update_row(table->record[1],
5802
table->record[0])) &&
5803
error != HA_ERR_RECORD_IS_THE_SAME)
5804
table->file->print_error(error, MYF(0));
5811
if ((error=table->file->ha_delete_row(table->record[0])))
5812
table->file->print_error(error, MYF(0));
5819
Handle a privilege table.
5822
handle_grant_table()
5823
tables The array with the four open tables.
5824
table_no The number of the table to handle (0..4).
5825
drop If user_from is to be dropped.
5826
user_from The the user to be searched/dropped/renamed.
5827
user_to The new name for the user if to be renamed,
5831
Scan through all records in a grant table and apply the requested
5832
operation. For the "user" table, a single index access is sufficient,
5833
since there is an unique index on (host, user).
5834
Delete from grant table if drop is true.
5835
Update in grant table if drop is false and user_to is not NULL.
5836
Search in grant table if drop is false and user_to is NULL.
5837
Tables are numbered as follows:
5845
> 0 At least one record matched.
5846
0 OK, but no record matched.
5850
static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
5851
LEX_USER *user_from, LEX_USER *user_to)
5855
TABLE *table= tables[table_no].table;
5856
Field *host_field= table->field[0];
5857
Field *user_field= table->field[table_no && table_no != 5 ? 2 : 1];
5858
char *host_str= user_from->host.str;
5859
char *user_str= user_from->user.str;
5862
uchar user_key[MAX_KEY_LENGTH];
5863
uint key_prefix_length;
5864
DBUG_ENTER("handle_grant_table");
5865
THD *thd= current_thd;
5867
table->use_all_columns();
5868
if (! table_no) // mysql.user table
5871
The 'user' table has an unique index on (host, user).
5872
Thus, we can handle everything with a single index access.
5873
The host- and user fields are consecutive in the user table records.
5874
So we set host- and user fields of table->record[0] and use the
5875
pointer to the host field as key.
5876
index_read_idx() will replace table->record[0] (its first argument)
5877
by the searched record, if it exists.
5879
DBUG_PRINT("info",("read table: '%s' search: '%s'@'%s'",
5880
table->s->table_name.str, user_str, host_str));
5881
host_field->store(host_str, user_from->host.length, system_charset_info);
5882
user_field->store(user_str, user_from->user.length, system_charset_info);
5884
key_prefix_length= (table->key_info->key_part[0].store_length +
5885
table->key_info->key_part[1].store_length);
5886
key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
5888
if ((error= table->file->index_read_idx_map(table->record[0], 0,
5889
user_key, (key_part_map)3,
5890
HA_READ_KEY_EXACT)))
5892
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
5894
table->file->print_error(error, MYF(0));
5900
/* If requested, delete or update the record. */
5901
result= ((drop || user_to) &&
5902
modify_grant_table(table, host_field, user_field, user_to)) ?
5903
-1 : 1; /* Error or found. */
5905
DBUG_PRINT("info",("read result: %d", result));
5910
The non-'user' table do not have indexes on (host, user).
5911
And their host- and user fields are not consecutive.
5912
Thus, we need to do a table scan to find all matching records.
5914
if ((error= table->file->ha_rnd_init(1)))
5916
table->file->print_error(error, MYF(0));
5922
DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'",
5923
table->s->table_name.str, user_str, host_str));
5925
while ((error= table->file->rnd_next(table->record[0])) !=
5930
/* Most probable 'deleted record'. */
5931
DBUG_PRINT("info",("scan error: %d", error));
5934
if (! (host= get_field(thd->mem_root, host_field)))
5936
if (! (user= get_field(thd->mem_root, user_field)))
5942
DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
5944
get_field(thd->mem_root, table->field[1]) /*db*/,
5945
get_field(thd->mem_root, table->field[3]) /*table*/,
5946
get_field(thd->mem_root,
5947
table->field[4]) /*column*/));
5950
if (strcmp(user_str, user) ||
5951
my_strcasecmp(system_charset_info, host_str, host))
5954
/* If requested, delete or update the record. */
5955
result= ((drop || user_to) &&
5956
modify_grant_table(table, host_field, user_field, user_to)) ?
5957
-1 : result ? result : 1; /* Error or keep result or found. */
5958
/* If search is requested, we do not need to search further. */
5959
if (! drop && ! user_to)
5962
(void) table->file->ha_rnd_end();
5963
DBUG_PRINT("info",("scan result: %d", result));
5967
DBUG_RETURN(result);
5972
Handle an in-memory privilege structure.
5974
@param struct_no The number of the structure to handle (0..5).
5975
@param drop If user_from is to be dropped.
5976
@param user_from The the user to be searched/dropped/renamed.
5977
@param user_to The new name for the user if to be renamed, NULL otherwise.
5980
Scan through all elements in an in-memory grant structure and apply
5981
the requested operation.
5982
Delete from grant structure if drop is true.
5983
Update in grant structure if drop is false and user_to is not NULL.
5984
Search in grant structure if drop is false and user_to is NULL.
5985
Structures are numbered as follows:
5993
@retval > 0 At least one element matched.
5994
@retval 0 OK, but no element matched.
5995
@retval -1 Wrong arguments to function.
5998
static int handle_grant_struct(uint struct_no, bool drop,
5999
LEX_USER *user_from, LEX_USER *user_to)
6006
ACL_USER *acl_user= NULL;
6007
ACL_DB *acl_db= NULL;
6008
ACL_PROXY_USER *acl_proxy_user= NULL;
6009
GRANT_NAME *grant_name= NULL;
6010
HASH *grant_name_hash= NULL;
6011
DBUG_ENTER("handle_grant_struct");
6012
DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
6013
struct_no, user_from->user.str, user_from->host.str));
6018
mysql_mutex_assert_owner(&acl_cache->lock);
6020
/* Get the number of elements in the in-memory structure. */
6021
switch (struct_no) {
6023
elements= acl_users.elements;
6026
elements= acl_dbs.elements;
6029
elements= column_priv_hash.records;
6030
grant_name_hash= &column_priv_hash;
6033
elements= proc_priv_hash.records;
6034
grant_name_hash= &proc_priv_hash;
6037
elements= func_priv_hash.records;
6038
grant_name_hash= &func_priv_hash;
6041
elements= acl_proxy_users.elements;
6048
DBUG_PRINT("loop",("scan struct: %u search user: '%s' host: '%s'",
6049
struct_no, user_from->user.str, user_from->host.str));
6051
/* Loop over all elements. */
6052
for (idx= 0; idx < elements; idx++)
6055
Get a pointer to the element.
6057
switch (struct_no) {
6059
acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
6060
user= acl_user->user;
6061
host= acl_user->host.hostname;
6065
acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
6067
host= acl_db->host.hostname;
6073
grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
6074
user= grant_name->user;
6075
host= grant_name->host.hostname;
6079
acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
6080
user= acl_proxy_user->get_user();
6081
host= acl_proxy_user->get_host();
6085
MY_ASSERT_UNREACHABLE();
6093
DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'",
6094
struct_no, idx, user, host));
6096
if (strcmp(user_from->user.str, user) ||
6097
my_strcasecmp(system_charset_info, user_from->host.str, host))
6100
result= 1; /* At least one element found. */
6103
switch ( struct_no ) {
6105
delete_dynamic_element(&acl_users, idx);
6109
delete_dynamic_element(&acl_dbs, idx);
6115
my_hash_delete(grant_name_hash, (uchar*) grant_name);
6119
delete_dynamic_element(&acl_proxy_users, idx);
6125
- If we are iterating through an array then we just have moved all
6126
elements after the current element one position closer to its head.
6127
This means that we have to take another look at the element at
6128
current position as it is a new element from the array's tail.
6129
- If we are iterating through a hash the current element was replaced
6130
with one of elements from the tail. So we also have to take a look
6131
at the new element in current position.
6132
Note that in our HASH implementation hash_delete() won't move any
6133
elements with position after current one to position before the
6134
current (i.e. from the tail to the head), so it is safe to continue
6135
iteration without re-starting.
6141
switch ( struct_no ) {
6143
acl_user->user= strdup_root(&mem, user_to->user.str);
6144
acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
6148
acl_db->user= strdup_root(&mem, user_to->user.str);
6149
acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
6157
Save old hash key and its length to be able properly update
6158
element position in hash.
6160
char *old_key= grant_name->hash_key;
6161
size_t old_key_length= grant_name->key_length;
6164
Update the grant structure with the new user name and host name.
6166
grant_name->set_user_details(user_to->host.str, grant_name->db,
6167
user_to->user.str, grant_name->tname,
6171
Since username is part of the hash key, when the user name
6172
is renamed, the hash key is changed. Update the hash to
6173
ensure that the position matches the new hash key value
6175
my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
6178
hash_update() operation could have moved element from the tail
6179
of the hash to the current position. So we need to take a look
6180
at the element in current position once again.
6181
Thanks to the fact that hash_update() for our HASH implementation
6182
won't move any elements from the tail of the hash to the positions
6183
before the current one (a.k.a. head) it is safe to continue
6184
iteration without restarting.
6191
acl_proxy_user->set_user (&mem, user_to->user.str);
6192
acl_proxy_user->set_host (&mem, user_to->host.str);
6199
/* If search is requested, we do not need to search further. */
6204
DBUG_PRINT("loop",("scan struct: %u result %d", struct_no, result));
6207
DBUG_RETURN(result);
6212
Handle all privilege tables and in-memory privilege structures.
6216
tables The array with the four open tables.
6217
drop If user_from is to be dropped.
6218
user_from The the user to be searched/dropped/renamed.
6219
user_to The new name for the user if to be renamed,
6223
Go through all grant tables and in-memory grant structures and apply
6224
the requested operation.
6225
Delete from grant data if drop is true.
6226
Update in grant data if drop is false and user_to is not NULL.
6227
Search in grant data if drop is false and user_to is NULL.
6230
> 0 At least one element matched.
6231
0 OK, but no element matched.
6235
static int handle_grant_data(TABLE_LIST *tables, bool drop,
6236
LEX_USER *user_from, LEX_USER *user_to)
6240
DBUG_ENTER("handle_grant_data");
6242
/* Handle user table. */
6243
if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
6245
/* Handle of table failed, don't touch the in-memory array. */
6250
/* Handle user array. */
6251
if ((handle_grant_struct(0, drop, user_from, user_to) && ! result) ||
6254
result= 1; /* At least one record/element found. */
6255
/* If search is requested, we do not need to search further. */
6256
if (! drop && ! user_to)
6261
/* Handle db table. */
6262
if ((found= handle_grant_table(tables, 1, drop, user_from, user_to)) < 0)
6264
/* Handle of table failed, don't touch the in-memory array. */
6269
/* Handle db array. */
6270
if (((handle_grant_struct(1, drop, user_from, user_to) && ! result) ||
6273
result= 1; /* At least one record/element found. */
6274
/* If search is requested, we do not need to search further. */
6275
if (! drop && ! user_to)
6280
/* Handle stored routines table. */
6281
if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0)
6283
/* Handle of table failed, don't touch in-memory array. */
6288
/* Handle procs array. */
6289
if (((handle_grant_struct(3, drop, user_from, user_to) && ! result) ||
6292
result= 1; /* At least one record/element found. */
6293
/* If search is requested, we do not need to search further. */
6294
if (! drop && ! user_to)
6297
/* Handle funcs array. */
6298
if (((handle_grant_struct(4, drop, user_from, user_to) && ! result) ||
6301
result= 1; /* At least one record/element found. */
6302
/* If search is requested, we do not need to search further. */
6303
if (! drop && ! user_to)
6308
/* Handle tables table. */
6309
if ((found= handle_grant_table(tables, 2, drop, user_from, user_to)) < 0)
6311
/* Handle of table failed, don't touch columns and in-memory array. */
6316
if (found && ! result)
6318
result= 1; /* At least one record found. */
6319
/* If search is requested, we do not need to search further. */
6320
if (! drop && ! user_to)
6324
/* Handle columns table. */
6325
if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
6327
/* Handle of table failed, don't touch the in-memory array. */
6332
/* Handle columns hash. */
6333
if (((handle_grant_struct(2, drop, user_from, user_to) && ! result) ||
6335
result= 1; /* At least one record/element found. */
6339
/* Handle proxies_priv table. */
6340
if (tables[5].table)
6342
if ((found= handle_grant_table(tables, 5, drop, user_from, user_to)) < 0)
6344
/* Handle of table failed, don't touch the in-memory array. */
6349
/* Handle proxies_priv array. */
6350
if ((handle_grant_struct(5, drop, user_from, user_to) && !result) ||
6352
result= 1; /* At least one record/element found. */
6356
DBUG_RETURN(result);
6360
static void append_user(String *str, LEX_USER *user)
6365
str->append(user->user.str);
6366
str->append(STRING_WITH_LEN("'@'"));
6367
str->append(user->host.str);
6373
Create a list of users.
6377
thd The current thread.
6378
list The users to create.
6385
bool mysql_create_user(THD *thd, List <LEX_USER> &list)
6389
LEX_USER *user_name, *tmp_user_name;
6390
List_iterator <LEX_USER> user_list(list);
6391
TABLE_LIST tables[GRANT_TABLES];
6392
bool some_users_created= FALSE;
6393
bool save_binlog_row_based;
6394
DBUG_ENTER("mysql_create_user");
6397
This statement will be replicated as a statement, even when using
6398
row-based replication. The flag will be reset at the end of the
6401
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
6402
thd->clear_current_stmt_binlog_format_row();
6404
/* CREATE USER may be skipped on replication client. */
6405
if ((result= open_grant_tables(thd, tables)))
6407
/* Restore the state of binlog format */
6408
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6409
if (save_binlog_row_based)
6410
thd->set_current_stmt_binlog_format_row();
6411
DBUG_RETURN(result != 1);
6414
mysql_rwlock_wrlock(&LOCK_grant);
6415
mysql_mutex_lock(&acl_cache->lock);
6417
while ((tmp_user_name= user_list++))
6419
if (!(user_name= get_current_user(thd, tmp_user_name)))
6426
Search all in-memory structures and grant tables
6427
for a mention of the new user name.
6429
if (handle_grant_data(tables, 0, user_name, NULL))
6431
append_user(&wrong_users, user_name);
6436
some_users_created= TRUE;
6437
if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
6439
append_user(&wrong_users, user_name);
6444
mysql_mutex_unlock(&acl_cache->lock);
6447
my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
6449
if (some_users_created)
6450
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
6452
mysql_rwlock_unlock(&LOCK_grant);
6453
/* Restore the state of binlog format */
6454
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6455
if (save_binlog_row_based)
6456
thd->set_current_stmt_binlog_format_row();
6457
DBUG_RETURN(result);
6462
Drop a list of users and all their privileges.
6466
thd The current thread.
6467
list The users to drop.
6474
bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
6478
LEX_USER *user_name, *tmp_user_name;
6479
List_iterator <LEX_USER> user_list(list);
6480
TABLE_LIST tables[GRANT_TABLES];
6481
bool some_users_deleted= FALSE;
6482
ulong old_sql_mode= thd->variables.sql_mode;
6483
bool save_binlog_row_based;
6484
DBUG_ENTER("mysql_drop_user");
6487
This statement will be replicated as a statement, even when using
6488
row-based replication. The flag will be reset at the end of the
6491
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
6492
thd->clear_current_stmt_binlog_format_row();
6494
/* DROP USER may be skipped on replication client. */
6495
if ((result= open_grant_tables(thd, tables)))
6497
/* Restore the state of binlog format */
6498
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6499
if (save_binlog_row_based)
6500
thd->set_current_stmt_binlog_format_row();
6501
DBUG_RETURN(result != 1);
6504
thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
6506
mysql_rwlock_wrlock(&LOCK_grant);
6507
mysql_mutex_lock(&acl_cache->lock);
6509
while ((tmp_user_name= user_list++))
6511
if (!(user_name= get_current_user(thd, tmp_user_name)))
6516
if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
6518
append_user(&wrong_users, user_name);
6522
some_users_deleted= TRUE;
6525
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
6526
rebuild_check_host();
6528
mysql_mutex_unlock(&acl_cache->lock);
6531
my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
6533
if (some_users_deleted)
6534
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
6536
mysql_rwlock_unlock(&LOCK_grant);
6537
thd->variables.sql_mode= old_sql_mode;
6538
/* Restore the state of binlog format */
6539
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6540
if (save_binlog_row_based)
6541
thd->set_current_stmt_binlog_format_row();
6542
DBUG_RETURN(result);
6551
thd The current thread.
6552
list The user name pairs: (from, to).
6559
bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
6563
LEX_USER *user_from, *tmp_user_from;
6564
LEX_USER *user_to, *tmp_user_to;
6565
List_iterator <LEX_USER> user_list(list);
6566
TABLE_LIST tables[GRANT_TABLES];
6567
bool some_users_renamed= FALSE;
6568
bool save_binlog_row_based;
6569
DBUG_ENTER("mysql_rename_user");
6572
This statement will be replicated as a statement, even when using
6573
row-based replication. The flag will be reset at the end of the
6576
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
6577
thd->clear_current_stmt_binlog_format_row();
6579
/* RENAME USER may be skipped on replication client. */
6580
if ((result= open_grant_tables(thd, tables)))
6582
/* Restore the state of binlog format */
6583
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6584
if (save_binlog_row_based)
6585
thd->set_current_stmt_binlog_format_row();
6586
DBUG_RETURN(result != 1);
6589
mysql_rwlock_wrlock(&LOCK_grant);
6590
mysql_mutex_lock(&acl_cache->lock);
6592
while ((tmp_user_from= user_list++))
6594
if (!(user_from= get_current_user(thd, tmp_user_from)))
6599
tmp_user_to= user_list++;
6600
if (!(user_to= get_current_user(thd, tmp_user_to)))
6605
DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
6608
Search all in-memory structures and grant tables
6609
for a mention of the new user name.
6611
if (handle_grant_data(tables, 0, user_to, NULL) ||
6612
handle_grant_data(tables, 0, user_from, user_to) <= 0)
6614
append_user(&wrong_users, user_from);
6618
some_users_renamed= TRUE;
6621
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
6622
rebuild_check_host();
6624
mysql_mutex_unlock(&acl_cache->lock);
6627
my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
6629
if (some_users_renamed && mysql_bin_log.is_open())
6630
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
6632
mysql_rwlock_unlock(&LOCK_grant);
6633
/* Restore the state of binlog format */
6634
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6635
if (save_binlog_row_based)
6636
thd->set_current_stmt_binlog_format_row();
6637
DBUG_RETURN(result);
6642
Revoke all privileges from a list of users.
6646
thd The current thread.
6647
list The users to revoke all privileges from.
6650
> 0 Error. Error message already sent.
6652
< 0 Error. Error message not yet sent.
6655
bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
6657
uint counter, revoked, is_proc;
6660
TABLE_LIST tables[GRANT_TABLES];
6661
bool save_binlog_row_based;
6662
DBUG_ENTER("mysql_revoke_all");
6665
This statement will be replicated as a statement, even when using
6666
row-based replication. The flag will be reset at the end of the
6669
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
6670
thd->clear_current_stmt_binlog_format_row();
6672
if ((result= open_grant_tables(thd, tables)))
6674
/* Restore the state of binlog format */
6675
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6676
if (save_binlog_row_based)
6677
thd->set_current_stmt_binlog_format_row();
6678
DBUG_RETURN(result != 1);
6681
mysql_rwlock_wrlock(&LOCK_grant);
6682
mysql_mutex_lock(&acl_cache->lock);
6684
LEX_USER *lex_user, *tmp_lex_user;
6685
List_iterator <LEX_USER> user_list(list);
6686
while ((tmp_lex_user= user_list++))
6688
if (!(lex_user= get_current_user(thd, tmp_lex_user)))
6693
if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
6699
if (replace_user_table(thd, tables[0].table,
6700
*lex_user, ~(ulong)0, 1, 0, 0))
6706
/* Remove db access privileges */
6708
Because acl_dbs and column_priv_hash shrink and may re-order
6709
as privileges are removed, removal occurs in a repeated loop
6710
until no more privileges are revoked.
6714
for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
6716
const char *user,*host;
6718
acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
6719
if (!(user=acl_db->user))
6721
if (!(host=acl_db->host.hostname))
6724
if (!strcmp(lex_user->user.str,user) &&
6725
!strcmp(lex_user->host.str, host))
6727
if (!replace_db_table(tables[1].table, acl_db->db, *lex_user,
6731
Don't increment counter as replace_db_table deleted the
6732
current element in acl_dbs.
6737
result= -1; // Something went wrong
6743
/* Remove column access */
6746
for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
6748
const char *user,*host;
6749
GRANT_TABLE *grant_table=
6750
(GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
6751
if (!(user=grant_table->user))
6753
if (!(host=grant_table->host.hostname))
6756
if (!strcmp(lex_user->user.str,user) &&
6757
!strcmp(lex_user->host.str, host))
6759
if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
6768
if (!grant_table->cols)
6773
List<LEX_COLUMN> columns;
6774
if (!replace_column_table(grant_table,tables[3].table, *lex_user,
6790
/* Remove procedure access */
6791
for (is_proc=0; is_proc<2; is_proc++) do {
6792
HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
6793
for (counter= 0, revoked= 0 ; counter < hash->records ; )
6795
const char *user,*host;
6796
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
6797
if (!(user=grant_proc->user))
6799
if (!(host=grant_proc->host.hostname))
6802
if (!strcmp(lex_user->user.str,user) &&
6803
!strcmp(lex_user->host.str, host))
6805
if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
6814
result= -1; // Something went wrong
6821
mysql_mutex_unlock(&acl_cache->lock);
6824
my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
6827
write_bin_log(thd, FALSE, thd->query(), thd->query_length());
6829
mysql_rwlock_unlock(&LOCK_grant);
6830
/* Restore the state of binlog format */
6831
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6832
if (save_binlog_row_based)
6833
thd->set_current_stmt_binlog_format_row();
6835
DBUG_RETURN(result);
6842
If the defining user for a routine does not exist, then the ACL lookup
6843
code should raise two errors which we should intercept. We convert the more
6844
descriptive error into a warning, and consume the other.
6846
If any other errors are raised, then we set a flag that should indicate
6847
that there was some failure we should complain at a higher level.
6849
class Silence_routine_definer_errors : public Internal_error_handler
6852
Silence_routine_definer_errors()
6856
virtual ~Silence_routine_definer_errors()
6859
virtual bool handle_condition(THD *thd,
6861
const char* sqlstate,
6862
MYSQL_ERROR::enum_warning_level level,
6864
MYSQL_ERROR ** cond_hdl);
6866
bool has_errors() { return is_grave; }
6873
Silence_routine_definer_errors::handle_condition(
6877
MYSQL_ERROR::enum_warning_level level,
6879
MYSQL_ERROR ** cond_hdl)
6882
if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
6886
case ER_NONEXISTING_PROC_GRANT:
6887
/* Convert the error into a warning. */
6888
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
6901
Revoke privileges for all users on a stored procedure. Use an error handler
6902
that converts errors about missing grants into warnings.
6905
thd The current thread.
6907
db DB of the stored procedure
6909
name Name of the stored procedure
6914
< 0 Error. Error message not yet sent.
6917
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
6920
uint counter, revoked;
6922
TABLE_LIST tables[GRANT_TABLES];
6923
HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
6924
Silence_routine_definer_errors error_handler;
6925
bool save_binlog_row_based;
6926
DBUG_ENTER("sp_revoke_privileges");
6928
if ((result= open_grant_tables(thd, tables)))
6929
DBUG_RETURN(result != 1);
6931
/* Be sure to pop this before exiting this scope! */
6932
thd->push_internal_handler(&error_handler);
6934
mysql_rwlock_wrlock(&LOCK_grant);
6935
mysql_mutex_lock(&acl_cache->lock);
6938
This statement will be replicated as a statement, even when using
6939
row-based replication. The flag will be reset at the end of the
6942
if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
6943
thd->clear_current_stmt_binlog_format_row();
6945
/* Remove procedure access */
6948
for (counter= 0, revoked= 0 ; counter < hash->records ; )
6950
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
6951
if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
6952
!my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
6955
lex_user.user.str= grant_proc->user;
6956
lex_user.user.length= strlen(grant_proc->user);
6957
lex_user.host.str= grant_proc->host.hostname ?
6958
grant_proc->host.hostname : (char*)"";
6959
lex_user.host.length= grant_proc->host.hostname ?
6960
strlen(grant_proc->host.hostname) : 0;
6962
if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
6963
grant_proc->db, grant_proc->tname,
6964
is_proc, ~(ulong)0, 1) == 0)
6974
mysql_mutex_unlock(&acl_cache->lock);
6975
mysql_rwlock_unlock(&LOCK_grant);
6977
thd->pop_internal_handler();
6978
/* Restore the state of binlog format */
6979
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6980
if (save_binlog_row_based)
6981
thd->set_current_stmt_binlog_format_row();
6983
DBUG_RETURN(error_handler.has_errors());
6988
Grant EXECUTE,ALTER privilege for a stored procedure
6990
@param thd The current thread.
6996
@retval FALSE Success
6997
@retval TRUE An error occured. Error message not yet sent.
7000
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
7003
Security_context *sctx= thd->security_ctx;
7005
TABLE_LIST tables[1];
7006
List<LEX_USER> user_list;
7009
char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
7010
Dummy_error_handler error_handler;
7011
DBUG_ENTER("sp_grant_privileges");
7013
if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
7016
combo->user.str= sctx->user;
7018
mysql_mutex_lock(&acl_cache->lock);
7020
if ((au= find_acl_user(combo->host.str=(char*)sctx->host_or_ip,combo->user.str,FALSE)))
7022
if ((au= find_acl_user(combo->host.str=(char*)sctx->host, combo->user.str,FALSE)))
7024
if ((au= find_acl_user(combo->host.str=(char*)sctx->ip, combo->user.str,FALSE)))
7026
if((au= find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE)))
7029
mysql_mutex_unlock(&acl_cache->lock);
7033
mysql_mutex_unlock(&acl_cache->lock);
7035
bzero((char*)tables, sizeof(TABLE_LIST));
7038
tables->db= (char*)sp_db;
7039
tables->table_name= tables->alias= (char*)sp_name;
7041
thd->make_lex_string(&combo->user,
7042
combo->user.str, strlen(combo->user.str), 0);
7043
thd->make_lex_string(&combo->host,
7044
combo->host.str, strlen(combo->host.str), 0);
7046
combo->password= empty_lex_str;
7047
combo->plugin= empty_lex_str;
7048
combo->auth= empty_lex_str;
7054
if (au->salt_len == SCRAMBLE_LENGTH)
7056
make_password_from_salt(passwd_buff, au->salt);
7057
combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
7059
else if (au->salt_len == SCRAMBLE_LENGTH_323)
7061
make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
7062
combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
7066
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PASSWD_LENGTH,
7067
ER(ER_PASSWD_LENGTH), SCRAMBLED_PASSWORD_CHAR_LENGTH);
7070
combo->password.str= passwd_buff;
7073
if (au->plugin.str != native_password_plugin_name.str &&
7074
au->plugin.str != old_password_plugin_name.str)
7076
combo->plugin= au->plugin;
7077
combo->auth= au->auth_string;
7081
if (user_list.push_back(combo))
7084
thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
7085
thd->lex->ssl_cipher= thd->lex->x509_subject= thd->lex->x509_issuer= 0;
7086
bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
7089
Only care about whether the operation failed or succeeded
7090
as all errors will be handled later.
7092
thd->push_internal_handler(&error_handler);
7093
result= mysql_routine_grant(thd, tables, is_proc, user_list,
7094
DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
7095
thd->pop_internal_handler();
7096
DBUG_RETURN(result);
7100
/*****************************************************************************
7101
Instantiate used templates
7102
*****************************************************************************/
7104
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
7105
template class List_iterator<LEX_COLUMN>;
7106
template class List_iterator<LEX_USER>;
7107
template class List<LEX_COLUMN>;
7108
template class List<LEX_USER>;
7112
Validate if a user can proxy as another user
7115
@param user the logged in user (proxy user)
7116
@param authenticated_as the effective user a plugin is trying to
7117
impersonate as (proxied user)
7118
@return proxy user definition
7119
@retval NULL proxy user definition not found or not applicable
7120
@retval non-null the proxy user data
7123
static ACL_PROXY_USER *
7124
acl_find_proxy_user(const char *user, const char *host, const char *ip,
7125
const char *authenticated_as, bool *proxy_used)
7128
/* if the proxied and proxy user are the same return OK */
7129
DBUG_ENTER("acl_find_proxy_user");
7130
DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
7131
user, host, ip, authenticated_as));
7133
if (!strcmp(authenticated_as, user))
7135
DBUG_PRINT ("info", ("user is the same as authenticated_as"));
7140
for (i=0; i < acl_proxy_users.elements; i++)
7142
ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
7144
if (proxy->matches(host, user, ip, authenticated_as))
7153
acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
7156
DBUG_ENTER("acl_check_proxy_grant_access");
7157
DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
7161
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
7165
/* replication slave thread can do anything */
7166
if (thd->slave_thread)
7168
DBUG_PRINT("info", ("replication slave"));
7172
/* one can grant proxy to himself to others */
7173
if (!strcmp(thd->security_ctx->user, user) &&
7174
!my_strcasecmp(system_charset_info, host,
7175
thd->security_ctx->host))
7177
DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
7178
thd->security_ctx->user, user,
7179
host, thd->security_ctx->host));
7183
/* check for matching WITH PROXY rights */
7184
for (uint i=0; i < acl_proxy_users.elements; i++)
7186
ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
7188
if (proxy->matches(thd->security_ctx->host,
7189
thd->security_ctx->user,
7190
thd->security_ctx->ip,
7192
proxy->get_with_grant())
7194
DBUG_PRINT("info", ("found"));
7199
my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
7200
thd->security_ctx->user,
7201
thd->security_ctx->host_or_ip);
7207
show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
7209
Protocol *protocol= thd->protocol;
7212
for (uint i=0; i < acl_proxy_users.elements; i++)
7214
ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
7216
if (proxy->granted_on(user->host.str, user->user.str))
7218
String global(buff, buffsize, system_charset_info);
7220
proxy->print_grant(&global);
7221
protocol->prepare_for_resend();
7222
protocol->store(global.ptr(), global.length(), global.charset());
7223
if (protocol->write())
7234
#endif /*NO_EMBEDDED_ACCESS_CHECKS */
7237
int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
7240
DBUG_ENTER("wild_case_compare");
7241
DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr));
7244
while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
7246
if (*wildstr == wild_prefix && wildstr[1])
7248
if (my_toupper(cs, *wildstr++) !=
7249
my_toupper(cs, *str++)) DBUG_RETURN(1);
7251
if (! *wildstr ) DBUG_RETURN (*str != 0);
7252
if (*wildstr++ == wild_one)
7254
if (! *str++) DBUG_RETURN (1); /* One char; skip */
7258
if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */
7259
flag=(*wildstr != wild_many && *wildstr != wild_one);
7265
if ((cmp= *wildstr) == wild_prefix && wildstr[1])
7267
cmp=my_toupper(cs, cmp);
7268
while (*str && my_toupper(cs, *str) != cmp)
7270
if (!*str) DBUG_RETURN (1);
7272
if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
7277
DBUG_RETURN (*str != '\0');
7281
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7282
static bool update_schema_privilege(THD *thd, TABLE *table, char *buff,
7283
const char* db, const char* t_name,
7284
const char* column, uint col_length,
7285
const char *priv, uint priv_length,
7286
const char* is_grantable)
7289
CHARSET_INFO *cs= system_charset_info;
7290
restore_record(table, s->default_values);
7291
table->field[0]->store(buff, (uint) strlen(buff), cs);
7292
table->field[1]->store(STRING_WITH_LEN("def"), cs);
7294
table->field[i++]->store(db, (uint) strlen(db), cs);
7296
table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
7298
table->field[i++]->store(column, col_length, cs);
7299
table->field[i++]->store(priv, priv_length, cs);
7300
table->field[i]->store(is_grantable, strlen(is_grantable), cs);
7301
return schema_table_store_record(thd, table);
7306
int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
7308
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7314
TABLE *table= tables->table;
7315
bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
7317
char *curr_host= thd->security_ctx->priv_host_name();
7318
DBUG_ENTER("fill_schema_user_privileges");
7322
mysql_mutex_lock(&acl_cache->lock);
7324
for (counter=0 ; counter < acl_users.elements ; counter++)
7326
const char *user,*host, *is_grantable="YES";
7327
acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
7328
if (!(user=acl_user->user))
7330
if (!(host=acl_user->host.hostname))
7333
if (no_global_access &&
7334
(strcmp(thd->security_ctx->priv_user, user) ||
7335
my_strcasecmp(system_charset_info, curr_host, host)))
7338
want_access= acl_user->access;
7339
if (!(want_access & GRANT_ACL))
7342
strxmov(buff,"'",user,"'@'",host,"'",NullS);
7343
if (!(want_access & ~GRANT_ACL))
7345
if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
7346
STRING_WITH_LEN("USAGE"), is_grantable))
7355
ulong j,test_access= want_access & ~GRANT_ACL;
7356
for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
7358
if (test_access & j)
7360
if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
7361
command_array[priv_id],
7362
command_lengths[priv_id], is_grantable))
7372
mysql_mutex_unlock(&acl_cache->lock);
7381
int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
7383
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7389
TABLE *table= tables->table;
7390
bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
7392
char *curr_host= thd->security_ctx->priv_host_name();
7393
DBUG_ENTER("fill_schema_schema_privileges");
7397
mysql_mutex_lock(&acl_cache->lock);
7399
for (counter=0 ; counter < acl_dbs.elements ; counter++)
7401
const char *user, *host, *is_grantable="YES";
7403
acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
7404
if (!(user=acl_db->user))
7406
if (!(host=acl_db->host.hostname))
7409
if (no_global_access &&
7410
(strcmp(thd->security_ctx->priv_user, user) ||
7411
my_strcasecmp(system_charset_info, curr_host, host)))
7414
want_access=acl_db->access;
7417
if (!(want_access & GRANT_ACL))
7421
strxmov(buff,"'",user,"'@'",host,"'",NullS);
7422
if (!(want_access & ~GRANT_ACL))
7424
if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0,
7425
0, STRING_WITH_LEN("USAGE"), is_grantable))
7434
ulong j,test_access= want_access & ~GRANT_ACL;
7435
for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
7436
if (test_access & j)
7438
if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0, 0,
7439
command_array[cnt], command_lengths[cnt],
7450
mysql_mutex_unlock(&acl_cache->lock);
7459
int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
7461
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7465
TABLE *table= tables->table;
7466
bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
7468
char *curr_host= thd->security_ctx->priv_host_name();
7469
DBUG_ENTER("fill_schema_table_privileges");
7471
mysql_rwlock_rdlock(&LOCK_grant);
7473
for (index=0 ; index < column_priv_hash.records ; index++)
7475
const char *user, *host, *is_grantable= "YES";
7476
GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
7478
if (!(user=grant_table->user))
7480
if (!(host= grant_table->host.hostname))
7483
if (no_global_access &&
7484
(strcmp(thd->security_ctx->priv_user, user) ||
7485
my_strcasecmp(system_charset_info, curr_host, host)))
7488
ulong table_access= grant_table->privs;
7491
ulong test_access= table_access & ~GRANT_ACL;
7493
We should skip 'usage' privilege on table if
7494
we have any privileges on column(s) of this table
7496
if (!test_access && grant_table->cols)
7498
if (!(table_access & GRANT_ACL))
7501
strxmov(buff, "'", user, "'@'", host, "'", NullS);
7504
if (update_schema_privilege(thd, table, buff, grant_table->db,
7505
grant_table->tname, 0, 0,
7506
STRING_WITH_LEN("USAGE"), is_grantable))
7516
for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
7518
if (test_access & j)
7520
if (update_schema_privilege(thd, table, buff, grant_table->db,
7521
grant_table->tname, 0, 0,
7523
command_lengths[cnt], is_grantable))
7534
mysql_rwlock_unlock(&LOCK_grant);
7543
int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
7545
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7549
TABLE *table= tables->table;
7550
bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
7552
char *curr_host= thd->security_ctx->priv_host_name();
7553
DBUG_ENTER("fill_schema_table_privileges");
7555
mysql_rwlock_rdlock(&LOCK_grant);
7557
for (index=0 ; index < column_priv_hash.records ; index++)
7559
const char *user, *host, *is_grantable= "YES";
7560
GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
7562
if (!(user=grant_table->user))
7564
if (!(host= grant_table->host.hostname))
7567
if (no_global_access &&
7568
(strcmp(thd->security_ctx->priv_user, user) ||
7569
my_strcasecmp(system_charset_info, curr_host, host)))
7572
ulong table_access= grant_table->cols;
7573
if (table_access != 0)
7575
if (!(grant_table->privs & GRANT_ACL))
7578
ulong test_access= table_access & ~GRANT_ACL;
7579
strxmov(buff, "'", user, "'@'", host, "'", NullS);
7586
for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
7588
if (test_access & j)
7590
for (uint col_index=0 ;
7591
col_index < grant_table->hash_columns.records ;
7594
GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
7595
my_hash_element(&grant_table->hash_columns,col_index);
7596
if ((grant_column->rights & j) && (table_access & j))
7598
if (update_schema_privilege(thd, table, buff, grant_table->db,
7600
grant_column->column,
7601
grant_column->key_length,
7603
command_lengths[cnt], is_grantable))
7616
mysql_rwlock_unlock(&LOCK_grant);
7625
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7627
fill effective privileges for table
7630
fill_effective_table_privileges()
7632
grant grants table descriptor
7637
void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
7638
const char *db, const char *table)
7640
Security_context *sctx= thd->security_ctx;
7641
DBUG_ENTER("fill_effective_table_privileges");
7642
DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
7643
sctx->priv_host, (sctx->ip ? sctx->ip : "(NULL)"),
7644
(sctx->priv_user ? sctx->priv_user : "(NULL)"),
7649
DBUG_PRINT("info", ("skip grants"));
7650
grant->privilege= ~NO_ACCESS; // everything is allowed
7651
DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
7655
/* global privileges */
7656
grant->privilege= sctx->master_access;
7658
if (!sctx->priv_user)
7660
DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
7661
DBUG_VOID_RETURN; // it is slave
7665
grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
7667
/* table privileges */
7668
mysql_rwlock_rdlock(&LOCK_grant);
7669
if (grant->version != grant_version)
7672
table_hash_search(sctx->host, sctx->ip, db,
7674
table, 0); /* purecov: inspected */
7675
grant->version= grant_version; /* purecov: inspected */
7677
if (grant->grant_table != 0)
7679
grant->privilege|= grant->grant_table->privs;
7681
mysql_rwlock_unlock(&LOCK_grant);
7683
DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
7687
#else /* NO_EMBEDDED_ACCESS_CHECKS */
7689
/****************************************************************************
7690
Dummy wrappers when we don't have any access checks
7691
****************************************************************************/
7693
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
7701
struct ACL_internal_schema_registry_entry
7703
const LEX_STRING *m_name;
7704
const ACL_internal_schema_access *m_access;
7708
Internal schema registered.
7709
Currently, this is only:
7710
- performance_schema
7711
- information_schema,
7712
This can be reused later for:
7715
static ACL_internal_schema_registry_entry registry_array[2];
7716
static uint m_registry_array_size= 0;
7719
Add an internal schema to the registry.
7720
@param name the schema name
7721
@param access the schema ACL specific rules
7723
void ACL_internal_schema_registry::register_schema
7724
(const LEX_STRING *name, const ACL_internal_schema_access *access)
7726
DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
7728
/* Not thread safe, and does not need to be. */
7729
registry_array[m_registry_array_size].m_name= name;
7730
registry_array[m_registry_array_size].m_access= access;
7731
m_registry_array_size++;
7735
Search per internal schema ACL by name.
7736
@param name a schema name
7737
@return per schema rules, or NULL
7739
const ACL_internal_schema_access *
7740
ACL_internal_schema_registry::lookup(const char *name)
7742
DBUG_ASSERT(name != NULL);
7746
for (i= 0; i<m_registry_array_size; i++)
7748
if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
7750
return registry_array[i].m_access;
7756
Get a cached internal schema access.
7757
@param grant_internal_info the cache
7758
@param schema_name the name of the internal schema
7760
const ACL_internal_schema_access *
7761
get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
7762
const char *schema_name)
7764
if (grant_internal_info)
7766
if (! grant_internal_info->m_schema_lookup_done)
7768
grant_internal_info->m_schema_access=
7769
ACL_internal_schema_registry::lookup(schema_name);
7770
grant_internal_info->m_schema_lookup_done= TRUE;
7772
return grant_internal_info->m_schema_access;
7774
return ACL_internal_schema_registry::lookup(schema_name);
7778
Get a cached internal table access.
7779
@param grant_internal_info the cache
7780
@param schema_name the name of the internal schema
7781
@param table_name the name of the internal table
7783
const ACL_internal_table_access *
7784
get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
7785
const char *schema_name,
7786
const char *table_name)
7788
DBUG_ASSERT(grant_internal_info);
7789
if (! grant_internal_info->m_table_lookup_done)
7791
const ACL_internal_schema_access *schema_access;
7792
schema_access= get_cached_schema_access(grant_internal_info, schema_name);
7794
grant_internal_info->m_table_access= schema_access->lookup(table_name);
7795
grant_internal_info->m_table_lookup_done= TRUE;
7797
return grant_internal_info->m_table_access;
7801
/****************************************************************************
7803
including initial connect handshake, invoking appropriate plugins,
7804
client-server plugin negotiation, COM_CHANGE_USER, and native
7805
MySQL authentication plugins.
7806
****************************************************************************/
7808
/* few defines to have less ifdef's in the code below */
7809
#ifdef EMBEDDED_LIBRARY
7811
#ifdef NO_EMBEDDED_ACCESS_CHECKS
7812
#define initialized 0
7815
#ifndef HAVE_OPENSSL
7816
#define ssl_acceptor_fd 0
7817
#define sslaccept(A,B,C) 1
7821
class Thd_charset_adapter
7825
Thd_charset_adapter(THD *thd_arg) : thd (thd_arg) {}
7826
bool init_client_charset(uint cs_number)
7828
if (thd_init_client_charset(thd, cs_number))
7830
thd->update_charset();
7831
return thd->is_error();
7834
CHARSET_INFO *charset() { return thd->charset(); }
7839
The internal version of what plugins know as MYSQL_PLUGIN_VIO,
7840
basically the context of the authentication session
7842
struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
7844
MYSQL_SERVER_AUTH_INFO auth_info;
7845
const ACL_USER *acl_user;
7846
plugin_ref plugin; ///< what plugin we're under
7847
LEX_STRING db; ///< db name from the handshake packet
7848
/** when restarting a plugin this caches the last client reply */
7850
char *plugin, *pkt; ///< pointers into NET::buff
7852
} cached_client_reply;
7853
/** this caches the first plugin packet for restart request on the client */
7857
} cached_server_packet;
7858
int packets_read, packets_written; ///< counters for send/received packets
7859
uint connect_errors; ///< if there were connect errors for this host
7860
/** when plugin returns a failure this tells us what really happened */
7861
enum { SUCCESS, FAILURE, RESTART } status;
7863
/* encapsulation members */
7864
ulong client_capabilities;
7867
struct rand_struct *rand;
7868
my_thread_id thread_id;
7869
uint *server_status;
7871
ulong max_client_packet_length;
7874
Thd_charset_adapter *charset_adapter;
7875
LEX_STRING acl_user_plugin;
7879
a helper function to report an access denied error in all the proper places
7881
static void login_failed_error(MPVIO_EXT *mpvio, int passwd_used)
7883
THD *thd= current_thd;
7884
if (passwd_used == 2)
7886
my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
7887
mpvio->auth_info.user_name,
7888
mpvio->auth_info.host_or_ip);
7889
general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
7890
mpvio->auth_info.user_name,
7891
mpvio->auth_info.host_or_ip);
7893
Log access denied messages to the error log when log-warnings = 2
7894
so that the overhead of the general query log is not required to track
7897
if (global_system_variables.log_warnings > 1)
7899
sql_print_warning(ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
7900
mpvio->auth_info.user_name,
7901
mpvio->auth_info.host_or_ip);
7906
my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
7907
mpvio->auth_info.user_name,
7908
mpvio->auth_info.host_or_ip,
7909
passwd_used ? ER(ER_YES) : ER(ER_NO));
7910
general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
7911
mpvio->auth_info.user_name,
7912
mpvio->auth_info.host_or_ip,
7913
passwd_used ? ER(ER_YES) : ER(ER_NO));
7915
Log access denied messages to the error log when log-warnings = 2
7916
so that the overhead of the general query log is not required to track
7919
if (global_system_variables.log_warnings > 1)
7921
sql_print_warning(ER(ER_ACCESS_DENIED_ERROR),
7922
mpvio->auth_info.user_name,
7923
mpvio->auth_info.host_or_ip,
7924
passwd_used ? ER(ER_YES) : ER(ER_NO));
7930
sends a server handshake initialization packet, the very first packet
7931
after the connection was established
7937
1 protocol version (always 10)
7938
n server version string, \0-terminated
7940
8 first 8 bytes of the plugin provided data (scramble)
7941
1 \0 byte, terminating the first part of a scramble
7942
2 server capabilities (two lower bytes)
7943
1 server character set
7945
2 server capabilities (two upper bytes)
7946
1 length of the scramble
7947
10 reserved, always 0
7948
n rest of the plugin provided data (at least 12 bytes)
7949
1 \0 byte, terminating the second part of a scramble
7954
static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
7955
const char *data, uint data_len)
7957
DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
7958
DBUG_ASSERT(data_len <= 255);
7960
char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + data_len + 64);
7961
char scramble_buf[SCRAMBLE_LENGTH];
7964
DBUG_ENTER("send_server_handshake_packet");
7965
*end++= protocol_version;
7967
mpvio->client_capabilities= CLIENT_BASIC_FLAGS;
7969
if (opt_using_transactions)
7970
mpvio->client_capabilities|= CLIENT_TRANSACTIONS;
7972
mpvio->client_capabilities|= CAN_CLIENT_COMPRESS;
7974
if (ssl_acceptor_fd)
7976
mpvio->client_capabilities|= CLIENT_SSL;
7977
mpvio->client_capabilities|= CLIENT_SSL_VERIFY_SERVER_CERT;
7982
mpvio->cached_server_packet.pkt= (char*) memdup_root(mpvio->mem_root,
7984
mpvio->cached_server_packet.pkt_len= data_len;
7987
if (data_len < SCRAMBLE_LENGTH)
7992
the first packet *must* have at least 20 bytes of a scramble.
7993
if a plugin provided less, we pad it to 20 with zeros
7995
memcpy(scramble_buf, data, data_len);
7996
bzero(scramble_buf + data_len, SCRAMBLE_LENGTH - data_len);
8002
if the default plugin does not provide the data for the scramble at
8003
all, we generate a scramble internally anyway, just in case the
8004
user account (that will be known only later) uses a
8005
native_password_plugin (which needs a scramble). If we don't send a
8006
scramble now - wasting 20 bytes in the packet -
8007
native_password_plugin will have to send it in a separate packet,
8008
adding one more round trip.
8010
create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
8011
data= mpvio->scramble;
8013
data_len= SCRAMBLE_LENGTH;
8016
end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
8017
int4store((uchar*) end, mpvio->thread_id);
8021
Old clients does not understand long scrambles, but can ignore packet
8022
tail: that's why first part of the scramble is placed here, and second
8023
part at the end of packet.
8025
end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
8026
end+= SCRAMBLE_LENGTH_323;
8029
int2store(end, mpvio->client_capabilities);
8030
/* write server characteristics: up to 16 bytes allowed */
8031
end[2]= (char) default_charset_info->number;
8032
int2store(end + 3, mpvio->server_status[0]);
8033
int2store(end + 5, mpvio->client_capabilities >> 16);
8035
DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
8038
/* write scramble tail */
8039
end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
8040
data_len - SCRAMBLE_LENGTH_323);
8041
end+= data_len - SCRAMBLE_LENGTH_323;
8042
end= strmake(end, plugin_name(mpvio->plugin)->str,
8043
plugin_name(mpvio->plugin)->length);
8045
int res= my_net_write(mpvio->net, (uchar*) buff, (size_t) (end - buff + 1)) ||
8046
net_flush(mpvio->net);
8051
static bool secure_auth(MPVIO_EXT *mpvio)
8054
if (!opt_secure_auth)
8057
If the server is running in secure auth mode, short scrambles are
8058
forbidden. Extra juggling to report the same error as the old code.
8062
if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
8064
my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
8065
mpvio->auth_info.user_name,
8066
mpvio->auth_info.host_or_ip);
8067
general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
8068
mpvio->auth_info.user_name,
8069
mpvio->auth_info.host_or_ip);
8073
my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
8074
general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
8080
sends a "change plugin" packet, requesting a client to restart authentication
8081
using a different authentication plugin
8087
1 byte with the value 254
8088
n client plugin to use, \0-terminated
8089
n plugin provided data
8091
In a special case of switching from native_password_plugin to
8092
old_password_plugin, the packet contains only one - the first - byte,
8093
plugin name is omitted, plugin data aren't needed as the scramble was
8094
already sent. This one-byte packet is identical to the "use the short
8095
scramble" packet in the protocol before plugins were introduced.
8100
static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
8101
const uchar *data, uint data_len)
8103
DBUG_ASSERT(mpvio->packets_written == 1);
8104
DBUG_ASSERT(mpvio->packets_read == 1);
8105
NET *net= mpvio->net;
8106
static uchar switch_plugin_request_buf[]= { 254 };
8108
DBUG_ENTER("send_plugin_request_packet");
8109
mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
8111
const char *client_auth_plugin=
8112
((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
8114
DBUG_ASSERT(client_auth_plugin);
8117
we send an old "short 4.0 scramble request", if we need to request a
8118
client to use 4.0 auth plugin (short scramble) and the scramble was
8119
already sent to the client
8121
below, cached_client_reply.plugin is the plugin name that client has used,
8122
client_auth_plugin is derived from mysql.user table, for the given
8123
user account, it's the plugin that the client need to use to login.
8125
bool switch_from_long_to_short_scramble=
8126
native_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
8127
client_auth_plugin == old_password_plugin_name.str;
8129
if (switch_from_long_to_short_scramble)
8130
DBUG_RETURN (secure_auth(mpvio) ||
8131
my_net_write(net, switch_plugin_request_buf, 1) ||
8135
We never request a client to switch from a short to long scramble.
8136
Plugin-aware clients can do that, but traditionally it meant to
8137
ask an old 4.0 client to use the new 4.1 authentication protocol.
8139
bool switch_from_short_to_long_scramble=
8140
old_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
8141
client_auth_plugin == native_password_plugin_name.str;
8143
if (switch_from_short_to_long_scramble)
8145
my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
8146
general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
8151
If we're dealing with an older client we can't just send a change plugin
8152
packet to re-initiate the authentication handshake, because the client
8153
won't understand it. The good thing is that we don't need to : the old client
8154
expects us to just check the user credentials here, which we can do by just reading
8155
the cached data that are placed there by parse_com_change_user_packet()
8156
In this case we just do nothing and behave as if normal authentication
8159
if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
8161
DBUG_PRINT("info", ("old client sent a COM_CHANGE_USER"));
8162
DBUG_ASSERT(mpvio->cached_client_reply.pkt);
8163
/* get the status back so the read can process the cached result */
8164
mpvio->status= MPVIO_EXT::RESTART;
8168
DBUG_PRINT("info", ("requesting client to use the %s plugin",
8169
client_auth_plugin));
8170
DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
8171
(uchar*) client_auth_plugin,
8172
strlen(client_auth_plugin) + 1,
8173
(uchar*) data, data_len));
8176
#ifndef NO_EMBEDDED_ACCESS_CHECKS
8178
Finds acl entry in user database for authentication purposes.
8180
Finds a user and copies it into mpvio. Reports an authentication
8181
failure if a user is not found.
8183
@note find_acl_user is not the same, because it doesn't take into
8184
account the case when user is not empty, but acl_user->user is empty
8189
static bool find_mpvio_user(MPVIO_EXT *mpvio)
8191
DBUG_ENTER("find_mpvio_user");
8192
DBUG_PRINT("info", ("entry: %s", mpvio->auth_info.user_name));
8193
DBUG_ASSERT(mpvio->acl_user == 0);
8194
mysql_mutex_lock(&acl_cache->lock);
8195
for (uint i=0; i < acl_users.elements; i++)
8197
ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
8198
if ((!acl_user_tmp->user ||
8199
!strcmp(mpvio->auth_info.user_name, acl_user_tmp->user)) &&
8200
compare_hostname(&acl_user_tmp->host, mpvio->host, mpvio->ip))
8202
mpvio->acl_user= acl_user_tmp->copy(mpvio->mem_root);
8203
if (acl_user_tmp->plugin.str == native_password_plugin_name.str ||
8204
acl_user_tmp->plugin.str == old_password_plugin_name.str)
8205
mpvio->acl_user_plugin= acl_user_tmp->plugin;
8207
make_lex_string_root(mpvio->mem_root,
8208
&mpvio->acl_user_plugin,
8209
acl_user_tmp->plugin.str,
8210
acl_user_tmp->plugin.length, 0);
8214
mysql_mutex_unlock(&acl_cache->lock);
8216
if (!mpvio->acl_user)
8218
login_failed_error(mpvio, mpvio->auth_info.password_used);
8222
/* user account requires non-default plugin and the client is too old */
8223
if (mpvio->acl_user->plugin.str != native_password_plugin_name.str &&
8224
mpvio->acl_user->plugin.str != old_password_plugin_name.str &&
8225
!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
8227
DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
8228
native_password_plugin_name.str));
8229
DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
8230
old_password_plugin_name.str));
8231
my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
8232
general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
8236
mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
8237
mpvio->auth_info.auth_string_length=
8238
(unsigned long) mpvio->acl_user->auth_string.length;
8239
strmake(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
8240
mpvio->acl_user->user : "", USERNAME_LENGTH);
8241
DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
8243
mpvio->auth_info.user_name,
8244
mpvio->auth_info.auth_string,
8245
mpvio->auth_info.authenticated_as,
8246
mpvio->acl_user->plugin.str));
8251
/* the packet format is described in send_change_user_packet() */
8252
static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
8254
NET *net= mpvio->net;
8256
char *user= (char*) net->read_pos;
8257
char *end= user + packet_length;
8258
/* Safe because there is always a trailing \0 at the end of the packet */
8259
char *passwd= strend(user) + 1;
8260
uint user_len= passwd - user - 1;
8262
char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
8263
char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
8266
DBUG_ENTER ("parse_com_change_user_packet");
8269
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
8274
Old clients send null-terminated string as password; new clients send
8275
the size (1 byte) + string (not null-terminated). Hence in case of empty
8276
password both send '\0'.
8278
This strlen() can't be easily deleted without changing protocol.
8280
Cast *passwd to an unsigned char, so that it doesn't extend the sign for
8281
*passwd > 127 and become 2**32-127+ after casting to uint.
8283
uint passwd_len= (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION ?
8284
(uchar) (*passwd++) : strlen(passwd));
8286
db+= passwd_len + 1;
8288
Database name is always NUL-terminated, so in case of empty database
8289
the packet must contain at least the trailing '\0'.
8293
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
8297
uint db_len= strlen(db);
8299
char *ptr= db + db_len + 1;
8303
if (mpvio->charset_adapter->init_client_charset(uint2korr(ptr)))
8308
/* Convert database and user names to utf8 */
8309
db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
8310
db, db_len, mpvio->charset_adapter->charset(),
8314
user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
8315
system_charset_info, user, user_len,
8316
mpvio->charset_adapter->charset(),
8318
user_buff[user_len]= 0;
8320
/* we should not free mpvio->user here: it's saved by dispatch_command() */
8321
if (!(mpvio->auth_info.user_name= my_strndup(user_buff, user_len, MYF(MY_WME))))
8323
mpvio->auth_info.user_name_length= user_len;
8325
if (make_lex_string_root(mpvio->mem_root,
8326
&mpvio->db, db_buff, db_len, 0) == 0)
8327
DBUG_RETURN(1); /* The error is set by make_lex_string(). */
8331
// if mysqld's been started with --skip-grant-tables option
8332
strmake(mpvio->auth_info.authenticated_as,
8333
mpvio->auth_info.user_name, USERNAME_LENGTH);
8335
mpvio->status= MPVIO_EXT::SUCCESS;
8339
#ifndef NO_EMBEDDED_ACCESS_CHECKS
8340
if (find_mpvio_user(mpvio))
8343
char *client_plugin;
8344
if (mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)
8346
client_plugin= ptr + 2;
8347
if (client_plugin >= end)
8349
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
8355
if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
8356
client_plugin= native_password_plugin_name.str;
8359
client_plugin= old_password_plugin_name.str;
8361
For a passwordless accounts we use native_password_plugin.
8362
But when an old 4.0 client connects to it, we change it to
8363
old_password_plugin, otherwise MySQL will think that server
8364
and client plugins don't match.
8366
if (mpvio->acl_user->salt_len == 0)
8367
mpvio->acl_user_plugin= old_password_plugin_name;
8371
DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
8373
Remember the data part of the packet, to present it to plugin in
8376
mpvio->cached_client_reply.pkt= passwd;
8377
mpvio->cached_client_reply.pkt_len= passwd_len;
8378
mpvio->cached_client_reply.plugin= client_plugin;
8379
mpvio->status= MPVIO_EXT::RESTART;
8385
#ifndef EMBEDDED_LIBRARY
8387
/** Get a string according to the protocol of the underlying buffer. */
8388
typedef char * (*get_proto_string_func_t) (char **, size_t *, size_t *);
8391
Get a string formatted according to the 4.1 version of the MySQL protocol.
8393
@param buffer[in, out] Pointer to the user-supplied buffer to be scanned.
8394
@param max_bytes_available[in, out] Limit the bytes to scan.
8395
@param string_length[out] The number of characters scanned not including
8398
@remark Strings are always null character terminated in this version of the
8401
@remark The string_length does not include the terminating null character.
8402
However, after the call, the buffer is increased by string_length+1
8403
bytes, beyond the null character if there still available bytes to
8406
@return pointer to beginning of the string scanned.
8407
@retval NULL The buffer content is malformed
8411
char *get_41_protocol_string(char **buffer,
8412
size_t *max_bytes_available,
8413
size_t *string_length)
8415
char *str= (char *)memchr(*buffer, '\0', *max_bytes_available);
8420
*string_length= (size_t)(str - *buffer);
8421
*max_bytes_available-= *string_length + 1;
8423
*buffer += *string_length + 1;
8430
Get a string formatted according to the 4.0 version of the MySQL protocol.
8432
@param buffer[in, out] Pointer to the user-supplied buffer to be scanned.
8433
@param max_bytes_available[in, out] Limit the bytes to scan.
8434
@param string_length[out] The number of characters scanned not including
8437
@remark If there are not enough bytes left after the current position of
8438
the buffer to satisfy the current string, the string is considered
8439
to be empty and a pointer to empty_c_string is returned.
8441
@remark A string at the end of the packet is not null terminated.
8443
@return Pointer to beginning of the string scanned, or a pointer to a empty
8447
char *get_40_protocol_string(char **buffer,
8448
size_t *max_bytes_available,
8449
size_t *string_length)
8454
/* No bytes to scan left, treat string as empty. */
8455
if ((*max_bytes_available) == 0)
8458
return empty_c_string;
8461
str= (char *) memchr(*buffer, '\0', *max_bytes_available);
8464
If the string was not null terminated by the client,
8465
the remainder of the packet is the string. Otherwise,
8466
advance the buffer past the end of the null terminated
8470
len= *string_length= *max_bytes_available;
8472
len= (*string_length= (size_t)(str - *buffer)) + 1;
8476
*max_bytes_available-= len;
8482
Get a length encoded string from a user-supplied buffer.
8484
@param buffer[in, out] The buffer to scan; updates position after scan.
8485
@param max_bytes_available[in, out] Limit the number of bytes to scan
8486
@param string_length[out] Number of characters scanned
8488
@remark In case the length is zero, then the total size of the string is
8489
considered to be 1 byte; the size byte.
8491
@return pointer to first byte after the header in buffer.
8492
@retval NULL The buffer content is malformed
8496
char *get_length_encoded_string(char **buffer,
8497
size_t *max_bytes_available,
8498
size_t *string_length)
8500
if (*max_bytes_available == 0)
8503
/* Do double cast to prevent overflow from signed / unsigned conversion */
8504
size_t str_len= (size_t)(unsigned char)**buffer;
8507
If the length encoded string has the length 0
8508
the total size of the string is only one byte long (the size byte)
8515
Return a pointer to the 0 character so the return value will be
8521
if (str_len >= *max_bytes_available)
8524
char *str= *buffer+1;
8525
*string_length= str_len;
8526
*max_bytes_available-= *string_length + 1;
8527
*buffer+= *string_length + 1;
8533
/* the packet format is described in send_client_reply_packet() */
8534
static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
8535
uchar **buff, ulong pkt_len)
8537
#ifndef EMBEDDED_LIBRARY
8538
NET *net= mpvio->net;
8540
bool packet_has_required_size= false;
8541
DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
8543
if (mpvio->connect_errors)
8544
reset_host_errors(mpvio->ip);
8546
uint charset_code= 0;
8547
end= (char *)net->read_pos;
8549
In order to safely scan a head for '\0' string terminators
8550
we must keep track of how many bytes remain in the allocated
8551
buffer or we might read past the end of the buffer.
8553
size_t bytes_remaining_in_packet= pkt_len;
8556
Peek ahead on the client capability packet and determine which version of
8557
the protocol should be used.
8559
if (bytes_remaining_in_packet < 2)
8560
return packet_error;
8562
mpvio->client_capabilities= uint2korr(end);
8565
JConnector only sends server capabilities before starting SSL
8566
negotiation. The below code is patch for this.
8568
if (bytes_remaining_in_packet == 4 &&
8569
mpvio->client_capabilities & CLIENT_SSL)
8571
mpvio->client_capabilities= uint4korr(end);
8572
mpvio->max_client_packet_length= 0xfffff;
8573
charset_code= default_charset_info->number;
8574
if (mpvio->charset_adapter->init_client_charset(charset_code))
8575
return packet_error;
8579
if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
8580
packet_has_required_size= bytes_remaining_in_packet >=
8581
AUTH_PACKET_HEADER_SIZE_PROTO_41;
8583
packet_has_required_size= bytes_remaining_in_packet >=
8584
AUTH_PACKET_HEADER_SIZE_PROTO_40;
8586
if (!packet_has_required_size)
8587
return packet_error;
8589
if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
8591
mpvio->client_capabilities= uint4korr(end);
8592
mpvio->max_client_packet_length= uint4korr(end + 4);
8593
charset_code= (uint)(uchar)*(end + 8);
8595
Skip 23 remaining filler bytes which have no particular meaning.
8597
end+= AUTH_PACKET_HEADER_SIZE_PROTO_41;
8598
bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_41;
8602
mpvio->client_capabilities= uint2korr(end);
8603
mpvio->max_client_packet_length= uint3korr(end + 2);
8604
end+= AUTH_PACKET_HEADER_SIZE_PROTO_40;
8605
bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_40;
8607
Old clients didn't have their own charset. Instead the assumption
8608
was that they used what ever the server used.
8610
charset_code= default_charset_info->number;
8613
DBUG_PRINT("info", ("client_character_set: %u", charset_code));
8614
if (mpvio->charset_adapter->init_client_charset(charset_code))
8615
return packet_error;
8618
#if defined(HAVE_OPENSSL)
8619
DBUG_PRINT("info", ("client capabilities: %lu", mpvio->client_capabilities));
8622
If client requested SSL then we must stop parsing, try to switch to SSL,
8623
and wait for the client to send a new handshake packet.
8624
The client isn't expected to send any more bytes until SSL is initialized.
8626
if (mpvio->client_capabilities & CLIENT_SSL)
8628
unsigned long errptr;
8630
/* Do the SSL layering. */
8631
if (!ssl_acceptor_fd)
8632
return packet_error;
8634
DBUG_PRINT("info", ("IO layer change in progress..."));
8635
if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr))
8637
DBUG_PRINT("error", ("Failed to accept new SSL connection"));
8638
return packet_error;
8641
DBUG_PRINT("info", ("Reading user information over SSL layer"));
8642
if ((pkt_len= my_net_read(net)) == packet_error)
8644
DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
8646
return packet_error;
8649
A new packet was read and the statistics reflecting the remaining bytes
8650
in the packet must be updated.
8652
bytes_remaining_in_packet= pkt_len;
8655
After the SSL handshake is performed the client resends the handshake
8656
packet but because of legacy reasons we chose not to parse the packet
8657
fields a second time and instead only assert the length of the packet.
8659
if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
8661
packet_has_required_size= bytes_remaining_in_packet >=
8662
AUTH_PACKET_HEADER_SIZE_PROTO_41;
8663
end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_41;
8664
bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_41;
8668
packet_has_required_size= bytes_remaining_in_packet >=
8669
AUTH_PACKET_HEADER_SIZE_PROTO_40;
8670
end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_40;
8671
bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_40;
8674
if (!packet_has_required_size)
8675
return packet_error;
8677
#endif /* HAVE_OPENSSL */
8679
if ((mpvio->client_capabilities & CLIENT_TRANSACTIONS) &&
8680
opt_using_transactions)
8681
net->return_status= mpvio->server_status;
8684
The 4.0 and 4.1 versions of the protocol differ on how strings
8685
are terminated. In the 4.0 version, if a string is at the end
8686
of the packet, the string is not null terminated. Do not assume
8687
that the returned string is always null terminated.
8689
get_proto_string_func_t get_string;
8691
if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
8692
get_string= get_41_protocol_string;
8694
get_string= get_40_protocol_string;
8697
In order to safely scan a head for '\0' string terminators
8698
we must keep track of how many bytes remain in the allocated
8699
buffer or we might read past the end of the buffer.
8701
bytes_remaining_in_packet= pkt_len - (end - (char *)net->read_pos);
8704
char *user= get_string(&end, &bytes_remaining_in_packet, &user_len);
8706
return packet_error;
8709
Old clients send a null-terminated string as password; new clients send
8710
the size (1 byte) + string (not null-terminated). Hence in case of empty
8711
password both send '\0'.
8713
size_t passwd_len= 0;
8716
if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
8719
4.1+ password. First byte is password length.
8721
passwd= get_length_encoded_string(&end, &bytes_remaining_in_packet,
8727
Old passwords are zero terminated strings.
8729
passwd= get_string(&end, &bytes_remaining_in_packet, &passwd_len);
8733
return packet_error;
8738
if (mpvio->client_capabilities & CLIENT_CONNECT_WITH_DB)
8740
db= get_string(&end, &bytes_remaining_in_packet, &db_len);
8742
return packet_error;
8746
Set the default for the password supplied flag for non-existing users
8747
as the default plugin (native passsword authentication) would do it
8748
for compatibility reasons.
8751
mpvio->auth_info.password_used= PASSWORD_USED_YES;
8753
size_t client_plugin_len= 0;
8754
char *client_plugin= get_string(&end, &bytes_remaining_in_packet,
8755
&client_plugin_len);
8756
if (client_plugin == NULL)
8757
client_plugin= &empty_c_string[0];
8759
char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
8760
char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
8765
Copy and convert the user and database names to the character set used
8766
by the server. Since 4.1 all database names are stored in UTF-8. Also,
8767
ensure that the names are properly null-terminated as this is relied
8772
db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
8773
db, db_len, mpvio->charset_adapter->charset(),
8775
db_buff[db_len]= '\0';
8779
user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
8780
system_charset_info, user, user_len,
8781
mpvio->charset_adapter->charset(),
8783
user_buff[user_len]= '\0';
8786
/* If username starts and ends in "'", chop them off */
8787
if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
8789
user[user_len - 1]= 0;
8794
if (make_lex_string_root(mpvio->mem_root,
8795
&mpvio->db, db, db_len, 0) == 0)
8796
return packet_error; /* The error is set by make_lex_string(). */
8797
if (mpvio->auth_info.user_name)
8798
my_free(mpvio->auth_info.user_name);
8799
if (!(mpvio->auth_info.user_name= my_strndup(user, user_len, MYF(MY_WME))))
8800
return packet_error; /* The error is set by my_strdup(). */
8801
mpvio->auth_info.user_name_length= user_len;
8805
// if mysqld's been started with --skip-grant-tables option
8806
mpvio->status= MPVIO_EXT::SUCCESS;
8807
return packet_error;
8810
if (find_mpvio_user(mpvio))
8811
return packet_error;
8813
if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
8816
An old client is connecting
8818
if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
8819
client_plugin= native_password_plugin_name.str;
8823
A really old client is connecting
8825
client_plugin= old_password_plugin_name.str;
8827
For a passwordless accounts we use native_password_plugin.
8828
But when an old 4.0 client connects to it, we change it to
8829
old_password_plugin, otherwise MySQL will think that server
8830
and client plugins don't match.
8832
if (mpvio->acl_user->salt_len == 0)
8833
mpvio->acl_user_plugin= old_password_plugin_name;
8838
if the acl_user needs a different plugin to authenticate
8839
(specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
8840
we need to restart the authentication in the server.
8841
But perhaps the client has already used the correct plugin -
8842
in that case the authentication on the client may not need to be
8843
restarted and a server auth plugin will read the data that the client
8844
has just send. Cache them to return in the next server_mpvio_read_packet().
8846
if (my_strcasecmp(system_charset_info, mpvio->acl_user_plugin.str,
8847
plugin_name(mpvio->plugin)->str) != 0)
8849
mpvio->cached_client_reply.pkt= passwd;
8850
mpvio->cached_client_reply.pkt_len= passwd_len;
8851
mpvio->cached_client_reply.plugin= client_plugin;
8852
mpvio->status= MPVIO_EXT::RESTART;
8853
return packet_error;
8857
ok, we don't need to restart the authentication on the server.
8858
but if the client used the wrong plugin, we need to restart
8859
the authentication on the client. Do it here, the server plugin
8860
doesn't need to know.
8862
const char *client_auth_plugin=
8863
((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
8865
if (client_auth_plugin &&
8866
my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
8868
mpvio->cached_client_reply.plugin= client_plugin;
8869
if (send_plugin_request_packet(mpvio,
8870
(uchar*) mpvio->cached_server_packet.pkt,
8871
mpvio->cached_server_packet.pkt_len))
8872
return packet_error;
8874
passwd_len= my_net_read(mpvio->net);
8875
passwd = (char*) mpvio->net->read_pos;
8878
*buff= (uchar*) passwd;
8887
Make sure that when sending plugin supplied data to the client they
8888
are not considered a special out-of-band command, like e.g.
8889
\255 (error) or \254 (change user request packet) or \0 (OK).
8890
To avoid this the server will send all plugin data packets "wrapped"
8892
Note that the client will continue sending its replies unrwapped.
8896
wrap_plguin_data_into_proper_command(NET *net,
8897
const uchar *packet, int packet_len)
8899
return net_write_command(net, 1, (uchar *) "", 0, packet, packet_len);
8904
vio->write_packet() callback method for server authentication plugins
8906
This function is called by a server authentication plugin, when it wants
8907
to send data to the client.
8909
It transparently wraps the data into a handshake packet,
8910
and handles plugin negotiation with the client. If necessary,
8911
it escapes the plugin data, if it starts with a mysql protocol packet byte.
8913
static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
8914
const uchar *packet, int packet_len)
8916
MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
8919
DBUG_ENTER("server_mpvio_write_packet");
8921
Reset cached_client_reply if not an old client doing mysql_change_user,
8922
as this is where the password from COM_CHANGE_USER is stored.
8924
if (!((!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)) &&
8925
mpvio->status == MPVIO_EXT::RESTART &&
8926
mpvio->cached_client_reply.plugin ==
8927
((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin
8929
mpvio->cached_client_reply.pkt= 0;
8930
/* for the 1st packet we wrap plugin data into the handshake packet */
8931
if (mpvio->packets_written == 0)
8932
res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
8933
else if (mpvio->status == MPVIO_EXT::RESTART)
8934
res= send_plugin_request_packet(mpvio, packet, packet_len);
8936
res= wrap_plguin_data_into_proper_command(mpvio->net, packet, packet_len);
8937
mpvio->packets_written++;
8942
vio->read_packet() callback method for server authentication plugins
8944
This function is called by a server authentication plugin, when it wants
8945
to read data from the client.
8947
It transparently extracts the client plugin data, if embedded into
8948
a client authentication handshake packet, and handles plugin negotiation
8949
with the client, if necessary.
8951
static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
8953
MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
8956
DBUG_ENTER("server_mpvio_read_packet");
8957
if (mpvio->packets_written == 0)
8960
plugin wants to read the data without sending anything first.
8961
send an empty packet to force a server handshake packet to be sent
8963
if (mpvio->write_packet(mpvio, 0, 0))
8964
pkt_len= packet_error;
8966
pkt_len= my_net_read(mpvio->net);
8968
else if (mpvio->cached_client_reply.pkt)
8970
DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
8971
DBUG_ASSERT(mpvio->packets_read > 0);
8973
if the have the data cached from the last server_mpvio_read_packet
8974
(which can be the case if it's a restarted authentication)
8975
and a client has used the correct plugin, then we can return the
8976
cached data straight away and avoid one round trip.
8978
const char *client_auth_plugin=
8979
((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
8980
if (client_auth_plugin == 0 ||
8981
my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
8982
client_auth_plugin) == 0)
8984
mpvio->status= MPVIO_EXT::FAILURE;
8985
*buf= (uchar*) mpvio->cached_client_reply.pkt;
8986
mpvio->cached_client_reply.pkt= 0;
8987
mpvio->packets_read++;
8988
DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
8991
/* older clients don't support change of client plugin request */
8992
if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
8994
mpvio->status= MPVIO_EXT::FAILURE;
8995
pkt_len= packet_error;
9000
But if the client has used the wrong plugin, the cached data are
9001
useless. Furthermore, we have to send a "change plugin" request
9004
if (mpvio->write_packet(mpvio, 0, 0))
9005
pkt_len= packet_error;
9007
pkt_len= my_net_read(mpvio->net);
9010
pkt_len= my_net_read(mpvio->net);
9012
if (pkt_len == packet_error)
9015
mpvio->packets_read++;
9018
the 1st packet has the plugin data wrapped into the client authentication
9021
if (mpvio->packets_read == 1)
9023
pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
9024
if (pkt_len == packet_error)
9028
*buf= mpvio->net->read_pos;
9030
DBUG_RETURN((int)pkt_len);
9033
if (mpvio->status == MPVIO_EXT::FAILURE)
9035
inc_host_errors(mpvio->ip);
9036
my_error(ER_HANDSHAKE_ERROR, MYF(0));
9042
fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
9045
static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
9046
MYSQL_PLUGIN_VIO_INFO *info)
9048
MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
9049
mpvio_info(mpvio->net->vio, info);
9052
#ifndef NO_EMBEDDED_ACCESS_CHECKS
9053
static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
9055
#if defined(HAVE_OPENSSL)
9056
Vio *vio= thd->net.vio;
9057
SSL *ssl= (SSL *) vio->ssl_arg;
9062
At this point we know that user is allowed to connect
9063
from given host by given username/password pair. Now
9064
we check if SSL is required, if user is using SSL and
9065
if X509 certificate attributes are OK
9067
switch (acl_user->ssl_type) {
9068
case SSL_TYPE_NOT_SPECIFIED: // Impossible
9069
case SSL_TYPE_NONE: // SSL is not required
9071
#if defined(HAVE_OPENSSL)
9072
case SSL_TYPE_ANY: // Any kind of SSL is ok
9073
return vio_type(vio) != VIO_TYPE_SSL;
9074
case SSL_TYPE_X509: /* Client should have any valid certificate. */
9076
Connections with non-valid certificates are dropped already
9077
in sslaccept() anyway, so we do not check validity here.
9079
We need to check for absence of SSL because without SSL
9080
we should reject connection.
9082
if (vio_type(vio) == VIO_TYPE_SSL &&
9083
SSL_get_verify_result(ssl) == X509_V_OK &&
9084
(cert= SSL_get_peer_certificate(ssl)))
9090
case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
9091
/* If a cipher name is specified, we compare it to actual cipher in use. */
9092
if (vio_type(vio) != VIO_TYPE_SSL ||
9093
SSL_get_verify_result(ssl) != X509_V_OK)
9095
if (acl_user->ssl_cipher)
9097
DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
9098
acl_user->ssl_cipher, SSL_get_cipher(ssl)));
9099
if (strcmp(acl_user->ssl_cipher, SSL_get_cipher(ssl)))
9101
if (global_system_variables.log_warnings)
9102
sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
9103
acl_user->ssl_cipher, SSL_get_cipher(ssl));
9107
/* Prepare certificate (if exists) */
9108
if (!(cert= SSL_get_peer_certificate(ssl)))
9110
/* If X509 issuer is specified, we check it... */
9111
if (acl_user->x509_issuer)
9113
char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
9114
DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
9115
acl_user->x509_issuer, ptr));
9116
if (strcmp(acl_user->x509_issuer, ptr))
9118
if (global_system_variables.log_warnings)
9119
sql_print_information("X509 issuer mismatch: should be '%s' "
9120
"but is '%s'", acl_user->x509_issuer, ptr);
9127
/* X509 subject is specified, we check it .. */
9128
if (acl_user->x509_subject)
9130
char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
9131
DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
9132
acl_user->x509_subject, ptr));
9133
if (strcmp(acl_user->x509_subject, ptr))
9135
if (global_system_variables.log_warnings)
9136
sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
9137
acl_user->x509_subject, ptr);
9146
#else /* HAVE_OPENSSL */
9149
If we don't have SSL but SSL is required for this user the
9150
authentication should fail.
9153
#endif /* HAVE_OPENSSL */
9160
static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
9163
int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
9164
bool unlock_plugin= false;
9167
if (auth_plugin_name->str == native_password_plugin_name.str)
9168
plugin= native_password_plugin;
9170
#ifndef EMBEDDED_LIBRARY
9171
if (auth_plugin_name->str == old_password_plugin_name.str)
9172
plugin= old_password_plugin;
9173
else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
9174
MYSQL_AUTHENTICATION_PLUGIN)))
9175
unlock_plugin= true;
9180
mpvio->plugin= plugin;
9181
old_status= mpvio->status;
9185
st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
9186
res= auth->authenticate_user(mpvio, &mpvio->auth_info);
9189
plugin_unlock(thd, plugin);
9193
/* Server cannot load the required plugin. */
9194
my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
9199
If the status was MPVIO_EXT::RESTART before the authenticate_user() call
9200
it can never be MPVIO_EXT::RESTART after the call, because any call
9201
to write_packet() or read_packet() will reset the status.
9203
But (!) if a plugin never called a read_packet() or write_packet(), the
9204
status will stay unchanged. We'll fix it, by resetting the status here.
9206
if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART)
9207
mpvio->status= MPVIO_EXT::FAILURE; // reset to the default
9214
server_mpvio_initialize(THD *thd, MPVIO_EXT *mpvio, uint connect_errors,
9215
Thd_charset_adapter *charset_adapter)
9217
memset(mpvio, 0, sizeof(MPVIO_EXT));
9218
mpvio->read_packet= server_mpvio_read_packet;
9219
mpvio->write_packet= server_mpvio_write_packet;
9220
mpvio->info= server_mpvio_info;
9221
mpvio->auth_info.host_or_ip= thd->security_ctx->host_or_ip;
9222
mpvio->auth_info.host_or_ip_length=
9223
(unsigned int) strlen(thd->security_ctx->host_or_ip);
9224
mpvio->auth_info.user_name= NULL;
9225
mpvio->auth_info.user_name_length= 0;
9226
mpvio->connect_errors= connect_errors;
9227
mpvio->status= MPVIO_EXT::FAILURE;
9229
mpvio->client_capabilities= thd->client_capabilities;
9230
mpvio->mem_root= thd->mem_root;
9231
mpvio->scramble= thd->scramble;
9232
mpvio->rand= &thd->rand;
9233
mpvio->thread_id= thd->thread_id;
9234
mpvio->server_status= &thd->server_status;
9235
mpvio->net= &thd->net;
9236
mpvio->ip= thd->security_ctx->ip;
9237
mpvio->host= thd->security_ctx->host;
9238
mpvio->charset_adapter= charset_adapter;
9243
server_mpvio_update_thd(THD *thd, MPVIO_EXT *mpvio)
9245
thd->client_capabilities= mpvio->client_capabilities;
9246
thd->max_client_packet_length= mpvio->max_client_packet_length;
9247
if (mpvio->client_capabilities & CLIENT_INTERACTIVE)
9248
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
9249
thd->security_ctx->user= mpvio->auth_info.user_name;
9250
if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
9251
thd->variables.sql_mode|= MODE_IGNORE_SPACE;
9255
Perform the handshake, authorize the client and update thd sctx variables.
9257
@param thd thread handle
9258
@param connect_errors number of previous failed connect attemps
9260
@param com_change_user_pkt_len size of the COM_CHANGE_USER packet
9261
(without the first, command, byte) or 0
9262
if it's not a COM_CHANGE_USER (that is, if
9263
it's a new connection)
9265
@retval 0 success, thd is updated.
9269
acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len)
9273
Thd_charset_adapter charset_adapter(thd);
9275
const LEX_STRING *auth_plugin_name= default_auth_plugin_name;
9276
enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
9279
DBUG_ENTER("acl_authenticate");
9280
compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
9282
server_mpvio_initialize(thd, &mpvio, connect_errors, &charset_adapter);
9284
DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
9287
Clear thd->db as it points to something, that will be freed when
9288
connection is closed. We don't want to accidentally free a wrong
9289
pointer if connect failed.
9291
thd->reset_db(NULL, 0);
9293
if (command == COM_CHANGE_USER)
9295
mpvio.packets_written++; // pretend that a server handshake packet was sent
9296
mpvio.packets_read++; // take COM_CHANGE_USER packet into account
9298
/* Clear variables that are allocated */
9299
thd->set_user_connect(NULL);
9301
if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
9303
server_mpvio_update_thd(thd, &mpvio);
9307
DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
9308
mpvio.status == MPVIO_EXT::SUCCESS);
9312
/* mark the thd as having no scramble yet */
9313
mpvio.scramble[SCRAMBLE_LENGTH]= 1;
9316
perform the first authentication attempt, with the default plugin.
9317
This sends the server handshake packet, reads the client reply
9318
with a user name, and performs the authentication if everyone has used
9322
res= do_auth_once(thd, auth_plugin_name, &mpvio);
9326
retry the authentication, if - after receiving the user name -
9327
we found that we need to switch to a non-default plugin
9329
if (mpvio.status == MPVIO_EXT::RESTART)
9331
DBUG_ASSERT(mpvio.acl_user);
9332
DBUG_ASSERT(command == COM_CHANGE_USER ||
9333
my_strcasecmp(system_charset_info, auth_plugin_name->str,
9334
mpvio.acl_user->plugin.str));
9335
auth_plugin_name= &mpvio.acl_user->plugin;
9336
res= do_auth_once(thd, auth_plugin_name, &mpvio);
9339
server_mpvio_update_thd(thd, &mpvio);
9341
Security_context *sctx= thd->security_ctx;
9342
const ACL_USER *acl_user= mpvio.acl_user;
9344
thd->password= mpvio.auth_info.password_used; // remember for error messages
9347
Log the command here so that the user can check the log
9348
for the tried logins and also to detect break-in attempts.
9350
if sctx->user is unset it's protocol failure, bad packet.
9352
if (mpvio.auth_info.user_name)
9354
if (strcmp(mpvio.auth_info.authenticated_as, mpvio.auth_info.user_name))
9356
general_log_print(thd, command, "%s@%s as %s on %s",
9357
mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
9358
mpvio.auth_info.authenticated_as ?
9359
mpvio.auth_info.authenticated_as : "anonymous",
9360
mpvio.db.str ? mpvio.db.str : (char*) "");
9363
general_log_print(thd, command, (char*) "%s@%s on %s",
9364
mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
9365
mpvio.db.str ? mpvio.db.str : (char*) "");
9368
if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
9370
DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
9372
if (!thd->is_error())
9373
login_failed_error(&mpvio, mpvio.auth_info.password_used);
9377
sctx->proxy_user[0]= 0;
9379
if (initialized) // if not --skip-grant-tables
9381
#ifndef NO_EMBEDDED_ACCESS_CHECKS
9382
bool is_proxy_user= FALSE;
9383
const char *auth_user = acl_user->user ? acl_user->user : "";
9384
ACL_PROXY_USER *proxy_user;
9385
/* check if the user is allowed to proxy as another user */
9386
proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
9387
mpvio.auth_info.authenticated_as,
9391
ACL_USER *acl_proxy_user;
9393
/* we need to find the proxy user, but there was none */
9396
if (!thd->is_error())
9397
login_failed_error(&mpvio, mpvio.auth_info.password_used);
9401
my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
9402
"'%s'@'%s'", auth_user,
9403
acl_user->host.hostname ? acl_user->host.hostname : "");
9405
/* we're proxying : find the proxy user definition */
9406
mysql_mutex_lock(&acl_cache->lock);
9407
acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ?
9408
proxy_user->get_proxied_host() : "",
9409
mpvio.auth_info.authenticated_as, TRUE);
9410
if (!acl_proxy_user)
9412
if (!thd->is_error())
9413
login_failed_error(&mpvio, mpvio.auth_info.password_used);
9414
mysql_mutex_unlock(&acl_cache->lock);
9417
acl_user= acl_proxy_user->copy(thd->mem_root);
9418
mysql_mutex_unlock(&acl_cache->lock);
9422
sctx->master_access= acl_user->access;
9424
strmake(sctx->priv_user, acl_user->user, USERNAME_LENGTH - 1);
9426
*sctx->priv_user= 0;
9428
if (acl_user->host.hostname)
9429
strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
9431
*sctx->priv_host= 0;
9433
#ifndef NO_EMBEDDED_ACCESS_CHECKS
9435
OK. Let's check the SSL. Historically it was checked after the password,
9436
as an additional layer, not instead of the password
9437
(in which case it would've been a plugin too).
9439
if (acl_check_ssl(thd, acl_user))
9441
if (!thd->is_error())
9442
login_failed_error(&mpvio, thd->password);
9446
/* Don't allow the user to connect if he has done too many queries */
9447
if ((acl_user->user_resource.questions || acl_user->user_resource.updates ||
9448
acl_user->user_resource.conn_per_hour ||
9449
acl_user->user_resource.user_conn ||
9450
global_system_variables.max_user_connections) &&
9451
get_or_create_user_conn(thd,
9452
(opt_old_style_user_limits ? sctx->user : sctx->priv_user),
9453
(opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
9454
&acl_user->user_resource))
9455
DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
9460
sctx->skip_grants();
9462
const USER_CONN *uc;
9463
if ((uc= thd->get_user_connect()) &&
9464
(uc->user_resources.conn_per_hour || uc->user_resources.user_conn ||
9465
global_system_variables.max_user_connections) &&
9466
check_for_max_user_connections(thd, uc))
9468
DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
9472
("Capabilities: %lu packet_length: %ld Host: '%s' "
9473
"Login user: '%s' Priv_user: '%s' Using password: %s "
9474
"Access: %lu db: '%s'",
9475
thd->client_capabilities, thd->max_client_packet_length,
9476
sctx->host_or_ip, sctx->user, sctx->priv_user,
9477
thd->password ? "yes": "no",
9478
sctx->master_access, mpvio.db.str));
9480
if (command == COM_CONNECT &&
9481
!(thd->main_security_ctx.master_access & SUPER_ACL))
9483
mysql_mutex_lock(&LOCK_connection_count);
9484
bool count_ok= (connection_count <= max_connections);
9485
mysql_mutex_unlock(&LOCK_connection_count);
9487
{ // too many connections
9488
release_user_connection(thd);
9489
my_error(ER_CON_COUNT_ERROR, MYF(0));
9495
This is the default access rights for the current database. It's
9496
set to 0 here because we don't have an active database yet (and we
9497
may not have an active database to set.
9501
/* Change a database if necessary */
9502
if (mpvio.db.length)
9504
if (mysql_change_db(thd, &mpvio.db, FALSE))
9506
/* mysql_change_db() has pushed the error message. */
9507
release_user_connection(thd);
9512
if (mpvio.auth_info.external_user[0])
9513
sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));
9515
if (res == CR_OK_HANDSHAKE_COMPLETE)
9516
thd->stmt_da->disable_status();
9520
#if defined(MYSQL_SERVER) && !defined(EMBEDDED_LIBRARY)
9522
Allow the network layer to skip big packets. Although a malicious
9523
authenticated session might use this to trick the server to read
9524
big packets indefinitely, this is a previously established behavior
9525
that needs to be preserved as to not break backwards compatibility.
9527
thd->net.skip_big_packet= TRUE;
9530
/* Ready to handle queries */
9535
MySQL Server Password Authentication Plugin
9537
In the MySQL authentication protocol:
9538
1. the server sends the random scramble to the client
9539
2. client sends the encrypted password back to the server
9540
3. the server checks the password.
9542
static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
9543
MYSQL_SERVER_AUTH_INFO *info)
9547
MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
9549
DBUG_ENTER("native_password_authenticate");
9551
/* generate the scramble, or reuse the old one */
9552
if (mpvio->scramble[SCRAMBLE_LENGTH])
9553
create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
9555
/* send it to the client */
9556
if (mpvio->write_packet(mpvio, (uchar*) mpvio->scramble, SCRAMBLE_LENGTH + 1))
9557
DBUG_RETURN(CR_ERROR);
9559
/* reply and authenticate */
9563
This is more complex than it looks.
9565
The plugin (we) may be called right after the client was connected -
9566
and will need to send a scramble, read reply, authenticate.
9568
Or the plugin may be called after another plugin has sent a scramble,
9569
and read the reply. If the client has used the correct client-plugin,
9570
we won't need to read anything here from the client, the client
9571
has already sent a reply with everything we need for authentication.
9573
Or the plugin may be called after another plugin has sent a scramble,
9574
and read the reply, but the client has used the wrong client-plugin.
9575
We'll need to sent a "switch to another plugin" packet to the
9576
client and read the reply. "Use the short scramble" packet is a special
9577
case of "switch to another plugin" packet.
9579
Or, perhaps, the plugin may be called after another plugin has
9580
done the handshake but did not send a useful scramble. We'll need
9581
to send a scramble (and perhaps a "switch to another plugin" packet)
9584
Besides, a client may be an old one, that doesn't understand plugins.
9585
Or doesn't even understand 4.0 scramble.
9587
And we want to keep the same protocol on the wire unless non-native
9588
plugins are involved.
9590
Anyway, it still looks simple from a plugin point of view:
9591
"send the scramble, read the reply and authenticate"
9592
All the magic is transparently handled by the server.
9596
/* read the reply with the encrypted password */
9597
if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
9598
DBUG_RETURN(CR_ERROR);
9599
DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
9601
#ifdef NO_EMBEDDED_ACCESS_CHECKS
9605
if (pkt_len == 0) /* no password */
9606
DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_ERROR : CR_OK);
9608
info->password_used= PASSWORD_USED_YES;
9609
if (pkt_len == SCRAMBLE_LENGTH)
9611
if (!mpvio->acl_user->salt_len)
9612
DBUG_RETURN(CR_ERROR);
9614
DBUG_RETURN(check_scramble(pkt, mpvio->scramble, mpvio->acl_user->salt) ?
9618
inc_host_errors(mpvio->ip);
9619
my_error(ER_HANDSHAKE_ERROR, MYF(0));
9620
DBUG_RETURN(CR_ERROR);
9623
static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
9624
MYSQL_SERVER_AUTH_INFO *info)
9628
MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
9630
/* generate the scramble, or reuse the old one */
9631
if (mpvio->scramble[SCRAMBLE_LENGTH])
9632
create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
9634
/* send it to the client */
9635
if (mpvio->write_packet(mpvio, (uchar*) mpvio->scramble, SCRAMBLE_LENGTH + 1))
9638
/* read the reply and authenticate */
9639
if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
9642
#ifdef NO_EMBEDDED_ACCESS_CHECKS
9647
legacy: if switch_from_long_to_short_scramble,
9648
the password is sent \0-terminated, the pkt_len is always 9 bytes.
9649
We need to figure out the correct scramble length here.
9651
if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
9652
pkt_len= strnlen((char*)pkt, pkt_len);
9654
if (pkt_len == 0) /* no password */
9655
return mpvio->acl_user->salt_len != 0 ? CR_ERROR : CR_OK;
9657
if (secure_auth(mpvio))
9660
info->password_used= PASSWORD_USED_YES;
9662
if (pkt_len == SCRAMBLE_LENGTH_323)
9664
if (!mpvio->acl_user->salt_len)
9667
return check_scramble_323(pkt, mpvio->scramble,
9668
(ulong *) mpvio->acl_user->salt) ?
9672
inc_host_errors(mpvio->ip);
9673
my_error(ER_HANDSHAKE_ERROR, MYF(0));
9677
static struct st_mysql_auth native_password_handler=
9679
MYSQL_AUTHENTICATION_INTERFACE_VERSION,
9680
native_password_plugin_name.str,
9681
native_password_authenticate
9684
static struct st_mysql_auth old_password_handler=
9686
MYSQL_AUTHENTICATION_INTERFACE_VERSION,
9687
old_password_plugin_name.str,
9688
old_password_authenticate
9691
mysql_declare_plugin(mysql_password)
9693
MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
9694
&native_password_handler, /* type descriptor */
9695
native_password_plugin_name.str, /* Name */
9696
"R.J.Silk, Sergei Golubchik", /* Author */
9697
"Native MySQL authentication", /* Description */
9698
PLUGIN_LICENSE_GPL, /* License */
9699
NULL, /* Init function */
9700
NULL, /* Deinit function */
9701
0x0100, /* Version (1.0) */
9702
NULL, /* status variables */
9703
NULL, /* system variables */
9704
NULL, /* config options */
9708
MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
9709
&old_password_handler, /* type descriptor */
9710
old_password_plugin_name.str, /* Name */
9711
"R.J.Silk, Sergei Golubchik", /* Author */
9712
"Old MySQL-4.0 authentication", /* Description */
9713
PLUGIN_LICENSE_GPL, /* License */
9714
NULL, /* Init function */
9715
NULL, /* Deinit function */
9716
0x0100, /* Version (1.0) */
9717
NULL, /* status variables */
9718
NULL, /* system variables */
9719
NULL, /* config options */
9722
mysql_declare_plugin_end;