sql_acl.cc 177 KB
Newer Older
1
/* Copyright (C) 2000-2003 MySQL AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4 5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
unknown's avatar
unknown committed
7

unknown's avatar
unknown committed
8 9 10 11
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
unknown's avatar
unknown committed
12

unknown's avatar
unknown committed
13 14 15 16 17 18 19
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */


/*
  The privileges are saved in the following tables:
20 21
  mysql/user	 ; super user who are allowed to do almost anything
  mysql/host	 ; host privileges. This is used if host is empty in mysql/db.
unknown's avatar
unknown committed
22 23 24 25 26 27 28 29
  mysql/db	 ; database privileges / user

  data in tables is sorted according to how many not-wild-cards there is
  in the relevant fields. Empty strings comes last.
*/

#include "mysql_priv.h"
#include "hash_filo.h"
30 31 32
#ifdef HAVE_REPLICATION
#include "sql_repl.h" //for tables_ok()
#endif
unknown's avatar
unknown committed
33 34
#include <m_ctype.h>
#include <stdarg.h>
35 36
#include "sp_head.h"
#include "sp.h"
unknown's avatar
unknown committed
37

unknown's avatar
SCRUM:  
unknown committed
38
#ifndef NO_EMBEDDED_ACCESS_CHECKS
39

unknown's avatar
unknown committed
40 41 42
class acl_entry :public hash_filo_element
{
public:
unknown's avatar
unknown committed
43
  ulong access;
unknown's avatar
unknown committed
44 45 46 47
  uint16 length;
  char key[1];					// Key will be stored here
};

unknown's avatar
unknown committed
48

unknown's avatar
unknown committed
49 50 51 52 53 54 55
static byte* acl_entry_get_key(acl_entry *entry,uint *length,
			       my_bool not_used __attribute__((unused)))
{
  *length=(uint) entry->length;
  return (byte*) entry->key;
}

unknown's avatar
unknown committed
56
#define IP_ADDR_STRLEN (3+1+3+1+3+1+3)
unknown's avatar
unknown committed
57
#define ACL_KEY_LENGTH (IP_ADDR_STRLEN+1+NAME_LEN+1+USERNAME_LENGTH+1)
unknown's avatar
unknown committed
58 59 60 61 62

static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
63
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
unknown's avatar
unknown committed
64 65
static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
66
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
unknown's avatar
unknown committed
67
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
unknown's avatar
unknown committed
68 69 70
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
71
static void rebuild_check_host(void);
72 73
static ACL_USER *find_acl_user(const char *host, const char *user,
                               my_bool exact);
74 75
static bool update_user_table(THD *thd, TABLE *table,
                              const char *host, const char *user,
76
			      const char *new_password, uint new_password_len);
unknown's avatar
unknown committed
77
static void update_hostname(acl_host_and_ip *host, const char *hostname);
78
static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
unknown's avatar
unknown committed
79
			     const char *ip);
80 81
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
static my_bool grant_load(TABLE_LIST *tables);
unknown's avatar
unknown committed
82

83 84 85 86 87 88 89 90 91 92 93 94 95 96
/*
  Convert scrambled password to binary form, according to scramble type, 
  Binary form is stored in user.salt.
*/

static
void
set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
{
  if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
  {
    get_salt_from_password(acl_user->salt, password);
    acl_user->salt_len= SCRAMBLE_LENGTH;
  }
97
  else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
98 99
  {
    get_salt_from_password_323((ulong *) acl_user->salt, password);
100
    acl_user->salt_len= SCRAMBLE_LENGTH_323;
101 102 103 104 105
  }
  else
    acl_user->salt_len= 0;
}

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
/*
  This after_update function is used when user.password is less than
  SCRAMBLE_LENGTH bytes.
*/

static void restrict_update_of_old_passwords_var(THD *thd,
                                                 enum_var_type var_type)
{
  if (var_type == OPT_GLOBAL)
  {
    pthread_mutex_lock(&LOCK_global_system_variables);
    global_system_variables.old_passwords= 1;
    pthread_mutex_unlock(&LOCK_global_system_variables);
  }
  else
    thd->variables.old_passwords= 1;
}

124

125
/*
126 127
  Initialize structures responsible for user/db-level privilege checking and
  load privilege information for them from tables in the 'mysql' database.
128 129 130

  SYNOPSIS
    acl_init()
131 132 133 134 135 136
      dont_read_acl_tables  TRUE if we want to skip loading data from
                            privilege tables and disable privilege checking.

  NOTES
    This function is mostly responsible for preparatory steps, main work
    on initialization and grants loading is done in acl_reload().
137 138 139 140 141 142

  RETURN VALUES
    0	ok
    1	Could not initialize grant's
*/

143
my_bool acl_init(bool dont_read_acl_tables)
unknown's avatar
unknown committed
144
{
unknown's avatar
unknown committed
145
  THD  *thd;
146
  my_bool return_val;
unknown's avatar
unknown committed
147 148
  DBUG_ENTER("acl_init");

149 150
  acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
                           (hash_get_key) acl_entry_get_key,
151
                           (hash_free_key) free,
152 153
                           lower_case_file_system ?
                           system_charset_info : &my_charset_bin);
unknown's avatar
unknown committed
154
  if (dont_read_acl_tables)
155
  {
unknown's avatar
unknown committed
156
    DBUG_RETURN(0); /* purecov: tested */
unknown's avatar
unknown committed
157 158
  }

159 160 161
  /*
    To be able to run this from boot, we allocate a temporary THD
  */
unknown's avatar
unknown committed
162 163
  if (!(thd=new THD))
    DBUG_RETURN(1); /* purecov: inspected */
164
  thd->thread_stack= (char*) &thd;
165
  thd->store_globals();
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
  /*
    It is safe to call acl_reload() since acl_* arrays and hashes which
    will be freed there are global static objects and thus are initialized
    by zeros at startup.
  */
  return_val= acl_reload(thd);
  delete thd;
  /* Remember that we don't have a THD */
  my_pthread_setspecific_ptr(THR_THD,  0);
  DBUG_RETURN(return_val);
}


/*
  Initialize structures responsible for user/db-level privilege checking
  and load information about grants from open privilege tables.

  SYNOPSIS
    acl_load()
      thd     Current thread
      tables  List containing open "mysql.host", "mysql.user" and
              "mysql.db" tables.

  RETURN VALUES
    FALSE  Success
    TRUE   Error
*/

static my_bool acl_load(THD *thd, TABLE_LIST *tables)
{
  TABLE *table;
  READ_RECORD read_record_info;
  my_bool return_val= 1;
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
unknown's avatar
unknown committed
200
  char tmp_name[NAME_LEN+1];
201
  int password_length;
202 203
  DBUG_ENTER("acl_load");

204
  grant_version++; /* Privileges updated */
205

unknown's avatar
unknown committed
206 207
  acl_cache->clear(1);				// Clear locked hostname cache

208
  init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
unknown's avatar
unknown committed
209
  init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0);
210
  VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
unknown's avatar
unknown committed
211 212 213
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_HOST host;
214 215
    update_hostname(&host.host,get_field(&mem, table->field[0]));
    host.db=	 get_field(&mem, table->field[1]);
216
    if (lower_case_table_names && host.db)
217 218
    {
      /*
219 220
        convert db to lower case and give a warning if the db wasn't
        already in lower case
221
      */
222 223
      (void) strmov(tmp_name, host.db);
      my_casedn_str(files_charset_info, host.db);
224 225 226
      if (strcmp(host.db, tmp_name) != 0)
        sql_print_warning("'host' entry '%s|%s' had database in mixed "
                          "case that has been forced to lowercase because "
227 228
                          "lower_case_table_names is set. It will not be "
                          "possible to remove this privilege using REVOKE.",
229 230
                          host.host.hostname ? host.host.hostname : "",
                          host.db ? host.db : "");
231
    }
unknown's avatar
unknown committed
232 233
    host.access= get_access(table,2);
    host.access= fix_rights_for_db(host.access);
234
    host.sort=	 get_sort(2,host.host.hostname,host.db);
unknown's avatar
SCRUM  
unknown committed
235 236
    if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
    {
unknown's avatar
unknown committed
237
      sql_print_warning("'host' entry '%s|%s' "
unknown's avatar
SCRUM  
unknown committed
238
		      "ignored in --skip-name-resolve mode.",
239 240
			host.host.hostname ? host.host.hostname : "",
			host.db ? host.db : "");
unknown's avatar
SCRUM  
unknown committed
241 242
      continue;
    }
unknown's avatar
unknown committed
243
#ifndef TO_BE_REMOVED
244
    if (table->s->fields == 8)
unknown's avatar
unknown committed
245 246
    {						// Without grant
      if (host.access & CREATE_ACL)
247
	host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
unknown's avatar
unknown committed
248 249 250 251 252 253 254 255 256 257
    }
#endif
    VOID(push_dynamic(&acl_hosts,(gptr) &host));
  }
  qsort((gptr) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
	sizeof(ACL_HOST),(qsort_cmp) acl_compare);
  end_read_record(&read_record_info);
  freeze_size(&acl_hosts);

  init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0);
258
  VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
259 260 261
  password_length= table->field[2]->field_length /
    table->field[2]->charset()->mbmaxlen;
  if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
unknown's avatar
unknown committed
262
  {
263 264 265
    sql_print_error("Fatal error: mysql.user table is damaged or in "
                    "unsupported 3.20 format.");
    goto end;
unknown's avatar
unknown committed
266 267
  }

268
  DBUG_PRINT("info",("user table fields: %d, password length: %d",
269
		     table->s->fields, password_length));
unknown's avatar
unknown committed
270

271
  pthread_mutex_lock(&LOCK_global_system_variables);
272
  if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
273
  {
274 275 276 277 278 279 280 281 282 283 284 285 286 287
    if (opt_secure_auth)
    {
      pthread_mutex_unlock(&LOCK_global_system_variables);
      sql_print_error("Fatal error: mysql.user table is in old format, "
                      "but server started with --secure-auth option.");
      goto end;
    }
    sys_old_passwords.after_update= restrict_update_of_old_passwords_var;
    if (global_system_variables.old_passwords)
      pthread_mutex_unlock(&LOCK_global_system_variables);
    else
    {
      global_system_variables.old_passwords= 1;
      pthread_mutex_unlock(&LOCK_global_system_variables);
288 289 290
      sql_print_warning("mysql.user table is not updated to new password format; "
                        "Disabling new password usage until "
                        "mysql_fix_privilege_tables is run");
291 292 293 294
    }
    thd->variables.old_passwords= 1;
  }
  else
295
  {
296 297
    sys_old_passwords.after_update= 0;
    pthread_mutex_unlock(&LOCK_global_system_variables);
298 299
  }

unknown's avatar
unknown committed
300 301 302 303
  allow_all_hosts=0;
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_USER user;
304 305
    update_hostname(&user.host, get_field(&mem, table->field[0]));
    user.user= get_field(&mem, table->field[1]);
unknown's avatar
SCRUM  
unknown committed
306 307
    if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
    {
unknown's avatar
unknown committed
308 309
      sql_print_warning("'user' entry '%s@%s' "
                        "ignored in --skip-name-resolve mode.",
310 311
			user.user ? user.user : "",
			user.host.hostname ? user.host.hostname : "");
unknown's avatar
SCRUM  
unknown committed
312 313 314
      continue;
    }

315 316 317 318
    const char *password= get_field(&mem, table->field[2]);
    uint password_len= password ? strlen(password) : 0;
    set_user_salt(&user, password, password_len);
    if (user.salt_len == 0 && password_len != 0)
unknown's avatar
unknown committed
319
    {
320 321
      switch (password_len) {
      case 45: /* 4.1: to be removed */
unknown's avatar
unknown committed
322 323 324 325 326
        sql_print_warning("Found 4.1 style password for user '%s@%s'. "
                          "Ignoring user. "
                          "You should change password for this user.",
                          user.user ? user.user : "",
                          user.host.hostname ? user.host.hostname : "");
327 328
        break;
      default:
unknown's avatar
unknown committed
329 330 331
        sql_print_warning("Found invalid password for user: '%s@%s'; "
                          "Ignoring user", user.user ? user.user : "",
                           user.host.hostname ? user.host.hostname : "");
332 333
        break;
      }
unknown's avatar
unknown committed
334
    }
335
    else                                        // password is correct
unknown's avatar
unknown committed
336
    {
unknown's avatar
unknown committed
337 338
      uint next_field;
      user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
339 340 341 342
      /*
        if it is pre 5.0.1 privilege table then map CREATE privilege on
        CREATE VIEW & SHOW VIEW privileges
      */
343
      if (table->s->fields <= 31 && (user.access & CREATE_ACL))
344
        user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
345 346 347 348 349

      /*
        if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
        CREATE PROCEDURE & ALTER PROCEDURE privileges
      */
350
      if (table->s->fields <= 33 && (user.access & CREATE_ACL))
351
        user.access|= CREATE_PROC_ACL;
352
      if (table->s->fields <= 33 && (user.access & ALTER_ACL))
353 354
        user.access|= ALTER_PROC_ACL;

unknown's avatar
unknown committed
355 356 357 358 359 360
      /*
        pre 5.0.3 did not have CREATE_USER_ACL
      */
      if (table->s->fields <= 36 && (user.access & GRANT_ACL))
        user.access|= CREATE_USER_ACL;

361 362 363
      user.sort= get_sort(2,user.host.hostname,user.user);
      user.hostname_length= (user.host.hostname ?
                             (uint) strlen(user.host.hostname) : 0);
unknown's avatar
VIEW  
unknown committed
364

365 366
      /* Starting from 4.0.2 we have more fields */
      if (table->s->fields >= 31)
367
      {
unknown's avatar
unknown committed
368
        char *ssl_type=get_field(&mem, table->field[next_field++]);
369 370 371 372 373 374 375 376 377
        if (!ssl_type)
          user.ssl_type=SSL_TYPE_NONE;
        else if (!strcmp(ssl_type, "ANY"))
          user.ssl_type=SSL_TYPE_ANY;
        else if (!strcmp(ssl_type, "X509"))
          user.ssl_type=SSL_TYPE_X509;
        else  /* !strcmp(ssl_type, "SPECIFIED") */
          user.ssl_type=SSL_TYPE_SPECIFIED;

unknown's avatar
unknown committed
378 379 380
        user.ssl_cipher=   get_field(&mem, table->field[next_field++]);
        user.x509_issuer=  get_field(&mem, table->field[next_field++]);
        user.x509_subject= get_field(&mem, table->field[next_field++]);
381

unknown's avatar
unknown committed
382 383 384 385 386
        char *ptr = get_field(&mem, table->field[next_field++]);
        user.user_resource.questions=ptr ? atoi(ptr) : 0;
        ptr = get_field(&mem, table->field[next_field++]);
        user.user_resource.updates=ptr ? atoi(ptr) : 0;
        ptr = get_field(&mem, table->field[next_field++]);
387
        user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
388
        if (user.user_resource.questions || user.user_resource.updates ||
389
            user.user_resource.conn_per_hour)
390
          mqh_used=1;
391

392
        if (table->s->fields >= 36)
393 394 395 396 397 398 399
        {
          /* Starting from 5.0.3 we have max_user_connections field */
          ptr= get_field(&mem, table->field[next_field++]);
          user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
        }
        else
          user.user_resource.user_conn= 0;
400
      }
401 402 403
      else
      {
        user.ssl_type=SSL_TYPE_NONE;
404
        bzero((char *)&(user.user_resource),sizeof(user.user_resource));
unknown's avatar
unknown committed
405
#ifndef TO_BE_REMOVED
406
        if (table->s->fields <= 13)
407 408 409 410 411 412 413 414 415 416
        {						// Without grant
          if (user.access & CREATE_ACL)
            user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
        }
        /* Convert old privileges */
        user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
        if (user.access & FILE_ACL)
          user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
        if (user.access & PROCESS_ACL)
          user.access|= SUPER_ACL | EXECUTE_ACL;
unknown's avatar
unknown committed
417
#endif
418 419
      }
      VOID(push_dynamic(&acl_users,(gptr) &user));
420 421
      if (!user.host.hostname ||
	  (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
422
        allow_all_hosts=1;			// Anyone can connect
unknown's avatar
unknown committed
423
    }
unknown's avatar
unknown committed
424 425 426 427 428
  }
  qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
	sizeof(ACL_USER),(qsort_cmp) acl_compare);
  end_read_record(&read_record_info);
  freeze_size(&acl_users);
unknown's avatar
unknown committed
429

unknown's avatar
unknown committed
430
  init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0);
431
  VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100));
unknown's avatar
unknown committed
432 433 434
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_DB db;
435 436
    update_hostname(&db.host,get_field(&mem, table->field[0]));
    db.db=get_field(&mem, table->field[1]);
437 438
    if (!db.db)
    {
unknown's avatar
unknown committed
439
      sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
440
      continue;
441
    }
442
    db.user=get_field(&mem, table->field[2]);
unknown's avatar
SCRUM  
unknown committed
443 444
    if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
    {
unknown's avatar
unknown committed
445 446
      sql_print_warning("'db' entry '%s %s@%s' "
		        "ignored in --skip-name-resolve mode.",
447 448 449
		        db.db,
			db.user ? db.user : "",
			db.host.hostname ? db.host.hostname : "");
unknown's avatar
SCRUM  
unknown committed
450 451
      continue;
    }
unknown's avatar
unknown committed
452 453
    db.access=get_access(table,3);
    db.access=fix_rights_for_db(db.access);
454 455 456
    if (lower_case_table_names)
    {
      /*
457 458
        convert db to lower case and give a warning if the db wasn't
        already in lower case
459 460
      */
      (void)strmov(tmp_name, db.db);
461
      my_casedn_str(files_charset_info, db.db);
462 463 464 465
      if (strcmp(db.db, tmp_name) != 0)
      {
        sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
                          "case that has been forced to lowercase because "
466 467
                          "lower_case_table_names is set. It will not be "
                          "possible to remove this privilege using REVOKE.",
468 469 470
		          db.db,
			  db.user ? db.user : "",
			  db.host.hostname ? db.host.hostname : "");
471 472
      }
    }
unknown's avatar
unknown committed
473 474
    db.sort=get_sort(3,db.host.hostname,db.db,db.user);
#ifndef TO_BE_REMOVED
475
    if (table->s->fields <=  9)
unknown's avatar
unknown committed
476 477 478 479 480 481 482 483 484 485 486 487 488
    {						// Without grant
      if (db.access & CREATE_ACL)
	db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
    }
#endif
    VOID(push_dynamic(&acl_dbs,(gptr) &db));
  }
  qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
	sizeof(ACL_DB),(qsort_cmp) acl_compare);
  end_read_record(&read_record_info);
  freeze_size(&acl_dbs);
  init_check_host();

489 490 491 492 493
  initialized=1;
  return_val=0;

end:
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
494 495 496 497 498
}


void acl_free(bool end)
{
499
  free_root(&mem,MYF(0));
unknown's avatar
unknown committed
500 501 502 503 504 505 506 507 508 509 510 511 512 513
  delete_dynamic(&acl_hosts);
  delete_dynamic(&acl_users);
  delete_dynamic(&acl_dbs);
  delete_dynamic(&acl_wild_hosts);
  hash_free(&acl_check_hosts);
  if (!end)
    acl_cache->clear(1); /* purecov: inspected */
  else
  {
    delete acl_cache;
    acl_cache=0;
  }
}

514 515

/*
516 517
  Forget current user/db-level privileges and read new privileges
  from the privilege tables.
518 519 520

  SYNOPSIS
    acl_reload()
521 522 523 524 525 526 527 528 529 530 531
      thd  Current thread

  NOTE
    All tables of calling thread which were open and locked by LOCK TABLES
    statement will be unlocked and closed.
    This function is also used for initialization of structures responsible
    for user/db-level privilege checking.

  RETURN VALUE
    FALSE  Success
    TRUE   Failure
532
*/
unknown's avatar
unknown committed
533

534
my_bool acl_reload(THD *thd)
unknown's avatar
unknown committed
535
{
536
  TABLE_LIST tables[3];
unknown's avatar
unknown committed
537 538 539
  DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs;
  MEM_ROOT old_mem;
  bool old_initialized;
540
  my_bool return_val= 1;
unknown's avatar
unknown committed
541 542
  DBUG_ENTER("acl_reload");

543
  if (thd->locked_tables)
unknown's avatar
unknown committed
544
  {					// Can't have locked tables here
545 546 547
    thd->lock=thd->locked_tables;
    thd->locked_tables=0;
    close_thread_tables(thd);
unknown's avatar
unknown committed
548
  }
549 550 551 552 553 554

  /*
    To avoid deadlocks we should obtain table locks before
    obtaining acl_cache->lock mutex.
  */
  bzero((char*) tables, sizeof(tables));
555 556 557 558 559 560
  tables[0].alias= tables[0].table_name= (char*) "host";
  tables[1].alias= tables[1].table_name= (char*) "user";
  tables[2].alias= tables[2].table_name= (char*) "db";
  tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
  tables[0].next_local= tables[0].next_global= tables+1;
  tables[1].next_local= tables[1].next_global= tables+2;
561 562 563 564 565 566 567 568 569
  tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;

  if (simple_open_n_lock_tables(thd, tables))
  {
    sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
		    thd->net.last_error);
    goto end;
  }

unknown's avatar
unknown committed
570 571 572 573 574 575 576 577 578 579
  if ((old_initialized=initialized))
    VOID(pthread_mutex_lock(&acl_cache->lock));

  old_acl_hosts=acl_hosts;
  old_acl_users=acl_users;
  old_acl_dbs=acl_dbs;
  old_mem=mem;
  delete_dynamic(&acl_wild_hosts);
  hash_free(&acl_check_hosts);

580
  if ((return_val= acl_load(thd, tables)))
unknown's avatar
unknown committed
581
  {					// Error. Revert to old list
582
    DBUG_PRINT("error",("Reverting to old privileges"));
583
    acl_free();				/* purecov: inspected */
unknown's avatar
unknown committed
584 585 586 587 588 589 590 591
    acl_hosts=old_acl_hosts;
    acl_users=old_acl_users;
    acl_dbs=old_acl_dbs;
    mem=old_mem;
    init_check_host();
  }
  else
  {
592
    free_root(&old_mem,MYF(0));
unknown's avatar
unknown committed
593 594 595 596 597 598
    delete_dynamic(&old_acl_hosts);
    delete_dynamic(&old_acl_users);
    delete_dynamic(&old_acl_dbs);
  }
  if (old_initialized)
    VOID(pthread_mutex_unlock(&acl_cache->lock));
599 600 601
end:
  close_thread_tables(thd);
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
602 603 604
}


unknown's avatar
unknown committed
605 606
/*
  Get all access bits from table after fieldnr
unknown's avatar
unknown committed
607 608

  IMPLEMENTATION
unknown's avatar
unknown committed
609 610
  We know that the access privileges ends when there is no more fields
  or the field is not an enum with two elements.
unknown's avatar
unknown committed
611 612 613 614 615 616 617 618 619 620 621

  SYNOPSIS
    get_access()
    form        an open table to read privileges from.
                The record should be already read in table->record[0]
    fieldnr     number of the first privilege (that is ENUM('N','Y') field
    next_field  on return - number of the field next to the last ENUM
                (unless next_field == 0)

  RETURN VALUE
    privilege mask
unknown's avatar
unknown committed
622
*/
unknown's avatar
unknown committed
623

unknown's avatar
unknown committed
624
static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
unknown's avatar
unknown committed
625
{
unknown's avatar
unknown committed
626
  ulong access_bits=0,bit;
unknown's avatar
unknown committed
627
  char buff[2];
unknown's avatar
unknown committed
628
  String res(buff,sizeof(buff),&my_charset_latin1);
unknown's avatar
unknown committed
629 630
  Field **pos;

unknown's avatar
unknown committed
631 632 633
  for (pos=form->field+fieldnr, bit=1;
       *pos && (*pos)->real_type() == FIELD_TYPE_ENUM &&
	 ((Field_enum*) (*pos))->typelib->count == 2 ;
unknown's avatar
unknown committed
634
       pos++, fieldnr++, bit<<=1)
unknown's avatar
unknown committed
635
  {
636
    (*pos)->val_str(&res);
unknown's avatar
unknown committed
637
    if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
unknown's avatar
unknown committed
638
      access_bits|= bit;
unknown's avatar
unknown committed
639
  }
unknown's avatar
unknown committed
640 641
  if (next_field)
    *next_field=fieldnr;
unknown's avatar
unknown committed
642 643 644 645 646
  return access_bits;
}


/*
unknown's avatar
unknown committed
647 648 649 650 651
  Return a number which, if sorted 'desc', puts strings in this order:
    no wildcards
    wildcards
    empty string
*/
unknown's avatar
unknown committed
652 653 654 655 656 657 658

static ulong get_sort(uint count,...)
{
  va_list args;
  va_start(args,count);
  ulong sort=0;

659 660 661
  /* Should not use this function with more than 4 arguments for compare. */
  DBUG_ASSERT(count <= 4);

unknown's avatar
unknown committed
662 663
  while (count--)
  {
664 665 666
    char *start, *str= va_arg(args,char*);
    uint chars= 0;
    uint wild_pos= 0;           /* first wildcard position */
unknown's avatar
unknown committed
667

unknown's avatar
unknown committed
668
    if ((start= str))
unknown's avatar
unknown committed
669 670 671 672
    {
      for (; *str ; str++)
      {
	if (*str == wild_many || *str == wild_one || *str == wild_prefix)
673
        {
unknown's avatar
unknown committed
674
          wild_pos= (uint) (str - start) + 1;
675 676
          break;
        }
unknown's avatar
unknown committed
677
        chars= 128;                             // Marker that chars existed
unknown's avatar
unknown committed
678 679
      }
    }
unknown's avatar
unknown committed
680
    sort= (sort << 8) + (wild_pos ? min(wild_pos, 127) : chars);
unknown's avatar
unknown committed
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
  }
  va_end(args);
  return sort;
}


static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
{
  if (a->sort > b->sort)
    return -1;
  if (a->sort < b->sort)
    return 1;
  return 0;
}

696

697
/*
unknown's avatar
unknown committed
698 699
  Seek ACL entry for a user, check password, SSL cypher, and if
  everything is OK, update THD user data and USER_RESOURCES struct.
700

unknown's avatar
unknown committed
701 702 703 704
  IMPLEMENTATION
   This function does not check if the user has any sensible privileges:
   only user's existence and  validity is checked.
   Note, that entire operation is protected by acl_cache_lock.
unknown's avatar
unknown committed
705

706
  SYNOPSIS
707 708
    acl_getroot()
    thd         thread handle. If all checks are OK,
709 710
                thd->security_ctx->priv_user/master_access are updated.
                thd->security_ctx->host/ip/user are used for checks.
711 712
    mqh         user resources; on success mqh is reset, else
                unchanged
713
    passwd      scrambled & crypted password, received from client
714 715 716 717 718 719 720
                (to check): thd->scramble or thd->scramble_323 is
                used to decrypt passwd, so they must contain
                original random string,
    passwd_len  length of passwd, must be one of 0, 8,
                SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH
    'thd' and 'mqh' are updated on success; other params are IN.
  
unknown's avatar
unknown committed
721
  RETURN VALUE
722 723
    0  success: thd->priv_user, thd->priv_host, thd->master_access, mqh are
       updated
724
    1  user not found or authentication failure
unknown's avatar
unknown committed
725
    2  user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
726
   -1  user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
unknown's avatar
unknown committed
727 728
*/

729 730
int acl_getroot(THD *thd, USER_RESOURCES  *mqh,
                const char *passwd, uint passwd_len)
unknown's avatar
unknown committed
731
{
unknown's avatar
merge  
unknown committed
732 733 734
  ulong user_access= NO_ACCESS;
  int res= 1;
  ACL_USER *acl_user= 0;
735
  Security_context *sctx= thd->security_ctx;
736
  DBUG_ENTER("acl_getroot");
unknown's avatar
unknown committed
737 738

  if (!initialized)
739
  {
740 741 742
    /* 
      here if mysqld's been started with --skip-grant-tables option.
    */
743
    sctx->skip_grants();
744
    bzero((char*) mqh, sizeof(*mqh));
745
    DBUG_RETURN(0);
746
  }
747

unknown's avatar
unknown committed
748
  VOID(pthread_mutex_lock(&acl_cache->lock));
unknown's avatar
unknown committed
749

unknown's avatar
unknown committed
750
  /*
751 752 753
    Find acl entry in user database. Note, that find_acl_user is not the same,
    because it doesn't take into account the case when user is not empty,
    but acl_user->user is empty
unknown's avatar
unknown committed
754
  */
unknown's avatar
unknown committed
755

756
  for (uint i=0 ; i < acl_users.elements ; i++)
757
  {
unknown's avatar
unknown committed
758
    ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
759
    if (!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user))
unknown's avatar
unknown committed
760
    {
761
      if (compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
unknown's avatar
unknown committed
762
      {
763
        /* check password: it should be empty or valid */
unknown's avatar
unknown committed
764
        if (passwd_len == acl_user_tmp->salt_len)
unknown's avatar
unknown committed
765
        {
unknown's avatar
unknown committed
766
          if (acl_user_tmp->salt_len == 0 ||
unknown's avatar
cleanup  
unknown committed
767 768
              (acl_user_tmp->salt_len == SCRAMBLE_LENGTH ?
              check_scramble(passwd, thd->scramble, acl_user_tmp->salt) :
769
              check_scramble_323(passwd, thd->scramble,
unknown's avatar
cleanup  
unknown committed
770
                                 (ulong *) acl_user_tmp->salt)) == 0)
771
          {
unknown's avatar
unknown committed
772
            acl_user= acl_user_tmp;
773 774
            res= 0;
          }
unknown's avatar
unknown committed
775
        }
776
        else if (passwd_len == SCRAMBLE_LENGTH &&
unknown's avatar
unknown committed
777
                 acl_user_tmp->salt_len == SCRAMBLE_LENGTH_323)
778
          res= -1;
unknown's avatar
unknown committed
779
        else if (passwd_len == SCRAMBLE_LENGTH_323 &&
unknown's avatar
unknown committed
780
                 acl_user_tmp->salt_len == SCRAMBLE_LENGTH)
unknown's avatar
unknown committed
781
          res= 2;
782 783
        /* linear search complete: */
        break;
unknown's avatar
unknown committed
784
      }
unknown's avatar
unknown committed
785
    }
786
  }
787 788 789 790
  /*
    This was moved to separate tree because of heavy HAVE_OPENSSL case.
    If acl_user is not null, res is 0.
  */
unknown's avatar
unknown committed
791 792 793

  if (acl_user)
  {
794
    /* OK. User found and password checked continue validation */
795
#ifdef HAVE_OPENSSL
unknown's avatar
unknown committed
796
    Vio *vio=thd->net.vio;
unknown's avatar
unknown committed
797
    SSL *ssl= (SSL*) vio->ssl_arg;
798
#endif
unknown's avatar
merge  
unknown committed
799

800
    /*
unknown's avatar
unknown committed
801
      At this point we know that user is allowed to connect
802 803 804 805 806 807
      from given host by given username/password pair. Now
      we check if SSL is required, if user is using SSL and
      if X509 certificate attributes are OK
    */
    switch (acl_user->ssl_type) {
    case SSL_TYPE_NOT_SPECIFIED:		// Impossible
unknown's avatar
merge  
unknown committed
808 809
    case SSL_TYPE_NONE:				// SSL is not required
      user_access= acl_user->access;
810
      break;
811
#ifdef HAVE_OPENSSL
unknown's avatar
merge  
unknown committed
812
    case SSL_TYPE_ANY:				// Any kind of SSL is ok
813
      if (vio_type(vio) == VIO_TYPE_SSL)
unknown's avatar
merge  
unknown committed
814
	user_access= acl_user->access;
815 816 817 818 819
      break;
    case SSL_TYPE_X509: /* Client should have any valid certificate. */
      /*
	Connections with non-valid certificates are dropped already
	in sslaccept() anyway, so we do not check validity here.
unknown's avatar
merge  
unknown committed
820

unknown's avatar
unknown committed
821 822
	We need to check for absence of SSL because without SSL
	we should reject connection.
823
      */
unknown's avatar
unknown committed
824
      if (vio_type(vio) == VIO_TYPE_SSL &&
unknown's avatar
unknown committed
825 826
	  SSL_get_verify_result(ssl) == X509_V_OK &&
	  SSL_get_peer_certificate(ssl))
unknown's avatar
merge  
unknown committed
827
	user_access= acl_user->access;
828 829 830 831 832 833 834 835
      break;
    case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
      /*
	We do not check for absence of SSL because without SSL it does
	not pass all checks here anyway.
	If cipher name is specified, we compare it to actual cipher in
	use.
      */
unknown's avatar
unknown committed
836
      X509 *cert;
unknown's avatar
unknown committed
837
      if (vio_type(vio) != VIO_TYPE_SSL ||
unknown's avatar
unknown committed
838
	  SSL_get_verify_result(ssl) != X509_V_OK)
unknown's avatar
unknown committed
839
	break;
840
      if (acl_user->ssl_cipher)
unknown's avatar
unknown committed
841
      {
842
	DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
unknown's avatar
unknown committed
843 844
			   acl_user->ssl_cipher,SSL_get_cipher(ssl)));
	if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl)))
unknown's avatar
merge  
unknown committed
845
	  user_access= acl_user->access;
846 847
	else
	{
unknown's avatar
unknown committed
848
	  if (global_system_variables.log_warnings)
unknown's avatar
unknown committed
849 850 851
	    sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
			      acl_user->ssl_cipher,
			      SSL_get_cipher(ssl));
852 853
	  break;
	}
unknown's avatar
unknown committed
854
      }
855 856
      /* Prepare certificate (if exists) */
      DBUG_PRINT("info",("checkpoint 1"));
unknown's avatar
unknown committed
857 858 859 860 861
      if (!(cert= SSL_get_peer_certificate(ssl)))
      {
	user_access=NO_ACCESS;
	break;
      }
862
      DBUG_PRINT("info",("checkpoint 2"));
863
      /* If X509 issuer is specified, we check it... */
864
      if (acl_user->x509_issuer)
unknown's avatar
unknown committed
865
      {
unknown's avatar
unknown committed
866
        DBUG_PRINT("info",("checkpoint 3"));
unknown's avatar
unknown committed
867 868
        char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
869
			   acl_user->x509_issuer, ptr));
unknown's avatar
unknown committed
870
        if (strcmp(acl_user->x509_issuer, ptr))
871
        {
unknown's avatar
unknown committed
872
          if (global_system_variables.log_warnings)
unknown's avatar
unknown committed
873 874
            sql_print_information("X509 issuer mismatch: should be '%s' "
			      "but is '%s'", acl_user->x509_issuer, ptr);
875
          free(ptr);
876
          user_access=NO_ACCESS;
unknown's avatar
unknown committed
877
          break;
878
        }
unknown's avatar
merge  
unknown committed
879
        user_access= acl_user->access;
unknown's avatar
unknown committed
880
        free(ptr);
unknown's avatar
unknown committed
881
      }
882 883 884 885
      DBUG_PRINT("info",("checkpoint 4"));
      /* X509 subject is specified, we check it .. */
      if (acl_user->x509_subject)
      {
unknown's avatar
unknown committed
886 887 888 889
        char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
                           acl_user->x509_subject, ptr));
        if (strcmp(acl_user->x509_subject,ptr))
890
        {
unknown's avatar
unknown committed
891
          if (global_system_variables.log_warnings)
892
            sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
unknown's avatar
unknown committed
893
                            acl_user->x509_subject, ptr);
894 895 896
          free(ptr);
          user_access=NO_ACCESS;
          break;
897
        }
898
        user_access= acl_user->access;
unknown's avatar
unknown committed
899
        free(ptr);
900 901
      }
      break;
unknown's avatar
unknown committed
902
#else  /* HAVE_OPENSSL */
unknown's avatar
unknown committed
903
    default:
904
      /*
unknown's avatar
unknown committed
905 906 907
        If we don't have SSL but SSL is required for this user the 
        authentication should fail.
      */
908 909
      break;
#endif /* HAVE_OPENSSL */
unknown's avatar
unknown committed
910
    }
911 912
    sctx->master_access= user_access;
    sctx->priv_user= acl_user->user ? sctx->user : (char *) "";
913
    *mqh= acl_user->user_resource;
914

915
    if (acl_user->host.hostname)
916
      strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
917
    else
918
      *sctx->priv_host= 0;
919
  }
unknown's avatar
unknown committed
920
  VOID(pthread_mutex_unlock(&acl_cache->lock));
921
  DBUG_RETURN(res);
unknown's avatar
unknown committed
922 923 924
}


925
/*
926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
  This is like acl_getroot() above, but it doesn't check password,
  and we don't care about the user resources.

  SYNOPSIS
    acl_getroot_no_password()
      sctx               Context which should be initialized
      user               user name
      host               host name
      ip                 IP
      db                 current data base name

  RETURN
    FALSE  OK
    TRUE   Error
*/

bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
                             char *ip, char *db)
944 945
{
  int res= 1;
946
  uint i;
947 948 949
  ACL_USER *acl_user= 0;
  DBUG_ENTER("acl_getroot_no_password");

950 951
  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
                       (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
952
                       user, (db ? db : "(NULL)")));
953 954 955 956 957
  sctx->user= user;
  sctx->host= host;
  sctx->ip= ip;
  sctx->host_or_ip= host ? host : (ip ? ip : "");

958 959
  if (!initialized)
  {
960
    /*
961 962
      here if mysqld's been started with --skip-grant-tables option.
    */
963
    sctx->skip_grants();
964
    DBUG_RETURN(FALSE);
965 966 967 968
  }

  VOID(pthread_mutex_lock(&acl_cache->lock));

969 970
  sctx->master_access= 0;
  sctx->db_access= 0;
971 972
  sctx->priv_user= (char *) "";
  *sctx->priv_host= 0;
973

974 975 976
  /*
     Find acl entry in user database.
     This is specially tailored to suit the check we do for CALL of
977
     a stored procedure; user is set to what is actually a
978 979
     priv_user, which can be ''.
  */
980
  for (i=0 ; i < acl_users.elements ; i++)
981 982
  {
    acl_user= dynamic_element(&acl_users,i,ACL_USER*);
983
    if ((!acl_user->user && !user[0]) ||
984
	(acl_user->user && strcmp(user, acl_user->user) == 0))
985
    {
986
      if (compare_hostname(&acl_user->host, host, ip))
987 988 989 990 991 992 993 994 995
      {
	res= 0;
	break;
      }
    }
  }

  if (acl_user)
  {
996 997 998 999
    for (i=0 ; i < acl_dbs.elements ; i++)
    {
      ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
      if (!acl_db->user ||
1000
	  (user && user[0] && !strcmp(user, acl_db->user)))
1001
      {
1002
	if (compare_hostname(&acl_db->host, host, ip))
1003
	{
1004
	  if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
1005
	  {
1006
	    sctx->db_access= acl_db->access;
1007 1008 1009 1010 1011
	    break;
	  }
	}
      }
    }
1012 1013
    sctx->master_access= acl_user->access;
    sctx->priv_user= acl_user->user ? user : (char *) "";
1014 1015

    if (acl_user->host.hostname)
1016
      strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
1017
    else
1018
      *sctx->priv_host= 0;
1019 1020 1021 1022 1023
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  DBUG_RETURN(res);
}

unknown's avatar
unknown committed
1024 1025 1026 1027 1028 1029 1030
static byte* check_get_key(ACL_USER *buff,uint *length,
			   my_bool not_used __attribute__((unused)))
{
  *length=buff->hostname_length;
  return (byte*) buff->host.hostname;
}

1031

unknown's avatar
unknown committed
1032
static void acl_update_user(const char *user, const char *host,
1033
			    const char *password, uint password_len,
1034 1035 1036 1037
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
unknown's avatar
unknown committed
1038
			    USER_RESOURCES  *mqh,
unknown's avatar
unknown committed
1039
			    ulong privileges)
unknown's avatar
unknown committed
1040
{
1041 1042
  safe_mutex_assert_owner(&acl_cache->lock);

unknown's avatar
unknown committed
1043 1044 1045 1046
  for (uint i=0 ; i < acl_users.elements ; i++)
  {
    ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
    if (!acl_user->user && !user[0] ||
1047
	acl_user->user && !strcmp(user,acl_user->user))
unknown's avatar
unknown committed
1048 1049
    {
      if (!acl_user->host.hostname && !host[0] ||
unknown's avatar
unknown committed
1050
	  acl_user->host.hostname &&
1051
	  !my_strcasecmp(system_charset_info, host, acl_user->host.hostname))
unknown's avatar
unknown committed
1052 1053
      {
	acl_user->access=privileges;
1054
	if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1055
	  acl_user->user_resource.questions=mqh->questions;
1056
	if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1057
	  acl_user->user_resource.updates=mqh->updates;
1058 1059 1060 1061
	if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
	  acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
	if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
	  acl_user->user_resource.user_conn= mqh->user_conn;
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
	if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
	{
	  acl_user->ssl_type= ssl_type;
	  acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&mem,ssl_cipher) :
				 0);
	  acl_user->x509_issuer= (x509_issuer ? strdup_root(&mem,x509_issuer) :
				  0);
	  acl_user->x509_subject= (x509_subject ?
				   strdup_root(&mem,x509_subject) : 0);
	}
unknown's avatar
unknown committed
1072 1073
	if (password)
	  set_user_salt(acl_user, password, password_len);
1074
        /* search complete: */
unknown's avatar
unknown committed
1075 1076 1077 1078 1079 1080 1081 1082
	break;
      }
    }
  }
}


static void acl_insert_user(const char *user, const char *host,
1083
			    const char *password, uint password_len,
1084 1085 1086 1087
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
1088
			    USER_RESOURCES *mqh,
unknown's avatar
unknown committed
1089
			    ulong privileges)
unknown's avatar
unknown committed
1090 1091
{
  ACL_USER acl_user;
1092 1093 1094

  safe_mutex_assert_owner(&acl_cache->lock);

1095
  acl_user.user=*user ? strdup_root(&mem,user) : 0;
unknown's avatar
unknown committed
1096
  update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
unknown's avatar
unknown committed
1097
  acl_user.access=privileges;
1098
  acl_user.user_resource = *mqh;
unknown's avatar
unknown committed
1099
  acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
1100
  acl_user.hostname_length=(uint) strlen(host);
1101 1102 1103 1104 1105
  acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ?
		      ssl_type : SSL_TYPE_NONE);
  acl_user.ssl_cipher=	ssl_cipher   ? strdup_root(&mem,ssl_cipher) : 0;
  acl_user.x509_issuer= x509_issuer  ? strdup_root(&mem,x509_issuer) : 0;
  acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
1106 1107

  set_user_salt(&acl_user, password, password_len);
unknown's avatar
unknown committed
1108 1109

  VOID(push_dynamic(&acl_users,(gptr) &acl_user));
1110 1111
  if (!acl_user.host.hostname ||
      (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
unknown's avatar
unknown committed
1112
    allow_all_hosts=1;		// Anyone can connect /* purecov: tested */
unknown's avatar
unknown committed
1113 1114 1115
  qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
	sizeof(ACL_USER),(qsort_cmp) acl_compare);

1116 1117
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();
unknown's avatar
unknown committed
1118 1119 1120 1121
}


static void acl_update_db(const char *user, const char *host, const char *db,
unknown's avatar
unknown committed
1122
			  ulong privileges)
unknown's avatar
unknown committed
1123
{
1124 1125
  safe_mutex_assert_owner(&acl_cache->lock);

unknown's avatar
unknown committed
1126 1127 1128 1129 1130 1131 1132 1133
  for (uint i=0 ; i < acl_dbs.elements ; i++)
  {
    ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
    if (!acl_db->user && !user[0] ||
	acl_db->user &&
	!strcmp(user,acl_db->user))
    {
      if (!acl_db->host.hostname && !host[0] ||
1134
	  acl_db->host.hostname &&
1135
	  !my_strcasecmp(system_charset_info, host, acl_db->host.hostname))
unknown's avatar
unknown committed
1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
      {
	if (!acl_db->db && !db[0] ||
	    acl_db->db && !strcmp(db,acl_db->db))
	{
	  if (privileges)
	    acl_db->access=privileges;
	  else
	    delete_dynamic_element(&acl_dbs,i);
	}
      }
    }
  }
}


1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164
/*
  Insert a user/db/host combination into the global acl_cache

  SYNOPSIS
    acl_insert_db()
    user		User name
    host		Host name
    db			Database name
    privileges		Bitmap of privileges

  NOTES
    acl_cache->lock must be locked when calling this
*/

unknown's avatar
unknown committed
1165
static void acl_insert_db(const char *user, const char *host, const char *db,
unknown's avatar
unknown committed
1166
			  ulong privileges)
unknown's avatar
unknown committed
1167 1168
{
  ACL_DB acl_db;
1169
  safe_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
1170
  acl_db.user=strdup_root(&mem,user);
1171
  update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
unknown's avatar
unknown committed
1172 1173 1174 1175 1176 1177 1178 1179 1180
  acl_db.db=strdup_root(&mem,db);
  acl_db.access=privileges;
  acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
  VOID(push_dynamic(&acl_dbs,(gptr) &acl_db));
  qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
	sizeof(ACL_DB),(qsort_cmp) acl_compare);
}


1181 1182 1183

/*
  Get privilege for a host, user and db combination
1184 1185 1186

  as db_is_pattern changes the semantics of comparison,
  acl_cache is not used if db_is_pattern is set.
1187
*/
unknown's avatar
unknown committed
1188

1189
ulong acl_get(const char *host, const char *ip,
1190
              const char *user, const char *db, my_bool db_is_pattern)
unknown's avatar
unknown committed
1191
{
1192
  ulong host_access= ~(ulong)0, db_access= 0;
unknown's avatar
unknown committed
1193
  uint i,key_length;
unknown's avatar
unknown committed
1194
  char key[ACL_KEY_LENGTH],*tmp_db,*end;
unknown's avatar
unknown committed
1195
  acl_entry *entry;
unknown's avatar
unknown committed
1196
  DBUG_ENTER("acl_get");
unknown's avatar
unknown committed
1197 1198

  VOID(pthread_mutex_lock(&acl_cache->lock));
1199
  end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db);
unknown's avatar
unknown committed
1200 1201
  if (lower_case_table_names)
  {
1202
    my_casedn_str(files_charset_info, tmp_db);
unknown's avatar
unknown committed
1203 1204
    db=tmp_db;
  }
unknown's avatar
unknown committed
1205
  key_length=(uint) (end-key);
1206
  if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search(key,key_length)))
unknown's avatar
unknown committed
1207 1208 1209
  {
    db_access=entry->access;
    VOID(pthread_mutex_unlock(&acl_cache->lock));
unknown's avatar
unknown committed
1210 1211
    DBUG_PRINT("exit", ("access: 0x%lx", db_access));
    DBUG_RETURN(db_access);
unknown's avatar
unknown committed
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
  }

  /*
    Check if there are some access rights for database and user
  */
  for (i=0 ; i < acl_dbs.elements ; i++)
  {
    ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
    if (!acl_db->user || !strcmp(user,acl_db->user))
    {
      if (compare_hostname(&acl_db->host,host,ip))
      {
1224
	if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
unknown's avatar
unknown committed
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
	{
	  db_access=acl_db->access;
	  if (acl_db->host.hostname)
	    goto exit;				// Fully specified. Take it
	  break; /* purecov: tested */
	}
      }
    }
  }
  if (!db_access)
    goto exit;					// Can't be better

  /*
    No host specified for user. Get hostdata from host table
  */
  host_access=0;				// Host must be found
  for (i=0 ; i < acl_hosts.elements ; i++)
  {
    ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
    if (compare_hostname(&acl_host->host,host,ip))
    {
1246
      if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
unknown's avatar
unknown committed
1247 1248 1249 1250 1251 1252 1253 1254
      {
	host_access=acl_host->access;		// Fully specified. Take it
	break;
      }
    }
  }
exit:
  /* Save entry in cache for quick retrieval */
1255 1256
  if (!db_is_pattern &&
      (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
unknown's avatar
unknown committed
1257 1258 1259 1260 1261 1262 1263
  {
    entry->access=(db_access & host_access);
    entry->length=key_length;
    memcpy((gptr) entry->key,key,key_length);
    acl_cache->add(entry);
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
unknown's avatar
unknown committed
1264 1265
  DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
  DBUG_RETURN(db_access & host_access);
unknown's avatar
unknown committed
1266 1267
}

1268 1269 1270 1271 1272 1273 1274
/*
  Check if there are any possible matching entries for this host

  NOTES
    All host names without wild cards are stored in a hash table,
    entries with wildcards are stored in a dynamic array
*/
unknown's avatar
unknown committed
1275 1276 1277 1278

static void init_check_host(void)
{
  DBUG_ENTER("init_check_host");
1279
  VOID(my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
unknown's avatar
unknown committed
1280
			  acl_users.elements,1));
1281
  VOID(hash_init(&acl_check_hosts,system_charset_info,acl_users.elements,0,0,
unknown's avatar
unknown committed
1282
		 (hash_get_key) check_get_key,0,0));
unknown's avatar
unknown committed
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
  if (!allow_all_hosts)
  {
    for (uint i=0 ; i < acl_users.elements ; i++)
    {
      ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
      if (strchr(acl_user->host.hostname,wild_many) ||
	  strchr(acl_user->host.hostname,wild_one) ||
	  acl_user->host.ip_mask)
      {						// Has wildcard
	uint j;
	for (j=0 ; j < acl_wild_hosts.elements ; j++)
	{					// Check if host already exists
	  acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j,
					       acl_host_and_ip *);
1297
	  if (!my_strcasecmp(system_charset_info,
1298
                             acl_user->host.hostname, acl->hostname))
unknown's avatar
unknown committed
1299 1300 1301 1302 1303
	    break;				// already stored
	}
	if (j == acl_wild_hosts.elements)	// If new
	  (void) push_dynamic(&acl_wild_hosts,(char*) &acl_user->host);
      }
1304
      else if (!hash_search(&acl_check_hosts,(byte*) acl_user->host.hostname,
unknown's avatar
unknown committed
1305
			    (uint) strlen(acl_user->host.hostname)))
unknown's avatar
unknown committed
1306
      {
unknown's avatar
SCRUM  
unknown committed
1307
	if (my_hash_insert(&acl_check_hosts,(byte*) acl_user))
unknown's avatar
unknown committed
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320
	{					// End of memory
	  allow_all_hosts=1;			// Should never happen
	  DBUG_VOID_RETURN;
	}
      }
    }
  }
  freeze_size(&acl_wild_hosts);
  freeze_size(&acl_check_hosts.array);
  DBUG_VOID_RETURN;
}


1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336
/*
  Rebuild lists used for checking of allowed hosts

  We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
  dropping or renaming user, since they contain pointers to elements of
  'acl_user' array, which are invalidated by drop operation, and use
  ACL_USER::host::hostname as a key, which is changed by rename.
*/
void rebuild_check_host(void)
{
  delete_dynamic(&acl_wild_hosts);
  hash_free(&acl_check_hosts);
  init_check_host();
}


unknown's avatar
unknown committed
1337 1338 1339 1340 1341 1342 1343 1344
/* Return true if there is no users that can match the given host */

bool acl_check_host(const char *host, const char *ip)
{
  if (allow_all_hosts)
    return 0;
  VOID(pthread_mutex_lock(&acl_cache->lock));

unknown's avatar
unknown committed
1345 1346
  if (host && hash_search(&acl_check_hosts,(byte*) host,(uint) strlen(host)) ||
      ip && hash_search(&acl_check_hosts,(byte*) ip,(uint) strlen(ip)))
unknown's avatar
unknown committed
1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock));
    return 0;					// Found host
  }
  for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
  {
    acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
    if (compare_hostname(acl, host, ip))
    {
      VOID(pthread_mutex_unlock(&acl_cache->lock));
      return 0;					// Host ok
    }
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  return 1;					// Host is not allowed
}


unknown's avatar
unknown committed
1365 1366 1367 1368 1369 1370 1371 1372
/*
  Check if the user is allowed to change password

  SYNOPSIS:
    check_change_password()
    thd		THD
    host	hostname for the user
    user	user name
1373 1374 1375 1376
    new_password new password

  NOTE:
    new_password cannot be NULL
unknown's avatar
merge  
unknown committed
1377

unknown's avatar
unknown committed
1378
    RETURN VALUE
1379 1380
      0		OK
      1		ERROR  ; In this case the error is sent to the client.
unknown's avatar
unknown committed
1381 1382
*/

1383
bool check_change_password(THD *thd, const char *host, const char *user,
1384
                           char *new_password, uint new_password_len)
unknown's avatar
unknown committed
1385
{
unknown's avatar
unknown committed
1386 1387
  if (!initialized)
  {
unknown's avatar
unknown committed
1388
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1389
    return(1);
unknown's avatar
unknown committed
1390
  }
unknown's avatar
unknown committed
1391
  if (!thd->slave_thread &&
1392 1393 1394
      (strcmp(thd->security_ctx->user, user) ||
       my_strcasecmp(system_charset_info, host,
                     thd->security_ctx->priv_host)))
unknown's avatar
unknown committed
1395
  {
1396
    if (check_access(thd, UPDATE_ACL, "mysql",0,1,0,0))
unknown's avatar
unknown committed
1397
      return(1);
unknown's avatar
unknown committed
1398
  }
1399
  if (!thd->slave_thread && !thd->security_ctx->user[0])
unknown's avatar
unknown committed
1400
  {
unknown's avatar
unknown committed
1401 1402
    my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
               MYF(0));
unknown's avatar
unknown committed
1403
    return(1);
unknown's avatar
unknown committed
1404
  }
1405
  uint len=strlen(new_password);
unknown's avatar
unknown committed
1406
  if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
1407 1408
      len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
  {
unknown's avatar
unknown committed
1409
    my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
1410 1411
    return -1;
  }
unknown's avatar
unknown committed
1412 1413 1414 1415
  return(0);
}


1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428
/*
  Change a password for a user

  SYNOPSIS
    change_password()
    thd			Thread handle
    host		Hostname
    user		User name
    new_password	New password for host@user

  RETURN VALUES
    0	ok
    1	ERROR; In this case the error is sent to the client.
unknown's avatar
unknown committed
1429
*/
1430

unknown's avatar
unknown committed
1431 1432 1433
bool change_password(THD *thd, const char *host, const char *user,
		     char *new_password)
{
1434 1435 1436 1437 1438
  TABLE_LIST tables;
  TABLE *table;
  /* Buffer should be extended when password length is extended. */
  char buff[512];
  ulong query_length;
1439
  uint new_password_len= strlen(new_password);
1440
  bool result= 1;
unknown's avatar
unknown committed
1441 1442 1443 1444 1445
  DBUG_ENTER("change_password");
  DBUG_PRINT("enter",("host: '%s'  user: '%s'  new_password: '%s'",
		      host,user,new_password));
  DBUG_ASSERT(host != 0);			// Ensured by parent

1446
  if (check_change_password(thd, host, user, new_password, new_password_len))
unknown's avatar
unknown committed
1447 1448
    DBUG_RETURN(1);

1449
  bzero((char*) &tables, sizeof(tables));
1450
  tables.alias= tables.table_name= (char*) "user";
1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465
  tables.db= (char*) "mysql";

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
  if (thd->slave_thread && table_rules_on)
  {
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.  It's ok to leave 'updating' set after tables_ok.
    */
    tables.updating= 1;
    /* Thanks to bzero, tables.next==0 */
1466
    if (!tables_ok(thd, &tables))
1467 1468 1469 1470 1471 1472 1473
      DBUG_RETURN(0);
  }
#endif

  if (!(table= open_ltable(thd, &tables, TL_WRITE)))
    DBUG_RETURN(1);

unknown's avatar
unknown committed
1474 1475
  VOID(pthread_mutex_lock(&acl_cache->lock));
  ACL_USER *acl_user;
1476
  if (!(acl_user= find_acl_user(host, user, TRUE)))
unknown's avatar
unknown committed
1477 1478
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock));
unknown's avatar
unknown committed
1479
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
1480
    goto end;
unknown's avatar
unknown committed
1481
  }
1482 1483 1484
  /* update loaded acl entry: */
  set_user_salt(acl_user, new_password, new_password_len);

1485
  if (update_user_table(thd, table,
unknown's avatar
unknown committed
1486
			acl_user->host.hostname ? acl_user->host.hostname : "",
unknown's avatar
unknown committed
1487
			acl_user->user ? acl_user->user : "",
1488
			new_password, new_password_len))
unknown's avatar
unknown committed
1489 1490
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
1491
    goto end;
unknown's avatar
unknown committed
1492
  }
unknown's avatar
unknown committed
1493

unknown's avatar
unknown committed
1494 1495
  acl_cache->clear(1);				// Clear locked hostname cache
  VOID(pthread_mutex_unlock(&acl_cache->lock));
1496 1497 1498
  result= 0;
  if (mysql_bin_log.is_open())
  {
1499 1500 1501 1502 1503 1504
    query_length=
      my_sprintf(buff,
                 (buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"",
                  acl_user->user ? acl_user->user : "",
                  acl_user->host.hostname ? acl_user->host.hostname : "",
                  new_password));
1505 1506 1507 1508 1509 1510 1511
    thd->clear_error();
    Query_log_event qinfo(thd, buff, query_length, 0, FALSE);
    mysql_bin_log.write(&qinfo);
  }
end:
  close_thread_tables(thd);
  DBUG_RETURN(result);
unknown's avatar
unknown committed
1512 1513 1514
}


1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530
/*
  Find user in ACL

  SYNOPSIS
    is_acl_user()
    host                 host name
    user                 user name

  RETURN
   FALSE  user not fond
   TRUE   there are such user
*/

bool is_acl_user(const char *host, const char *user)
{
  bool res;
1531 1532 1533 1534 1535

  /* --skip-grants */
  if (!initialized)
    return TRUE;

1536
  VOID(pthread_mutex_lock(&acl_cache->lock));
1537
  res= find_acl_user(host, user, TRUE) != NULL;
1538 1539 1540 1541 1542
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  return res;
}


unknown's avatar
unknown committed
1543 1544 1545 1546 1547
/*
  Find first entry that matches the current user
*/

static ACL_USER *
1548
find_acl_user(const char *host, const char *user, my_bool exact)
unknown's avatar
unknown committed
1549
{
unknown's avatar
unknown committed
1550
  DBUG_ENTER("find_acl_user");
1551
  DBUG_PRINT("enter",("host: '%s'  user: '%s'",host,user));
1552 1553 1554

  safe_mutex_assert_owner(&acl_cache->lock);

unknown's avatar
unknown committed
1555 1556 1557
  for (uint i=0 ; i < acl_users.elements ; i++)
  {
    ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
unknown's avatar
unknown committed
1558
    DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
1559 1560 1561 1562
                       user, acl_user->user ? acl_user->user : "",
                       host,
                       acl_user->host.hostname ? acl_user->host.hostname :
                       ""));
unknown's avatar
unknown committed
1563 1564 1565
    if (!acl_user->user && !user[0] ||
	acl_user->user && !strcmp(user,acl_user->user))
    {
1566
      if (exact ? !my_strcasecmp(system_charset_info, host,
1567 1568
                                 acl_user->host.hostname ?
				 acl_user->host.hostname : "") :
1569
          compare_hostname(&acl_user->host,host,host))
unknown's avatar
unknown committed
1570 1571 1572
      {
	DBUG_RETURN(acl_user);
      }
unknown's avatar
unknown committed
1573 1574
    }
  }
unknown's avatar
unknown committed
1575
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1576 1577 1578
}


1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589
/*
  Comparing of hostnames

  NOTES
  A hostname may be of type:
  hostname   (May include wildcards);   monty.pp.sci.fi
  ip	   (May include wildcards);   192.168.0.0
  ip/netmask			      192.168.0.0/255.255.255.0

  A net mask of 0.0.0.0 is not allowed.
*/
unknown's avatar
unknown committed
1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611

static const char *calc_ip(const char *ip, long *val, char end)
{
  long ip_val,tmp;
  if (!(ip=str2int(ip,10,0,255,&ip_val)) || *ip != '.')
    return 0;
  ip_val<<=24;
  if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
    return 0;
  ip_val+=tmp<<16;
  if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
    return 0;
  ip_val+=tmp<<8;
  if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != end)
    return 0;
  *val=ip_val+tmp;
  return ip;
}


static void update_hostname(acl_host_and_ip *host, const char *hostname)
{
1612
  host->hostname=(char*) hostname;             // This will not be modified!
1613
  if (!hostname ||
unknown's avatar
unknown committed
1614 1615 1616
      (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
       !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
  {
1617
    host->ip= host->ip_mask=0;			// Not a masked ip
unknown's avatar
unknown committed
1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630
  }
}


static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
			     const char *ip)
{
  long tmp;
  if (host->ip_mask && ip && calc_ip(ip,&tmp,'\0'))
  {
    return (tmp & host->ip_mask) == host->ip;
  }
  return (!host->hostname ||
1631
	  (hostname && !wild_case_compare(system_charset_info,
1632 1633
                                          hostname, host->hostname)) ||
	  (ip && !wild_compare(ip, host->hostname, 0)));
unknown's avatar
unknown committed
1634 1635
}

unknown's avatar
SCRUM  
unknown committed
1636 1637 1638 1639
bool hostname_requires_resolving(const char *hostname)
{
  char cur;
  if (!hostname)
unknown's avatar
unknown committed
1640
    return FALSE;
unknown's avatar
SCRUM  
unknown committed
1641 1642 1643
  int namelen= strlen(hostname);
  int lhlen= strlen(my_localhost);
  if ((namelen == lhlen) &&
1644
      !my_strnncoll(system_charset_info, (const uchar *)hostname,  namelen,
unknown's avatar
SCRUM  
unknown committed
1645
		    (const uchar *)my_localhost, strlen(my_localhost)))
unknown's avatar
unknown committed
1646
    return FALSE;
unknown's avatar
SCRUM  
unknown committed
1647 1648
  for (; (cur=*hostname); hostname++)
  {
1649
    if ((cur != '%') && (cur != '_') && (cur != '.') && (cur != '/') &&
unknown's avatar
SCRUM  
unknown committed
1650
	((cur < '0') || (cur > '9')))
unknown's avatar
unknown committed
1651
      return TRUE;
unknown's avatar
SCRUM  
unknown committed
1652
  }
unknown's avatar
unknown committed
1653
  return FALSE;
unknown's avatar
SCRUM  
unknown committed
1654
}
unknown's avatar
unknown committed
1655

1656

1657
/*
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667
  Update record for user in mysql.user privilege table with new password.

  SYNOPSIS
    update_user_table()
      thd               Thread handle
      table             Pointer to TABLE object for open mysql.user table
      host/user         Hostname/username pair identifying user for which
                        new password should be set
      new_password      New password
      new_password_len  Length of new password
1668
*/
unknown's avatar
unknown committed
1669

1670 1671
static bool update_user_table(THD *thd, TABLE *table,
                              const char *host, const char *user,
1672
			      const char *new_password, uint new_password_len)
unknown's avatar
unknown committed
1673
{
1674
  char user_key[MAX_KEY_LENGTH];
1675
  int error;
unknown's avatar
unknown committed
1676 1677 1678
  DBUG_ENTER("update_user_table");
  DBUG_PRINT("enter",("user: %s  host: %s",user,host));

1679 1680
  table->field[0]->store(host,(uint) strlen(host), system_charset_info);
  table->field[1]->store(user,(uint) strlen(user), system_charset_info);
unknown's avatar
unknown committed
1681
  key_copy((byte *) user_key, table->record[0], table->key_info,
1682
           table->key_info->key_length);
unknown's avatar
unknown committed
1683

unknown's avatar
unknown committed
1684
  table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
1685
  if (table->file->index_read_idx(table->record[0], 0,
unknown's avatar
unknown committed
1686
				  (byte *) user_key, table->key_info->key_length,
unknown's avatar
unknown committed
1687 1688
				  HA_READ_KEY_EXACT))
  {
unknown's avatar
unknown committed
1689 1690
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
               MYF(0));	/* purecov: deadcode */
unknown's avatar
unknown committed
1691 1692
    DBUG_RETURN(1);				/* purecov: deadcode */
  }
unknown's avatar
unknown committed
1693
  store_record(table,record[1]);
1694
  table->field[2]->store(new_password, new_password_len, system_charset_info);
unknown's avatar
unknown committed
1695 1696 1697
  if ((error=table->file->update_row(table->record[1],table->record[0])))
  {
    table->file->print_error(error,MYF(0));	/* purecov: deadcode */
1698
    DBUG_RETURN(1);
unknown's avatar
unknown committed
1699
  }
1700
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1701 1702
}

unknown's avatar
unknown committed
1703

unknown's avatar
unknown committed
1704 1705 1706 1707 1708 1709
/*
  Return 1 if we are allowed to create new users
  the logic here is: INSERT_ACL is sufficient.
  It's also a requirement in opt_safe_user_create,
  otherwise CREATE_USER_ACL is enough.
*/
unknown's avatar
unknown committed
1710 1711 1712

static bool test_if_create_new_users(THD *thd)
{
1713
  Security_context *sctx= thd->security_ctx;
1714
  bool create_new_users= test(sctx->master_access & INSERT_ACL) ||
unknown's avatar
unknown committed
1715
                         (!opt_safe_user_create &&
1716
                          test(sctx->master_access & CREATE_USER_ACL));
unknown's avatar
unknown committed
1717
  if (!create_new_users)
unknown's avatar
unknown committed
1718 1719
  {
    TABLE_LIST tl;
unknown's avatar
unknown committed
1720
    ulong db_access;
unknown's avatar
unknown committed
1721 1722
    bzero((char*) &tl,sizeof(tl));
    tl.db=	   (char*) "mysql";
1723
    tl.table_name=  (char*) "user";
unknown's avatar
unknown committed
1724
    create_new_users= 1;
unknown's avatar
unknown committed
1725

1726 1727
    db_access=acl_get(sctx->host, sctx->ip,
		      sctx->priv_user, tl.db, 0);
unknown's avatar
unknown committed
1728 1729
    if (!(db_access & INSERT_ACL))
    {
unknown's avatar
unknown committed
1730
      if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1))
unknown's avatar
unknown committed
1731 1732 1733 1734 1735 1736 1737
	create_new_users=0;
    }
  }
  return create_new_users;
}


unknown's avatar
unknown committed
1738
/****************************************************************************
1739
  Handle GRANT commands
unknown's avatar
unknown committed
1740 1741
****************************************************************************/

1742
static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
unknown's avatar
unknown committed
1743
			      ulong rights, bool revoke_grant,
unknown's avatar
unknown committed
1744
			      bool can_create_user, bool no_auto_create)
unknown's avatar
unknown committed
1745 1746
{
  int error = -1;
unknown's avatar
unknown committed
1747
  bool old_row_exists=0;
1748
  const char *password= "";
1749
  uint password_len= 0;
unknown's avatar
unknown committed
1750
  char what= (revoke_grant) ? 'N' : 'Y';
1751
  byte user_key[MAX_KEY_LENGTH];
1752
  LEX *lex= thd->lex;
unknown's avatar
unknown committed
1753
  DBUG_ENTER("replace_user_table");
unknown's avatar
unknown committed
1754

1755
  safe_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
1756 1757

  if (combo.password.str && combo.password.str[0])
1758
  {
1759 1760
    if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
        combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1761
    {
1762
      my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
unknown's avatar
unknown committed
1763
      DBUG_RETURN(-1);
1764
    }
1765
    password_len= combo.password.length;
unknown's avatar
unknown committed
1766
    password=combo.password.str;
1767
  }
unknown's avatar
unknown committed
1768

1769 1770
  table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
  table->field[1]->store(combo.user.str,combo.user.length, system_charset_info);
1771 1772 1773
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

unknown's avatar
unknown committed
1774
  table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
unknown's avatar
unknown committed
1775
  if (table->file->index_read_idx(table->record[0], 0,
1776 1777
                                  user_key, table->key_info->key_length,
                                  HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
1778
  {
1779 1780
    /* what == 'N' means revoke */
    if (what == 'N')
unknown's avatar
unknown committed
1781
    {
1782 1783 1784 1785
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
      goto end;
    }
    /*
unknown's avatar
unknown committed
1786 1787
      There are four options which affect the process of creation of
      a new user (mysqld option --safe-create-user, 'insert' privilege
1788 1789 1790 1791 1792 1793 1794
      on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
      SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
      how it should work.
      if (safe-user-create && ! INSERT_priv) => reject
      else if (identified_by) => create
      else if (no_auto_create_user) => reject
      else create
unknown's avatar
unknown committed
1795 1796

      see also test_if_create_new_users()
1797
    */
unknown's avatar
unknown committed
1798 1799 1800 1801 1802 1803
    else if (!password_len && no_auto_create)
    {
      my_error(ER_PASSWORD_NO_MATCH, MYF(0), combo.user.str, combo.host.str);
      goto end;
    }
    else if (!can_create_user)
1804
    {
unknown's avatar
unknown committed
1805
      my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0),
1806
               thd->security_ctx->user, thd->security_ctx->host_or_ip);
unknown's avatar
unknown committed
1807 1808
      goto end;
    }
unknown's avatar
unknown committed
1809
    old_row_exists = 0;
1810
    restore_record(table,s->default_values);
1811
    table->field[0]->store(combo.host.str,combo.host.length,
1812
                           system_charset_info);
1813
    table->field[1]->store(combo.user.str,combo.user.length,
1814
                           system_charset_info);
1815
    table->field[2]->store(password, password_len,
1816
                           system_charset_info);
unknown's avatar
unknown committed
1817 1818 1819
  }
  else
  {
unknown's avatar
unknown committed
1820
    old_row_exists = 1;
unknown's avatar
unknown committed
1821
    store_record(table,record[1]);			// Save copy for update
unknown's avatar
unknown committed
1822
    if (combo.password.str)			// If password given
1823
      table->field[2]->store(password, password_len, system_charset_info);
1824
    else if (!rights && !revoke_grant &&
1825 1826
             lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
             !lex->mqh.specified_limits)
unknown's avatar
unknown committed
1827 1828 1829
    {
      DBUG_RETURN(0);
    }
unknown's avatar
unknown committed
1830 1831
  }

unknown's avatar
unknown committed
1832 1833 1834 1835
  /* Update table columns with new privileges */

  Field **tmp_field;
  ulong priv;
1836
  uint next_field;
unknown's avatar
unknown committed
1837 1838 1839 1840
  for (tmp_field= table->field+3, priv = SELECT_ACL;
       *tmp_field && (*tmp_field)->real_type() == FIELD_TYPE_ENUM &&
	 ((Field_enum*) (*tmp_field))->typelib->count == 2 ;
       tmp_field++, priv <<= 1)
unknown's avatar
unknown committed
1841
  {
unknown's avatar
unknown committed
1842
    if (priv & rights)				 // set requested privileges
unknown's avatar
unknown committed
1843
      (*tmp_field)->store(&what, 1, &my_charset_latin1);
unknown's avatar
unknown committed
1844
  }
1845
  rights= get_access(table, 3, &next_field);
1846 1847
  DBUG_PRINT("info",("table fields: %d",table->s->fields));
  if (table->s->fields >= 31)		/* From 4.0.0 we have more fields */
1848
  {
unknown's avatar
unknown committed
1849
    /* We write down SSL related ACL stuff */
1850
    switch (lex->ssl_type) {
unknown's avatar
unknown committed
1851
    case SSL_TYPE_ANY:
1852 1853
      table->field[next_field]->store(STRING_WITH_LEN("ANY"),
                                      &my_charset_latin1);
1854 1855 1856
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
unknown's avatar
unknown committed
1857 1858
      break;
    case SSL_TYPE_X509:
1859 1860
      table->field[next_field]->store(STRING_WITH_LEN("X509"),
                                      &my_charset_latin1);
1861 1862 1863
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
unknown's avatar
unknown committed
1864 1865
      break;
    case SSL_TYPE_SPECIFIED:
1866 1867
      table->field[next_field]->store(STRING_WITH_LEN("SPECIFIED"),
                                      &my_charset_latin1);
1868 1869 1870
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
1871
      if (lex->ssl_cipher)
unknown's avatar
unknown committed
1872 1873
        table->field[next_field+1]->store(lex->ssl_cipher,
                                strlen(lex->ssl_cipher), system_charset_info);
1874
      if (lex->x509_issuer)
unknown's avatar
unknown committed
1875 1876
        table->field[next_field+2]->store(lex->x509_issuer,
                                strlen(lex->x509_issuer), system_charset_info);
1877
      if (lex->x509_subject)
unknown's avatar
unknown committed
1878 1879
        table->field[next_field+3]->store(lex->x509_subject,
                                strlen(lex->x509_subject), system_charset_info);
unknown's avatar
unknown committed
1880
      break;
unknown's avatar
unknown committed
1881
    case SSL_TYPE_NOT_SPECIFIED:
unknown's avatar
unknown committed
1882 1883
      break;
    case SSL_TYPE_NONE:
1884 1885 1886 1887
      table->field[next_field]->store("", 0, &my_charset_latin1);
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
unknown's avatar
unknown committed
1888
      break;
1889
    }
unknown's avatar
unknown committed
1890
    next_field+=4;
unknown's avatar
unknown committed
1891

1892
    USER_RESOURCES mqh= lex->mqh;
1893
    if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1894
      table->field[next_field]->store((longlong) mqh.questions, TRUE);
1895
    if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1896
      table->field[next_field+1]->store((longlong) mqh.updates, TRUE);
1897
    if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
1898
      table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE);
1899
    if (table->s->fields >= 36 &&
1900
        (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
unknown's avatar
unknown committed
1901
      table->field[next_field+3]->store((longlong) mqh.user_conn);
1902
    mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
unknown's avatar
unknown committed
1903
  }
unknown's avatar
unknown committed
1904
  if (old_row_exists)
unknown's avatar
unknown committed
1905 1906 1907 1908 1909
  {
    /*
      We should NEVER delete from the user table, as a uses can still
      use mysqld even if he doesn't have any privileges in the user table!
    */
unknown's avatar
unknown committed
1910
    table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
unknown's avatar
unknown committed
1911
    if (cmp_record(table,record[1]) &&
unknown's avatar
unknown committed
1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930
	(error=table->file->update_row(table->record[1],table->record[0])))
    {						// This should never happen
      table->file->print_error(error,MYF(0));	/* purecov: deadcode */
      error= -1;				/* purecov: deadcode */
      goto end;					/* purecov: deadcode */
    }
  }
  else if ((error=table->file->write_row(table->record[0]))) // insert
  {						// This should never happen
    if (error && error != HA_ERR_FOUND_DUPP_KEY &&
	error != HA_ERR_FOUND_DUPP_UNIQUE)	/* purecov: inspected */
    {
      table->file->print_error(error,MYF(0));	/* purecov: deadcode */
      error= -1;				/* purecov: deadcode */
      goto end;					/* purecov: deadcode */
    }
  }
  error=0;					// Privileges granted / revoked

unknown's avatar
unknown committed
1931
end:
unknown's avatar
unknown committed
1932 1933 1934
  if (!error)
  {
    acl_cache->clear(1);			// Clear privilege cache
unknown's avatar
unknown committed
1935
    if (old_row_exists)
1936 1937
      acl_update_user(combo.user.str, combo.host.str,
                      combo.password.str, password_len,
1938 1939 1940 1941 1942
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
unknown's avatar
unknown committed
1943
		      rights);
unknown's avatar
unknown committed
1944
    else
1945
      acl_insert_user(combo.user.str, combo.host.str, password, password_len,
1946 1947 1948 1949 1950
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
unknown's avatar
unknown committed
1951
		      rights);
unknown's avatar
unknown committed
1952 1953 1954 1955 1956 1957
  }
  DBUG_RETURN(error);
}


/*
unknown's avatar
unknown committed
1958
  change grants in the mysql.db table
unknown's avatar
unknown committed
1959 1960 1961 1962
*/

static int replace_db_table(TABLE *table, const char *db,
			    const LEX_USER &combo,
unknown's avatar
unknown committed
1963
			    ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
1964
{
unknown's avatar
unknown committed
1965 1966
  uint i;
  ulong priv,store_rights;
unknown's avatar
unknown committed
1967
  bool old_row_exists=0;
unknown's avatar
unknown committed
1968
  int error;
unknown's avatar
unknown committed
1969
  char what= (revoke_grant) ? 'N' : 'Y';
1970
  byte user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
1971 1972
  DBUG_ENTER("replace_db_table");

1973 1974
  if (!initialized)
  {
unknown's avatar
unknown committed
1975
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1976 1977 1978
    DBUG_RETURN(-1);
  }

1979
  /* Check if there is such a user in user table in memory? */
1980
  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
unknown's avatar
unknown committed
1981
  {
unknown's avatar
unknown committed
1982
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
unknown's avatar
unknown committed
1983 1984 1985
    DBUG_RETURN(-1);
  }

1986 1987 1988
  table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
  table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
1989 1990 1991
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

unknown's avatar
unknown committed
1992 1993
  table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
  if (table->file->index_read_idx(table->record[0],0,
1994 1995
                                  user_key, table->key_info->key_length,
                                  HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
1996 1997 1998
  {
    if (what == 'N')
    { // no row, no revoke
unknown's avatar
unknown committed
1999
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
unknown's avatar
unknown committed
2000 2001
      goto abort;
    }
unknown's avatar
unknown committed
2002
    old_row_exists = 0;
2003
    restore_record(table, s->default_values);
2004 2005 2006
    table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
    table->field[1]->store(db,(uint) strlen(db), system_charset_info);
    table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
unknown's avatar
unknown committed
2007 2008 2009
  }
  else
  {
unknown's avatar
unknown committed
2010
    old_row_exists = 1;
unknown's avatar
unknown committed
2011
    store_record(table,record[1]);
unknown's avatar
unknown committed
2012 2013 2014
  }

  store_rights=get_rights_for_db(rights);
2015
  for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
unknown's avatar
unknown committed
2016
  {
unknown's avatar
unknown committed
2017
    if (priv & store_rights)			// do it if priv is chosen
unknown's avatar
unknown committed
2018
      table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
unknown's avatar
unknown committed
2019 2020 2021 2022
  }
  rights=get_access(table,3);
  rights=fix_rights_for_db(rights);

unknown's avatar
unknown committed
2023
  if (old_row_exists)
unknown's avatar
unknown committed
2024
  {
2025
    /* update old existing row */
unknown's avatar
unknown committed
2026 2027
    if (rights)
    {
unknown's avatar
unknown committed
2028
      table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
unknown's avatar
unknown committed
2029 2030 2031 2032 2033 2034 2035 2036 2037
      if ((error=table->file->update_row(table->record[1],table->record[0])))
	goto table_error;			/* purecov: deadcode */
    }
    else	/* must have been a revoke of all privileges */
    {
      if ((error = table->file->delete_row(table->record[1])))
	goto table_error;			/* purecov: deadcode */
    }
  }
2038
  else if (rights && (error=table->file->write_row(table->record[0])))
unknown's avatar
unknown committed
2039 2040 2041 2042 2043 2044
  {
    if (error && error != HA_ERR_FOUND_DUPP_KEY) /* purecov: inspected */
      goto table_error; /* purecov: deadcode */
  }

  acl_cache->clear(1);				// Clear privilege cache
unknown's avatar
unknown committed
2045
  if (old_row_exists)
unknown's avatar
unknown committed
2046 2047
    acl_update_db(combo.user.str,combo.host.str,db,rights);
  else
2048
  if (rights)
unknown's avatar
unknown committed
2049 2050 2051 2052
    acl_insert_db(combo.user.str,combo.host.str,db,rights);
  DBUG_RETURN(0);

  /* This could only happen if the grant tables got corrupted */
2053
table_error:
unknown's avatar
unknown committed
2054 2055
  table->file->print_error(error,MYF(0));	/* purecov: deadcode */

2056
abort:
unknown's avatar
unknown committed
2057 2058 2059 2060 2061 2062 2063 2064
  DBUG_RETURN(-1);
}


class GRANT_COLUMN :public Sql_alloc
{
public:
  char *column;
unknown's avatar
unknown committed
2065 2066 2067
  ulong rights;
  uint key_length;
  GRANT_COLUMN(String &c,  ulong y) :rights (y)
unknown's avatar
unknown committed
2068
  {
unknown's avatar
unknown committed
2069
    column= memdup_root(&memex,c.ptr(), key_length=c.length());
unknown's avatar
unknown committed
2070 2071 2072
  }
};

unknown's avatar
unknown committed
2073

unknown's avatar
unknown committed
2074 2075 2076 2077 2078 2079 2080
static byte* get_key_column(GRANT_COLUMN *buff,uint *length,
			    my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
  return (byte*) buff->column;
}

unknown's avatar
unknown committed
2081

2082
class GRANT_NAME :public Sql_alloc
unknown's avatar
unknown committed
2083 2084
{
public:
2085 2086
  acl_host_and_ip host;
  char *db, *user, *tname, *hash_key;
2087
  ulong privs;
2088
  ulong sort;
unknown's avatar
unknown committed
2089
  uint key_length;
2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101
  GRANT_NAME(const char *h, const char *d,const char *u,
             const char *t, ulong p);
  GRANT_NAME (TABLE *form);
  virtual ~GRANT_NAME() {};
  virtual bool ok() { return privs != 0; }
};


class GRANT_TABLE :public GRANT_NAME
{
public:
  ulong cols;
unknown's avatar
unknown committed
2102
  HASH hash_columns;
unknown's avatar
unknown committed
2103 2104 2105 2106

  GRANT_TABLE(const char *h, const char *d,const char *u,
              const char *t, ulong p, ulong c);
  GRANT_TABLE (TABLE *form, TABLE *col_privs);
2107
  ~GRANT_TABLE();
2108 2109
  bool ok() { return privs != 0 || cols != 0; }
};
unknown's avatar
unknown committed
2110

2111

unknown's avatar
unknown committed
2112

2113 2114 2115
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
                       const char *t, ulong p)
  :privs(p)
2116 2117
{
  /* Host given by user */
2118
  update_hostname(&host, strdup_root(&memex, h));
2119 2120
  db =   strdup_root(&memex,d);
  user = strdup_root(&memex,u);
2121
  sort=  get_sort(3,host.hostname,db,user);
2122 2123
  tname= strdup_root(&memex,t);
  if (lower_case_table_names)
unknown's avatar
unknown committed
2124
  {
2125 2126
    my_casedn_str(files_charset_info, db);
    my_casedn_str(files_charset_info, tname);
2127 2128 2129 2130
  }
  key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3;
  hash_key = (char*) alloc_root(&memex,key_length);
  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
2131 2132 2133 2134 2135 2136 2137
}


GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
                	 const char *t, ulong p, ulong c)
  :GRANT_NAME(h,d,u,t,p), cols(c)
{
2138
  (void) hash_init(&hash_columns,system_charset_info,
unknown's avatar
unknown committed
2139
                   0,0,0, (hash_get_key) get_key_column,0,0);
2140
}
unknown's avatar
unknown committed
2141

2142

2143
GRANT_NAME::GRANT_NAME(TABLE *form)
2144
{
2145
  update_hostname(&host, get_field(&memex, form->field[0]));
unknown's avatar
unknown committed
2146 2147
  db=    get_field(&memex,form->field[1]);
  user=  get_field(&memex,form->field[2]);
2148 2149
  if (!user)
    user= (char*) "";
2150
  sort=  get_sort(3, host.hostname, db, user);
unknown's avatar
unknown committed
2151
  tname= get_field(&memex,form->field[3]);
2152 2153 2154
  if (!db || !tname)
  {
    /* Wrong table row; Ignore it */
2155
    privs= 0;
2156 2157 2158 2159
    return;					/* purecov: inspected */
  }
  if (lower_case_table_names)
  {
2160 2161
    my_casedn_str(files_charset_info, db);
    my_casedn_str(files_charset_info, tname);
2162 2163 2164 2165 2166 2167 2168
  }
  key_length = ((uint) strlen(db) + (uint) strlen(user) +
                (uint) strlen(tname) + 3);
  hash_key = (char*) alloc_root(&memex,key_length);
  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
  privs = (ulong) form->field[6]->val_int();
  privs = fix_rights_for_table(privs);
2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184
}


GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
  :GRANT_NAME(form)
{
  byte key[MAX_KEY_LENGTH];

  if (!db || !tname)
  {
    /* Wrong table row; Ignore it */
    hash_clear(&hash_columns);                  /* allow for destruction */
    cols= 0;
    return;
  }
  cols= (ulong) form->field[7]->val_int();
2185 2186
  cols =  fix_rights_for_column(cols);

2187
  (void) hash_init(&hash_columns,system_charset_info,
unknown's avatar
unknown committed
2188
                   0,0,0, (hash_get_key) get_key_column,0,0);
2189 2190
  if (cols)
  {
2191 2192
    uint key_prefix_len;
    KEY_PART_INFO *key_part= col_privs->key_info->key_part;
2193 2194
    col_privs->field[0]->store(host.hostname,
                               host.hostname ? (uint) strlen(host.hostname) : 0,
2195 2196 2197 2198
                               system_charset_info);
    col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
    col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
    col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
2199 2200 2201 2202 2203 2204

    key_prefix_len= (key_part[0].store_length +
                     key_part[1].store_length +
                     key_part[2].store_length +
                     key_part[3].store_length);
    key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
unknown's avatar
unknown committed
2205
    col_privs->field[4]->store("",0, &my_charset_latin1);
2206

unknown's avatar
unknown committed
2207 2208
    col_privs->file->ha_index_init(0);
    if (col_privs->file->index_read(col_privs->record[0],
2209 2210
                                    (byte*) key,
                                    key_prefix_len, HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
2211
    {
2212
      cols = 0; /* purecov: deadcode */
unknown's avatar
unknown committed
2213
      col_privs->file->ha_index_end();
2214
      return;
unknown's avatar
unknown committed
2215
    }
2216
    do
unknown's avatar
unknown committed
2217
    {
2218 2219 2220
      String *res,column_name;
      GRANT_COLUMN *mem_check;
      /* As column name is a string, we don't have to supply a buffer */
unknown's avatar
unknown committed
2221
      res=col_privs->field[4]->val_str(&column_name);
2222 2223 2224
      ulong priv= (ulong) col_privs->field[6]->val_int();
      if (!(mem_check = new GRANT_COLUMN(*res,
                                         fix_rights_for_column(priv))))
unknown's avatar
unknown committed
2225
      {
2226 2227 2228
        /* Don't use this entry */
        privs = cols = 0;			/* purecov: deadcode */
        return;				/* purecov: deadcode */
unknown's avatar
unknown committed
2229
      }
unknown's avatar
unknown committed
2230
      my_hash_insert(&hash_columns, (byte *) mem_check);
2231
    } while (!col_privs->file->index_next(col_privs->record[0]) &&
2232
             !key_cmp_if_same(col_privs,key,0,key_prefix_len));
unknown's avatar
unknown committed
2233
    col_privs->file->ha_index_end();
unknown's avatar
unknown committed
2234
  }
2235
}
unknown's avatar
unknown committed
2236

unknown's avatar
unknown committed
2237

2238 2239 2240 2241 2242 2243
GRANT_TABLE::~GRANT_TABLE()
{
  hash_free(&hash_columns);
}


2244
static byte* get_grant_table(GRANT_NAME *buff,uint *length,
unknown's avatar
unknown committed
2245 2246 2247 2248 2249 2250
			     my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
  return (byte*) buff->hash_key;
}

unknown's avatar
unknown committed
2251

unknown's avatar
unknown committed
2252 2253 2254 2255 2256
void free_grant_table(GRANT_TABLE *grant_table)
{
  hash_free(&grant_table->hash_columns);
}

unknown's avatar
unknown committed
2257

unknown's avatar
unknown committed
2258 2259
/* Search after a matching grant. Prefer exact grants before not exact ones */

2260 2261
static GRANT_NAME *name_hash_search(HASH *name_hash,
				      const char *host,const char* ip,
unknown's avatar
unknown committed
2262 2263 2264 2265
				      const char *db,
				      const char *user, const char *tname,
				      bool exact)
{
unknown's avatar
unknown committed
2266
  char helping [NAME_LEN*2+USERNAME_LENGTH+3];
unknown's avatar
unknown committed
2267
  uint len;
2268
  GRANT_NAME *grant_name,*found=0;
2269
  HASH_SEARCH_STATE state;
unknown's avatar
unknown committed
2270 2271

  len  = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1;
2272 2273
  for (grant_name= (GRANT_NAME*) hash_first(name_hash, (byte*) helping,
                                            len, &state);
2274 2275
       grant_name ;
       grant_name= (GRANT_NAME*) hash_next(name_hash,(byte*) helping,
2276
                                           len, &state))
unknown's avatar
unknown committed
2277 2278 2279
  {
    if (exact)
    {
unknown's avatar
unknown committed
2280 2281
      if (!grant_name->host.hostname ||
          (host &&
2282
	   !my_strcasecmp(system_charset_info, host,
unknown's avatar
unknown committed
2283 2284
                          grant_name->host.hostname)) ||
	  (ip && !strcmp(ip, grant_name->host.hostname)))
2285
	return grant_name;
unknown's avatar
unknown committed
2286 2287 2288
    }
    else
    {
unknown's avatar
unknown committed
2289
      if (compare_hostname(&grant_name->host, host, ip) &&
2290 2291
          (!found || found->sort < grant_name->sort))
	found=grant_name;					// Host ok
unknown's avatar
unknown committed
2292 2293 2294 2295 2296 2297
    }
  }
  return found;
}


2298
inline GRANT_NAME *
2299 2300
routine_hash_search(const char *host, const char *ip, const char *db,
                 const char *user, const char *tname, bool proc, bool exact)
2301
{
2302 2303 2304
  return (GRANT_TABLE*)
    name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
		     host, ip, db, user, tname, exact);
2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315
}


inline GRANT_TABLE *
table_hash_search(const char *host, const char *ip, const char *db,
		  const char *user, const char *tname, bool exact)
{
  return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
					 user, tname, exact);
}

unknown's avatar
unknown committed
2316

unknown's avatar
unknown committed
2317
inline GRANT_COLUMN *
unknown's avatar
unknown committed
2318
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
unknown's avatar
unknown committed
2319 2320 2321 2322 2323 2324 2325 2326 2327
{
  return (GRANT_COLUMN*) hash_search(&t->hash_columns, (byte*) cname,length);
}


static int replace_column_table(GRANT_TABLE *g_t,
				TABLE *table, const LEX_USER &combo,
				List <LEX_COLUMN> &columns,
				const char *db, const char *table_name,
unknown's avatar
unknown committed
2328
				ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
2329 2330 2331
{
  int error=0,result=0;
  byte key[MAX_KEY_LENGTH];
2332 2333
  uint key_prefix_length;
  KEY_PART_INFO *key_part= table->key_info->key_part;
unknown's avatar
unknown committed
2334 2335
  DBUG_ENTER("replace_column_table");

unknown's avatar
unknown committed
2336 2337 2338 2339 2340 2341 2342 2343
  table->field[0]->store(combo.host.str,combo.host.length,
                         system_charset_info);
  table->field[1]->store(db,(uint) strlen(db),
                         system_charset_info);
  table->field[2]->store(combo.user.str,combo.user.length,
                         system_charset_info);
  table->field[3]->store(table_name,(uint) strlen(table_name),
                         system_charset_info);
unknown's avatar
unknown committed
2344

2345 2346 2347 2348
  /* Get length of 3 first key parts */
  key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
                      key_part[2].store_length + key_part[3].store_length);
  key_copy(key, table->record[0], table->key_info, key_prefix_length);
unknown's avatar
unknown committed
2349

2350
  rights&= COL_ACLS;				// Only ACL for columns
unknown's avatar
unknown committed
2351 2352 2353 2354

  /* first fix privileges for all columns in column list */

  List_iterator <LEX_COLUMN> iter(columns);
unknown's avatar
unknown committed
2355
  class LEX_COLUMN *column;
unknown's avatar
unknown committed
2356
  table->file->ha_index_init(0);
unknown's avatar
unknown committed
2357
  while ((column= iter++))
unknown's avatar
unknown committed
2358
  {
unknown's avatar
unknown committed
2359
    ulong privileges= column->rights;
unknown's avatar
unknown committed
2360
    bool old_row_exists=0;
2361 2362 2363 2364
    byte user_key[MAX_KEY_LENGTH];

    key_restore(table->record[0],key,table->key_info,
                key_prefix_length);
unknown's avatar
unknown committed
2365
    table->field[4]->store(column->column.ptr(), column->column.length(),
2366
                           system_charset_info);
2367 2368 2369
    /* Get key for the first 4 columns */
    key_copy(user_key, table->record[0], table->key_info,
             table->key_info->key_length);
unknown's avatar
unknown committed
2370

unknown's avatar
unknown committed
2371
    table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
2372 2373 2374
    if (table->file->index_read(table->record[0], user_key,
				table->key_info->key_length,
                                HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
2375 2376 2377
    {
      if (revoke_grant)
      {
unknown's avatar
unknown committed
2378
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
2379
                 combo.user.str, combo.host.str,
unknown's avatar
unknown committed
2380 2381 2382
                 table_name);                   /* purecov: inspected */
	result= -1;                             /* purecov: inspected */
	continue;                               /* purecov: inspected */
unknown's avatar
unknown committed
2383
      }
unknown's avatar
unknown committed
2384
      old_row_exists = 0;
2385
      restore_record(table, s->default_values);		// Get empty record
2386 2387
      key_restore(table->record[0],key,table->key_info,
                  key_prefix_length);
unknown's avatar
unknown committed
2388
      table->field[4]->store(column->column.ptr(),column->column.length(),
2389
                             system_charset_info);
unknown's avatar
unknown committed
2390 2391 2392
    }
    else
    {
unknown's avatar
unknown committed
2393
      ulong tmp= (ulong) table->field[6]->val_int();
unknown's avatar
unknown committed
2394 2395 2396 2397 2398 2399
      tmp=fix_rights_for_column(tmp);

      if (revoke_grant)
	privileges = tmp & ~(privileges | rights);
      else
	privileges |= tmp;
unknown's avatar
unknown committed
2400
      old_row_exists = 1;
unknown's avatar
unknown committed
2401
      store_record(table,record[1]);			// copy original row
unknown's avatar
unknown committed
2402 2403
    }

2404
    table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
unknown's avatar
unknown committed
2405

unknown's avatar
unknown committed
2406
    if (old_row_exists)
unknown's avatar
unknown committed
2407
    {
unknown's avatar
unknown committed
2408
      GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
2409 2410 2411 2412 2413 2414 2415 2416 2417 2418
      if (privileges)
	error=table->file->update_row(table->record[1],table->record[0]);
      else
	error=table->file->delete_row(table->record[1]);
      if (error)
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
unknown's avatar
unknown committed
2419 2420
      grant_column= column_hash_search(g_t, column->column.ptr(),
                                       column->column.length());
unknown's avatar
unknown committed
2421
      if (grant_column)				// Should always be true
unknown's avatar
unknown committed
2422
	grant_column->rights= privileges;	// Update hash
unknown's avatar
unknown committed
2423 2424 2425
    }
    else					// new grant
    {
unknown's avatar
unknown committed
2426
      GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
2427 2428 2429 2430 2431 2432
      if ((error=table->file->write_row(table->record[0])))
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
unknown's avatar
unknown committed
2433
      grant_column= new GRANT_COLUMN(column->column,privileges);
unknown's avatar
SCRUM  
unknown committed
2434
      my_hash_insert(&g_t->hash_columns,(byte*) grant_column);
unknown's avatar
unknown committed
2435 2436 2437 2438 2439 2440 2441 2442 2443 2444
    }
  }

  /*
    If revoke of privileges on the table level, remove all such privileges
    for all columns
  */

  if (revoke_grant)
  {
2445 2446
    byte user_key[MAX_KEY_LENGTH];
    key_copy(user_key, table->record[0], table->key_info,
unknown's avatar
unknown committed
2447 2448
             key_prefix_length);

unknown's avatar
unknown committed
2449
    table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
2450
    if (table->file->index_read(table->record[0], user_key,
unknown's avatar
unknown committed
2451
				key_prefix_length,
2452
                                HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
2453 2454
      goto end;

2455
    /* Scan through all rows with the same host,db,user and table */
unknown's avatar
unknown committed
2456 2457
    do
    {
unknown's avatar
unknown committed
2458
      ulong privileges = (ulong) table->field[6]->val_int();
unknown's avatar
unknown committed
2459
      privileges=fix_rights_for_column(privileges);
unknown's avatar
unknown committed
2460
      store_record(table,record[1]);
unknown's avatar
unknown committed
2461 2462 2463 2464 2465

      if (privileges & rights)	// is in this record the priv to be revoked ??
      {
	GRANT_COLUMN *grant_column = NULL;
	char  colum_name_buf[HOSTNAME_LENGTH+1];
2466
	String column_name(colum_name_buf,sizeof(colum_name_buf),
unknown's avatar
unknown committed
2467
                           system_charset_info);
unknown's avatar
unknown committed
2468 2469 2470

	privileges&= ~rights;
	table->field[6]->store((longlong)
2471
			       get_rights_for_column(privileges), TRUE);
2472
	table->field[4]->val_str(&column_name);
unknown's avatar
unknown committed
2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502
	grant_column = column_hash_search(g_t,
					  column_name.ptr(),
					  column_name.length());
	if (privileges)
	{
	  int tmp_error;
	  if ((tmp_error=table->file->update_row(table->record[1],
						 table->record[0])))
	  {					/* purecov: deadcode */
	    table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
	    result= -1;				/* purecov: deadcode */
	    goto end;				/* purecov: deadcode */
	  }
	  if (grant_column)
	    grant_column->rights  = privileges; // Update hash
	}
	else
	{
	  int tmp_error;
	  if ((tmp_error = table->file->delete_row(table->record[1])))
	  {					/* purecov: deadcode */
	    table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
	    result= -1;				/* purecov: deadcode */
	    goto end;				/* purecov: deadcode */
	  }
	  if (grant_column)
	    hash_delete(&g_t->hash_columns,(byte*) grant_column);
	}
      }
    } while (!table->file->index_next(table->record[0]) &&
2503
	     !key_cmp_if_same(table, key, 0, key_prefix_length));
unknown's avatar
unknown committed
2504 2505
  }

2506
end:
unknown's avatar
unknown committed
2507
  table->file->ha_index_end();
unknown's avatar
unknown committed
2508 2509 2510 2511 2512 2513 2514
  DBUG_RETURN(result);
}


static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
			       TABLE *table, const LEX_USER &combo,
			       const char *db, const char *table_name,
unknown's avatar
unknown committed
2515 2516
			       ulong rights, ulong col_rights,
			       bool revoke_grant)
unknown's avatar
unknown committed
2517
{
2518
  char grantor[USER_HOST_BUFF_SIZE];
unknown's avatar
unknown committed
2519
  int old_row_exists = 1;
unknown's avatar
unknown committed
2520
  int error=0;
unknown's avatar
unknown committed
2521
  ulong store_table_rights, store_col_rights;
2522
  byte user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
2523 2524
  DBUG_ENTER("replace_table_table");

2525 2526
  strxmov(grantor, thd->security_ctx->user, "@",
          thd->security_ctx->host_or_ip, NullS);
unknown's avatar
unknown committed
2527

unknown's avatar
unknown committed
2528 2529 2530 2531
  /*
    The following should always succeed as new users are created before
    this function is called!
  */
2532
  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
unknown's avatar
unknown committed
2533
  {
unknown's avatar
unknown committed
2534 2535
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
               MYF(0));	/* purecov: deadcode */
unknown's avatar
unknown committed
2536 2537 2538
    DBUG_RETURN(-1);				/* purecov: deadcode */
  }

2539
  restore_record(table, s->default_values);     // Get empty record
2540 2541 2542 2543
  table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
  table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
  table->field[3]->store(table_name,(uint) strlen(table_name), system_charset_info);
unknown's avatar
unknown committed
2544
  store_record(table,record[1]);			// store at pos 1
2545 2546
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);
unknown's avatar
unknown committed
2547

unknown's avatar
unknown committed
2548
  table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
2549 2550
  if (table->file->index_read_idx(table->record[0], 0,
                                  user_key, table->key_info->key_length,
unknown's avatar
unknown committed
2551 2552 2553 2554 2555 2556 2557 2558 2559
				  HA_READ_KEY_EXACT))
  {
    /*
      The following should never happen as we first check the in memory
      grant tables for the user.  There is however always a small change that
      the user has modified the grant tables directly.
    */
    if (revoke_grant)
    { // no row, no revoke
unknown's avatar
unknown committed
2560 2561
      my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
               combo.user.str, combo.host.str,
2562
               table_name);		        /* purecov: deadcode */
unknown's avatar
unknown committed
2563 2564
      DBUG_RETURN(-1);				/* purecov: deadcode */
    }
unknown's avatar
unknown committed
2565
    old_row_exists = 0;
unknown's avatar
unknown committed
2566
    restore_record(table,record[1]);			// Get saved record
unknown's avatar
unknown committed
2567 2568
  }

unknown's avatar
unknown committed
2569 2570
  store_table_rights= get_rights_for_table(rights);
  store_col_rights=   get_rights_for_column(col_rights);
unknown's avatar
unknown committed
2571
  if (old_row_exists)
unknown's avatar
unknown committed
2572
  {
unknown's avatar
unknown committed
2573
    ulong j,k;
unknown's avatar
unknown committed
2574
    store_record(table,record[1]);
unknown's avatar
unknown committed
2575 2576
    j = (ulong) table->field[6]->val_int();
    k = (ulong) table->field[7]->val_int();
unknown's avatar
unknown committed
2577 2578 2579

    if (revoke_grant)
    {
2580
      /* column rights are already fixed in mysql_table_grant */
unknown's avatar
unknown committed
2581 2582 2583 2584
      store_table_rights=j & ~store_table_rights;
    }
    else
    {
unknown's avatar
unknown committed
2585 2586
      store_table_rights|= j;
      store_col_rights|=   k;
unknown's avatar
unknown committed
2587 2588 2589
    }
  }

2590
  table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
2591 2592
  table->field[6]->store((longlong) store_table_rights, TRUE);
  table->field[7]->store((longlong) store_col_rights, TRUE);
unknown's avatar
unknown committed
2593
  rights=fix_rights_for_table(store_table_rights);
unknown's avatar
unknown committed
2594
  col_rights=fix_rights_for_column(store_col_rights);
unknown's avatar
unknown committed
2595

unknown's avatar
unknown committed
2596
  if (old_row_exists)
unknown's avatar
unknown committed
2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612
  {
    if (store_table_rights || store_col_rights)
    {
      if ((error=table->file->update_row(table->record[1],table->record[0])))
	goto table_error;			/* purecov: deadcode */
    }
    else if ((error = table->file->delete_row(table->record[1])))
      goto table_error;				/* purecov: deadcode */
  }
  else
  {
    error=table->file->write_row(table->record[0]);
    if (error && error != HA_ERR_FOUND_DUPP_KEY)
      goto table_error;				/* purecov: deadcode */
  }

unknown's avatar
unknown committed
2613
  if (rights | col_rights)
unknown's avatar
unknown committed
2614
  {
unknown's avatar
unknown committed
2615
    grant_table->privs= rights;
2616
    grant_table->cols=	col_rights;
unknown's avatar
unknown committed
2617 2618 2619
  }
  else
  {
2620
    hash_delete(&column_priv_hash,(byte*) grant_table);
unknown's avatar
unknown committed
2621 2622 2623
  }
  DBUG_RETURN(0);

2624 2625
  /* This should never happen */
table_error:
unknown's avatar
unknown committed
2626 2627 2628 2629 2630
  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
  DBUG_RETURN(-1); /* purecov: deadcode */
}


2631
static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
2632
			      TABLE *table, const LEX_USER &combo,
2633 2634
			      const char *db, const char *routine_name,
			      bool is_proc, ulong rights, bool revoke_grant)
2635
{
2636
  char grantor[USER_HOST_BUFF_SIZE];
2637 2638 2639
  int old_row_exists= 1;
  int error=0;
  ulong store_proc_rights;
2640
  DBUG_ENTER("replace_routine_table");
2641 2642 2643 2644 2645 2646 2647

  if (!initialized)
  {
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
    DBUG_RETURN(-1);
  }

2648 2649
  strxmov(grantor, thd->security_ctx->user, "@",
          thd->security_ctx->host_or_ip, NullS);
2650 2651 2652 2653 2654

  /*
    The following should always succeed as new users are created before
    this function is called!
  */
unknown's avatar
unknown committed
2655
  if (!find_acl_user(combo.host.str, combo.user.str, FALSE))
2656 2657 2658 2659 2660
  {
    my_error(ER_PASSWORD_NO_MATCH,MYF(0));
    DBUG_RETURN(-1);
  }

2661
  restore_record(table, s->default_values);		// Get empty record
2662 2663 2664
  table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
  table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
  table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
2665 2666 2667
  table->field[3]->store(routine_name,(uint) strlen(routine_name),
                         &my_charset_latin1);
  table->field[4]->store((longlong)(is_proc ? 
2668 2669
                                    TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION),
                         TRUE);
2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683
  store_record(table,record[1]);			// store at pos 1

  if (table->file->index_read_idx(table->record[0],0,
				  (byte*) table->field[0]->ptr,0,
				  HA_READ_KEY_EXACT))
  {
    /*
      The following should never happen as we first check the in memory
      grant tables for the user.  There is however always a small change that
      the user has modified the grant tables directly.
    */
    if (revoke_grant)
    { // no row, no revoke
      my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
2684
               combo.user.str, combo.host.str, routine_name);
2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708
      DBUG_RETURN(-1);
    }
    old_row_exists= 0;
    restore_record(table,record[1]);			// Get saved record
  }

  store_proc_rights= get_rights_for_procedure(rights);
  if (old_row_exists)
  {
    ulong j;
    store_record(table,record[1]);
    j= (ulong) table->field[6]->val_int();

    if (revoke_grant)
    {
      /* column rights are already fixed in mysql_table_grant */
      store_proc_rights=j & ~store_proc_rights;
    }
    else
    {
      store_proc_rights|= j;
    }
  }

2709
  table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
2710
  table->field[6]->store((longlong) store_proc_rights, TRUE);
2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735
  rights=fix_rights_for_procedure(store_proc_rights);

  if (old_row_exists)
  {
    if (store_proc_rights)
    {
      if ((error=table->file->update_row(table->record[1],table->record[0])))
	goto table_error;
    }
    else if ((error= table->file->delete_row(table->record[1])))
      goto table_error;
  }
  else
  {
    error=table->file->write_row(table->record[0]);
    if (error && error != HA_ERR_FOUND_DUPP_KEY)
      goto table_error;
  }

  if (rights)
  {
    grant_name->privs= rights;
  }
  else
  {
2736
    hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
2737 2738 2739 2740 2741 2742 2743 2744 2745 2746
  }
  DBUG_RETURN(0);

  /* This should never happen */
table_error:
  table->file->print_error(error,MYF(0));
  DBUG_RETURN(-1);
}


2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759
/*
  Store table level and column level grants in the privilege tables

  SYNOPSIS
    mysql_table_grant()
    thd			Thread handle
    table_list		List of tables to give grant
    user_list		List of users to give grant
    columns		List of columns to give grant
    rights		Table level grant
    revoke_grant	Set to 1 if this is a REVOKE command

  RETURN
unknown's avatar
unknown committed
2760 2761
    FALSE ok
    TRUE  error
2762 2763
*/

unknown's avatar
unknown committed
2764
bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
unknown's avatar
unknown committed
2765 2766 2767
		      List <LEX_USER> &user_list,
		      List <LEX_COLUMN> &columns, ulong rights,
		      bool revoke_grant)
unknown's avatar
unknown committed
2768
{
2769
  ulong column_priv= 0;
unknown's avatar
unknown committed
2770
  List_iterator <LEX_USER> str_list (user_list);
2771
  LEX_USER *Str, *tmp_Str;
unknown's avatar
unknown committed
2772
  TABLE_LIST tables[3];
unknown's avatar
unknown committed
2773
  bool create_new_users=0;
2774
  char *db_name, *table_name;
unknown's avatar
unknown committed
2775 2776 2777 2778
  DBUG_ENTER("mysql_table_grant");

  if (!initialized)
  {
unknown's avatar
unknown committed
2779 2780
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: inspected */
unknown's avatar
unknown committed
2781
    DBUG_RETURN(TRUE);				/* purecov: inspected */
unknown's avatar
unknown committed
2782 2783 2784
  }
  if (rights & ~TABLE_ACLS)
  {
unknown's avatar
unknown committed
2785 2786
    my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
               MYF(0));
unknown's avatar
unknown committed
2787
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2788 2789
  }

2790
  if (!revoke_grant)
unknown's avatar
unknown committed
2791
  {
unknown's avatar
unknown committed
2792
    if (columns.elements)
unknown's avatar
unknown committed
2793
    {
2794 2795
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
unknown's avatar
unknown committed
2796 2797 2798

      if (open_and_lock_tables(thd, table_list))
        DBUG_RETURN(TRUE);
2799 2800

      while ((column = column_iter++))
unknown's avatar
unknown committed
2801
      {
unknown's avatar
merged  
unknown committed
2802
        uint unused_field_idx= NO_CACHED_FIELD_INDEX;
unknown's avatar
unknown committed
2803 2804
        TABLE_LIST *dummy;
        Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
2805
                                         column->column.length(),
unknown's avatar
unknown committed
2806
                                         column->column.ptr(), NULL, NULL,
2807
                                         NULL, TRUE, FALSE,
unknown's avatar
unknown committed
2808
                                         &unused_field_idx, FALSE, &dummy);
unknown's avatar
unknown committed
2809
        if (f == (Field*)0)
2810
        {
unknown's avatar
merged  
unknown committed
2811 2812
          my_error(ER_BAD_FIELD_ERROR, MYF(0),
                   column->column.c_ptr(), table_list->alias);
unknown's avatar
unknown committed
2813
          DBUG_RETURN(TRUE);
2814
        }
unknown's avatar
unknown committed
2815 2816
        if (f == (Field *)-1)
          DBUG_RETURN(TRUE);
2817
        column_priv|= column->rights;
unknown's avatar
unknown committed
2818
      }
2819
      close_thread_tables(thd);
unknown's avatar
unknown committed
2820
    }
2821
    else
unknown's avatar
unknown committed
2822
    {
2823 2824 2825 2826
      if (!(rights & CREATE_ACL))
      {
        char buf[FN_REFLEN];
        sprintf(buf,"%s/%s/%s.frm",mysql_data_home, table_list->db,
2827
                table_list->table_name);
2828 2829 2830
        fn_format(buf,buf,"","",4+16+32);
        if (access(buf,F_OK))
        {
unknown's avatar
merged  
unknown committed
2831
          my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
unknown's avatar
unknown committed
2832
          DBUG_RETURN(TRUE);
2833 2834 2835 2836 2837 2838 2839 2840
        }
      }
      if (table_list->grant.want_privilege)
      {
        char command[128];
        get_privilege_desc(command, sizeof(command),
                           table_list->grant.want_privilege);
        my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
2841 2842
                 command, thd->security_ctx->priv_user,
                 thd->security_ctx->host_or_ip, table_list->alias);
2843 2844
        DBUG_RETURN(-1);
      }
unknown's avatar
unknown committed
2845 2846 2847 2848 2849 2850
    }
  }

  /* open the mysql.tables_priv and mysql.columns_priv tables */

  bzero((char*) &tables,sizeof(tables));
2851 2852 2853
  tables[0].alias=tables[0].table_name= (char*) "user";
  tables[1].alias=tables[1].table_name= (char*) "tables_priv";
  tables[2].alias=tables[2].table_name= (char*) "columns_priv";
unknown's avatar
VIEW  
unknown committed
2854
  tables[0].next_local= tables[0].next_global= tables+1;
unknown's avatar
unknown committed
2855
  /* Don't open column table if we don't need it ! */
unknown's avatar
VIEW  
unknown committed
2856 2857 2858 2859 2860
  tables[1].next_local=
    tables[1].next_global= ((column_priv ||
			     (revoke_grant &&
			      ((rights & COL_ACLS) || columns.elements)))
			    ? tables+2 : 0);
unknown's avatar
unknown committed
2861 2862 2863
  tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE;
  tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";

2864 2865 2866 2867 2868
#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
2869 2870
  if (thd->slave_thread && table_rules_on)
  {
unknown's avatar
unknown committed
2871 2872 2873
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
2874
    */
2875
    tables[0].updating= tables[1].updating= tables[2].updating= 1;
2876
    if (!tables_ok(thd, tables))
unknown's avatar
unknown committed
2877
      DBUG_RETURN(FALSE);
2878
  }
2879 2880
#endif

2881
  if (simple_open_n_lock_tables(thd,tables))
unknown's avatar
unknown committed
2882 2883
  {						// Should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
unknown's avatar
unknown committed
2884
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
unknown's avatar
unknown committed
2885 2886
  }

unknown's avatar
unknown committed
2887 2888
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
unknown's avatar
unknown committed
2889
  bool result= FALSE;
2890
  rw_wrlock(&LOCK_grant);
2891
  pthread_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
2892 2893
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;
2894
  grant_version++;
unknown's avatar
unknown committed
2895

2896
  while ((tmp_Str = str_list++))
unknown's avatar
unknown committed
2897
  {
unknown's avatar
unknown committed
2898
    int error;
unknown's avatar
unknown committed
2899
    GRANT_TABLE *grant_table;
2900 2901 2902 2903 2904
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
    }  
unknown's avatar
unknown committed
2905
    /* Create user if needed */
unknown's avatar
unknown committed
2906
    error=replace_user_table(thd, tables[0].table, *Str,
unknown's avatar
unknown committed
2907
			     0, revoke_grant, create_new_users,
unknown's avatar
unknown committed
2908 2909
                             test(thd->variables.sql_mode &
                                  MODE_NO_AUTO_CREATE_USER));
unknown's avatar
unknown committed
2910
    if (error)
unknown's avatar
unknown committed
2911
    {
unknown's avatar
unknown committed
2912
      result= TRUE;				// Remember error
unknown's avatar
unknown committed
2913 2914 2915
      continue;					// Add next user
    }

unknown's avatar
VIEW  
unknown committed
2916 2917 2918
    db_name= (table_list->view_db.length ?
	      table_list->view_db.str :
	      table_list->db);
2919
    table_name= (table_list->view_name.length ?
unknown's avatar
VIEW  
unknown committed
2920
		table_list->view_name.str :
2921
		table_list->table_name);
unknown's avatar
VIEW  
unknown committed
2922

unknown's avatar
unknown committed
2923
    /* Find/create cached table grant */
unknown's avatar
VIEW  
unknown committed
2924
    grant_table= table_hash_search(Str->host.str, NullS, db_name,
2925
				   Str->user.str, table_name, 1);
unknown's avatar
unknown committed
2926 2927 2928 2929
    if (!grant_table)
    {
      if (revoke_grant)
      {
unknown's avatar
unknown committed
2930
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
2931
                 Str->user.str, Str->host.str, table_list->table_name);
unknown's avatar
unknown committed
2932
	result= TRUE;
unknown's avatar
unknown committed
2933 2934
	continue;
      }
unknown's avatar
VIEW  
unknown committed
2935
      grant_table = new GRANT_TABLE (Str->host.str, db_name,
2936
				     Str->user.str, table_name,
unknown's avatar
unknown committed
2937 2938 2939 2940
				     rights,
				     column_priv);
      if (!grant_table)				// end of memory
      {
unknown's avatar
unknown committed
2941
	result= TRUE;				/* purecov: deadcode */
unknown's avatar
unknown committed
2942 2943
	continue;				/* purecov: deadcode */
      }
unknown's avatar
SCRUM  
unknown committed
2944
      my_hash_insert(&column_priv_hash,(byte*) grant_table);
unknown's avatar
unknown committed
2945 2946 2947 2948 2949
    }

    /* If revoke_grant, calculate the new column privilege for tables_priv */
    if (revoke_grant)
    {
2950 2951
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
unknown's avatar
unknown committed
2952 2953 2954
      GRANT_COLUMN *grant_column;

      /* Fix old grants */
2955
      while ((column = column_iter++))
unknown's avatar
unknown committed
2956 2957
      {
	grant_column = column_hash_search(grant_table,
2958 2959
					  column->column.ptr(),
					  column->column.length());
unknown's avatar
unknown committed
2960
	if (grant_column)
2961
	  grant_column->rights&= ~(column->rights | rights);
unknown's avatar
unknown committed
2962 2963
      }
      /* scan trough all columns to get new column grant */
2964
      column_priv= 0;
unknown's avatar
unknown committed
2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980
      for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
      {
	grant_column= (GRANT_COLUMN*) hash_element(&grant_table->hash_columns,
						   idx);
	grant_column->rights&= ~rights;		// Fix other columns
	column_priv|= grant_column->rights;
      }
    }
    else
    {
      column_priv|= grant_table->cols;
    }


    /* update table and columns */

unknown's avatar
VIEW  
unknown committed
2981
    if (replace_table_table(thd, grant_table, tables[1].table, *Str,
2982
			    db_name, table_name,
unknown's avatar
unknown committed
2983
			    rights, column_priv, revoke_grant))
2984 2985
    {
      /* Should only happen if table is crashed */
unknown's avatar
unknown committed
2986
      result= TRUE;			       /* purecov: deadcode */
unknown's avatar
unknown committed
2987 2988 2989
    }
    else if (tables[2].table)
    {
unknown's avatar
VIEW  
unknown committed
2990
      if ((replace_column_table(grant_table, tables[2].table, *Str,
unknown's avatar
unknown committed
2991
				columns,
2992
				db_name, table_name,
unknown's avatar
unknown committed
2993 2994
				rights, revoke_grant)))
      {
unknown's avatar
unknown committed
2995
	result= TRUE;
unknown's avatar
unknown committed
2996 2997 2998 2999
      }
    }
  }
  grant_option=TRUE;
unknown's avatar
unknown committed
3000
  thd->mem_root= old_root;
3001
  pthread_mutex_unlock(&acl_cache->lock);
3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012

  if (!result) /* success */
  {
    if (mysql_bin_log.is_open())
    {
      thd->clear_error();
      Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
      mysql_bin_log.write(&qinfo);
    }
  }

3013
  rw_unlock(&LOCK_grant);
3014 3015

  if (!result) /* success */
3016
    send_ok(thd);
3017

3018
  /* Tables are automatically closed */
unknown's avatar
unknown committed
3019 3020 3021 3022
  DBUG_RETURN(result);
}


3023
/*
3024
  Store routine level grants in the privilege tables
3025 3026

  SYNOPSIS
3027
    mysql_routine_grant()
3028
    thd			Thread handle
3029 3030
    table_list		List of routines to give grant
    is_proc             true indicates routine list are procedures
3031 3032 3033 3034 3035 3036 3037 3038 3039
    user_list		List of users to give grant
    rights		Table level grant
    revoke_grant	Set to 1 if this is a REVOKE command

  RETURN
    0	ok
    1	error
*/

3040 3041 3042
bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
			 List <LEX_USER> &user_list, ulong rights,
			 bool revoke_grant, bool no_error)
3043 3044
{
  List_iterator <LEX_USER> str_list (user_list);
3045
  LEX_USER *Str, *tmp_Str;
3046 3047
  TABLE_LIST tables[2];
  bool create_new_users=0, result=0;
3048
  char *db_name, *table_name;
3049
  DBUG_ENTER("mysql_routine_grant");
3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067

  if (!initialized)
  {
    if (!no_error)
      my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
               "--skip-grant-tables");
    DBUG_RETURN(TRUE);
  }
  if (rights & ~PROC_ACLS)
  {
    if (!no_error)
      my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
        	 MYF(0));
    DBUG_RETURN(TRUE);
  }

  if (!revoke_grant)
  {
3068
    if (sp_exist_routines(thd, table_list, is_proc, no_error)<0)
3069 3070 3071 3072 3073 3074
      DBUG_RETURN(TRUE);
  }

  /* open the mysql.user and mysql.procs_priv tables */

  bzero((char*) &tables,sizeof(tables));
3075 3076
  tables[0].alias=tables[0].table_name= (char*) "user";
  tables[1].alias=tables[1].table_name= (char*) "procs_priv";
3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092
  tables[0].next_local= tables[0].next_global= tables+1;
  tables[0].lock_type=tables[1].lock_type=TL_WRITE;
  tables[0].db=tables[1].db=(char*) "mysql";

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
  if (thd->slave_thread && table_rules_on)
  {
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
    */
    tables[0].updating= tables[1].updating= 1;
3093
    if (!tables_ok(thd, tables))
3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106
      DBUG_RETURN(FALSE);
  }
#endif

  if (simple_open_n_lock_tables(thd,tables))
  {						// Should never happen
    close_thread_tables(thd);
    DBUG_RETURN(TRUE);
  }

  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
  rw_wrlock(&LOCK_grant);
3107
  pthread_mutex_lock(&acl_cache->lock);
3108 3109 3110 3111 3112
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;

  DBUG_PRINT("info",("now time to iterate and add users"));

3113
  while ((tmp_Str= str_list++))
3114 3115 3116
  {
    int error;
    GRANT_NAME *grant_name;
3117 3118 3119 3120 3121
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
    }  
3122 3123
    /* Create user if needed */
    error=replace_user_table(thd, tables[0].table, *Str,
unknown's avatar
unknown committed
3124
			     0, revoke_grant, create_new_users,
unknown's avatar
unknown committed
3125 3126
                             test(thd->variables.sql_mode &
                                  MODE_NO_AUTO_CREATE_USER));
3127 3128 3129 3130 3131 3132 3133
    if (error)
    {
      result= TRUE;				// Remember error
      continue;					// Add next user
    }

    db_name= table_list->db;
3134
    table_name= table_list->table_name;
3135

3136 3137
    grant_name= routine_hash_search(Str->host.str, NullS, db_name,
                                    Str->user.str, table_name, is_proc, 1);
3138 3139 3140 3141 3142 3143
    if (!grant_name)
    {
      if (revoke_grant)
      {
        if (!no_error)
          my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
3144
		   Str->user.str, Str->host.str, table_name);
3145 3146 3147 3148
	result= TRUE;
	continue;
      }
      grant_name= new GRANT_NAME(Str->host.str, db_name,
3149
				 Str->user.str, table_name,
3150 3151 3152 3153 3154 3155
				 rights);
      if (!grant_name)
      {
        result= TRUE;
	continue;
      }
3156
      my_hash_insert(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
3157
    }
unknown's avatar
unknown committed
3158

3159 3160
    if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
			   db_name, table_name, is_proc, rights, revoke_grant))
3161 3162 3163 3164 3165 3166 3167
    {
      result= TRUE;
      continue;
    }
  }
  grant_option=TRUE;
  thd->mem_root= old_root;
3168
  pthread_mutex_unlock(&acl_cache->lock);
3169 3170 3171 3172 3173 3174 3175 3176 3177 3178
  if (!result && !no_error)
  {
    if (mysql_bin_log.is_open())
    {
      thd->clear_error();
      Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
      mysql_bin_log.write(&qinfo);
    }
  }

3179
  rw_unlock(&LOCK_grant);
3180

3181 3182
  if (!result && !no_error)
    send_ok(thd);
3183

3184 3185 3186 3187 3188
  /* Tables are automatically closed */
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
3189 3190
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
                 ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
3191 3192
{
  List_iterator <LEX_USER> str_list (list);
3193
  LEX_USER *Str, *tmp_Str;
unknown's avatar
unknown committed
3194
  char tmp_db[NAME_LEN+1];
unknown's avatar
unknown committed
3195
  bool create_new_users=0;
unknown's avatar
unknown committed
3196 3197 3198 3199
  TABLE_LIST tables[2];
  DBUG_ENTER("mysql_grant");
  if (!initialized)
  {
unknown's avatar
unknown committed
3200 3201
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: tested */
unknown's avatar
unknown committed
3202
    DBUG_RETURN(TRUE);				/* purecov: tested */
unknown's avatar
unknown committed
3203 3204
  }

unknown's avatar
unknown committed
3205 3206 3207
  if (lower_case_table_names && db)
  {
    strmov(tmp_db,db);
3208
    my_casedn_str(files_charset_info, tmp_db);
unknown's avatar
unknown committed
3209 3210
    db=tmp_db;
  }
unknown's avatar
unknown committed
3211 3212

  /* open the mysql.user and mysql.db tables */
3213
  bzero((char*) &tables,sizeof(tables));
3214 3215
  tables[0].alias=tables[0].table_name=(char*) "user";
  tables[1].alias=tables[1].table_name=(char*) "db";
unknown's avatar
VIEW  
unknown committed
3216
  tables[0].next_local= tables[0].next_global= tables+1;
unknown's avatar
unknown committed
3217 3218
  tables[0].lock_type=tables[1].lock_type=TL_WRITE;
  tables[0].db=tables[1].db=(char*) "mysql";
3219 3220 3221 3222 3223 3224

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
3225 3226
  if (thd->slave_thread && table_rules_on)
  {
unknown's avatar
unknown committed
3227 3228 3229
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
3230
    */
3231
    tables[0].updating= tables[1].updating= 1;
3232
    if (!tables_ok(thd, tables))
unknown's avatar
unknown committed
3233
      DBUG_RETURN(FALSE);
3234
  }
3235 3236
#endif

3237
  if (simple_open_n_lock_tables(thd,tables))
unknown's avatar
unknown committed
3238 3239
  {						// This should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
unknown's avatar
unknown committed
3240
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
unknown's avatar
unknown committed
3241 3242
  }

unknown's avatar
unknown committed
3243 3244
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
unknown's avatar
unknown committed
3245

3246
  /* go through users in user_list */
3247
  rw_wrlock(&LOCK_grant);
unknown's avatar
unknown committed
3248 3249 3250 3251
  VOID(pthread_mutex_lock(&acl_cache->lock));
  grant_version++;

  int result=0;
3252
  while ((tmp_Str = str_list++))
unknown's avatar
unknown committed
3253
  {
3254 3255 3256 3257
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
unknown's avatar
unknown committed
3258
    }
unknown's avatar
unknown committed
3259 3260
    if (replace_user_table(thd, tables[0].table, *Str,
                           (!db ? rights : 0), revoke_grant, create_new_users,
unknown's avatar
unknown committed
3261 3262
                           test(thd->variables.sql_mode &
                                MODE_NO_AUTO_CREATE_USER)))
unknown's avatar
unknown committed
3263
      result= -1;
unknown's avatar
unknown committed
3264
    else if (db)
unknown's avatar
unknown committed
3265
    {
unknown's avatar
unknown committed
3266 3267 3268 3269 3270 3271 3272 3273 3274
      ulong db_rights= rights & DB_ACLS;
      if (db_rights  == rights)
      {
	if (replace_db_table(tables[1].table, db, *Str, db_rights,
			     revoke_grant))
	  result= -1;
      }
      else
      {
unknown's avatar
unknown committed
3275
	my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
unknown's avatar
unknown committed
3276
	result= -1;
unknown's avatar
unknown committed
3277
      }
unknown's avatar
unknown committed
3278
    }
unknown's avatar
unknown committed
3279 3280
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291

  if (!result)
  {
    if (mysql_bin_log.is_open())
    {
      thd->clear_error();
      Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
      mysql_bin_log.write(&qinfo);
    }
  }

3292
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3293 3294 3295
  close_thread_tables(thd);

  if (!result)
3296
    send_ok(thd);
3297

unknown's avatar
unknown committed
3298 3299 3300
  DBUG_RETURN(result);
}

unknown's avatar
unknown committed
3301 3302

/* Free grant array if possible */
unknown's avatar
unknown committed
3303 3304 3305 3306 3307

void  grant_free(void)
{
  DBUG_ENTER("grant_free");
  grant_option = FALSE;
3308
  hash_free(&column_priv_hash);
3309
  hash_free(&proc_priv_hash);
unknown's avatar
unknown committed
3310
  hash_free(&func_priv_hash);
3311
  free_root(&memex,MYF(0));
unknown's avatar
unknown committed
3312 3313 3314 3315
  DBUG_VOID_RETURN;
}


3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326
/*
  Initialize structures responsible for table/column-level privilege checking
  and load information for them from tables in the 'mysql' database.

  SYNOPSIS
    grant_init()

  RETURN VALUES
    0	ok
    1	Could not initialize grant's
*/
unknown's avatar
unknown committed
3327

3328
my_bool grant_init()
unknown's avatar
unknown committed
3329
{
unknown's avatar
unknown committed
3330
  THD  *thd;
3331 3332 3333 3334 3335
  my_bool return_val;
  DBUG_ENTER("grant_init");

  if (!(thd= new THD))
    DBUG_RETURN(1);				/* purecov: deadcode */
3336
  thd->thread_stack= (char*) &thd;
3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362
  thd->store_globals();
  return_val=  grant_reload(thd);
  delete thd;
  /* Remember that we don't have a THD */
  my_pthread_setspecific_ptr(THR_THD,  0);
  DBUG_RETURN(return_val);
}


/*
  Initialize structures responsible for table/column-level privilege
  checking and load information about grants from open privilege tables.

  SYNOPSIS
    grant_load()
      thd     Current thread
      tables  List containing open "mysql.tables_priv" and
              "mysql.columns_priv" tables.

  RETURN VALUES
    FALSE - success
    TRUE  - error
*/

static my_bool grant_load(TABLE_LIST *tables)
{
unknown's avatar
unknown committed
3363
  MEM_ROOT *memex_ptr;
3364
  my_bool return_val= 1;
3365
  TABLE *t_table, *c_table, *p_table;
unknown's avatar
SCRUM  
unknown committed
3366
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
3367 3368 3369
  MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
                                                           THR_MALLOC);
  DBUG_ENTER("grant_load");
unknown's avatar
unknown committed
3370 3371

  grant_option = FALSE;
3372
  (void) hash_init(&column_priv_hash,system_charset_info,
unknown's avatar
unknown committed
3373
		   0,0,0, (hash_get_key) get_grant_table,
unknown's avatar
unknown committed
3374
		   (hash_free_key) free_grant_table,0);
3375 3376 3377
  (void) hash_init(&proc_priv_hash,system_charset_info,
		   0,0,0, (hash_get_key) get_grant_table,
		   0,0);
3378 3379 3380
  (void) hash_init(&func_priv_hash,system_charset_info,
		   0,0,0, (hash_get_key) get_grant_table,
		   0,0);
3381
  init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
unknown's avatar
unknown committed
3382 3383

  t_table = tables[0].table; c_table = tables[1].table;
3384
  p_table= tables[2].table;
unknown's avatar
unknown committed
3385
  t_table->file->ha_index_init(0);
3386 3387
  p_table->file->ha_index_init(0);
  if (!t_table->file->index_first(t_table->record[0]))
unknown's avatar
unknown committed
3388
  {
3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
    {
      GRANT_TABLE *mem_check;
      if (!(mem_check=new GRANT_TABLE(t_table,c_table)))
      {
	/* This could only happen if we are out memory */
	grant_option= FALSE;
	goto end_unlock;
      }
unknown's avatar
unknown committed
3400

3401 3402
      if (check_no_resolve)
      {
unknown's avatar
unknown committed
3403
	if (hostname_requires_resolving(mem_check->host.hostname))
3404 3405 3406
	{
          sql_print_warning("'tables_priv' entry '%s %s@%s' "
                            "ignored in --skip-name-resolve mode.",
3407 3408 3409 3410
                            mem_check->tname,
                            mem_check->user ? mem_check->user : "",
                            mem_check->host.hostname ?
                            mem_check->host.hostname : "");
3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426
	  continue;
	}
      }

      if (! mem_check->ok())
	delete mem_check;
      else if (my_hash_insert(&column_priv_hash,(byte*) mem_check))
      {
	delete mem_check;
	grant_option= FALSE;
	goto end_unlock;
      }
    }
    while (!t_table->file->index_next(t_table->record[0]));
  }
  if (!p_table->file->index_first(p_table->record[0]))
unknown's avatar
unknown committed
3427
  {
3428 3429 3430
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
unknown's avatar
unknown committed
3431
    {
3432
      GRANT_NAME *mem_check;
3433
      HASH *hash;
3434 3435 3436 3437 3438 3439
      if (!(mem_check=new GRANT_NAME(p_table)))
      {
	/* This could only happen if we are out memory */
	grant_option= FALSE;
	goto end_unlock;
      }
unknown's avatar
SCRUM  
unknown committed
3440

3441
      if (check_no_resolve)
unknown's avatar
SCRUM  
unknown committed
3442
      {
unknown's avatar
unknown committed
3443
	if (hostname_requires_resolving(mem_check->host.hostname))
3444 3445 3446 3447
	{
          sql_print_warning("'procs_priv' entry '%s %s@%s' "
                            "ignored in --skip-name-resolve mode.",
                            mem_check->tname, mem_check->user,
3448 3449
                            mem_check->host.hostname ?
                            mem_check->host.hostname : "");
3450 3451
	  continue;
	}
unknown's avatar
SCRUM  
unknown committed
3452
      }
3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468
      if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
      {
        hash= &proc_priv_hash;
      }
      else
      if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
      {
        hash= &func_priv_hash;
      }
      else
      {
        sql_print_warning("'procs_priv' entry '%s' "
                          "ignored, bad routine type",
                          mem_check->tname);
	continue;
      }
unknown's avatar
SCRUM  
unknown committed
3469

3470 3471 3472
      mem_check->privs= fix_rights_for_procedure(mem_check->privs);
      if (! mem_check->ok())
	delete mem_check;
3473
      else if (my_hash_insert(hash, (byte*) mem_check))
3474 3475 3476 3477 3478
      {
	delete mem_check;
	grant_option= FALSE;
	goto end_unlock;
      }
unknown's avatar
SCRUM  
unknown committed
3479
    }
3480
    while (!p_table->file->index_next(p_table->record[0]));
unknown's avatar
unknown committed
3481
  }
3482
  grant_option= TRUE;
3483 3484 3485
  return_val=0;					// Return ok

end_unlock:
unknown's avatar
unknown committed
3486
  t_table->file->ha_index_end();
3487
  p_table->file->ha_index_end();
3488
  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
3489
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
3490 3491 3492
}


3493
/*
3494
  Reload information about table and column level privileges if possible.
3495 3496 3497

  SYNOPSIS
    grant_reload()
3498
      thd  Current thread
3499 3500

  NOTES
3501 3502 3503 3504 3505 3506 3507 3508
    Locked tables are checked by acl_reload() and doesn't have to be checked
    in this call.
    This function is also used for initialization of structures responsible
    for table/column-level privilege checking.

  RETURN VALUE
    FALSE Success
    TRUE  Error
3509
*/
unknown's avatar
unknown committed
3510

3511
my_bool grant_reload(THD *thd)
unknown's avatar
unknown committed
3512
{
3513
  TABLE_LIST tables[3];
3514
  HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
3515
  bool old_grant_option;
unknown's avatar
unknown committed
3516
  MEM_ROOT old_mem;
3517
  my_bool return_val= 1;
unknown's avatar
unknown committed
3518 3519
  DBUG_ENTER("grant_reload");

3520 3521 3522 3523 3524
  /* Don't do anything if running with --skip-grant-tables */
  if (!initialized)
    DBUG_RETURN(0);

  bzero((char*) tables, sizeof(tables));
3525 3526 3527 3528 3529 3530 3531
  tables[0].alias= tables[0].table_name= (char*) "tables_priv";
  tables[1].alias= tables[1].table_name= (char*) "columns_priv";
  tables[2].alias= tables[2].table_name= (char*) "procs_priv";
  tables[0].db= tables[1].db= tables[2].db= (char *) "mysql";
  tables[0].next_local= tables[0].next_global= tables+1;
  tables[1].next_local= tables[1].next_global= tables+2;
  tables[0].lock_type= tables[1].lock_type= tables[2].lock_type= TL_READ;
3532 3533 3534 3535 3536 3537 3538 3539

  /*
    To avoid deadlocks we should obtain table locks before
    obtaining LOCK_grant rwlock.
  */
  if (simple_open_n_lock_tables(thd, tables))
    goto end;

3540
  rw_wrlock(&LOCK_grant);
unknown's avatar
unknown committed
3541
  grant_version++;
3542
  old_column_priv_hash= column_priv_hash;
3543
  old_proc_priv_hash= proc_priv_hash;
3544
  old_func_priv_hash= func_priv_hash;
unknown's avatar
unknown committed
3545
  old_grant_option= grant_option;
unknown's avatar
unknown committed
3546
  old_mem= memex;
unknown's avatar
unknown committed
3547

3548
  if ((return_val= grant_load(tables)))
unknown's avatar
unknown committed
3549
  {						// Error. Revert to old hash
3550
    DBUG_PRINT("error",("Reverting to old privileges"));
unknown's avatar
unknown committed
3551
    grant_free();				/* purecov: deadcode */
3552
    column_priv_hash= old_column_priv_hash;	/* purecov: deadcode */
3553
    proc_priv_hash= old_proc_priv_hash;
3554
    func_priv_hash= old_func_priv_hash;
unknown's avatar
unknown committed
3555
    grant_option= old_grant_option;		/* purecov: deadcode */
unknown's avatar
unknown committed
3556
    memex= old_mem;				/* purecov: deadcode */
unknown's avatar
unknown committed
3557 3558 3559
  }
  else
  {
3560
    hash_free(&old_column_priv_hash);
3561
    hash_free(&old_proc_priv_hash);
3562
    hash_free(&old_func_priv_hash);
3563
    free_root(&old_mem,MYF(0));
unknown's avatar
unknown committed
3564
  }
3565
  rw_unlock(&LOCK_grant);
3566 3567 3568
end:
  close_thread_tables(thd);
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
3569 3570 3571 3572
}


/****************************************************************************
3573
  Check table level grants
3574

3575
  SYNOPSIS
3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588
   bool check_grant()
   thd		Thread handler
   want_access  Bits of privileges user needs to have
   tables	List of tables to check. The user should have 'want_access'
		to all tables in list.
   show_table	<> 0 if we are in show table. In this case it's enough to have
	        any privilege for the table
   number	Check at most this number of tables.
   no_errors	If 0 then we write an error. The error is sent directly to
		the client

   RETURN
     0  ok
3589
     1  Error: User did not have the requested privileges
3590 3591 3592 3593 3594 3595 3596

   NOTE
     This functions assumes that either number of tables to be inspected
     by it is limited explicitly (i.e. is is not UINT_MAX) or table list
     used and thd->lex->query_tables_own_last value correspond to each
     other (the latter should be either 0 or point to next_global member
     of one of elements of this table list).
unknown's avatar
unknown committed
3597 3598
****************************************************************************/

unknown's avatar
unknown committed
3599
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
unknown's avatar
unknown committed
3600
		 uint show_table, uint number, bool no_errors)
unknown's avatar
unknown committed
3601
{
3602
  TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table();
3603
  Security_context *sctx= thd->security_ctx;
3604
  uint i;
3605
  ulong orig_want_access= want_access;
3606 3607
  DBUG_ENTER("check_grant");
  DBUG_ASSERT(number > 0);
unknown's avatar
unknown committed
3608

3609
  /*
unknown's avatar
unknown committed
3610 3611 3612 3613 3614 3615 3616 3617
    Walk through the list of tables that belong to the query and save the
    requested access (orig_want_privilege) to be able to use it when
    checking access rights to the underlying tables of a view. Our grant
    system gradually eliminates checked bits from want_privilege and thus
    after all checks are done we can no longer use it.
    The check that first_not_own_table is not reached is for the case when
    the given table list refers to the list for prelocking (contains tables
    of other queries). For simple queries first_not_own_table is 0.
3618 3619
  */
  for (i= 0, table= tables;
unknown's avatar
unknown committed
3620
       table != first_not_own_table && i < number;
3621 3622 3623 3624 3625 3626
       table= table->next_global, i++)
  {
    /* Remove SHOW_VIEW_ACL, because it will be checked during making view */
    table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
  }

3627
  rw_rdlock(&LOCK_grant);
3628 3629 3630
  for (table= tables;
       table && number-- && table != first_not_own_table;
       table= table->next_global)
unknown's avatar
unknown committed
3631
  {
3632
    GRANT_TABLE *grant_table;
3633 3634 3635 3636 3637 3638 3639 3640
    sctx = test(table->security_ctx) ?
      table->security_ctx : thd->security_ctx;

    want_access= orig_want_access;
    want_access&= ~sctx->master_access;
    if (!want_access)
      continue;                                 // ok

3641
    if (!(~table->grant.privilege & want_access) || 
3642
        table->derived || table->schema_table)
unknown's avatar
unknown committed
3643
    {
unknown's avatar
VIEW  
unknown committed
3644 3645 3646 3647
      /*
        It is subquery in the FROM clause. VIEW set table->derived after
        table opening, but this function always called before table opening.
      */
3648 3649 3650 3651 3652 3653 3654 3655 3656 3657
      if (!table->referencing_view)
      {
        /*
          If it's a temporary table created for a subquery in the FROM
          clause, or an INFORMATION_SCHEMA table, drop the request for
          a privilege.
        */
        table->grant.want_privilege= 0;
      }
      continue;
unknown's avatar
unknown committed
3658
    }
3659 3660 3661
    if (!(grant_table= table_hash_search(sctx->host, sctx->ip,
                                         table->db, sctx->priv_user,
                                         table->table_name,0)))
unknown's avatar
unknown committed
3662 3663 3664 3665
    {
      want_access &= ~table->grant.privilege;
      goto err;					// No grants
    }
unknown's avatar
unknown committed
3666 3667
    if (show_table)
      continue;					// We have some priv on this
unknown's avatar
unknown committed
3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683

    table->grant.grant_table=grant_table;	// Remember for column test
    table->grant.version=grant_version;
    table->grant.privilege|= grant_table->privs;
    table->grant.want_privilege= ((want_access & COL_ACLS)
				  & ~table->grant.privilege);

    if (!(~table->grant.privilege & want_access))
      continue;

    if (want_access & ~(grant_table->cols | table->grant.privilege))
    {
      want_access &= ~(grant_table->cols | table->grant.privilege);
      goto err;					// impossible
    }
  }
3684
  rw_unlock(&LOCK_grant);
3685
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3686

3687
err:
3688
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3689
  if (!no_errors)				// Not a silent skip of table
unknown's avatar
unknown committed
3690
  {
3691 3692
    char command[128];
    get_privilege_desc(command, sizeof(command), want_access);
3693 3694
    my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
             command,
3695 3696
             sctx->priv_user,
             sctx->host_or_ip,
3697
             table ? table->table_name : "unknown");
unknown's avatar
unknown committed
3698
  }
3699
  DBUG_RETURN(1);
unknown's avatar
unknown committed
3700 3701 3702
}


3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720
/*
  Check column rights in given security context

  SYNOPSIS
    check_grant_column()
    thd                  thread handler
    grant                grant information structure
    db_name              db name
    table_name           table  name
    name                 column name
    length               column name length
    sctx                 security context

  RETURN
    FALSE OK
    TRUE  access denied
*/

unknown's avatar
VIEW  
unknown committed
3721
bool check_grant_column(THD *thd, GRANT_INFO *grant,
3722
			const char *db_name, const char *table_name,
3723
			const char *name, uint length,  Security_context *sctx)
unknown's avatar
unknown committed
3724 3725 3726
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
unknown's avatar
VIEW  
unknown committed
3727
  ulong want_access= grant->want_privilege & ~grant->privilege;
unknown's avatar
unknown committed
3728 3729 3730
  DBUG_ENTER("check_grant_column");
  DBUG_PRINT("enter", ("table: %s  want_access: %u", table_name, want_access));

unknown's avatar
unknown committed
3731
  if (!want_access)
unknown's avatar
unknown committed
3732
    DBUG_RETURN(0);				// Already checked
unknown's avatar
unknown committed
3733

3734
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
3735

3736
  /* reload table if someone has modified any grants */
unknown's avatar
unknown committed
3737

unknown's avatar
VIEW  
unknown committed
3738
  if (grant->version != grant_version)
unknown's avatar
unknown committed
3739
  {
unknown's avatar
VIEW  
unknown committed
3740
    grant->grant_table=
3741 3742
      table_hash_search(sctx->host, sctx->ip, db_name,
			sctx->priv_user,
unknown's avatar
unknown committed
3743
			table_name, 0);         /* purecov: inspected */
unknown's avatar
VIEW  
unknown committed
3744
    grant->version= grant_version;		/* purecov: inspected */
unknown's avatar
unknown committed
3745
  }
unknown's avatar
VIEW  
unknown committed
3746
  if (!(grant_table= grant->grant_table))
unknown's avatar
unknown committed
3747 3748 3749 3750 3751
    goto err;					/* purecov: deadcode */

  grant_column=column_hash_search(grant_table, name, length);
  if (grant_column && !(~grant_column->rights & want_access))
  {
3752
    rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3753
    DBUG_RETURN(0);
unknown's avatar
unknown committed
3754 3755
  }

3756
err:
3757
  rw_unlock(&LOCK_grant);
3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801
  char command[128];
  get_privilege_desc(command, sizeof(command), want_access);
  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
           command,
           sctx->priv_user,
           sctx->host_or_ip,
           name,
           table_name);
  DBUG_RETURN(1);
}


/*
  Check the access right to a column depending on the type of table.

  SYNOPSIS
    check_column_grant_in_table_ref()
    thd              thread handler
    table_ref        table reference where to check the field
    name             name of field to check
    length           length of name

  DESCRIPTION
    Check the access rights to a column depending on the type of table
    reference where the column is checked. The function provides a
    generic interface to check column access rights that hides the
    heterogeneity of the column representation - whether it is a view
    or a stored table colum.

  RETURN
    FALSE OK
    TRUE  access denied
*/

bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
                                     const char *name, uint length)
{
  GRANT_INFO *grant;
  const char *db_name;
  const char *table_name;
  Security_context *sctx= test(table_ref->security_ctx) ?
                          table_ref->security_ctx : thd->security_ctx;

  if (table_ref->view || table_ref->field_translation)
unknown's avatar
unknown committed
3802
  {
3803
    /* View or derived information schema table. */
3804
    ulong view_privs;
3805 3806 3807
    grant= &(table_ref->grant);
    db_name= table_ref->view_db.str;
    table_name= table_ref->view_name.str;
3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821
    if (table_ref->belong_to_view && 
        (thd->lex->sql_command == SQLCOM_SHOW_FIELDS ||
         thd->lex->sql_command == SQLCOM_SHOW_CREATE))
    {
      view_privs= get_column_grant(thd, grant, db_name, table_name, name);
      if (view_privs & VIEW_ANY_ACL)
      {
        table_ref->belong_to_view->allowed_show= TRUE;
        return FALSE;
      }
      table_ref->belong_to_view->allowed_show= FALSE;
      my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
      return TRUE;
    }
unknown's avatar
unknown committed
3822
  }
3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837
  else
  {
    /* Normal or temporary table. */
    TABLE *table= table_ref->table;
    grant= &(table->grant);
    db_name= table->s->db;
    table_name= table->s->table_name;
  }

  if (grant->want_privilege)
    return check_grant_column(thd, grant, db_name, table_name, name,
                              length, sctx);
  else
    return FALSE;

unknown's avatar
unknown committed
3838 3839 3840
}


unknown's avatar
VIEW  
unknown committed
3841
bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant,
3842
                             const char* db_name, const char *table_name,
unknown's avatar
VIEW  
unknown committed
3843
                             Field_iterator *fields)
unknown's avatar
unknown committed
3844
{
3845
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
3846 3847 3848
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;

unknown's avatar
VIEW  
unknown committed
3849
  want_access &= ~grant->privilege;
unknown's avatar
unknown committed
3850
  if (!want_access)
unknown's avatar
unknown committed
3851
    return 0;				// Already checked
unknown's avatar
unknown committed
3852 3853
  if (!grant_option)
    goto err2;
unknown's avatar
unknown committed
3854

3855
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
3856

3857
  /* reload table if someone has modified any grants */
unknown's avatar
unknown committed
3858

unknown's avatar
VIEW  
unknown committed
3859
  if (grant->version != grant_version)
unknown's avatar
unknown committed
3860
  {
unknown's avatar
VIEW  
unknown committed
3861
    grant->grant_table=
3862 3863
      table_hash_search(sctx->host, sctx->ip, db_name,
			sctx->priv_user,
unknown's avatar
VIEW  
unknown committed
3864 3865
			table_name, 0);	/* purecov: inspected */
    grant->version= grant_version;		/* purecov: inspected */
unknown's avatar
unknown committed
3866
  }
3867
  /* The following should always be true */
unknown's avatar
VIEW  
unknown committed
3868
  if (!(grant_table= grant->grant_table))
unknown's avatar
unknown committed
3869 3870
    goto err;					/* purecov: inspected */

3871
  for (; !fields->end_of_fields(); fields->next())
unknown's avatar
unknown committed
3872
  {
unknown's avatar
VIEW  
unknown committed
3873 3874 3875
    const char *field_name= fields->name();
    grant_column= column_hash_search(grant_table, field_name,
				    (uint) strlen(field_name));
unknown's avatar
unknown committed
3876 3877 3878
    if (!grant_column || (~grant_column->rights & want_access))
      goto err;
  }
3879
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3880 3881
  return 0;

unknown's avatar
unknown committed
3882
err:
3883
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3884
err2:
3885 3886
  char command[128];
  get_privilege_desc(command, sizeof(command), want_access);
3887 3888
  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
           command,
3889 3890
           sctx->priv_user,
           sctx->host_or_ip,
3891 3892
           fields->name(),
           table_name);
unknown's avatar
unknown committed
3893 3894 3895 3896
  return 1;
}


3897
/*
unknown's avatar
unknown committed
3898
  Check if a user has the right to access a database
3899
  Access is accepted if he has a grant for any table/routine in the database
unknown's avatar
unknown committed
3900
  Return 1 if access is denied
3901
*/
unknown's avatar
unknown committed
3902 3903 3904

bool check_grant_db(THD *thd,const char *db)
{
3905
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
3906
  char helping [NAME_LEN+USERNAME_LENGTH+2];
unknown's avatar
unknown committed
3907
  uint len;
unknown's avatar
unknown committed
3908
  bool error= 1;
unknown's avatar
unknown committed
3909

3910
  len= (uint) (strmov(strmov(helping, sctx->priv_user) + 1, db) - helping) + 1;
3911
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
3912

3913
  for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
unknown's avatar
unknown committed
3914
  {
3915 3916
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  idx);
unknown's avatar
unknown committed
3917 3918
    if (len < grant_table->key_length &&
	!memcmp(grant_table->hash_key,helping,len) &&
3919
        compare_hostname(&grant_table->host, sctx->host, sctx->ip))
unknown's avatar
unknown committed
3920 3921 3922 3923 3924
    {
      error=0;					// Found match
      break;
    }
  }
3925
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3926 3927 3928
  return error;
}

3929 3930

/****************************************************************************
3931
  Check routine level grants
3932 3933

  SYNPOSIS
3934
   bool check_grant_routine()
3935 3936
   thd		Thread handler
   want_access  Bits of privileges user needs to have
3937 3938
   procs	List of routines to check. The user should have 'want_access'
   is_proc	True if the list is all procedures, else functions
3939 3940 3941 3942 3943 3944 3945 3946
   no_errors	If 0 then we write an error. The error is sent directly to
		the client

   RETURN
     0  ok
     1  Error: User did not have the requested privielges
****************************************************************************/

3947
bool check_grant_routine(THD *thd, ulong want_access,
3948
			 TABLE_LIST *procs, bool is_proc, bool no_errors)
3949 3950
{
  TABLE_LIST *table;
3951
  Security_context *sctx= thd->security_ctx;
3952 3953
  char *user= sctx->priv_user;
  char *host= sctx->priv_host;
3954
  DBUG_ENTER("check_grant_routine");
3955

3956
  want_access&= ~sctx->master_access;
3957 3958 3959 3960 3961 3962 3963
  if (!want_access)
    DBUG_RETURN(0);                             // ok

  rw_rdlock(&LOCK_grant);
  for (table= procs; table; table= table->next_global)
  {
    GRANT_NAME *grant_proc;
3964
    if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
3965
					 table->table_name, is_proc, 0)))
3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982
      table->grant.privilege|= grant_proc->privs;

    if (want_access & ~table->grant.privilege)
    {
      want_access &= ~table->grant.privilege;
      goto err;
    }
  }
  rw_unlock(&LOCK_grant);
  DBUG_RETURN(0);
err:
  rw_unlock(&LOCK_grant);
  if (!no_errors)
  {
    char buff[1024];
    const char *command="";
    if (table)
3983
      strxmov(buff, table->db, ".", table->table_name, NullS);
3984 3985 3986
    if (want_access & EXECUTE_ACL)
      command= "execute";
    else if (want_access & ALTER_PROC_ACL)
3987
      command= "alter routine";
3988 3989 3990 3991 3992 3993 3994 3995 3996
    else if (want_access & GRANT_ACL)
      command= "grant";
    my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
             command, user, host, table ? buff : "unknown");
  }
  DBUG_RETURN(1);
}


3997 3998
/*
  Check if routine has any of the 
3999
  routine level grants
4000 4001 4002 4003 4004 4005 4006 4007 4008
  
  SYNPOSIS
   bool    check_routine_level_acl()
   thd	        Thread handler
   db           Database name
   name         Routine name

  RETURN
   0            Ok 
4009
   1            error
4010 4011
*/

unknown's avatar
unknown committed
4012 4013
bool check_routine_level_acl(THD *thd, const char *db, const char *name, 
                             bool is_proc)
4014 4015 4016 4017 4018
{
  bool no_routine_acl= 1;
  if (grant_option)
  {
    GRANT_NAME *grant_proc;
4019
    Security_context *sctx= thd->security_ctx;
4020
    rw_rdlock(&LOCK_grant);
4021 4022 4023
    if ((grant_proc= routine_hash_search(sctx->priv_host,
                                         sctx->ip, db,
                                         sctx->priv_user,
4024
                                         name, is_proc, 0)))
4025 4026 4027 4028 4029 4030 4031
      no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
    rw_unlock(&LOCK_grant);
  }
  return no_routine_acl;
}


unknown's avatar
unknown committed
4032
/*****************************************************************************
unknown's avatar
unknown committed
4033
  Functions to retrieve the grant for a table/column  (for SHOW functions)
unknown's avatar
unknown committed
4034 4035
*****************************************************************************/

unknown's avatar
unknown committed
4036
ulong get_table_grant(THD *thd, TABLE_LIST *table)
unknown's avatar
unknown committed
4037
{
unknown's avatar
unknown committed
4038
  ulong privilege;
4039
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
4040 4041 4042
  const char *db = table->db ? table->db : thd->db;
  GRANT_TABLE *grant_table;

4043
  rw_rdlock(&LOCK_grant);
4044 4045 4046
#ifdef EMBEDDED_LIBRARY
  grant_table= NULL;
#else
4047
  grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
4048
				 table->table_name, 0);
4049
#endif
unknown's avatar
unknown committed
4050 4051 4052 4053
  table->grant.grant_table=grant_table; // Remember for column test
  table->grant.version=grant_version;
  if (grant_table)
    table->grant.privilege|= grant_table->privs;
unknown's avatar
unknown committed
4054
  privilege= table->grant.privilege;
4055
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4056
  return privilege;
unknown's avatar
unknown committed
4057 4058 4059
}


unknown's avatar
unknown committed
4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077
/*
  Determine the access priviliges for a field.

  SYNOPSIS
    get_column_grant()
    thd         thread handler
    grant       grants table descriptor
    db_name     name of database that the field belongs to
    table_name  name of table that the field belongs to
    field_name  name of field

  DESCRIPTION
    The procedure may also modify: grant->grant_table and grant->version.

  RETURN
    The access priviliges for the field db_name.table_name.field_name
*/

unknown's avatar
VIEW  
unknown committed
4078 4079 4080
ulong get_column_grant(THD *thd, GRANT_INFO *grant,
                       const char *db_name, const char *table_name,
                       const char *field_name)
unknown's avatar
unknown committed
4081 4082 4083
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
4084
  ulong priv;
unknown's avatar
unknown committed
4085

4086
  rw_rdlock(&LOCK_grant);
4087
  /* reload table if someone has modified any grants */
unknown's avatar
VIEW  
unknown committed
4088
  if (grant->version != grant_version)
unknown's avatar
unknown committed
4089
  {
4090
    Security_context *sctx= thd->security_ctx;
unknown's avatar
VIEW  
unknown committed
4091
    grant->grant_table=
4092 4093
      table_hash_search(sctx->host, sctx->ip,
                        db_name, sctx->priv_user,
unknown's avatar
VIEW  
unknown committed
4094 4095
			table_name, 0);	        /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
unknown's avatar
unknown committed
4096 4097
  }

unknown's avatar
VIEW  
unknown committed
4098 4099
  if (!(grant_table= grant->grant_table))
    priv= grant->privilege;
unknown's avatar
unknown committed
4100 4101
  else
  {
unknown's avatar
VIEW  
unknown committed
4102 4103
    grant_column= column_hash_search(grant_table, field_name,
                                     (uint) strlen(field_name));
unknown's avatar
unknown committed
4104
    if (!grant_column)
4105
      priv= (grant->privilege | grant_table->privs);
unknown's avatar
unknown committed
4106
    else
4107
      priv= (grant->privilege | grant_table->privs | grant_column->rights);
unknown's avatar
unknown committed
4108
  }
4109
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4110 4111 4112
  return priv;
}

unknown's avatar
VIEW  
unknown committed
4113

4114
/* Help function for mysql_show_grants */
unknown's avatar
unknown committed
4115

4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127
static void add_user_option(String *grant, ulong value, const char *name)
{
  if (value)
  {
    char buff[22], *p; // just as in int2str
    grant->append(' ');
    grant->append(name, strlen(name));
    grant->append(' ');
    p=int10_to_str(value, buff, 10);
    grant->append(buff,p-buff);
  }
}
unknown's avatar
unknown committed
4128 4129

static const char *command_array[]=
unknown's avatar
unknown committed
4130
{
unknown's avatar
VIEW  
unknown committed
4131 4132 4133 4134
  "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
  "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
  "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
  "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
4135
  "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
unknown's avatar
unknown committed
4136
  "CREATE USER"
unknown's avatar
unknown committed
4137
};
4138

unknown's avatar
unknown committed
4139 4140
static uint command_lengths[]=
{
unknown's avatar
unknown committed
4141 4142
  6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
  14, 13, 11
unknown's avatar
unknown committed
4143 4144
};

unknown's avatar
unknown committed
4145

4146 4147 4148 4149 4150
static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
                               const char *type, int typelen,
                               char *buff, int buffsize);


4151 4152 4153 4154 4155 4156 4157
/*
  SHOW GRANTS;  Send grants for a user to the client

  IMPLEMENTATION
   Send to client grant-like strings depicting user@host privileges
*/

unknown's avatar
unknown committed
4158
bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
unknown's avatar
unknown committed
4159
{
unknown's avatar
unknown committed
4160 4161
  ulong want_access;
  uint counter,index;
unknown's avatar
unknown committed
4162
  int  error = 0;
unknown's avatar
unknown committed
4163 4164
  ACL_USER *acl_user;
  ACL_DB *acl_db;
unknown's avatar
unknown committed
4165
  char buff[1024];
unknown's avatar
unknown committed
4166
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
4167
  DBUG_ENTER("mysql_show_grants");
unknown's avatar
unknown committed
4168 4169 4170 4171

  LINT_INIT(acl_user);
  if (!initialized)
  {
unknown's avatar
unknown committed
4172
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
unknown's avatar
unknown committed
4173
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4174
  }
unknown's avatar
unknown committed
4175

4176 4177 4178 4179 4180
  rw_rdlock(&LOCK_grant);
  VOID(pthread_mutex_lock(&acl_cache->lock));

  acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE);
  if (!acl_user)
unknown's avatar
unknown committed
4181
  {
4182 4183 4184
    VOID(pthread_mutex_unlock(&acl_cache->lock));
    rw_unlock(&LOCK_grant);

unknown's avatar
unknown committed
4185 4186
    my_error(ER_NONEXISTING_GRANT, MYF(0),
             lex_user->user.str, lex_user->host.str);
unknown's avatar
unknown committed
4187
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4188 4189
  }

unknown's avatar
unknown committed
4190
  Item_string *field=new Item_string("",0,&my_charset_latin1);
unknown's avatar
unknown committed
4191 4192 4193 4194 4195 4196
  List<Item> field_list;
  field->name=buff;
  field->max_length=1024;
  strxmov(buff,"Grants for ",lex_user->user.str,"@",
	  lex_user->host.str,NullS);
  field_list.push_back(field);
4197 4198
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
4199 4200 4201
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock));
    rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4202

4203 4204
    DBUG_RETURN(TRUE);
  }
unknown's avatar
unknown committed
4205 4206 4207

  /* Add first global access grants */
  {
4208
    String global(buff,sizeof(buff),system_charset_info);
unknown's avatar
unknown committed
4209
    global.length(0);
4210
    global.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
4211

unknown's avatar
unknown committed
4212
    want_access= acl_user->access;
unknown's avatar
unknown committed
4213
    if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
4214
      global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
4215
    else if (!(want_access & ~GRANT_ACL))
4216
      global.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
4217
    else
unknown's avatar
unknown committed
4218 4219
    {
      bool found=0;
unknown's avatar
unknown committed
4220
      ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
4221 4222
      for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
      {
unknown's avatar
unknown committed
4223
	if (test_access & j)
unknown's avatar
unknown committed
4224 4225
	{
	  if (found)
4226
	    global.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
4227 4228 4229 4230 4231
	  found=1;
	  global.append(command_array[counter],command_lengths[counter]);
	}
      }
    }
4232
    global.append (STRING_WITH_LEN(" ON *.* TO '"));
4233 4234
    global.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
4235
    global.append (STRING_WITH_LEN("'@'"));
4236 4237
    global.append(lex_user->host.str,lex_user->host.length,
		  system_charset_info);
unknown's avatar
unknown committed
4238
    global.append ('\'');
4239
    if (acl_user->salt_len)
unknown's avatar
unknown committed
4240
    {
4241 4242 4243 4244 4245
      char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
      if (acl_user->salt_len == SCRAMBLE_LENGTH)
        make_password_from_salt(passwd_buff, acl_user->salt);
      else
        make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt);
4246
      global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
4247
      global.append(passwd_buff);
unknown's avatar
unknown committed
4248 4249
      global.append('\'');
    }
unknown's avatar
unknown committed
4250 4251
    /* "show grants" SSL related stuff */
    if (acl_user->ssl_type == SSL_TYPE_ANY)
4252
      global.append(STRING_WITH_LEN(" REQUIRE SSL"));
4253
    else if (acl_user->ssl_type == SSL_TYPE_X509)
4254
      global.append(STRING_WITH_LEN(" REQUIRE X509"));
4255
    else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
4256
    {
4257
      int ssl_options = 0;
4258
      global.append(STRING_WITH_LEN(" REQUIRE "));
unknown's avatar
unknown committed
4259 4260
      if (acl_user->x509_issuer)
      {
4261
	ssl_options++;
4262
	global.append(STRING_WITH_LEN("ISSUER \'"));
4263
	global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
4264
	global.append('\'');
4265
      }
unknown's avatar
unknown committed
4266 4267
      if (acl_user->x509_subject)
      {
4268 4269
	if (ssl_options++)
	  global.append(' ');
4270
	global.append(STRING_WITH_LEN("SUBJECT \'"));
4271 4272
	global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
                      system_charset_info);
4273
	global.append('\'');
unknown's avatar
unknown committed
4274
      }
unknown's avatar
unknown committed
4275 4276
      if (acl_user->ssl_cipher)
      {
4277 4278
	if (ssl_options++)
	  global.append(' ');
4279
	global.append(STRING_WITH_LEN("CIPHER '"));
4280 4281
	global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
                      system_charset_info);
4282
	global.append('\'');
4283 4284
      }
    }
unknown's avatar
unknown committed
4285
    if ((want_access & GRANT_ACL) ||
4286 4287 4288 4289
	(acl_user->user_resource.questions ||
         acl_user->user_resource.updates ||
         acl_user->user_resource.conn_per_hour ||
         acl_user->user_resource.user_conn))
4290
    {
4291
      global.append(STRING_WITH_LEN(" WITH"));
unknown's avatar
unknown committed
4292
      if (want_access & GRANT_ACL)
4293
	global.append(STRING_WITH_LEN(" GRANT OPTION"));
4294 4295 4296 4297
      add_user_option(&global, acl_user->user_resource.questions,
		      "MAX_QUERIES_PER_HOUR");
      add_user_option(&global, acl_user->user_resource.updates,
		      "MAX_UPDATES_PER_HOUR");
4298
      add_user_option(&global, acl_user->user_resource.conn_per_hour,
4299
		      "MAX_CONNECTIONS_PER_HOUR");
4300 4301
      add_user_option(&global, acl_user->user_resource.user_conn,
		      "MAX_USER_CONNECTIONS");
unknown's avatar
unknown committed
4302
    }
unknown's avatar
unknown committed
4303
    protocol->prepare_for_resend();
4304
    protocol->store(global.ptr(),global.length(),global.charset());
unknown's avatar
unknown committed
4305
    if (protocol->write())
unknown's avatar
unknown committed
4306
    {
unknown's avatar
unknown committed
4307
      error= -1;
unknown's avatar
unknown committed
4308
      goto end;
unknown's avatar
unknown committed
4309 4310 4311 4312 4313 4314
    }
  }

  /* Add database access */
  for (counter=0 ; counter < acl_dbs.elements ; counter++)
  {
unknown's avatar
unknown committed
4315
    const char *user, *host;
unknown's avatar
unknown committed
4316 4317 4318

    acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
    if (!(user=acl_db->user))
unknown's avatar
unknown committed
4319
      user= "";
unknown's avatar
unknown committed
4320
    if (!(host=acl_db->host.hostname))
unknown's avatar
unknown committed
4321
      host= "";
unknown's avatar
unknown committed
4322 4323

    if (!strcmp(lex_user->user.str,user) &&
4324
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
unknown's avatar
unknown committed
4325 4326
    {
      want_access=acl_db->access;
unknown's avatar
unknown committed
4327
      if (want_access)
unknown's avatar
unknown committed
4328
      {
4329
	String db(buff,sizeof(buff),system_charset_info);
unknown's avatar
unknown committed
4330
	db.length(0);
4331
	db.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
4332 4333

	if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
4334
	  db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
4335
	else if (!(want_access & ~GRANT_ACL))
4336
	  db.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
4337 4338 4339
	else
	{
	  int found=0, cnt;
unknown's avatar
unknown committed
4340
	  ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
4341 4342 4343 4344 4345
	  for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
	  {
	    if (test_access & j)
	    {
	      if (found)
4346
		db.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
4347 4348 4349 4350 4351
	      found = 1;
	      db.append(command_array[cnt],command_lengths[cnt]);
	    }
	  }
	}
4352
	db.append (STRING_WITH_LEN(" ON "));
4353
	append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
4354
	db.append (STRING_WITH_LEN(".* TO '"));
4355 4356
	db.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
4357
	db.append (STRING_WITH_LEN("'@'"));
4358 4359
	db.append(lex_user->host.str, lex_user->host.length,
                  system_charset_info);
unknown's avatar
unknown committed
4360
	db.append ('\'');
unknown's avatar
unknown committed
4361
	if (want_access & GRANT_ACL)
4362
	  db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
unknown's avatar
unknown committed
4363
	protocol->prepare_for_resend();
4364
	protocol->store(db.ptr(),db.length(),db.charset());
unknown's avatar
unknown committed
4365
	if (protocol->write())
unknown's avatar
unknown committed
4366
	{
unknown's avatar
unknown committed
4367
	  error= -1;
unknown's avatar
unknown committed
4368 4369 4370 4371 4372 4373
	  goto end;
	}
      }
    }
  }

4374
  /* Add table & column access */
4375
  for (index=0 ; index < column_priv_hash.records ; index++)
unknown's avatar
unknown committed
4376
  {
4377
    const char *user, *host;
4378 4379
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
unknown's avatar
unknown committed
4380 4381

    if (!(user=grant_table->user))
4382
      user= "";
4383 4384
    if (!(host= grant_table->host.hostname))
      host= "";
unknown's avatar
unknown committed
4385 4386

    if (!strcmp(lex_user->user.str,user) &&
4387
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
unknown's avatar
unknown committed
4388
    {
4389 4390
      ulong table_access= grant_table->privs;
      if ((table_access | grant_table->cols) != 0)
unknown's avatar
unknown committed
4391
      {
4392
	String global(buff, sizeof(buff), system_charset_info);
unknown's avatar
unknown committed
4393 4394
	ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;

unknown's avatar
unknown committed
4395
	global.length(0);
4396
	global.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
4397

4398
	if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
4399
	  global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
4400
	else if (!test_access)
4401
	  global.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
4402
	else
unknown's avatar
unknown committed
4403
	{
4404
          /* Add specific column access */
4405
	  int found= 0;
unknown's avatar
unknown committed
4406
	  ulong j;
unknown's avatar
unknown committed
4407

4408
	  for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
unknown's avatar
unknown committed
4409
	  {
unknown's avatar
unknown committed
4410
	    if (test_access & j)
unknown's avatar
unknown committed
4411 4412
	    {
	      if (found)
4413
		global.append(STRING_WITH_LEN(", "));
4414
	      found= 1;
unknown's avatar
unknown committed
4415 4416
	      global.append(command_array[counter],command_lengths[counter]);

unknown's avatar
unknown committed
4417
	      if (grant_table->cols)
unknown's avatar
unknown committed
4418
	      {
4419
		uint found_col= 0;
unknown's avatar
unknown committed
4420 4421 4422 4423 4424 4425
		for (uint col_index=0 ;
		     col_index < grant_table->hash_columns.records ;
		     col_index++)
		{
		  GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
		    hash_element(&grant_table->hash_columns,col_index);
unknown's avatar
unknown committed
4426
		  if (grant_column->rights & j)
unknown's avatar
unknown committed
4427
		  {
unknown's avatar
unknown committed
4428
		    if (!found_col)
unknown's avatar
unknown committed
4429
		    {
4430 4431 4432 4433 4434 4435 4436
		      found_col= 1;
		      /*
			If we have a duplicated table level privilege, we
			must write the access privilege name again.
		      */
		      if (table_access & j)
		      {
4437
			global.append(STRING_WITH_LEN(", "));
4438 4439 4440
			global.append(command_array[counter],
				      command_lengths[counter]);
		      }
4441
		      global.append(STRING_WITH_LEN(" ("));
unknown's avatar
unknown committed
4442 4443
		    }
		    else
4444
		      global.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
4445
		    global.append(grant_column->column,
4446 4447
				  grant_column->key_length,
				  system_charset_info);
unknown's avatar
unknown committed
4448 4449 4450 4451 4452 4453 4454 4455
		  }
		}
		if (found_col)
		  global.append(')');
	      }
	    }
	  }
	}
4456
	global.append(STRING_WITH_LEN(" ON "));
4457 4458 4459 4460 4461
	append_identifier(thd, &global, grant_table->db,
			  strlen(grant_table->db));
	global.append('.');
	append_identifier(thd, &global, grant_table->tname,
			  strlen(grant_table->tname));
4462
	global.append(STRING_WITH_LEN(" TO '"));
4463 4464
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
4465
	global.append(STRING_WITH_LEN("'@'"));
4466 4467
	global.append(lex_user->host.str,lex_user->host.length,
		      system_charset_info);
unknown's avatar
unknown committed
4468
	global.append('\'');
4469
	if (table_access & GRANT_ACL)
4470
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
unknown's avatar
unknown committed
4471
	protocol->prepare_for_resend();
4472
	protocol->store(global.ptr(),global.length(),global.charset());
unknown's avatar
unknown committed
4473
	if (protocol->write())
unknown's avatar
unknown committed
4474
	{
unknown's avatar
unknown committed
4475
	  error= -1;
unknown's avatar
unknown committed
4476
	  break;
unknown's avatar
unknown committed
4477 4478 4479 4480
	}
      }
    }
  }
4481

4482
  if (show_routine_grants(thd, lex_user, &proc_priv_hash, 
4483
                          STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
4484 4485 4486 4487 4488 4489
  {
    error= -1;
    goto end;
  }

  if (show_routine_grants(thd, lex_user, &func_priv_hash,
4490
                          STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512
  {
    error= -1;
    goto end;
  }

end:
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);

  send_eof(thd);
  DBUG_RETURN(error);
}

static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
                               const char *type, int typelen,
                               char *buff, int buffsize)
{
  uint counter, index;
  int error= 0;
  Protocol *protocol= thd->protocol;
  /* Add routine access */
  for (index=0 ; index < hash->records ; index++)
4513
  {
4514
    const char *user, *host;
4515
    GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, index);
4516 4517 4518

    if (!(user=grant_proc->user))
      user= "";
4519 4520
    if (!(host= grant_proc->host.hostname))
      host= "";
4521 4522

    if (!strcmp(lex_user->user.str,user) &&
4523
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
4524 4525 4526 4527
    {
      ulong proc_access= grant_proc->privs;
      if (proc_access != 0)
      {
4528
	String global(buff, buffsize, system_charset_info);
4529 4530 4531
	ulong test_access= proc_access & ~GRANT_ACL;

	global.length(0);
4532
	global.append(STRING_WITH_LEN("GRANT "));
4533 4534

	if (!test_access)
4535
 	  global.append(STRING_WITH_LEN("USAGE"));
4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546
	else
	{
          /* Add specific procedure access */
	  int found= 0;
	  ulong j;

	  for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
	  {
	    if (test_access & j)
	    {
	      if (found)
4547
		global.append(STRING_WITH_LEN(", "));
4548 4549 4550 4551 4552
	      found= 1;
	      global.append(command_array[counter],command_lengths[counter]);
	    }
	  }
	}
4553
	global.append(STRING_WITH_LEN(" ON "));
4554 4555
        global.append(type,typelen);
        global.append(' ');
4556 4557 4558 4559 4560
	append_identifier(thd, &global, grant_proc->db,
			  strlen(grant_proc->db));
	global.append('.');
	append_identifier(thd, &global, grant_proc->tname,
			  strlen(grant_proc->tname));
4561
	global.append(STRING_WITH_LEN(" TO '"));
4562 4563
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
4564
	global.append(STRING_WITH_LEN("'@'"));
4565 4566 4567 4568
	global.append(lex_user->host.str,lex_user->host.length,
		      system_charset_info);
	global.append('\'');
	if (proc_access & GRANT_ACL)
4569
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
4570 4571 4572 4573 4574 4575 4576 4577 4578 4579
	protocol->prepare_for_resend();
	protocol->store(global.ptr(),global.length(),global.charset());
	if (protocol->write())
	{
	  error= -1;
	  break;
	}
      }
    }
  }
4580
  return error;
unknown's avatar
unknown committed
4581 4582
}

unknown's avatar
unknown committed
4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610
/*
  Make a clear-text version of the requested privilege.
*/

void get_privilege_desc(char *to, uint max_length, ulong access)
{
  uint pos;
  char *start=to;
  DBUG_ASSERT(max_length >= 30);		// For end ',' removal

  if (access)
  {
    max_length--;				// Reserve place for end-zero
    for (pos=0 ; access ; pos++, access>>=1)
    {
      if ((access & 1) &&
	  command_lengths[pos] + (uint) (to-start) < max_length)
      {
	to= strmov(to, command_array[pos]);
	*to++=',';
      }
    }
    to--;					// Remove end ','
  }
  *to=0;
}


4611
void get_mqh(const char *user, const char *host, USER_CONN *uc)
unknown's avatar
unknown committed
4612 4613
{
  ACL_USER *acl_user;
4614 4615 4616

  pthread_mutex_lock(&acl_cache->lock);

4617
  if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
4618 4619 4620
    uc->user_resources= acl_user->user_resource;
  else
    bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
4621 4622

  pthread_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
4623 4624
}

4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645
/*
  Open the grant tables.

  SYNOPSIS
    open_grant_tables()
    thd                         The current thread.
    tables (out)                The 4 elements array for the opened tables.

  DESCRIPTION
    Tables are numbered as follows:
    0 user
    1 db
    2 tables_priv
    3 columns_priv

  RETURN
    1           Skip GRANT handling during replication.
    0           OK.
    < 0         Error.
*/

4646
#define GRANT_TABLES 5
4647 4648 4649 4650 4651 4652
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("open_grant_tables");

  if (!initialized)
  {
unknown's avatar
unknown committed
4653
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
4654 4655 4656
    DBUG_RETURN(-1);
  }

4657
  bzero((char*) tables, GRANT_TABLES*sizeof(*tables));
4658 4659 4660 4661 4662
  tables->alias= tables->table_name= (char*) "user";
  (tables+1)->alias= (tables+1)->table_name= (char*) "db";
  (tables+2)->alias= (tables+2)->table_name= (char*) "tables_priv";
  (tables+3)->alias= (tables+3)->table_name= (char*) "columns_priv";
  (tables+4)->alias= (tables+4)->table_name= (char*) "procs_priv";
unknown's avatar
VIEW  
unknown committed
4663 4664 4665
  tables->next_local= tables->next_global= tables+1;
  (tables+1)->next_local= (tables+1)->next_global= tables+2;
  (tables+2)->next_local= (tables+2)->next_global= tables+3;
4666
  (tables+3)->next_local= (tables+3)->next_global= tables+4;
4667
  tables->lock_type= (tables+1)->lock_type=
4668 4669 4670 4671
    (tables+2)->lock_type= (tables+3)->lock_type= 
    (tables+4)->lock_type= TL_WRITE;
  tables->db= (tables+1)->db= (tables+2)->db= 
    (tables+3)->db= (tables+4)->db= (char*) "mysql";
4672 4673 4674 4675 4676 4677

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
4678 4679
  if (thd->slave_thread && table_rules_on)
  {
unknown's avatar
unknown committed
4680 4681 4682
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
4683
    */
4684 4685
    tables[0].updating=tables[1].updating=tables[2].updating=
      tables[3].updating=tables[4].updating=1;
4686
    if (!tables_ok(thd, tables))
4687
      DBUG_RETURN(1);
4688 4689
    tables[0].updating=tables[1].updating=tables[2].updating=
      tables[3].updating=tables[4].updating=0;;
4690
  }
4691 4692
#endif

4693
  if (simple_open_n_lock_tables(thd, tables))
4694 4695 4696 4697 4698 4699 4700 4701 4702
  {						// This should never happen
    close_thread_tables(thd);
    DBUG_RETURN(-1);
  }

  DBUG_RETURN(0);
}

ACL_USER *check_acl_user(LEX_USER *user_name,
unknown's avatar
merge  
unknown committed
4703
			 uint *acl_acl_userdx)
4704 4705 4706 4707
{
  ACL_USER *acl_user= 0;
  uint counter;

4708 4709
  safe_mutex_assert_owner(&acl_cache->lock);

4710 4711 4712 4713 4714
  for (counter= 0 ; counter < acl_users.elements ; counter++)
  {
    const char *user,*host;
    acl_user= dynamic_element(&acl_users, counter, ACL_USER*);
    if (!(user=acl_user->user))
unknown's avatar
unknown committed
4715
      user= "";
4716
    if (!(host=acl_user->host.hostname))
4717
      host= "";
4718 4719 4720 4721 4722 4723 4724
    if (!strcmp(user_name->user.str,user) &&
	!my_strcasecmp(system_charset_info, user_name->host.str, host))
      break;
  }
  if (counter == acl_users.elements)
    return 0;

unknown's avatar
merge  
unknown committed
4725
  *acl_acl_userdx= counter;
unknown's avatar
unknown committed
4726
  return acl_user;
4727
}
unknown's avatar
unknown committed
4728

4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750
/*
  Modify a privilege table.

  SYNOPSIS
    modify_grant_table()
    table                       The table to modify.
    host_field                  The host name field.
    user_field                  The user name field.
    user_to                     The new name for the user if to be renamed,
                                NULL otherwise.

  DESCRIPTION
  Update user/host in the current record if user_to is not NULL.
  Delete the current record if user_to is NULL.

  RETURN
    0           OK.
    != 0        Error.
*/

static int modify_grant_table(TABLE *table, Field *host_field,
                              Field *user_field, LEX_USER *user_to)
4751
{
4752 4753
  int error;
  DBUG_ENTER("modify_grant_table");
4754

4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771
  if (user_to)
  {
    /* rename */
    store_record(table, record[1]);
    host_field->store(user_to->host.str, user_to->host.length,
                      system_charset_info);
    user_field->store(user_to->user.str, user_to->user.length,
                      system_charset_info);
    if ((error= table->file->update_row(table->record[1], table->record[0])))
      table->file->print_error(error, MYF(0));
  }
  else
  {
    /* delete */
    if ((error=table->file->delete_row(table->record[0])))
      table->file->print_error(error, MYF(0));
  }
4772

4773 4774
  DBUG_RETURN(error);
}
4775

4776 4777 4778 4779 4780 4781
/*
  Handle a privilege table.

  SYNOPSIS
    handle_grant_table()
    tables                      The array with the four open tables.
4782
    table_no                    The number of the table to handle (0..4).
4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799
    drop                        If user_from is to be dropped.
    user_from                   The the user to be searched/dropped/renamed.
    user_to                     The new name for the user if to be renamed,
                                NULL otherwise.

  DESCRIPTION
    Scan through all records in a grant table and apply the requested
    operation. For the "user" table, a single index access is sufficient,
    since there is an unique index on (host, user).
    Delete from grant table if drop is true.
    Update in grant table if drop is false and user_to is not NULL.
    Search in grant table if drop is false and user_to is NULL.
    Tables are numbered as follows:
    0 user
    1 db
    2 tables_priv
    3 columns_priv
4800
    4 procs_priv
4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819

  RETURN
    > 0         At least one record matched.
    0           OK, but no record matched.
    < 0         Error.
*/

static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
                              LEX_USER *user_from, LEX_USER *user_to)
{
  int result= 0;
  int error;
  TABLE *table= tables[table_no].table;
  Field *host_field= table->field[0];
  Field *user_field= table->field[table_no ? 2 : 1];
  char *host_str= user_from->host.str;
  char *user_str= user_from->user.str;
  const char *host;
  const char *user;
4820
  byte user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
4821
  uint key_prefix_length;
4822 4823
  DBUG_ENTER("handle_grant_table");

unknown's avatar
unknown committed
4824
  if (! table_no) // mysql.user table
4825
  {
4826 4827 4828 4829 4830 4831 4832 4833 4834 4835
    /*
      The 'user' table has an unique index on (host, user).
      Thus, we can handle everything with a single index access.
      The host- and user fields are consecutive in the user table records.
      So we set host- and user fields of table->record[0] and use the
      pointer to the host field as key.
      index_read_idx() will replace table->record[0] (its first argument)
      by the searched record, if it exists.
    */
    DBUG_PRINT("info",("read table: '%s'  search: '%s'@'%s'",
4836
                       table->s->table_name, user_str, host_str));
4837 4838
    host_field->store(host_str, user_from->host.length, system_charset_info);
    user_field->store(user_str, user_from->user.length, system_charset_info);
unknown's avatar
unknown committed
4839 4840 4841 4842 4843

    key_prefix_length= (table->key_info->key_part[0].store_length +
                        table->key_info->key_part[1].store_length);
    key_copy(user_key, table->record[0], table->key_info, key_prefix_length);

4844
    if ((error= table->file->index_read_idx(table->record[0], 0,
unknown's avatar
unknown committed
4845
                                            user_key, key_prefix_length,
4846
                                            HA_READ_KEY_EXACT)))
4847
    {
4848 4849 4850 4851 4852
      if (error != HA_ERR_KEY_NOT_FOUND)
      {
        table->file->print_error(error, MYF(0));
        result= -1;
      }
4853
    }
4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870
    else
    {
      /* If requested, delete or update the record. */
      result= ((drop || user_to) &&
               modify_grant_table(table, host_field, user_field, user_to)) ?
        -1 : 1; /* Error or found. */
    }
    DBUG_PRINT("info",("read result: %d", result));
  }
  else
  {
    /*
      The non-'user' table do not have indexes on (host, user).
      And their host- and user fields are not consecutive.
      Thus, we need to do a table scan to find all matching records.
    */
    if ((error= table->file->ha_rnd_init(1)))
4871
    {
4872
      table->file->print_error(error, MYF(0));
4873
      result= -1;
4874 4875 4876 4877 4878
    }
    else
    {
#ifdef EXTRA_DEBUG
      DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
4879
                         table->s->table_name, user_str, host_str));
4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927
#endif
      while ((error= table->file->rnd_next(table->record[0])) != 
             HA_ERR_END_OF_FILE)
      {
        if (error)
        {
          /* Most probable 'deleted record'. */
          DBUG_PRINT("info",("scan error: %d", error));
          continue;
        }
        if (! (host= get_field(&mem, host_field)))
          host= "";
        if (! (user= get_field(&mem, user_field)))
          user= "";

#ifdef EXTRA_DEBUG
        DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
                           user, host,
                           get_field(&mem, table->field[1]) /*db*/,
                           get_field(&mem, table->field[3]) /*table*/,
                           get_field(&mem, table->field[4]) /*column*/));
#endif
        if (strcmp(user_str, user) ||
            my_strcasecmp(system_charset_info, host_str, host))
          continue;

        /* If requested, delete or update the record. */
        result= ((drop || user_to) &&
                 modify_grant_table(table, host_field, user_field, user_to)) ?
          -1 : result ? result : 1; /* Error or keep result or found. */
        /* If search is requested, we do not need to search further. */
        if (! drop && ! user_to)
          break ;
      }
      (void) table->file->ha_rnd_end();
      DBUG_PRINT("info",("scan result: %d", result));
    }
  }

  DBUG_RETURN(result);
}


/*
  Handle an in-memory privilege structure.

  SYNOPSIS
    handle_grant_struct()
4928
    struct_no                   The number of the structure to handle (0..3).
4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943
    drop                        If user_from is to be dropped.
    user_from                   The the user to be searched/dropped/renamed.
    user_to                     The new name for the user if to be renamed,
                                NULL otherwise.

  DESCRIPTION
    Scan through all elements in an in-memory grant structure and apply
    the requested operation.
    Delete from grant structure if drop is true.
    Update in grant structure if drop is false and user_to is not NULL.
    Search in grant structure if drop is false and user_to is NULL.
    Structures are numbered as follows:
    0 acl_users
    1 acl_dbs
    2 column_priv_hash
4944
    3 procs_priv_hash
4945 4946 4947 4948

  RETURN
    > 0         At least one element matched.
    0           OK, but no element matched.
4949
    -1		Wrong arguments to function
4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961
*/

static int handle_grant_struct(uint struct_no, bool drop,
                               LEX_USER *user_from, LEX_USER *user_to)
{
  int result= 0;
  uint idx;
  uint elements;
  const char *user;
  const char *host;
  ACL_USER *acl_user;
  ACL_DB *acl_db;
4962
  GRANT_NAME *grant_name;
4963
  DBUG_ENTER("handle_grant_struct");
unknown's avatar
unknown committed
4964 4965 4966
  DBUG_PRINT("info",("scan struct: %u  search: '%s'@'%s'",
                     struct_no, user_from->user.str, user_from->host.str));

4967 4968
  LINT_INIT(acl_user);
  LINT_INIT(acl_db);
4969
  LINT_INIT(grant_name);
4970

4971 4972
  safe_mutex_assert_owner(&acl_cache->lock);

4973
  /* Get the number of elements in the in-memory structure. */
4974
  switch (struct_no) {
4975 4976 4977 4978 4979 4980
  case 0:
    elements= acl_users.elements;
    break;
  case 1:
    elements= acl_dbs.elements;
    break;
4981
  case 2:
4982
    elements= column_priv_hash.records;
4983 4984 4985 4986 4987 4988
    break;
  case 3:
    elements= proc_priv_hash.records;
    break;
  default:
    return -1;
4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000
  }

#ifdef EXTRA_DEBUG
    DBUG_PRINT("loop",("scan struct: %u  search    user: '%s'  host: '%s'",
                       struct_no, user_from->user.str, user_from->host.str));
#endif
  /* Loop over all elements. */
  for (idx= 0; idx < elements; idx++)
  {
    /*
      Get a pointer to the element.
    */
5001
    switch (struct_no) {
5002 5003 5004
    case 0:
      acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
      user= acl_user->user;
5005 5006
      host= acl_user->host.hostname;
    break;
5007 5008 5009 5010

    case 1:
      acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
      user= acl_db->user;
5011
      host= acl_db->host.hostname;
5012 5013
      break;

5014 5015 5016
    case 2:
      grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx);
      user= grant_name->user;
5017
      host= grant_name->host.hostname;
5018 5019 5020 5021 5022
      break;

    case 3:
      grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx);
      user= grant_name->user;
5023
      host= grant_name->host.hostname;
5024
      break;
5025 5026
    }
    if (! user)
5027
      user= "";
unknown's avatar
unknown committed
5028 5029 5030
    if (! host)
      host= "";

5031 5032 5033 5034 5035 5036
#ifdef EXTRA_DEBUG
    DBUG_PRINT("loop",("scan struct: %u  index: %u  user: '%s'  host: '%s'",
                       struct_no, idx, user, host));
#endif
    if (strcmp(user_from->user.str, user) ||
        my_strcasecmp(system_charset_info, user_from->host.str, host))
5037
      continue;
5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051

    result= 1; /* At least one element found. */
    if ( drop )
    {
      switch ( struct_no )
      {
      case 0:
        delete_dynamic_element(&acl_users, idx);
        break;

      case 1:
        delete_dynamic_element(&acl_dbs, idx);
        break;

5052 5053 5054 5055 5056 5057 5058
      case 2:
        hash_delete(&column_priv_hash, (byte*) grant_name);
	break;

      case 3:
        hash_delete(&proc_priv_hash, (byte*) grant_name);
	break;
5059 5060 5061
      }
      elements--;
      idx--;
5062
    }
5063 5064
    else if ( user_to )
    {
5065
      switch ( struct_no ) {
5066 5067 5068 5069
      case 0:
        acl_user->user= strdup_root(&mem, user_to->user.str);
        acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
        break;
5070

5071 5072 5073 5074 5075
      case 1:
        acl_db->user= strdup_root(&mem, user_to->user.str);
        acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
        break;

5076 5077 5078
      case 2:
      case 3:
        grant_name->user= strdup_root(&mem, user_to->user.str);
unknown's avatar
unknown committed
5079 5080
        update_hostname(&grant_name->host,
                        strdup_root(&mem, user_to->host.str));
5081
	break;
5082 5083 5084
      }
    }
    else
5085
    {
5086 5087 5088 5089 5090 5091 5092
      /* If search is requested, we do not need to search further. */
      break;
    }
  }
#ifdef EXTRA_DEBUG
  DBUG_PRINT("loop",("scan struct: %u  result %d", struct_no, result));
#endif
5093

5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137
  DBUG_RETURN(result);
}


/*
  Handle all privilege tables and in-memory privilege structures.

  SYNOPSIS
    handle_grant_data()
    tables                      The array with the four open tables.
    drop                        If user_from is to be dropped.
    user_from                   The the user to be searched/dropped/renamed.
    user_to                     The new name for the user if to be renamed,
                                NULL otherwise.

  DESCRIPTION
    Go through all grant tables and in-memory grant structures and apply
    the requested operation.
    Delete from grant data if drop is true.
    Update in grant data if drop is false and user_to is not NULL.
    Search in grant data if drop is false and user_to is NULL.

  RETURN
    > 0         At least one element matched.
    0           OK, but no element matched.
    < 0         Error.
*/

static int handle_grant_data(TABLE_LIST *tables, bool drop,
                             LEX_USER *user_from, LEX_USER *user_to)
{
  int result= 0;
  int found;
  DBUG_ENTER("handle_grant_data");

  /* Handle user table. */
  if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
  {
    /* Handle of table failed, don't touch the in-memory array. */
    result= -1;
  }
  else
  {
    /* Handle user array. */
unknown's avatar
unknown committed
5138 5139
    if ((handle_grant_struct(0, drop, user_from, user_to) && ! result) ||
        found)
5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166
    {
      result= 1; /* At least one record/element found. */
      /* If search is requested, we do not need to search further. */
      if (! drop && ! user_to)
        goto end;
    }
  }

  /* Handle db table. */
  if ((found= handle_grant_table(tables, 1, drop, user_from, user_to)) < 0)
  {
    /* Handle of table failed, don't touch the in-memory array. */
    result= -1;
  }
  else
  {
    /* Handle db array. */
    if (((handle_grant_struct(1, drop, user_from, user_to) && ! result) ||
         found) && ! result)
    {
      result= 1; /* At least one record/element found. */
      /* If search is requested, we do not need to search further. */
      if (! drop && ! user_to)
        goto end;
    }
  }

5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185
  /* Handle procedures table. */
  if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0)
  {
    /* Handle of table failed, don't touch in-memory array. */
    result= -1;
  }
  else
  {
    /* Handle procs array. */
    if (((handle_grant_struct(3, drop, user_from, user_to) && ! result) ||
         found) && ! result)
    {
      result= 1; /* At least one record/element found. */
      /* If search is requested, we do not need to search further. */
      if (! drop && ! user_to)
        goto end;
    }
  }

5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199
  /* Handle tables table. */
  if ((found= handle_grant_table(tables, 2, drop, user_from, user_to)) < 0)
  {
    /* Handle of table failed, don't touch columns and in-memory array. */
    result= -1;
  }
  else
  {
    if (found && ! result)
    {
      result= 1; /* At least one record found. */
      /* If search is requested, we do not need to search further. */
      if (! drop && ! user_to)
        goto end;
5200
    }
5201 5202 5203

    /* Handle columns table. */
    if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
5204
    {
5205
      /* Handle of table failed, don't touch the in-memory array. */
5206 5207
      result= -1;
    }
5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219
    else
    {
      /* Handle columns hash. */
      if (((handle_grant_struct(2, drop, user_from, user_to) && ! result) ||
           found) && ! result)
        result= 1; /* At least one record/element found. */
    }
  }
 end:
  DBUG_RETURN(result);
}

unknown's avatar
unknown committed
5220

unknown's avatar
unknown committed
5221 5222 5223 5224 5225 5226
static void append_user(String *str, LEX_USER *user)
{
  if (str->length())
    str->append(',');
  str->append('\'');
  str->append(user->user.str);
5227
  str->append(STRING_WITH_LEN("'@'"));
unknown's avatar
unknown committed
5228 5229 5230
  str->append(user->host.str);
  str->append('\'');
}
5231

unknown's avatar
unknown committed
5232

5233 5234 5235 5236 5237 5238 5239
/*
  Create a list of users.

  SYNOPSIS
    mysql_create_user()
    thd                         The current thread.
    list                        The users to create.
5240

5241 5242 5243 5244 5245 5246 5247 5248
  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_create_user(THD *thd, List <LEX_USER> &list)
{
  int result;
unknown's avatar
unknown committed
5249
  String wrong_users;
5250
  ulong sql_mode;
5251
  LEX_USER *user_name, *tmp_user_name;
5252
  List_iterator <LEX_USER> user_list(list);
5253
  TABLE_LIST tables[GRANT_TABLES];
5254 5255 5256 5257 5258 5259 5260 5261 5262
  DBUG_ENTER("mysql_create_user");

  /* CREATE USER may be skipped on replication client. */
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

  rw_wrlock(&LOCK_grant);
  VOID(pthread_mutex_lock(&acl_cache->lock));

5263
  while ((tmp_user_name= user_list++))
5264
  {
5265 5266 5267 5268
    if (!(user_name= get_current_user(thd, tmp_user_name)))
    {
      result= TRUE;
      continue;
5269 5270
    }

5271 5272 5273 5274
    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
5275
    if (handle_grant_data(tables, 0, user_name, NULL))
5276
    {
unknown's avatar
unknown committed
5277
      append_user(&wrong_users, user_name);
5278
      result= TRUE;
unknown's avatar
unknown committed
5279
      continue;
5280
    }
5281

5282
    sql_mode= thd->variables.sql_mode;
unknown's avatar
unknown committed
5283
    if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
5284
    {
unknown's avatar
unknown committed
5285
      append_user(&wrong_users, user_name);
5286 5287 5288 5289 5290
      result= TRUE;
    }
  }

  VOID(pthread_mutex_unlock(&acl_cache->lock));
5291 5292 5293 5294 5295 5296 5297

  if (mysql_bin_log.is_open())
  {
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
    mysql_bin_log.write(&qinfo);
  }

5298 5299 5300
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
5301
    my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321
  DBUG_RETURN(result);
}


/*
  Drop a list of users and all their privileges.

  SYNOPSIS
    mysql_drop_user()
    thd                         The current thread.
    list                        The users to drop.

  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
{
  int result;
unknown's avatar
unknown committed
5322
  String wrong_users;
5323
  LEX_USER *user_name, *tmp_user_name;
5324
  List_iterator <LEX_USER> user_list(list);
5325
  TABLE_LIST tables[GRANT_TABLES];
5326 5327
  DBUG_ENTER("mysql_drop_user");

unknown's avatar
unknown committed
5328
  /* DROP USER may be skipped on replication client. */
5329 5330 5331 5332 5333 5334
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

  rw_wrlock(&LOCK_grant);
  VOID(pthread_mutex_lock(&acl_cache->lock));

5335
  while ((tmp_user_name= user_list++))
5336
  {
5337 5338 5339 5340 5341 5342
    user_name= get_current_user(thd, tmp_user_name);
    if (!(user_name= get_current_user(thd, tmp_user_name)))
    {
      result= TRUE;
      continue;
    }  
5343
    if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
5344
    {
unknown's avatar
unknown committed
5345
      append_user(&wrong_users, user_name);
5346
      result= TRUE;
5347
    }
5348
  }
5349

5350 5351 5352
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

5353
  VOID(pthread_mutex_unlock(&acl_cache->lock));
5354 5355 5356 5357 5358 5359 5360

  if (mysql_bin_log.is_open())
  {
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
    mysql_bin_log.write(&qinfo);
  }

5361 5362 5363
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
unknown's avatar
unknown committed
5364
    my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383
  DBUG_RETURN(result);
}


/*
  Rename a user.

  SYNOPSIS
    mysql_rename_user()
    thd                         The current thread.
    list                        The user name pairs: (from, to).

  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
{
5384
  int result;
unknown's avatar
unknown committed
5385
  String wrong_users;
5386 5387
  LEX_USER *user_from, *tmp_user_from;
  LEX_USER *user_to, *tmp_user_to;
5388
  List_iterator <LEX_USER> user_list(list);
5389
  TABLE_LIST tables[GRANT_TABLES];
5390 5391
  DBUG_ENTER("mysql_rename_user");

unknown's avatar
unknown committed
5392
  /* RENAME USER may be skipped on replication client. */
5393 5394 5395 5396 5397 5398
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

  rw_wrlock(&LOCK_grant);
  VOID(pthread_mutex_lock(&acl_cache->lock));

5399
  while ((tmp_user_from= user_list++))
5400
  {
5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411
    if (!(user_from= get_current_user(thd, tmp_user_from)))
    {
      result= TRUE;
      continue;
    }  
    tmp_user_to= user_list++;
    if (!(user_to= get_current_user(thd, tmp_user_to)))
    {
      result= TRUE;
      continue;
    }  
5412
    DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
5413 5414 5415 5416 5417

    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
unknown's avatar
unknown committed
5418 5419
    if (handle_grant_data(tables, 0, user_to, NULL) ||
        handle_grant_data(tables, 0, user_from, user_to) <= 0)
5420
    {
unknown's avatar
unknown committed
5421
      append_user(&wrong_users, user_from);
5422 5423
      result= TRUE;
    }
5424
  }
unknown's avatar
unknown committed
5425
  
5426 5427 5428
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

5429
  VOID(pthread_mutex_unlock(&acl_cache->lock));
5430 5431 5432 5433 5434 5435 5436

  if (mysql_bin_log.is_open())
  {
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
    mysql_bin_log.write(&qinfo);
  }

5437 5438 5439
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
5440
    my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
5441 5442 5443
  DBUG_RETURN(result);
}

5444

5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458
/*
  Revoke all privileges from a list of users.

  SYNOPSIS
    mysql_revoke_all()
    thd                         The current thread.
    list                        The users to revoke all privileges from.

  RETURN
    > 0         Error. Error message already sent.
    0           OK.
    < 0         Error. Error message not yet sent.
*/

unknown's avatar
unknown committed
5459
bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
5460
{
5461
  uint counter, revoked, is_proc;
5462
  int result;
unknown's avatar
unknown committed
5463
  ACL_DB *acl_db;
5464
  TABLE_LIST tables[GRANT_TABLES];
5465 5466 5467
  DBUG_ENTER("mysql_revoke_all");

  if ((result= open_grant_tables(thd, tables)))
unknown's avatar
unknown committed
5468
    DBUG_RETURN(result != 1);
5469 5470 5471 5472

  rw_wrlock(&LOCK_grant);
  VOID(pthread_mutex_lock(&acl_cache->lock));

5473
  LEX_USER *lex_user, *tmp_lex_user;
5474
  List_iterator <LEX_USER> user_list(list);
5475
  while ((tmp_lex_user= user_list++))
5476
  {
5477 5478 5479 5480 5481
    if (!(lex_user= get_current_user(thd, tmp_lex_user)))
    {
      result= -1;
      continue;
    }  
5482
    if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
5483 5484 5485 5486
    {
      result= -1;
      continue;
    }
unknown's avatar
unknown committed
5487

5488
    if (replace_user_table(thd, tables[0].table,
5489
			   *lex_user, ~(ulong)0, 1, 0, 0))
5490 5491 5492 5493 5494 5495
    {
      result= -1;
      continue;
    }

    /* Remove db access privileges */
unknown's avatar
unknown committed
5496 5497 5498 5499 5500
    /*
      Because acl_dbs and column_priv_hash shrink and may re-order
      as privileges are removed, removal occurs in a repeated loop
      until no more privileges are revoked.
     */
unknown's avatar
unknown committed
5501
    do
5502
    {
unknown's avatar
unknown committed
5503
      for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
5504
      {
unknown's avatar
unknown committed
5505
	const char *user,*host;
unknown's avatar
unknown committed
5506

unknown's avatar
unknown committed
5507 5508 5509 5510 5511
	acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
	if (!(user=acl_db->user))
	  user= "";
	if (!(host=acl_db->host.hostname))
	  host= "";
unknown's avatar
unknown committed
5512

unknown's avatar
unknown committed
5513 5514 5515
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
5516
	  if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, ~(ulong)0, 1))
unknown's avatar
unknown committed
5517
	  {
unknown's avatar
unknown committed
5518 5519 5520 5521 5522
	    /*
	      Don't increment counter as replace_db_table deleted the
	      current element in acl_dbs.
	     */
	    revoked= 1;
unknown's avatar
unknown committed
5523 5524
	    continue;
	  }
unknown's avatar
unknown committed
5525
	  result= -1; // Something went wrong
unknown's avatar
unknown committed
5526
	}
unknown's avatar
unknown committed
5527
	counter++;
5528
      }
unknown's avatar
unknown committed
5529
    } while (revoked);
5530 5531

    /* Remove column access */
unknown's avatar
unknown committed
5532
    do
5533
    {
unknown's avatar
unknown committed
5534
      for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
5535
      {
unknown's avatar
unknown committed
5536 5537 5538 5539 5540
	const char *user,*host;
	GRANT_TABLE *grant_table= (GRANT_TABLE*)hash_element(&column_priv_hash,
							     counter);
	if (!(user=grant_table->user))
	  user= "";
5541
	if (!(host=grant_table->host.hostname))
unknown's avatar
unknown committed
5542
	  host= "";
unknown's avatar
unknown committed
5543

unknown's avatar
unknown committed
5544 5545
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
5546
	{
unknown's avatar
unknown committed
5547 5548 5549
	  if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
				  grant_table->db,
				  grant_table->tname,
5550
				  ~(ulong)0, 0, 1))
unknown's avatar
unknown committed
5551
	  {
unknown's avatar
unknown committed
5552
	    result= -1;
unknown's avatar
unknown committed
5553
	  }
unknown's avatar
unknown committed
5554
	  else
unknown's avatar
unknown committed
5555
	  {
unknown's avatar
unknown committed
5556
	    if (!grant_table->cols)
unknown's avatar
unknown committed
5557
	    {
unknown's avatar
unknown committed
5558 5559
	      revoked= 1;
	      continue;
unknown's avatar
unknown committed
5560
	    }
unknown's avatar
unknown committed
5561 5562
	    List<LEX_COLUMN> columns;
	    if (!replace_column_table(grant_table,tables[3].table, *lex_user,
unknown's avatar
unknown committed
5563 5564 5565
				      columns,
				      grant_table->db,
				      grant_table->tname,
5566
				      ~(ulong)0, 1))
unknown's avatar
unknown committed
5567
	    {
unknown's avatar
unknown committed
5568
	      revoked= 1;
5569
	      continue;
unknown's avatar
unknown committed
5570
	    }
5571
	    result= -1;
unknown's avatar
unknown committed
5572
	  }
5573
	}
unknown's avatar
unknown committed
5574
	counter++;
5575
      }
unknown's avatar
unknown committed
5576
    } while (revoked);
5577 5578

    /* Remove procedure access */
5579 5580 5581
    for (is_proc=0; is_proc<2; is_proc++) do {
      HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
      for (counter= 0, revoked= 0 ; counter < hash->records ; )
5582 5583
      {
	const char *user,*host;
5584
	GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
5585 5586
	if (!(user=grant_proc->user))
	  user= "";
unknown's avatar
unknown committed
5587
	if (!(host=grant_proc->host.hostname))
5588 5589 5590 5591 5592
	  host= "";

	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
5593
	  if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
5594 5595
				  grant_proc->db,
				  grant_proc->tname,
5596
                                  is_proc,
unknown's avatar
unknown committed
5597
				  ~(ulong)0, 1))
5598 5599 5600 5601 5602 5603 5604 5605 5606
	  {
	    revoked= 1;
	    continue;
	  }
	  result= -1;	// Something went wrong
	}
	counter++;
      }
    } while (revoked);
5607
  }
unknown's avatar
unknown committed
5608

5609
  VOID(pthread_mutex_unlock(&acl_cache->lock));
5610 5611 5612 5613 5614 5615 5616

  if (mysql_bin_log.is_open())
  {
    Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
    mysql_bin_log.write(&qinfo);
  }

5617 5618
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
unknown's avatar
unknown committed
5619

5620
  if (result)
unknown's avatar
unknown committed
5621
    my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
unknown's avatar
unknown committed
5622

5623 5624
  DBUG_RETURN(result);
}
unknown's avatar
unknown committed
5625

5626

5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640
/*
  Revoke privileges for all users on a stored procedure

  SYNOPSIS
    sp_revoke_privileges()
    thd                         The current thread.
    db				DB of the stored procedure
    name			Name of the stored procedure

  RETURN
    0           OK.
    < 0         Error. Error message not yet sent.
*/

5641 5642
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
                          bool is_proc)
5643 5644 5645 5646
{
  uint counter, revoked;
  int result;
  TABLE_LIST tables[GRANT_TABLES];
5647
  HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
5648 5649 5650 5651 5652 5653 5654 5655 5656
  DBUG_ENTER("sp_revoke_privileges");

  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

  rw_wrlock(&LOCK_grant);
  VOID(pthread_mutex_lock(&acl_cache->lock));

  /* Remove procedure access */
5657 5658
  do
  {
5659
    for (counter= 0, revoked= 0 ; counter < hash->records ; )
5660
    {
5661
      GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
5662 5663 5664 5665 5666 5667
      if (!my_strcasecmp(system_charset_info, grant_proc->db, sp_db) &&
	  !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
      {
        LEX_USER lex_user;
	lex_user.user.str= grant_proc->user;
	lex_user.user.length= strlen(grant_proc->user);
5668 5669 5670 5671
	lex_user.host.str= grant_proc->host.hostname ?
	  grant_proc->host.hostname : (char*)"";
	lex_user.host.length= grant_proc->host.hostname ?
	  strlen(grant_proc->host.hostname) : 0;
5672 5673
	if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
				   grant_proc->db, grant_proc->tname,
unknown's avatar
unknown committed
5674
                                   is_proc, ~(ulong)0, 1))
5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709
	{
	  revoked= 1;
	  continue;
	}
	result= -1;	// Something went wrong
      }
      counter++;
    }
  } while (revoked);

  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);

  if (result)
    my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));

  DBUG_RETURN(result);
}


/*
  Grant EXECUTE,ALTER privilege for a stored procedure

  SYNOPSIS
    sp_grant_privileges()
    thd                         The current thread.
    db				DB of the stored procedure
    name			Name of the stored procedure

  RETURN
    0           OK.
    < 0         Error. Error message not yet sent.
*/

5710 5711
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
                         bool is_proc)
5712
{
5713
  Security_context *sctx= thd->security_ctx;
5714 5715 5716 5717
  LEX_USER *combo;
  TABLE_LIST tables[1];
  List<LEX_USER> user_list;
  bool result;
5718 5719
  ACL_USER *au;
  char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
5720
  DBUG_ENTER("sp_grant_privileges");
5721 5722 5723 5724

  if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
    DBUG_RETURN(TRUE);

5725
  combo->user.str= sctx->user;
5726

5727
  VOID(pthread_mutex_lock(&acl_cache->lock));
5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741

  if ((au= find_acl_user(combo->host.str=(char*)sctx->host_or_ip,combo->user.str,FALSE)))
    goto found_acl;
  if ((au= find_acl_user(combo->host.str=(char*)sctx->host, combo->user.str,FALSE)))
    goto found_acl;
  if ((au= find_acl_user(combo->host.str=(char*)sctx->ip, combo->user.str,FALSE)))
    goto found_acl;
  if((au= find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE)))
    goto found_acl;

  VOID(pthread_mutex_unlock(&acl_cache->lock));
  DBUG_RETURN(TRUE);

 found_acl:
5742
  VOID(pthread_mutex_unlock(&acl_cache->lock));
5743 5744 5745 5746 5747

  bzero((char*)tables, sizeof(TABLE_LIST));
  user_list.empty();

  tables->db= (char*)sp_db;
5748
  tables->table_name= tables->alias= (char*)sp_name;
5749

5750 5751 5752 5753
  combo->host.length= strlen(combo->host.str);
  combo->user.length= strlen(combo->user.str);
  combo->host.str= thd->strmake(combo->host.str,combo->host.length);
  combo->user.str= thd->strmake(combo->user.str,combo->user.length);
5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779


  if(au && au->salt_len)
  {
    if (au->salt_len == SCRAMBLE_LENGTH)
    {
      make_password_from_salt(passwd_buff, au->salt);
      combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
    }
    else if (au->salt_len == SCRAMBLE_LENGTH_323)
    {
      make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
      combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
    }
    else
    {
      my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
      return -1;
    }
    combo->password.str= passwd_buff;
  }
  else
  {
    combo->password.str= (char*)"";
    combo->password.length= 0;
  }
5780 5781 5782 5783 5784

  if (user_list.push_back(combo))
    DBUG_RETURN(TRUE);

  thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
5785
  bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
5786

5787
  result= mysql_routine_grant(thd, tables, is_proc, user_list,
5788 5789 5790 5791 5792
  				DEFAULT_CREATE_PROC_ACLS, 0, 1);
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
5793
/*****************************************************************************
unknown's avatar
unknown committed
5794
  Instantiate used templates
unknown's avatar
unknown committed
5795 5796
*****************************************************************************/

5797
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
unknown's avatar
unknown committed
5798 5799 5800 5801 5802
template class List_iterator<LEX_COLUMN>;
template class List_iterator<LEX_USER>;
template class List<LEX_COLUMN>;
template class List<LEX_USER>;
#endif
unknown's avatar
SCRUM:  
unknown committed
5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849

#endif /*NO_EMBEDDED_ACCESS_CHECKS */


int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
{
  reg3 int flag;
  DBUG_ENTER("wild_case_compare");
  DBUG_PRINT("enter",("str: '%s'  wildstr: '%s'",str,wildstr));
  while (*wildstr)
  {
    while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
    {
      if (*wildstr == wild_prefix && wildstr[1])
	wildstr++;
      if (my_toupper(cs, *wildstr++) !=
          my_toupper(cs, *str++)) DBUG_RETURN(1);
    }
    if (! *wildstr ) DBUG_RETURN (*str != 0);
    if (*wildstr++ == wild_one)
    {
      if (! *str++) DBUG_RETURN (1);	/* One char; skip */
    }
    else
    {						/* Found '*' */
      if (!*wildstr) DBUG_RETURN(0);		/* '*' as last char: OK */
      flag=(*wildstr != wild_many && *wildstr != wild_one);
      do
      {
	if (flag)
	{
	  char cmp;
	  if ((cmp= *wildstr) == wild_prefix && wildstr[1])
	    cmp=wildstr[1];
	  cmp=my_toupper(cs, cmp);
	  while (*str && my_toupper(cs, *str) != cmp)
	    str++;
	  if (!*str) DBUG_RETURN (1);
	}
	if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
      } while (*str++);
      DBUG_RETURN(1);
    }
  }
  DBUG_RETURN (*str != '\0');
}

5850 5851 5852 5853 5854 5855 5856 5857

void update_schema_privilege(TABLE *table, char *buff, const char* db,
                             const char* t_name, const char* column,
                             uint col_length, const char *priv, 
                             uint priv_length, const char* is_grantable)
{
  int i= 2;
  CHARSET_INFO *cs= system_charset_info;
5858
  restore_record(table, s->default_values);
5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879
  table->field[0]->store(buff, strlen(buff), cs);
  if (db)
    table->field[i++]->store(db, strlen(db), cs);
  if (t_name)
    table->field[i++]->store(t_name, strlen(t_name), cs);
  if (column)
    table->field[i++]->store(column, col_length, cs);
  table->field[i++]->store(priv, priv_length, cs);
  table->field[i]->store(is_grantable, strlen(is_grantable), cs);
  table->file->write_row(table->record[0]);
}


int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  uint counter;
  ACL_USER *acl_user;
  ulong want_access;
  char buff[100];
  TABLE *table= tables->table;
5880
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
5881
  char *curr_host= thd->security_ctx->priv_host_name();
5882
  DBUG_ENTER("fill_schema_user_privileges");
5883

5884 5885
  pthread_mutex_lock(&acl_cache->lock);

5886 5887 5888 5889 5890 5891 5892 5893
  for (counter=0 ; counter < acl_users.elements ; counter++)
  {
    const char *user,*host, *is_grantable="YES";
    acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
    if (!(user=acl_user->user))
      user= "";
    if (!(host=acl_user->host.hostname))
      host= "";
5894 5895

    if (no_global_access &&
5896
        (strcmp(thd->security_ctx->priv_user, user) ||
5897 5898 5899
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;
      
5900 5901 5902 5903 5904 5905
    want_access= acl_user->access;
    if (!(want_access & GRANT_ACL))
      is_grantable= "NO";

    strxmov(buff,"'",user,"'@'",host,"'",NullS);
    if (!(want_access & ~GRANT_ACL))
5906 5907
      update_schema_privilege(table, buff, 0, 0, 0, 0,
                              STRING_WITH_LEN("USAGE"), is_grantable);
5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920
    else
    {
      uint priv_id;
      ulong j,test_access= want_access & ~GRANT_ACL;
      for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
      {
	if (test_access & j)
          update_schema_privilege(table, buff, 0, 0, 0, 0, 
                                  command_array[priv_id],
                                  command_lengths[priv_id], is_grantable);
      }
    }
  }
5921 5922 5923

  pthread_mutex_unlock(&acl_cache->lock);

5924
  DBUG_RETURN(0);
5925 5926 5927
#else
  return(0);
#endif
5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938
}


int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  uint counter;
  ACL_DB *acl_db;
  ulong want_access;
  char buff[100];
  TABLE *table= tables->table;
5939
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
5940
  char *curr_host= thd->security_ctx->priv_host_name();
5941 5942
  DBUG_ENTER("fill_schema_schema_privileges");

5943 5944
  pthread_mutex_lock(&acl_cache->lock);

5945 5946 5947 5948 5949 5950 5951 5952 5953 5954
  for (counter=0 ; counter < acl_dbs.elements ; counter++)
  {
    const char *user, *host, *is_grantable="YES";

    acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
    if (!(user=acl_db->user))
      user= "";
    if (!(host=acl_db->host.hostname))
      host= "";

5955
    if (no_global_access &&
5956
        (strcmp(thd->security_ctx->priv_user, user) ||
5957 5958 5959
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;

5960 5961 5962 5963 5964 5965 5966 5967 5968 5969
    want_access=acl_db->access;
    if (want_access)
    {
      if (!(want_access & GRANT_ACL))
      {
        is_grantable= "NO";
      }
      strxmov(buff,"'",user,"'@'",host,"'",NullS);
      if (!(want_access & ~GRANT_ACL))
        update_schema_privilege(table, buff, acl_db->db, 0, 0,
5970
                                0, STRING_WITH_LEN("USAGE"), is_grantable);
5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982
      else
      {
        int cnt;
        ulong j,test_access= want_access & ~GRANT_ACL;
        for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
          if (test_access & j)
            update_schema_privilege(table, buff, acl_db->db, 0, 0, 0,
                                    command_array[cnt], command_lengths[cnt],
                                    is_grantable);
      }
    }
  }
5983 5984 5985

  pthread_mutex_unlock(&acl_cache->lock);

5986
  DBUG_RETURN(0);
5987 5988 5989
#else
  return (0);
#endif
5990 5991 5992 5993 5994 5995 5996 5997 5998
}


int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  uint index;
  char buff[100];
  TABLE *table= tables->table;
5999
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
6000
  char *curr_host= thd->security_ctx->priv_host_name();
6001 6002
  DBUG_ENTER("fill_schema_table_privileges");

6003 6004
  rw_rdlock(&LOCK_grant);

6005 6006
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
6007
    const char *user, *host, *is_grantable= "YES";
6008 6009 6010 6011
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
6012 6013
    if (!(host= grant_table->host.hostname))
      host= "";
6014 6015

    if (no_global_access &&
6016
        (strcmp(thd->security_ctx->priv_user, user) ||
6017
         my_strcasecmp(system_charset_info, curr_host, host)))
6018 6019
      continue;

6020
    ulong table_access= grant_table->privs;
6021
    if (table_access)
6022 6023
    {
      ulong test_access= table_access & ~GRANT_ACL;
unknown's avatar
unknown committed
6024 6025 6026 6027
      /*
        We should skip 'usage' privilege on table if
        we have any privileges on column(s) of this table
      */
6028 6029
      if (!test_access && grant_table->cols)
        continue;
6030 6031 6032
      if (!(table_access & GRANT_ACL))
        is_grantable= "NO";

6033
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
6034 6035
      if (!test_access)
        update_schema_privilege(table, buff, grant_table->db, grant_table->tname,
6036
                                0, 0, STRING_WITH_LEN("USAGE"), is_grantable);
6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050
      else
      {
        ulong j;
        int cnt;
        for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
        {
          if (test_access & j)
            update_schema_privilege(table, buff, grant_table->db, 
                                    grant_table->tname, 0, 0, command_array[cnt],
                                    command_lengths[cnt], is_grantable);
        }
      }
    }
  }
6051 6052 6053

  rw_unlock(&LOCK_grant);

6054
  DBUG_RETURN(0);
6055 6056 6057
#else
  return (0);
#endif
6058 6059 6060 6061 6062 6063 6064 6065 6066
}


int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  uint index;
  char buff[100];
  TABLE *table= tables->table;
6067
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
6068
  char *curr_host= thd->security_ctx->priv_host_name();
6069 6070
  DBUG_ENTER("fill_schema_table_privileges");

6071 6072
  rw_rdlock(&LOCK_grant);

6073 6074
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
6075
    const char *user, *host, *is_grantable= "YES";
6076 6077 6078 6079
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
6080 6081
    if (!(host= grant_table->host.hostname))
      host= "";
6082 6083

    if (no_global_access &&
6084
        (strcmp(thd->security_ctx->priv_user, user) ||
6085
         my_strcasecmp(system_charset_info, curr_host, host)))
6086 6087
      continue;

6088 6089 6090
    ulong table_access= grant_table->cols;
    if (table_access != 0)
    {
unknown's avatar
unknown committed
6091
      if (!(grant_table->privs & GRANT_ACL))
6092 6093
        is_grantable= "NO";

unknown's avatar
unknown committed
6094
      ulong test_access= table_access & ~GRANT_ACL;
6095
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124
      if (!test_access)
        continue;
      else
      {
        ulong j;
        int cnt;
        for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
        {
          if (test_access & j)
          {
            for (uint col_index=0 ;
                 col_index < grant_table->hash_columns.records ;
                 col_index++)
            {
              GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
                hash_element(&grant_table->hash_columns,col_index);
              if ((grant_column->rights & j) && (table_access & j))
                  update_schema_privilege(table, buff, grant_table->db,
                                          grant_table->tname,
                                          grant_column->column,
                                          grant_column->key_length,
                                          command_array[cnt],
                                          command_lengths[cnt], is_grantable);
            }
          }
        }
      }
    }
  }
6125 6126 6127

  rw_unlock(&LOCK_grant);

6128
  DBUG_RETURN(0);
6129 6130 6131
#else
  return (0);
#endif
6132 6133 6134
}


unknown's avatar
VIEW  
unknown committed
6135 6136 6137 6138 6139
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
  fill effective privileges for table

  SYNOPSIS
6140 6141
    fill_effective_table_privileges()
    thd     thread handler
unknown's avatar
VIEW  
unknown committed
6142 6143 6144 6145 6146 6147 6148 6149
    grant   grants table descriptor
    db      db name
    table   table name
*/

void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
                                     const char *db, const char *table)
{
6150
  Security_context *sctx= thd->security_ctx;
6151 6152 6153 6154 6155
  DBUG_ENTER("fill_effective_table_privileges");
  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
                       sctx->priv_host, (sctx->ip ? sctx->ip : "(NULL)"),
                       (sctx->priv_user ? sctx->priv_user : "(NULL)"),
                       db, table));
6156 6157 6158
  /* --skip-grants */
  if (!initialized)
  {
6159
    DBUG_PRINT("info", ("skip grants"));
6160
    grant->privilege= ~NO_ACCESS;             // everything is allowed
6161 6162
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;
6163 6164
  }

unknown's avatar
VIEW  
unknown committed
6165
  /* global privileges */
6166
  grant->privilege= sctx->master_access;
6167

6168
  if (!sctx->priv_user)
6169 6170 6171 6172
  {
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;                         // it is slave
  }
6173

6174
  /* db privileges */
6175
  grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
6176

6177
  if (!grant_option)
6178 6179 6180 6181
  {
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;
  }
6182

unknown's avatar
VIEW  
unknown committed
6183
  /* table privileges */
6184
  rw_rdlock(&LOCK_grant);
unknown's avatar
VIEW  
unknown committed
6185 6186 6187
  if (grant->version != grant_version)
  {
    grant->grant_table=
6188 6189
      table_hash_search(sctx->host, sctx->ip, db,
			sctx->priv_user,
unknown's avatar
VIEW  
unknown committed
6190 6191 6192 6193 6194 6195 6196
			table, 0);              /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
  }
  if (grant->grant_table != 0)
  {
    grant->privilege|= grant->grant_table->privs;
  }
6197 6198
  rw_unlock(&LOCK_grant);

6199 6200
  DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
  DBUG_VOID_RETURN;
unknown's avatar
VIEW  
unknown committed
6201
}
6202 6203 6204 6205 6206 6207 6208

#else /* NO_EMBEDDED_ACCESS_CHECKS */

/****************************************************************************
 Dummy wrappers when we don't have any access checks
****************************************************************************/

unknown's avatar
unknown committed
6209 6210
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
                             bool is_proc)
6211 6212 6213 6214
{
  return FALSE;
}

unknown's avatar
VIEW  
unknown committed
6215
#endif