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
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)
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 200
  /*
    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;
  char tmp_name[NAME_LEN+1];
201
  int password_length;
202 203
  DBUG_ENTER("acl_load");

204 205
  grant_version++; /* Privileges updated */
  mysql_proc_table_exists= 1;			// Assume mysql.proc exists
206

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

209
  init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
unknown's avatar
unknown committed
210
  init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0);
211
  VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
unknown's avatar
unknown committed
212 213 214
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_HOST host;
215 216
    update_hostname(&host.host,get_field(&mem, table->field[0]));
    host.db=	 get_field(&mem, table->field[1]);
217
    if (lower_case_table_names && host.db)
218 219
    {
      /*
220 221
        convert db to lower case and give a warning if the db wasn't
        already in lower case
222
      */
223 224
      (void) strmov(tmp_name, host.db);
      my_casedn_str(files_charset_info, host.db);
225 226 227
      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 "
228 229
                          "lower_case_table_names is set. It will not be "
                          "possible to remove this privilege using REVOKE.",
230 231
                          host.host.hostname ? host.host.hostname : "",
                          host.db ? host.db : "");
232
    }
unknown's avatar
unknown committed
233 234
    host.access= get_access(table,2);
    host.access= fix_rights_for_db(host.access);
235
    host.sort=	 get_sort(2,host.host.hostname,host.db);
unknown's avatar
SCRUM  
unknown committed
236 237
    if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
    {
unknown's avatar
unknown committed
238
      sql_print_warning("'host' entry '%s|%s' "
unknown's avatar
SCRUM  
unknown committed
239
		      "ignored in --skip-name-resolve mode.",
240 241
			host.host.hostname ? host.host.hostname : "",
			host.db ? host.db : "");
unknown's avatar
SCRUM  
unknown committed
242 243
      continue;
    }
unknown's avatar
unknown committed
244
#ifndef TO_BE_REMOVED
245
    if (table->s->fields == 8)
unknown's avatar
unknown committed
246 247
    {						// Without grant
      if (host.access & CREATE_ACL)
248
	host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
unknown's avatar
unknown committed
249 250 251 252 253 254 255 256 257 258
    }
#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);
259
  VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
260 261 262
  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
263
  {
264 265 266
    sql_print_error("Fatal error: mysql.user table is damaged or in "
                    "unsupported 3.20 format.");
    goto end;
unknown's avatar
unknown committed
267 268
  }

269
  DBUG_PRINT("info",("user table fields: %d, password length: %d",
270
		     table->s->fields, password_length));
271

272
  pthread_mutex_lock(&LOCK_global_system_variables);
273
  if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
274
  {
275 276 277 278 279 280 281 282 283 284 285 286 287 288
    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);
289 290 291
      sql_print_warning("mysql.user table is not updated to new password format; "
                        "Disabling new password usage until "
                        "mysql_fix_privilege_tables is run");
292 293 294 295
    }
    thd->variables.old_passwords= 1;
  }
  else
296
  {
297 298
    sys_old_passwords.after_update= 0;
    pthread_mutex_unlock(&LOCK_global_system_variables);
299 300
  }

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

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

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

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

362 363 364
      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
365

366 367
      /* Starting from 4.0.2 we have more fields */
      if (table->s->fields >= 31)
368
      {
unknown's avatar
unknown committed
369
        char *ssl_type=get_field(&mem, table->field[next_field++]);
370 371 372 373 374 375 376 377 378
        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
379 380 381
        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++]);
382

unknown's avatar
unknown committed
383 384 385 386 387
        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++]);
388
        user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
389
        if (user.user_resource.questions || user.user_resource.updates ||
390
            user.user_resource.conn_per_hour)
391
          mqh_used=1;
392

393
        if (table->s->fields >= 36)
394 395 396 397 398 399 400
        {
          /* 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;
401
      }
402 403 404
      else
      {
        user.ssl_type=SSL_TYPE_NONE;
405
        bzero((char *)&(user.user_resource),sizeof(user.user_resource));
unknown's avatar
unknown committed
406
#ifndef TO_BE_REMOVED
407
        if (table->s->fields <= 13)
408 409 410 411 412 413 414 415 416 417
        {						// 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
418
#endif
419 420
      }
      VOID(push_dynamic(&acl_users,(gptr) &user));
421 422
      if (!user.host.hostname ||
	  (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
423
        allow_all_hosts=1;			// Anyone can connect
unknown's avatar
unknown committed
424
    }
unknown's avatar
unknown committed
425 426 427 428 429
  }
  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
430

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

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

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


void acl_free(bool end)
{
500
  free_root(&mem,MYF(0));
unknown's avatar
unknown committed
501 502 503 504 505 506 507 508 509 510 511 512 513 514
  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;
  }
}

515 516

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

  SYNOPSIS
    acl_reload()
522 523 524 525 526 527 528 529 530 531 532
      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
533
*/
unknown's avatar
unknown committed
534

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

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

  /*
    To avoid deadlocks we should obtain table locks before
    obtaining acl_cache->lock mutex.
  */
  bzero((char*) tables, sizeof(tables));
556 557 558 559 560 561
  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;
562 563 564 565 566 567 568 569 570
  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
571 572 573 574 575 576 577 578 579 580
  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);

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


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

  IMPLEMENTATION
unknown's avatar
unknown committed
610 611
  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
612 613 614 615 616 617 618 619 620 621 622

  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
623
*/
unknown's avatar
unknown committed
624

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

unknown's avatar
unknown committed
632 633 634
  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
635
       pos++, fieldnr++, bit<<=1)
unknown's avatar
unknown committed
636
  {
637
    (*pos)->val_str(&res);
unknown's avatar
unknown committed
638
    if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
unknown's avatar
unknown committed
639
      access_bits|= bit;
unknown's avatar
unknown committed
640
  }
unknown's avatar
unknown committed
641 642
  if (next_field)
    *next_field=fieldnr;
unknown's avatar
unknown committed
643 644 645 646 647
  return access_bits;
}


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

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

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

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

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

697

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

unknown's avatar
unknown committed
702 703 704 705
  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
706

707
  SYNOPSIS
708 709
    acl_getroot()
    thd         thread handle. If all checks are OK,
710 711
                thd->security_ctx->priv_user/master_access are updated.
                thd->security_ctx->host/ip/user are used for checks.
712 713
    mqh         user resources; on success mqh is reset, else
                unchanged
714
    passwd      scrambled & crypted password, received from client
715 716 717 718 719 720 721
                (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
722
  RETURN VALUE
723 724
    0  success: thd->priv_user, thd->priv_host, thd->master_access, mqh are
       updated
725
    1  user not found or authentication failure
unknown's avatar
unknown committed
726
    2  user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
727
   -1  user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
unknown's avatar
unknown committed
728 729
*/

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

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

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

unknown's avatar
unknown committed
751
  /*
752 753 754
    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
755
  */
unknown's avatar
unknown committed
756

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

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

801
    /*
unknown's avatar
unknown committed
802
      At this point we know that user is allowed to connect
803 804 805 806 807 808
      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
809 810
    case SSL_TYPE_NONE:				// SSL is not required
      user_access= acl_user->access;
811
      break;
812
#ifdef HAVE_OPENSSL
unknown's avatar
merge  
unknown committed
813
    case SSL_TYPE_ANY:				// Any kind of SSL is ok
814
      if (vio_type(vio) == VIO_TYPE_SSL)
unknown's avatar
merge  
unknown committed
815
	user_access= acl_user->access;
816 817 818 819 820
      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
821

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

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


923
/*
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
  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)
942 943
{
  int res= 1;
944
  uint i;
945 946 947
  ACL_USER *acl_user= 0;
  DBUG_ENTER("acl_getroot_no_password");

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

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

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

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

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

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

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

unknown's avatar
unknown committed
1022 1023 1024 1025 1026 1027 1028
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;
}

1029

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

unknown's avatar
unknown committed
1041 1042 1043 1044
  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] ||
1045
	acl_user->user && !strcmp(user,acl_user->user))
unknown's avatar
unknown committed
1046 1047
    {
      if (!acl_user->host.hostname && !host[0] ||
unknown's avatar
unknown committed
1048
	  acl_user->host.hostname &&
1049
	  !my_strcasecmp(system_charset_info, host, acl_user->host.hostname))
unknown's avatar
unknown committed
1050 1051
      {
	acl_user->access=privileges;
1052
	if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1053
	  acl_user->user_resource.questions=mqh->questions;
1054
	if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1055
	  acl_user->user_resource.updates=mqh->updates;
1056 1057 1058 1059
	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;
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
	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
1070 1071
	if (password)
	  set_user_salt(acl_user, password, password_len);
1072
        /* search complete: */
unknown's avatar
unknown committed
1073 1074 1075 1076 1077 1078 1079 1080
	break;
      }
    }
  }
}


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

  safe_mutex_assert_owner(&acl_cache->lock);

1093
  acl_user.user=*user ? strdup_root(&mem,user) : 0;
unknown's avatar
unknown committed
1094
  update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
unknown's avatar
unknown committed
1095
  acl_user.access=privileges;
1096
  acl_user.user_resource = *mqh;
unknown's avatar
unknown committed
1097
  acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
1098
  acl_user.hostname_length=(uint) strlen(host);
1099 1100 1101 1102 1103
  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;
1104 1105

  set_user_salt(&acl_user, password, password_len);
unknown's avatar
unknown committed
1106 1107

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

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


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

unknown's avatar
unknown committed
1124 1125 1126 1127 1128 1129 1130 1131
  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] ||
1132
	  acl_db->host.hostname &&
1133
	  !my_strcasecmp(system_charset_info, host, acl_db->host.hostname))
unknown's avatar
unknown committed
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
      {
	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);
	}
      }
    }
  }
}


1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162
/*
  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
1163
static void acl_insert_db(const char *user, const char *host, const char *db,
unknown's avatar
unknown committed
1164
			  ulong privileges)
unknown's avatar
unknown committed
1165 1166
{
  ACL_DB acl_db;
1167
  safe_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
1168
  acl_db.user=strdup_root(&mem,user);
1169
  update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
unknown's avatar
unknown committed
1170 1171 1172 1173 1174 1175 1176 1177 1178
  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);
}


1179 1180 1181

/*
  Get privilege for a host, user and db combination
1182 1183 1184

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

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

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

  /*
    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))
      {
1222
	if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
unknown's avatar
unknown committed
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
	{
	  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))
    {
1244
      if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
unknown's avatar
unknown committed
1245 1246 1247 1248 1249 1250 1251 1252
      {
	host_access=acl_host->access;		// Fully specified. Take it
	break;
      }
    }
  }
exit:
  /* Save entry in cache for quick retrieval */
1253 1254
  if (!db_is_pattern &&
      (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
unknown's avatar
unknown committed
1255 1256 1257 1258 1259 1260 1261
  {
    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
1262 1263
  DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
  DBUG_RETURN(db_access & host_access);
unknown's avatar
unknown committed
1264 1265
}

1266 1267 1268 1269 1270 1271 1272
/*
  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
1273 1274 1275 1276

static void init_check_host(void)
{
  DBUG_ENTER("init_check_host");
1277
  VOID(my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
unknown's avatar
unknown committed
1278
			  acl_users.elements,1));
1279
  VOID(hash_init(&acl_check_hosts,system_charset_info,acl_users.elements,0,0,
unknown's avatar
unknown committed
1280
		 (hash_get_key) check_get_key,0,0));
unknown's avatar
unknown committed
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294
  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 *);
1295
	  if (!my_strcasecmp(system_charset_info,
1296
                             acl_user->host.hostname, acl->hostname))
unknown's avatar
unknown committed
1297 1298 1299 1300 1301
	    break;				// already stored
	}
	if (j == acl_wild_hosts.elements)	// If new
	  (void) push_dynamic(&acl_wild_hosts,(char*) &acl_user->host);
      }
1302
      else if (!hash_search(&acl_check_hosts,(byte*) acl_user->host.hostname,
unknown's avatar
unknown committed
1303
			    (uint) strlen(acl_user->host.hostname)))
unknown's avatar
unknown committed
1304
      {
unknown's avatar
SCRUM  
unknown committed
1305
	if (my_hash_insert(&acl_check_hosts,(byte*) acl_user))
unknown's avatar
unknown committed
1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
	{					// 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;
}


1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334
/*
  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
1335 1336 1337 1338 1339 1340 1341 1342
/* 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
1343 1344
  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
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362
  {
    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
1363 1364 1365 1366 1367 1368 1369 1370
/*
  Check if the user is allowed to change password

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

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

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

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


1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426
/*
  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
1427
*/
1428

unknown's avatar
unknown committed
1429 1430 1431
bool change_password(THD *thd, const char *host, const char *user,
		     char *new_password)
{
1432 1433 1434 1435 1436
  TABLE_LIST tables;
  TABLE *table;
  /* Buffer should be extended when password length is extended. */
  char buff[512];
  ulong query_length;
1437
  uint new_password_len= strlen(new_password);
1438
  bool result= 1;
unknown's avatar
unknown committed
1439 1440 1441 1442 1443
  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

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

1447
  bzero((char*) &tables, sizeof(tables));
1448
  tables.alias= tables.table_name= (char*) "user";
1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463
  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 */
1464
    if (!tables_ok(thd, &tables))
1465 1466 1467 1468 1469 1470 1471
      DBUG_RETURN(0);
  }
#endif

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

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

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

unknown's avatar
unknown committed
1492 1493
  acl_cache->clear(1);				// Clear locked hostname cache
  VOID(pthread_mutex_unlock(&acl_cache->lock));
1494 1495 1496
  result= 0;
  if (mysql_bin_log.is_open())
  {
1497 1498 1499 1500 1501 1502
    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));
1503 1504 1505 1506 1507 1508 1509
    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
1510 1511 1512
}


1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528
/*
  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;
1529 1530 1531 1532 1533

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

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


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

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

  safe_mutex_assert_owner(&acl_cache->lock);

unknown's avatar
unknown committed
1553 1554 1555
  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
1556
    DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
1557 1558 1559 1560
                       user, acl_user->user ? acl_user->user : "",
                       host,
                       acl_user->host.hostname ? acl_user->host.hostname :
                       ""));
unknown's avatar
unknown committed
1561 1562 1563
    if (!acl_user->user && !user[0] ||
	acl_user->user && !strcmp(user,acl_user->user))
    {
1564
      if (exact ? !my_strcasecmp(system_charset_info, host,
1565 1566
                                 acl_user->host.hostname ?
				 acl_user->host.hostname : "") :
1567
          compare_hostname(&acl_user->host,host,host))
unknown's avatar
unknown committed
1568 1569 1570
      {
	DBUG_RETURN(acl_user);
      }
unknown's avatar
unknown committed
1571 1572
    }
  }
unknown's avatar
unknown committed
1573
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1574 1575 1576
}


1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587
/*
  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
1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609

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)
{
1610
  host->hostname=(char*) hostname;             // This will not be modified!
1611
  if (!hostname ||
unknown's avatar
unknown committed
1612 1613 1614
      (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
       !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
  {
1615
    host->ip= host->ip_mask=0;			// Not a masked ip
unknown's avatar
unknown committed
1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628
  }
}


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 ||
1629
	  (hostname && !wild_case_compare(system_charset_info,
1630 1631
                                          hostname, host->hostname)) ||
	  (ip && !wild_compare(ip, host->hostname, 0)));
unknown's avatar
unknown committed
1632 1633
}

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

1654

1655
/*
1656 1657 1658 1659 1660 1661 1662 1663 1664 1665
  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
1666
*/
unknown's avatar
unknown committed
1667

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

1677 1678
  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
1679
  key_copy((byte *) user_key, table->record[0], table->key_info,
1680
           table->key_info->key_length);
unknown's avatar
unknown committed
1681

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

unknown's avatar
unknown committed
1701

1702 1703 1704 1705 1706 1707
/*
  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
1708 1709 1710

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

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


unknown's avatar
unknown committed
1736
/****************************************************************************
1737
  Handle GRANT commands
unknown's avatar
unknown committed
1738 1739
****************************************************************************/

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

1753
  safe_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
1754 1755

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

1767 1768
  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);
1769 1770 1771
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

1772
  table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
unknown's avatar
unknown committed
1773
  if (table->file->index_read_idx(table->record[0], 0,
1774 1775
                                  user_key, table->key_info->key_length,
                                  HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
1776
  {
1777 1778
    /* what == 'N' means revoke */
    if (what == 'N')
unknown's avatar
unknown committed
1779
    {
1780 1781 1782 1783
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
      goto end;
    }
    /*
1784 1785
      There are four options which affect the process of creation of
      a new user (mysqld option --safe-create-user, 'insert' privilege
1786 1787 1788 1789 1790 1791 1792
      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
1793 1794

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

unknown's avatar
unknown committed
1830 1831 1832 1833
  /* Update table columns with new privileges */

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

1890
    USER_RESOURCES mqh= lex->mqh;
1891
    if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1892
      table->field[next_field]->store((longlong) mqh.questions, TRUE);
1893
    if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1894
      table->field[next_field+1]->store((longlong) mqh.updates, TRUE);
1895
    if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
1896
      table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE);
1897
    if (table->s->fields >= 36 &&
1898
        (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
unknown's avatar
unknown committed
1899
      table->field[next_field+3]->store((longlong) mqh.user_conn);
1900
    mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
1901
  }
unknown's avatar
unknown committed
1902
  if (old_row_exists)
unknown's avatar
unknown committed
1903 1904 1905 1906 1907
  {
    /*
      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!
    */
1908
    table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
unknown's avatar
unknown committed
1909
    if (cmp_record(table,record[1]) &&
unknown's avatar
unknown committed
1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928
	(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

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


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

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

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

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

1984 1985 1986
  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);
1987 1988 1989
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

1990 1991
  table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
  if (table->file->index_read_idx(table->record[0],0,
1992 1993
                                  user_key, table->key_info->key_length,
                                  HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
1994 1995 1996
  {
    if (what == 'N')
    { // no row, no revoke
unknown's avatar
unknown committed
1997
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
unknown's avatar
unknown committed
1998 1999
      goto abort;
    }
unknown's avatar
unknown committed
2000
    old_row_exists = 0;
2001
    restore_record(table, s->default_values);
2002 2003 2004
    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
2005 2006 2007
  }
  else
  {
unknown's avatar
unknown committed
2008
    old_row_exists = 1;
unknown's avatar
unknown committed
2009
    store_record(table,record[1]);
unknown's avatar
unknown committed
2010 2011 2012
  }

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

unknown's avatar
unknown committed
2021
  if (old_row_exists)
unknown's avatar
unknown committed
2022
  {
2023
    /* update old existing row */
unknown's avatar
unknown committed
2024 2025
    if (rights)
    {
2026
      table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
unknown's avatar
unknown committed
2027 2028 2029 2030 2031 2032 2033 2034 2035
      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 */
    }
  }
2036
  else if (rights && (error=table->file->write_row(table->record[0])))
unknown's avatar
unknown committed
2037 2038 2039 2040 2041 2042
  {
    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
2043
  if (old_row_exists)
unknown's avatar
unknown committed
2044 2045
    acl_update_db(combo.user.str,combo.host.str,db,rights);
  else
2046
  if (rights)
unknown's avatar
unknown committed
2047 2048 2049 2050
    acl_insert_db(combo.user.str,combo.host.str,db,rights);
  DBUG_RETURN(0);

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

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


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

unknown's avatar
unknown committed
2071

unknown's avatar
unknown committed
2072 2073 2074 2075 2076 2077 2078
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
2079

2080
class GRANT_NAME :public Sql_alloc
unknown's avatar
unknown committed
2081 2082
{
public:
2083 2084
  acl_host_and_ip host;
  char *db, *user, *tname, *hash_key;
2085
  ulong privs;
2086
  ulong sort;
unknown's avatar
unknown committed
2087
  uint key_length;
2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099
  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
2100
  HASH hash_columns;
unknown's avatar
unknown committed
2101 2102 2103 2104

  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);
2105
  ~GRANT_TABLE();
2106 2107
  bool ok() { return privs != 0 || cols != 0; }
};
unknown's avatar
unknown committed
2108

2109

unknown's avatar
unknown committed
2110

2111 2112 2113
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
                       const char *t, ulong p)
  :privs(p)
2114 2115
{
  /* Host given by user */
2116
  update_hostname(&host, strdup_root(&memex, h));
2117 2118
  db =   strdup_root(&memex,d);
  user = strdup_root(&memex,u);
2119
  sort=  get_sort(3,host.hostname,db,user);
2120 2121
  tname= strdup_root(&memex,t);
  if (lower_case_table_names)
unknown's avatar
unknown committed
2122
  {
2123 2124
    my_casedn_str(files_charset_info, db);
    my_casedn_str(files_charset_info, tname);
2125 2126 2127 2128
  }
  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);
2129 2130 2131 2132 2133 2134 2135
}


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)
{
2136
  (void) hash_init(&hash_columns,system_charset_info,
unknown's avatar
unknown committed
2137
                   0,0,0, (hash_get_key) get_key_column,0,0);
2138
}
unknown's avatar
unknown committed
2139

2140

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


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();
2183 2184
  cols =  fix_rights_for_column(cols);

2185
  (void) hash_init(&hash_columns,system_charset_info,
unknown's avatar
unknown committed
2186
                   0,0,0, (hash_get_key) get_key_column,0,0);
2187 2188
  if (cols)
  {
2189 2190
    uint key_prefix_len;
    KEY_PART_INFO *key_part= col_privs->key_info->key_part;
2191 2192
    col_privs->field[0]->store(host.hostname,
                               host.hostname ? (uint) strlen(host.hostname) : 0,
2193 2194 2195 2196
                               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);
2197 2198 2199 2200 2201 2202

    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
2203
    col_privs->field[4]->store("",0, &my_charset_latin1);
2204

2205 2206
    col_privs->file->ha_index_init(0);
    if (col_privs->file->index_read(col_privs->record[0],
2207 2208
                                    (byte*) key,
                                    key_prefix_len, HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
2209
    {
2210
      cols = 0; /* purecov: deadcode */
2211
      col_privs->file->ha_index_end();
2212
      return;
unknown's avatar
unknown committed
2213
    }
2214
    do
unknown's avatar
unknown committed
2215
    {
2216 2217 2218
      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
2219
      res=col_privs->field[4]->val_str(&column_name);
2220 2221 2222
      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
2223
      {
2224 2225 2226
        /* Don't use this entry */
        privs = cols = 0;			/* purecov: deadcode */
        return;				/* purecov: deadcode */
unknown's avatar
unknown committed
2227
      }
unknown's avatar
unknown committed
2228
      my_hash_insert(&hash_columns, (byte *) mem_check);
2229
    } while (!col_privs->file->index_next(col_privs->record[0]) &&
2230
             !key_cmp_if_same(col_privs,key,0,key_prefix_len));
2231
    col_privs->file->ha_index_end();
unknown's avatar
unknown committed
2232
  }
2233
}
unknown's avatar
unknown committed
2234

unknown's avatar
unknown committed
2235

2236 2237 2238 2239 2240 2241
GRANT_TABLE::~GRANT_TABLE()
{
  hash_free(&hash_columns);
}


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

unknown's avatar
unknown committed
2249

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

unknown's avatar
unknown committed
2255

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

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

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


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


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
2314

unknown's avatar
unknown committed
2315
inline GRANT_COLUMN *
unknown's avatar
unknown committed
2316
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
unknown's avatar
unknown committed
2317 2318 2319 2320 2321 2322 2323 2324 2325
{
  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
2326
				ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
2327 2328 2329
{
  int error=0,result=0;
  byte key[MAX_KEY_LENGTH];
2330 2331
  uint key_prefix_length;
  KEY_PART_INFO *key_part= table->key_info->key_part;
unknown's avatar
unknown committed
2332 2333
  DBUG_ENTER("replace_column_table");

unknown's avatar
unknown committed
2334 2335 2336 2337 2338 2339 2340 2341
  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
2342

2343 2344 2345 2346
  /* 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
2347

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

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

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

    key_restore(table->record[0],key,table->key_info,
                key_prefix_length);
unknown's avatar
unknown committed
2363
    table->field[4]->store(column->column.ptr(), column->column.length(),
2364
                           system_charset_info);
2365 2366 2367
    /* 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
2368

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

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

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

unknown's avatar
unknown committed
2404
    if (old_row_exists)
unknown's avatar
unknown committed
2405
    {
unknown's avatar
unknown committed
2406
      GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
2407 2408 2409 2410 2411 2412 2413 2414 2415 2416
      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
2417 2418
      grant_column= column_hash_search(g_t, column->column.ptr(),
                                       column->column.length());
unknown's avatar
unknown committed
2419
      if (grant_column)				// Should always be true
unknown's avatar
unknown committed
2420
	grant_column->rights= privileges;	// Update hash
unknown's avatar
unknown committed
2421 2422 2423
    }
    else					// new grant
    {
unknown's avatar
unknown committed
2424
      GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
2425 2426 2427 2428 2429 2430
      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
2431
      grant_column= new GRANT_COLUMN(column->column,privileges);
unknown's avatar
SCRUM  
unknown committed
2432
      my_hash_insert(&g_t->hash_columns,(byte*) grant_column);
unknown's avatar
unknown committed
2433 2434 2435 2436 2437 2438 2439 2440 2441 2442
    }
  }

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

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

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

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

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

	privileges&= ~rights;
	table->field[6]->store((longlong)
2469
			       get_rights_for_column(privileges), TRUE);
2470
	table->field[4]->val_str(&column_name);
unknown's avatar
unknown committed
2471 2472 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
	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]) &&
2501
	     !key_cmp_if_same(table, key, 0, key_prefix_length));
unknown's avatar
unknown committed
2502 2503
  }

2504
end:
unknown's avatar
unknown committed
2505
  table->file->ha_index_end();
unknown's avatar
unknown committed
2506 2507 2508 2509 2510 2511 2512
  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
2513 2514
			       ulong rights, ulong col_rights,
			       bool revoke_grant)
unknown's avatar
unknown committed
2515
{
2516
  char grantor[USER_HOST_BUFF_SIZE];
unknown's avatar
unknown committed
2517
  int old_row_exists = 1;
unknown's avatar
unknown committed
2518
  int error=0;
unknown's avatar
unknown committed
2519
  ulong store_table_rights, store_col_rights;
2520
  byte user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
2521 2522
  DBUG_ENTER("replace_table_table");

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

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

2537
  restore_record(table, s->default_values);     // Get empty record
2538 2539 2540 2541
  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
2542
  store_record(table,record[1]);			// store at pos 1
2543 2544
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);
unknown's avatar
unknown committed
2545

2546
  table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
2547 2548
  if (table->file->index_read_idx(table->record[0], 0,
                                  user_key, table->key_info->key_length,
unknown's avatar
unknown committed
2549 2550 2551 2552 2553 2554 2555 2556 2557
				  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
2558 2559
      my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
               combo.user.str, combo.host.str,
2560
               table_name);		        /* purecov: deadcode */
unknown's avatar
unknown committed
2561 2562
      DBUG_RETURN(-1);				/* purecov: deadcode */
    }
unknown's avatar
unknown committed
2563
    old_row_exists = 0;
unknown's avatar
unknown committed
2564
    restore_record(table,record[1]);			// Get saved record
unknown's avatar
unknown committed
2565 2566
  }

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

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

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

unknown's avatar
unknown committed
2594
  if (old_row_exists)
unknown's avatar
unknown committed
2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610
  {
    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
2611
  if (rights | col_rights)
unknown's avatar
unknown committed
2612
  {
unknown's avatar
unknown committed
2613
    grant_table->privs= rights;
2614
    grant_table->cols=	col_rights;
unknown's avatar
unknown committed
2615 2616 2617
  }
  else
  {
2618
    hash_delete(&column_priv_hash,(byte*) grant_table);
unknown's avatar
unknown committed
2619 2620 2621
  }
  DBUG_RETURN(0);

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


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

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

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

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

2659
  restore_record(table, s->default_values);		// Get empty record
2660 2661 2662
  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);
2663 2664 2665
  table->field[3]->store(routine_name,(uint) strlen(routine_name),
                         &my_charset_latin1);
  table->field[4]->store((longlong)(is_proc ? 
2666 2667
                                    TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION),
                         TRUE);
2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681
  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),
2682
               combo.user.str, combo.host.str, routine_name);
2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706
      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;
    }
  }

2707
  table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
2708
  table->field[6]->store((longlong) store_proc_rights, TRUE);
2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733
  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
  {
2734
    hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
2735 2736 2737 2738 2739 2740 2741 2742 2743 2744
  }
  DBUG_RETURN(0);

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


2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757
/*
  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
2758 2759
    FALSE ok
    TRUE  error
2760 2761
*/

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

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

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

      if (open_and_lock_tables(thd, table_list))
        DBUG_RETURN(TRUE);
2797 2798

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

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

  bzero((char*) &tables,sizeof(tables));
2849 2850 2851
  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
2852
  tables[0].next_local= tables[0].next_global= tables+1;
unknown's avatar
unknown committed
2853
  /* Don't open column table if we don't need it ! */
unknown's avatar
VIEW  
unknown committed
2854 2855 2856 2857 2858
  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
2859 2860 2861
  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";

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

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

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

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

unknown's avatar
VIEW  
unknown committed
2922 2923 2924
    db_name= (table_list->view_db.length ?
	      table_list->view_db.str :
	      table_list->db);
2925
    table_name= (table_list->view_name.length ?
unknown's avatar
VIEW  
unknown committed
2926
		table_list->view_name.str :
2927
		table_list->table_name);
unknown's avatar
VIEW  
unknown committed
2928

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

    /* If revoke_grant, calculate the new column privilege for tables_priv */
    if (revoke_grant)
    {
2956 2957
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
unknown's avatar
unknown committed
2958 2959 2960
      GRANT_COLUMN *grant_column;

      /* Fix old grants */
2961
      while ((column = column_iter++))
unknown's avatar
unknown committed
2962 2963
      {
	grant_column = column_hash_search(grant_table,
2964 2965
					  column->column.ptr(),
					  column->column.length());
unknown's avatar
unknown committed
2966
	if (grant_column)
2967
	  grant_column->rights&= ~(column->rights | rights);
unknown's avatar
unknown committed
2968 2969
      }
      /* scan trough all columns to get new column grant */
2970
      column_priv= 0;
unknown's avatar
unknown committed
2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986
      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
2987
    if (replace_table_table(thd, grant_table, tables[1].table, *Str,
2988
			    db_name, table_name,
unknown's avatar
unknown committed
2989
			    rights, column_priv, revoke_grant))
2990 2991
    {
      /* Should only happen if table is crashed */
unknown's avatar
unknown committed
2992
      result= TRUE;			       /* purecov: deadcode */
unknown's avatar
unknown committed
2993 2994 2995
    }
    else if (tables[2].table)
    {
unknown's avatar
VIEW  
unknown committed
2996
      if ((replace_column_table(grant_table, tables[2].table, *Str,
unknown's avatar
unknown committed
2997
				columns,
2998
				db_name, table_name,
unknown's avatar
unknown committed
2999 3000
				rights, revoke_grant)))
      {
unknown's avatar
unknown committed
3001
	result= TRUE;
unknown's avatar
unknown committed
3002 3003 3004 3005
      }
    }
  }
  grant_option=TRUE;
unknown's avatar
unknown committed
3006
  thd->mem_root= old_root;
3007
  pthread_mutex_unlock(&acl_cache->lock);
3008
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3009
  if (!result)
3010
    send_ok(thd);
3011
  /* Tables are automatically closed */
unknown's avatar
unknown committed
3012 3013 3014 3015
  DBUG_RETURN(result);
}


3016
/*
3017
  Store routine level grants in the privilege tables
3018 3019

  SYNOPSIS
3020
    mysql_routine_grant()
3021
    thd			Thread handle
3022 3023
    table_list		List of routines to give grant
    is_proc             true indicates routine list are procedures
3024 3025 3026 3027 3028 3029 3030 3031 3032
    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
*/

3033 3034 3035
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)
3036 3037
{
  List_iterator <LEX_USER> str_list (user_list);
3038
  LEX_USER *Str, *tmp_Str;
3039 3040
  TABLE_LIST tables[2];
  bool create_new_users=0, result=0;
3041
  char *db_name, *table_name;
3042
  DBUG_ENTER("mysql_routine_grant");
3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060

  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)
  {
3061
    if (sp_exist_routines(thd, table_list, is_proc, no_error)<0)
3062 3063 3064 3065 3066 3067
      DBUG_RETURN(TRUE);
  }

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

  bzero((char*) &tables,sizeof(tables));
3068 3069
  tables[0].alias=tables[0].table_name= (char*) "user";
  tables[1].alias=tables[1].table_name= (char*) "procs_priv";
3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085
  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;
3086
    if (!tables_ok(thd, tables))
3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099
      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);
3100
  pthread_mutex_lock(&acl_cache->lock);
3101 3102 3103 3104 3105
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;

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

3106
  while ((tmp_Str= str_list++))
3107 3108 3109
  {
    int error;
    GRANT_NAME *grant_name;
3110 3111 3112 3113 3114
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
    }  
3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125
    if (Str->host.length > HOSTNAME_LENGTH ||
	Str->user.length > USERNAME_LENGTH)
    {
      if (!no_error)
	my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
                   MYF(0));
      result= TRUE;
      continue;
    }
    /* Create user if needed */
    error=replace_user_table(thd, tables[0].table, *Str,
unknown's avatar
unknown committed
3126
			     0, revoke_grant, create_new_users,
unknown's avatar
unknown committed
3127 3128
                             test(thd->variables.sql_mode &
                                  MODE_NO_AUTO_CREATE_USER));
3129 3130 3131 3132 3133 3134 3135
    if (error)
    {
      result= TRUE;				// Remember error
      continue;					// Add next user
    }

    db_name= table_list->db;
3136
    table_name= table_list->table_name;
3137

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

3161 3162
    if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
			   db_name, table_name, is_proc, rights, revoke_grant))
3163 3164 3165 3166 3167 3168 3169
    {
      result= TRUE;
      continue;
    }
  }
  grant_option=TRUE;
  thd->mem_root= old_root;
3170
  pthread_mutex_unlock(&acl_cache->lock);
3171 3172 3173 3174 3175 3176 3177 3178
  rw_unlock(&LOCK_grant);
  if (!result && !no_error)
    send_ok(thd);
  /* Tables are automatically closed */
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
3179 3180
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
                 ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
3181 3182
{
  List_iterator <LEX_USER> str_list (list);
3183
  LEX_USER *Str, *tmp_Str;
unknown's avatar
unknown committed
3184
  char tmp_db[NAME_LEN+1];
unknown's avatar
unknown committed
3185
  bool create_new_users=0;
unknown's avatar
unknown committed
3186 3187 3188 3189
  TABLE_LIST tables[2];
  DBUG_ENTER("mysql_grant");
  if (!initialized)
  {
unknown's avatar
unknown committed
3190 3191
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: tested */
unknown's avatar
unknown committed
3192
    DBUG_RETURN(TRUE);				/* purecov: tested */
unknown's avatar
unknown committed
3193 3194
  }

unknown's avatar
unknown committed
3195 3196 3197
  if (lower_case_table_names && db)
  {
    strmov(tmp_db,db);
3198
    my_casedn_str(files_charset_info, tmp_db);
unknown's avatar
unknown committed
3199 3200
    db=tmp_db;
  }
unknown's avatar
unknown committed
3201 3202

  /* open the mysql.user and mysql.db tables */
3203
  bzero((char*) &tables,sizeof(tables));
3204 3205
  tables[0].alias=tables[0].table_name=(char*) "user";
  tables[1].alias=tables[1].table_name=(char*) "db";
unknown's avatar
VIEW  
unknown committed
3206
  tables[0].next_local= tables[0].next_global= tables+1;
unknown's avatar
unknown committed
3207 3208
  tables[0].lock_type=tables[1].lock_type=TL_WRITE;
  tables[0].db=tables[1].db=(char*) "mysql";
3209 3210 3211 3212 3213 3214

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
3215 3216
  if (thd->slave_thread && table_rules_on)
  {
unknown's avatar
unknown committed
3217 3218 3219
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
3220
    */
3221
    tables[0].updating= tables[1].updating= 1;
3222
    if (!tables_ok(thd, tables))
unknown's avatar
unknown committed
3223
      DBUG_RETURN(FALSE);
3224
  }
3225 3226
#endif

3227
  if (simple_open_n_lock_tables(thd,tables))
unknown's avatar
unknown committed
3228 3229
  {						// This should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
unknown's avatar
unknown committed
3230
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
unknown's avatar
unknown committed
3231 3232
  }

unknown's avatar
unknown committed
3233 3234
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
unknown's avatar
unknown committed
3235

3236
  /* go through users in user_list */
3237
  rw_wrlock(&LOCK_grant);
unknown's avatar
unknown committed
3238 3239 3240 3241
  VOID(pthread_mutex_lock(&acl_cache->lock));
  grant_version++;

  int result=0;
3242
  while ((tmp_Str = str_list++))
unknown's avatar
unknown committed
3243
  {
3244 3245 3246 3247 3248
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
    }  
unknown's avatar
unknown committed
3249 3250 3251
    if (Str->host.length > HOSTNAME_LENGTH ||
	Str->user.length > USERNAME_LENGTH)
    {
unknown's avatar
unknown committed
3252 3253
      my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
                 MYF(0));
unknown's avatar
unknown committed
3254 3255 3256
      result= -1;
      continue;
    }
unknown's avatar
unknown committed
3257 3258
    if (replace_user_table(thd, tables[0].table, *Str,
                           (!db ? rights : 0), revoke_grant, create_new_users,
unknown's avatar
unknown committed
3259 3260
                           test(thd->variables.sql_mode &
                                MODE_NO_AUTO_CREATE_USER)))
3261
      result= -1;
unknown's avatar
unknown committed
3262
    else if (db)
unknown's avatar
unknown committed
3263
    {
unknown's avatar
unknown committed
3264 3265 3266 3267 3268 3269 3270 3271 3272
      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
3273
	my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
unknown's avatar
unknown committed
3274
	result= -1;
unknown's avatar
unknown committed
3275
      }
unknown's avatar
unknown committed
3276
    }
unknown's avatar
unknown committed
3277 3278
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
3279
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3280 3281 3282
  close_thread_tables(thd);

  if (!result)
3283
    send_ok(thd);
unknown's avatar
unknown committed
3284 3285 3286
  DBUG_RETURN(result);
}

unknown's avatar
unknown committed
3287 3288

/* Free grant array if possible */
unknown's avatar
unknown committed
3289 3290 3291 3292 3293

void  grant_free(void)
{
  DBUG_ENTER("grant_free");
  grant_option = FALSE;
3294
  hash_free(&column_priv_hash);
3295
  hash_free(&proc_priv_hash);
unknown's avatar
unknown committed
3296
  hash_free(&func_priv_hash);
3297
  free_root(&memex,MYF(0));
unknown's avatar
unknown committed
3298 3299 3300 3301
  DBUG_VOID_RETURN;
}


3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312
/*
  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
3313

3314
my_bool grant_init()
unknown's avatar
unknown committed
3315
{
unknown's avatar
unknown committed
3316
  THD  *thd;
3317 3318 3319 3320 3321
  my_bool return_val;
  DBUG_ENTER("grant_init");

  if (!(thd= new THD))
    DBUG_RETURN(1);				/* purecov: deadcode */
3322
  thd->thread_stack= (char*) &thd;
3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348
  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
3349
  MEM_ROOT *memex_ptr;
3350
  my_bool return_val= 1;
3351
  TABLE *t_table, *c_table, *p_table;
unknown's avatar
SCRUM  
unknown committed
3352
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
3353 3354 3355
  MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
                                                           THR_MALLOC);
  DBUG_ENTER("grant_load");
unknown's avatar
unknown committed
3356 3357

  grant_option = FALSE;
3358
  (void) hash_init(&column_priv_hash,system_charset_info,
unknown's avatar
unknown committed
3359
		   0,0,0, (hash_get_key) get_grant_table,
unknown's avatar
unknown committed
3360
		   (hash_free_key) free_grant_table,0);
3361 3362 3363
  (void) hash_init(&proc_priv_hash,system_charset_info,
		   0,0,0, (hash_get_key) get_grant_table,
		   0,0);
3364 3365 3366
  (void) hash_init(&func_priv_hash,system_charset_info,
		   0,0,0, (hash_get_key) get_grant_table,
		   0,0);
3367
  init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
unknown's avatar
unknown committed
3368 3369

  t_table = tables[0].table; c_table = tables[1].table;
3370
  p_table= tables[2].table;
unknown's avatar
unknown committed
3371
  t_table->file->ha_index_init(0);
3372 3373
  p_table->file->ha_index_init(0);
  if (!t_table->file->index_first(t_table->record[0]))
unknown's avatar
unknown committed
3374
  {
3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385
    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
3386

3387 3388
      if (check_no_resolve)
      {
3389
	if (hostname_requires_resolving(mem_check->host.hostname))
3390 3391 3392
	{
          sql_print_warning("'tables_priv' entry '%s %s@%s' "
                            "ignored in --skip-name-resolve mode.",
3393 3394 3395 3396
                            mem_check->tname,
                            mem_check->user ? mem_check->user : "",
                            mem_check->host.hostname ?
                            mem_check->host.hostname : "");
3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412
	  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
3413
  {
3414 3415 3416
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
unknown's avatar
unknown committed
3417
    {
3418
      GRANT_NAME *mem_check;
3419
      HASH *hash;
3420 3421 3422 3423 3424 3425
      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
3426

3427
      if (check_no_resolve)
unknown's avatar
SCRUM  
unknown committed
3428
      {
unknown's avatar
unknown committed
3429
	if (hostname_requires_resolving(mem_check->host.hostname))
3430 3431 3432 3433
	{
          sql_print_warning("'procs_priv' entry '%s %s@%s' "
                            "ignored in --skip-name-resolve mode.",
                            mem_check->tname, mem_check->user,
3434 3435
                            mem_check->host.hostname ?
                            mem_check->host.hostname : "");
3436 3437
	  continue;
	}
unknown's avatar
SCRUM  
unknown committed
3438
      }
3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454
      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
3455

3456 3457 3458
      mem_check->privs= fix_rights_for_procedure(mem_check->privs);
      if (! mem_check->ok())
	delete mem_check;
3459
      else if (my_hash_insert(hash, (byte*) mem_check))
3460 3461 3462 3463 3464
      {
	delete mem_check;
	grant_option= FALSE;
	goto end_unlock;
      }
unknown's avatar
SCRUM  
unknown committed
3465
    }
3466
    while (!p_table->file->index_next(p_table->record[0]));
unknown's avatar
unknown committed
3467
  }
3468
  grant_option= TRUE;
3469 3470 3471
  return_val=0;					// Return ok

end_unlock:
unknown's avatar
unknown committed
3472
  t_table->file->ha_index_end();
3473
  p_table->file->ha_index_end();
3474
  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
3475
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
3476 3477 3478
}


3479
/*
3480
  Reload information about table and column level privileges if possible.
3481 3482 3483

  SYNOPSIS
    grant_reload()
3484
      thd  Current thread
3485 3486

  NOTES
3487 3488 3489 3490 3491 3492 3493 3494
    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
3495
*/
unknown's avatar
unknown committed
3496

3497
my_bool grant_reload(THD *thd)
unknown's avatar
unknown committed
3498
{
3499
  TABLE_LIST tables[3];
3500
  HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
3501
  bool old_grant_option;
unknown's avatar
unknown committed
3502
  MEM_ROOT old_mem;
3503
  my_bool return_val= 1;
unknown's avatar
unknown committed
3504 3505
  DBUG_ENTER("grant_reload");

3506 3507 3508 3509 3510
  /* Don't do anything if running with --skip-grant-tables */
  if (!initialized)
    DBUG_RETURN(0);

  bzero((char*) tables, sizeof(tables));
3511 3512 3513 3514 3515 3516 3517
  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;
3518 3519 3520 3521 3522 3523 3524 3525

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

3526
  rw_wrlock(&LOCK_grant);
unknown's avatar
unknown committed
3527
  grant_version++;
3528
  old_column_priv_hash= column_priv_hash;
3529
  old_proc_priv_hash= proc_priv_hash;
3530
  old_func_priv_hash= func_priv_hash;
unknown's avatar
unknown committed
3531
  old_grant_option= grant_option;
unknown's avatar
unknown committed
3532
  old_mem= memex;
unknown's avatar
unknown committed
3533

3534
  if ((return_val= grant_load(tables)))
unknown's avatar
unknown committed
3535
  {						// Error. Revert to old hash
3536
    DBUG_PRINT("error",("Reverting to old privileges"));
unknown's avatar
unknown committed
3537
    grant_free();				/* purecov: deadcode */
3538
    column_priv_hash= old_column_priv_hash;	/* purecov: deadcode */
3539
    proc_priv_hash= old_proc_priv_hash;
3540
    func_priv_hash= old_func_priv_hash;
unknown's avatar
unknown committed
3541
    grant_option= old_grant_option;		/* purecov: deadcode */
unknown's avatar
unknown committed
3542
    memex= old_mem;				/* purecov: deadcode */
unknown's avatar
unknown committed
3543 3544 3545
  }
  else
  {
3546
    hash_free(&old_column_priv_hash);
3547
    hash_free(&old_proc_priv_hash);
3548
    hash_free(&old_func_priv_hash);
3549
    free_root(&old_mem,MYF(0));
unknown's avatar
unknown committed
3550
  }
3551
  rw_unlock(&LOCK_grant);
3552 3553 3554
end:
  close_thread_tables(thd);
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
3555 3556 3557 3558
}


/****************************************************************************
3559
  Check table level grants
3560

3561
  SYNOPSIS
3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574
   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
3575
     1  Error: User did not have the requested privileges
3576 3577 3578 3579 3580 3581 3582

   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
3583 3584
****************************************************************************/

unknown's avatar
unknown committed
3585
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
unknown's avatar
unknown committed
3586
		 uint show_table, uint number, bool no_errors)
unknown's avatar
unknown committed
3587
{
3588
  TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table();
3589
  Security_context *sctx= thd->security_ctx;
3590
  uint i;
3591
  ulong orig_want_access= want_access;
3592 3593
  DBUG_ENTER("check_grant");
  DBUG_ASSERT(number > 0);
unknown's avatar
unknown committed
3594

3595
  /*
unknown's avatar
unknown committed
3596 3597 3598 3599 3600 3601 3602 3603
    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.
3604 3605
  */
  for (i= 0, table= tables;
unknown's avatar
unknown committed
3606
       table != first_not_own_table && i < number;
3607 3608 3609 3610 3611 3612
       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);
  }

3613
  rw_rdlock(&LOCK_grant);
3614 3615 3616
  for (table= tables;
       table && number-- && table != first_not_own_table;
       table= table->next_global)
unknown's avatar
unknown committed
3617
  {
3618
    GRANT_TABLE *grant_table;
3619 3620 3621 3622 3623 3624 3625 3626
    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

3627
    if (!(~table->grant.privilege & want_access) || 
3628
        table->derived || table->schema_table)
unknown's avatar
unknown committed
3629
    {
unknown's avatar
VIEW  
unknown committed
3630 3631 3632 3633
      /*
        It is subquery in the FROM clause. VIEW set table->derived after
        table opening, but this function always called before table opening.
      */
3634 3635 3636 3637 3638 3639 3640 3641 3642 3643
      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
3644
    }
3645 3646 3647
    if (!(grant_table= table_hash_search(sctx->host, sctx->ip,
                                         table->db, sctx->priv_user,
                                         table->table_name,0)))
unknown's avatar
unknown committed
3648 3649 3650 3651
    {
      want_access &= ~table->grant.privilege;
      goto err;					// No grants
    }
unknown's avatar
unknown committed
3652 3653
    if (show_table)
      continue;					// We have some priv on this
unknown's avatar
unknown committed
3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669

    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
    }
  }
3670
  rw_unlock(&LOCK_grant);
3671
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3672

3673
err:
3674
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3675
  if (!no_errors)				// Not a silent skip of table
unknown's avatar
unknown committed
3676
  {
3677 3678
    char command[128];
    get_privilege_desc(command, sizeof(command), want_access);
3679 3680
    my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
             command,
3681 3682
             sctx->priv_user,
             sctx->host_or_ip,
3683
             table ? table->table_name : "unknown");
unknown's avatar
unknown committed
3684
  }
3685
  DBUG_RETURN(1);
unknown's avatar
unknown committed
3686 3687 3688
}


3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706
/*
  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
3707
bool check_grant_column(THD *thd, GRANT_INFO *grant,
3708
			const char *db_name, const char *table_name,
3709
			const char *name, uint length,  Security_context *sctx)
unknown's avatar
unknown committed
3710 3711 3712
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
unknown's avatar
VIEW  
unknown committed
3713
  ulong want_access= grant->want_privilege & ~grant->privilege;
unknown's avatar
unknown committed
3714 3715 3716
  DBUG_ENTER("check_grant_column");
  DBUG_PRINT("enter", ("table: %s  want_access: %u", table_name, want_access));

unknown's avatar
unknown committed
3717
  if (!want_access)
unknown's avatar
unknown committed
3718
    DBUG_RETURN(0);				// Already checked
unknown's avatar
unknown committed
3719

3720
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
3721

3722
  /* reload table if someone has modified any grants */
unknown's avatar
unknown committed
3723

unknown's avatar
VIEW  
unknown committed
3724
  if (grant->version != grant_version)
unknown's avatar
unknown committed
3725
  {
unknown's avatar
VIEW  
unknown committed
3726
    grant->grant_table=
3727 3728
      table_hash_search(sctx->host, sctx->ip, db_name,
			sctx->priv_user,
unknown's avatar
unknown committed
3729
			table_name, 0);         /* purecov: inspected */
unknown's avatar
VIEW  
unknown committed
3730
    grant->version= grant_version;		/* purecov: inspected */
unknown's avatar
unknown committed
3731
  }
unknown's avatar
VIEW  
unknown committed
3732
  if (!(grant_table= grant->grant_table))
unknown's avatar
unknown committed
3733 3734 3735 3736 3737
    goto err;					/* purecov: deadcode */

  grant_column=column_hash_search(grant_table, name, length);
  if (grant_column && !(~grant_column->rights & want_access))
  {
3738
    rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3739
    DBUG_RETURN(0);
unknown's avatar
unknown committed
3740 3741
  }

3742
err:
3743
  rw_unlock(&LOCK_grant);
3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 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
  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
3788
  {
3789 3790 3791 3792
    /* View or derived information schema table. */
    grant= &(table_ref->grant);
    db_name= table_ref->view_db.str;
    table_name= table_ref->view_name.str;
unknown's avatar
unknown committed
3793
  }
3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808
  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
3809 3810 3811
}


unknown's avatar
VIEW  
unknown committed
3812
bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant,
3813
                             const char* db_name, const char *table_name,
unknown's avatar
VIEW  
unknown committed
3814
                             Field_iterator *fields)
unknown's avatar
unknown committed
3815
{
3816
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
3817 3818 3819
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;

unknown's avatar
VIEW  
unknown committed
3820
  want_access &= ~grant->privilege;
unknown's avatar
unknown committed
3821
  if (!want_access)
unknown's avatar
unknown committed
3822
    return 0;				// Already checked
unknown's avatar
unknown committed
3823 3824
  if (!grant_option)
    goto err2;
unknown's avatar
unknown committed
3825

3826
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
3827

3828
  /* reload table if someone has modified any grants */
unknown's avatar
unknown committed
3829

unknown's avatar
VIEW  
unknown committed
3830
  if (grant->version != grant_version)
unknown's avatar
unknown committed
3831
  {
unknown's avatar
VIEW  
unknown committed
3832
    grant->grant_table=
3833 3834
      table_hash_search(sctx->host, sctx->ip, db_name,
			sctx->priv_user,
unknown's avatar
VIEW  
unknown committed
3835 3836
			table_name, 0);	/* purecov: inspected */
    grant->version= grant_version;		/* purecov: inspected */
unknown's avatar
unknown committed
3837
  }
3838
  /* The following should always be true */
unknown's avatar
VIEW  
unknown committed
3839
  if (!(grant_table= grant->grant_table))
unknown's avatar
unknown committed
3840 3841
    goto err;					/* purecov: inspected */

3842
  for (; !fields->end_of_fields(); fields->next())
unknown's avatar
unknown committed
3843
  {
unknown's avatar
VIEW  
unknown committed
3844 3845 3846
    const char *field_name= fields->name();
    grant_column= column_hash_search(grant_table, field_name,
				    (uint) strlen(field_name));
unknown's avatar
unknown committed
3847 3848 3849
    if (!grant_column || (~grant_column->rights & want_access))
      goto err;
  }
3850
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3851 3852
  return 0;

unknown's avatar
unknown committed
3853
err:
3854
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3855
err2:
3856 3857
  char command[128];
  get_privilege_desc(command, sizeof(command), want_access);
3858 3859
  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
           command,
3860 3861
           sctx->priv_user,
           sctx->host_or_ip,
3862 3863
           fields->name(),
           table_name);
unknown's avatar
unknown committed
3864 3865 3866 3867
  return 1;
}


3868
/*
unknown's avatar
unknown committed
3869
  Check if a user has the right to access a database
3870
  Access is accepted if he has a grant for any table/routine in the database
unknown's avatar
unknown committed
3871
  Return 1 if access is denied
3872
*/
unknown's avatar
unknown committed
3873 3874 3875

bool check_grant_db(THD *thd,const char *db)
{
3876
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
3877 3878
  char helping [NAME_LEN+USERNAME_LENGTH+2];
  uint len;
unknown's avatar
unknown committed
3879
  bool error= 1;
unknown's avatar
unknown committed
3880

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

3884
  for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
unknown's avatar
unknown committed
3885
  {
3886 3887
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  idx);
unknown's avatar
unknown committed
3888 3889
    if (len < grant_table->key_length &&
	!memcmp(grant_table->hash_key,helping,len) &&
3890
        compare_hostname(&grant_table->host, sctx->host, sctx->ip))
unknown's avatar
unknown committed
3891 3892 3893 3894 3895
    {
      error=0;					// Found match
      break;
    }
  }
3896
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3897 3898 3899
  return error;
}

3900 3901

/****************************************************************************
3902
  Check routine level grants
3903 3904

  SYNPOSIS
3905
   bool check_grant_routine()
3906 3907
   thd		Thread handler
   want_access  Bits of privileges user needs to have
3908 3909
   procs	List of routines to check. The user should have 'want_access'
   is_proc	True if the list is all procedures, else functions
3910 3911 3912 3913 3914 3915 3916 3917
   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
****************************************************************************/

3918
bool check_grant_routine(THD *thd, ulong want_access,
3919
			 TABLE_LIST *procs, bool is_proc, bool no_errors)
3920 3921
{
  TABLE_LIST *table;
3922
  Security_context *sctx= thd->security_ctx;
3923 3924
  char *user= sctx->priv_user;
  char *host= sctx->priv_host;
3925
  DBUG_ENTER("check_grant_routine");
3926

3927
  want_access&= ~sctx->master_access;
3928 3929 3930 3931 3932 3933 3934
  if (!want_access)
    DBUG_RETURN(0);                             // ok

  rw_rdlock(&LOCK_grant);
  for (table= procs; table; table= table->next_global)
  {
    GRANT_NAME *grant_proc;
3935
    if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
3936
					 table->table_name, is_proc, 0)))
3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953
      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)
3954
      strxmov(buff, table->db, ".", table->table_name, NullS);
3955 3956 3957
    if (want_access & EXECUTE_ACL)
      command= "execute";
    else if (want_access & ALTER_PROC_ACL)
3958
      command= "alter routine";
3959 3960 3961 3962 3963 3964 3965 3966 3967
    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);
}


3968 3969
/*
  Check if routine has any of the 
3970
  routine level grants
3971 3972 3973 3974 3975 3976 3977 3978 3979
  
  SYNPOSIS
   bool    check_routine_level_acl()
   thd	        Thread handler
   db           Database name
   name         Routine name

  RETURN
   0            Ok 
3980
   1            error
3981 3982
*/

unknown's avatar
unknown committed
3983 3984
bool check_routine_level_acl(THD *thd, const char *db, const char *name, 
                             bool is_proc)
3985 3986 3987 3988 3989
{
  bool no_routine_acl= 1;
  if (grant_option)
  {
    GRANT_NAME *grant_proc;
3990
    Security_context *sctx= thd->security_ctx;
3991
    rw_rdlock(&LOCK_grant);
3992 3993 3994
    if ((grant_proc= routine_hash_search(sctx->priv_host,
                                         sctx->ip, db,
                                         sctx->priv_user,
3995
                                         name, is_proc, 0)))
3996 3997 3998 3999 4000 4001 4002
      no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
    rw_unlock(&LOCK_grant);
  }
  return no_routine_acl;
}


unknown's avatar
unknown committed
4003
/*****************************************************************************
unknown's avatar
unknown committed
4004
  Functions to retrieve the grant for a table/column  (for SHOW functions)
unknown's avatar
unknown committed
4005 4006
*****************************************************************************/

unknown's avatar
unknown committed
4007
ulong get_table_grant(THD *thd, TABLE_LIST *table)
unknown's avatar
unknown committed
4008
{
unknown's avatar
unknown committed
4009
  ulong privilege;
4010
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
4011 4012 4013
  const char *db = table->db ? table->db : thd->db;
  GRANT_TABLE *grant_table;

4014
  rw_rdlock(&LOCK_grant);
4015 4016 4017
#ifdef EMBEDDED_LIBRARY
  grant_table= NULL;
#else
4018
  grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
4019
				 table->table_name, 0);
4020
#endif
unknown's avatar
unknown committed
4021 4022 4023 4024
  table->grant.grant_table=grant_table; // Remember for column test
  table->grant.version=grant_version;
  if (grant_table)
    table->grant.privilege|= grant_table->privs;
4025
  privilege= table->grant.privilege;
4026
  rw_unlock(&LOCK_grant);
4027
  return privilege;
unknown's avatar
unknown committed
4028 4029 4030
}


unknown's avatar
unknown committed
4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048
/*
  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
4049 4050 4051
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
4052 4053 4054
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
4055
  ulong priv;
unknown's avatar
unknown committed
4056

4057
  rw_rdlock(&LOCK_grant);
4058
  /* reload table if someone has modified any grants */
unknown's avatar
VIEW  
unknown committed
4059
  if (grant->version != grant_version)
unknown's avatar
unknown committed
4060
  {
4061
    Security_context *sctx= thd->security_ctx;
unknown's avatar
VIEW  
unknown committed
4062
    grant->grant_table=
4063 4064
      table_hash_search(sctx->host, sctx->ip,
                        db_name, sctx->priv_user,
unknown's avatar
VIEW  
unknown committed
4065 4066
			table_name, 0);	        /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
unknown's avatar
unknown committed
4067 4068
  }

unknown's avatar
VIEW  
unknown committed
4069 4070
  if (!(grant_table= grant->grant_table))
    priv= grant->privilege;
unknown's avatar
unknown committed
4071 4072
  else
  {
unknown's avatar
VIEW  
unknown committed
4073 4074
    grant_column= column_hash_search(grant_table, field_name,
                                     (uint) strlen(field_name));
unknown's avatar
unknown committed
4075
    if (!grant_column)
4076
      priv= (grant->privilege | grant_table->privs);
unknown's avatar
unknown committed
4077
    else
4078
      priv= (grant->privilege | grant_table->privs | grant_column->rights);
unknown's avatar
unknown committed
4079
  }
4080
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4081 4082 4083
  return priv;
}

unknown's avatar
VIEW  
unknown committed
4084

4085
/* Help function for mysql_show_grants */
unknown's avatar
unknown committed
4086

4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098
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
4099 4100

static const char *command_array[]=
unknown's avatar
unknown committed
4101
{
unknown's avatar
VIEW  
unknown committed
4102 4103 4104 4105
  "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",
4106
  "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
4107
  "CREATE USER"
unknown's avatar
unknown committed
4108
};
4109

unknown's avatar
unknown committed
4110 4111
static uint command_lengths[]=
{
4112 4113
  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
4114 4115
};

unknown's avatar
unknown committed
4116

4117 4118 4119 4120 4121
static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
                               const char *type, int typelen,
                               char *buff, int buffsize);


4122 4123 4124 4125 4126 4127 4128
/*
  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
4129
bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
unknown's avatar
unknown committed
4130
{
unknown's avatar
unknown committed
4131 4132
  ulong want_access;
  uint counter,index;
unknown's avatar
unknown committed
4133
  int  error = 0;
unknown's avatar
unknown committed
4134 4135
  ACL_USER *acl_user;
  ACL_DB *acl_db;
unknown's avatar
unknown committed
4136
  char buff[1024];
4137
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
4138
  DBUG_ENTER("mysql_show_grants");
unknown's avatar
unknown committed
4139 4140 4141 4142

  LINT_INIT(acl_user);
  if (!initialized)
  {
unknown's avatar
unknown committed
4143
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
unknown's avatar
unknown committed
4144
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4145
  }
unknown's avatar
unknown committed
4146

unknown's avatar
unknown committed
4147 4148 4149
  if (lex_user->host.length > HOSTNAME_LENGTH ||
      lex_user->user.length > USERNAME_LENGTH)
  {
unknown's avatar
unknown committed
4150 4151
    my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
               MYF(0));
unknown's avatar
unknown committed
4152
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4153 4154
  }

4155 4156 4157 4158 4159
  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
4160
  {
4161 4162 4163
    VOID(pthread_mutex_unlock(&acl_cache->lock));
    rw_unlock(&LOCK_grant);

unknown's avatar
unknown committed
4164 4165
    my_error(ER_NONEXISTING_GRANT, MYF(0),
             lex_user->user.str, lex_user->host.str);
unknown's avatar
unknown committed
4166
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4167 4168
  }

unknown's avatar
unknown committed
4169
  Item_string *field=new Item_string("",0,&my_charset_latin1);
unknown's avatar
unknown committed
4170 4171 4172 4173 4174 4175
  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);
4176 4177
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
4178 4179 4180
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock));
    rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4181

4182 4183
    DBUG_RETURN(TRUE);
  }
unknown's avatar
unknown committed
4184 4185 4186

  /* Add first global access grants */
  {
4187
    String global(buff,sizeof(buff),system_charset_info);
unknown's avatar
unknown committed
4188
    global.length(0);
4189
    global.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
4190

unknown's avatar
unknown committed
4191
    want_access= acl_user->access;
unknown's avatar
unknown committed
4192
    if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
4193
      global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
4194
    else if (!(want_access & ~GRANT_ACL))
4195
      global.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
4196
    else
unknown's avatar
unknown committed
4197 4198
    {
      bool found=0;
unknown's avatar
unknown committed
4199
      ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
4200 4201
      for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
      {
unknown's avatar
unknown committed
4202
	if (test_access & j)
unknown's avatar
unknown committed
4203 4204
	{
	  if (found)
4205
	    global.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
4206 4207 4208 4209 4210
	  found=1;
	  global.append(command_array[counter],command_lengths[counter]);
	}
      }
    }
4211
    global.append (STRING_WITH_LEN(" ON *.* TO '"));
4212 4213
    global.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
4214
    global.append (STRING_WITH_LEN("'@'"));
4215 4216
    global.append(lex_user->host.str,lex_user->host.length,
		  system_charset_info);
unknown's avatar
unknown committed
4217
    global.append ('\'');
4218
    if (acl_user->salt_len)
unknown's avatar
unknown committed
4219
    {
4220 4221 4222 4223 4224
      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);
4225
      global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
4226
      global.append(passwd_buff);
unknown's avatar
unknown committed
4227 4228
      global.append('\'');
    }
4229 4230
    /* "show grants" SSL related stuff */
    if (acl_user->ssl_type == SSL_TYPE_ANY)
4231
      global.append(STRING_WITH_LEN(" REQUIRE SSL"));
4232
    else if (acl_user->ssl_type == SSL_TYPE_X509)
4233
      global.append(STRING_WITH_LEN(" REQUIRE X509"));
4234
    else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
4235
    {
4236
      int ssl_options = 0;
4237
      global.append(STRING_WITH_LEN(" REQUIRE "));
4238 4239
      if (acl_user->x509_issuer)
      {
4240
	ssl_options++;
4241
	global.append(STRING_WITH_LEN("ISSUER \'"));
4242
	global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
4243
	global.append('\'');
4244
      }
4245 4246
      if (acl_user->x509_subject)
      {
4247 4248
	if (ssl_options++)
	  global.append(' ');
4249
	global.append(STRING_WITH_LEN("SUBJECT \'"));
4250 4251
	global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
                      system_charset_info);
4252
	global.append('\'');
unknown's avatar
unknown committed
4253
      }
4254 4255
      if (acl_user->ssl_cipher)
      {
4256 4257
	if (ssl_options++)
	  global.append(' ');
4258
	global.append(STRING_WITH_LEN("CIPHER '"));
4259 4260
	global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
                      system_charset_info);
4261
	global.append('\'');
4262 4263
      }
    }
unknown's avatar
unknown committed
4264
    if ((want_access & GRANT_ACL) ||
4265 4266 4267 4268
	(acl_user->user_resource.questions ||
         acl_user->user_resource.updates ||
         acl_user->user_resource.conn_per_hour ||
         acl_user->user_resource.user_conn))
4269
    {
4270
      global.append(STRING_WITH_LEN(" WITH"));
unknown's avatar
unknown committed
4271
      if (want_access & GRANT_ACL)
4272
	global.append(STRING_WITH_LEN(" GRANT OPTION"));
4273 4274 4275 4276
      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");
4277
      add_user_option(&global, acl_user->user_resource.conn_per_hour,
4278
		      "MAX_CONNECTIONS_PER_HOUR");
4279 4280
      add_user_option(&global, acl_user->user_resource.user_conn,
		      "MAX_USER_CONNECTIONS");
unknown's avatar
unknown committed
4281
    }
4282
    protocol->prepare_for_resend();
4283
    protocol->store(global.ptr(),global.length(),global.charset());
4284
    if (protocol->write())
unknown's avatar
unknown committed
4285
    {
unknown's avatar
unknown committed
4286
      error= -1;
4287
      goto end;
unknown's avatar
unknown committed
4288 4289 4290 4291 4292 4293
    }
  }

  /* Add database access */
  for (counter=0 ; counter < acl_dbs.elements ; counter++)
  {
unknown's avatar
unknown committed
4294
    const char *user, *host;
unknown's avatar
unknown committed
4295 4296 4297

    acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
    if (!(user=acl_db->user))
unknown's avatar
unknown committed
4298
      user= "";
unknown's avatar
unknown committed
4299
    if (!(host=acl_db->host.hostname))
unknown's avatar
unknown committed
4300
      host= "";
unknown's avatar
unknown committed
4301 4302

    if (!strcmp(lex_user->user.str,user) &&
4303
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
unknown's avatar
unknown committed
4304 4305
    {
      want_access=acl_db->access;
unknown's avatar
unknown committed
4306
      if (want_access)
unknown's avatar
unknown committed
4307
      {
4308
	String db(buff,sizeof(buff),system_charset_info);
unknown's avatar
unknown committed
4309
	db.length(0);
4310
	db.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
4311 4312

	if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
4313
	  db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
4314
	else if (!(want_access & ~GRANT_ACL))
4315
	  db.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
4316 4317 4318
	else
	{
	  int found=0, cnt;
unknown's avatar
unknown committed
4319
	  ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
4320 4321 4322 4323 4324
	  for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
	  {
	    if (test_access & j)
	    {
	      if (found)
4325
		db.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
4326 4327 4328 4329 4330
	      found = 1;
	      db.append(command_array[cnt],command_lengths[cnt]);
	    }
	  }
	}
4331
	db.append (STRING_WITH_LEN(" ON "));
4332
	append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
4333
	db.append (STRING_WITH_LEN(".* TO '"));
4334 4335
	db.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
4336
	db.append (STRING_WITH_LEN("'@'"));
4337 4338
	db.append(lex_user->host.str, lex_user->host.length,
                  system_charset_info);
unknown's avatar
unknown committed
4339
	db.append ('\'');
unknown's avatar
unknown committed
4340
	if (want_access & GRANT_ACL)
4341
	  db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
4342
	protocol->prepare_for_resend();
4343
	protocol->store(db.ptr(),db.length(),db.charset());
4344
	if (protocol->write())
unknown's avatar
unknown committed
4345
	{
unknown's avatar
unknown committed
4346
	  error= -1;
unknown's avatar
unknown committed
4347 4348 4349 4350 4351 4352
	  goto end;
	}
      }
    }
  }

4353
  /* Add table & column access */
4354
  for (index=0 ; index < column_priv_hash.records ; index++)
unknown's avatar
unknown committed
4355
  {
4356
    const char *user, *host;
4357 4358
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
unknown's avatar
unknown committed
4359 4360

    if (!(user=grant_table->user))
4361
      user= "";
4362 4363
    if (!(host= grant_table->host.hostname))
      host= "";
unknown's avatar
unknown committed
4364 4365

    if (!strcmp(lex_user->user.str,user) &&
4366
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
unknown's avatar
unknown committed
4367
    {
4368 4369
      ulong table_access= grant_table->privs;
      if ((table_access | grant_table->cols) != 0)
unknown's avatar
unknown committed
4370
      {
4371
	String global(buff, sizeof(buff), system_charset_info);
unknown's avatar
unknown committed
4372 4373
	ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;

unknown's avatar
unknown committed
4374
	global.length(0);
4375
	global.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
4376

4377
	if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
4378
	  global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
4379
	else if (!test_access)
4380
	  global.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
4381
	else
unknown's avatar
unknown committed
4382
	{
4383
          /* Add specific column access */
4384
	  int found= 0;
unknown's avatar
unknown committed
4385
	  ulong j;
unknown's avatar
unknown committed
4386

4387
	  for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
unknown's avatar
unknown committed
4388
	  {
unknown's avatar
unknown committed
4389
	    if (test_access & j)
unknown's avatar
unknown committed
4390 4391
	    {
	      if (found)
4392
		global.append(STRING_WITH_LEN(", "));
4393
	      found= 1;
unknown's avatar
unknown committed
4394 4395
	      global.append(command_array[counter],command_lengths[counter]);

unknown's avatar
unknown committed
4396
	      if (grant_table->cols)
unknown's avatar
unknown committed
4397
	      {
4398
		uint found_col= 0;
unknown's avatar
unknown committed
4399 4400 4401 4402 4403 4404
		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
4405
		  if (grant_column->rights & j)
unknown's avatar
unknown committed
4406
		  {
unknown's avatar
unknown committed
4407
		    if (!found_col)
unknown's avatar
unknown committed
4408
		    {
4409 4410 4411 4412 4413 4414 4415
		      found_col= 1;
		      /*
			If we have a duplicated table level privilege, we
			must write the access privilege name again.
		      */
		      if (table_access & j)
		      {
4416
			global.append(STRING_WITH_LEN(", "));
4417 4418 4419
			global.append(command_array[counter],
				      command_lengths[counter]);
		      }
4420
		      global.append(STRING_WITH_LEN(" ("));
unknown's avatar
unknown committed
4421 4422
		    }
		    else
4423
		      global.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
4424
		    global.append(grant_column->column,
4425 4426
				  grant_column->key_length,
				  system_charset_info);
unknown's avatar
unknown committed
4427 4428 4429 4430 4431 4432 4433 4434
		  }
		}
		if (found_col)
		  global.append(')');
	      }
	    }
	  }
	}
4435
	global.append(STRING_WITH_LEN(" ON "));
4436 4437 4438 4439 4440
	append_identifier(thd, &global, grant_table->db,
			  strlen(grant_table->db));
	global.append('.');
	append_identifier(thd, &global, grant_table->tname,
			  strlen(grant_table->tname));
4441
	global.append(STRING_WITH_LEN(" TO '"));
4442 4443
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
4444
	global.append(STRING_WITH_LEN("'@'"));
4445 4446
	global.append(lex_user->host.str,lex_user->host.length,
		      system_charset_info);
unknown's avatar
unknown committed
4447
	global.append('\'');
4448
	if (table_access & GRANT_ACL)
4449
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
4450
	protocol->prepare_for_resend();
4451
	protocol->store(global.ptr(),global.length(),global.charset());
4452
	if (protocol->write())
unknown's avatar
unknown committed
4453
	{
unknown's avatar
unknown committed
4454
	  error= -1;
4455
	  break;
unknown's avatar
unknown committed
4456 4457 4458 4459
	}
      }
    }
  }
4460

4461
  if (show_routine_grants(thd, lex_user, &proc_priv_hash, 
4462
                          STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
4463 4464 4465 4466 4467 4468
  {
    error= -1;
    goto end;
  }

  if (show_routine_grants(thd, lex_user, &func_priv_hash,
4469
                          STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491
  {
    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++)
4492
  {
4493
    const char *user, *host;
4494
    GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, index);
4495 4496 4497

    if (!(user=grant_proc->user))
      user= "";
4498 4499
    if (!(host= grant_proc->host.hostname))
      host= "";
4500 4501

    if (!strcmp(lex_user->user.str,user) &&
4502
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
4503 4504 4505 4506
    {
      ulong proc_access= grant_proc->privs;
      if (proc_access != 0)
      {
4507
	String global(buff, buffsize, system_charset_info);
4508 4509 4510
	ulong test_access= proc_access & ~GRANT_ACL;

	global.length(0);
4511
	global.append(STRING_WITH_LEN("GRANT "));
4512 4513

	if (!test_access)
4514
 	  global.append(STRING_WITH_LEN("USAGE"));
4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525
	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)
4526
		global.append(STRING_WITH_LEN(", "));
4527 4528 4529 4530 4531
	      found= 1;
	      global.append(command_array[counter],command_lengths[counter]);
	    }
	  }
	}
4532
	global.append(STRING_WITH_LEN(" ON "));
4533 4534
        global.append(type,typelen);
        global.append(' ');
4535 4536 4537 4538 4539
	append_identifier(thd, &global, grant_proc->db,
			  strlen(grant_proc->db));
	global.append('.');
	append_identifier(thd, &global, grant_proc->tname,
			  strlen(grant_proc->tname));
4540
	global.append(STRING_WITH_LEN(" TO '"));
4541 4542
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
4543
	global.append(STRING_WITH_LEN("'@'"));
4544 4545 4546 4547
	global.append(lex_user->host.str,lex_user->host.length,
		      system_charset_info);
	global.append('\'');
	if (proc_access & GRANT_ACL)
4548
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
4549 4550 4551 4552 4553 4554 4555 4556 4557 4558
	protocol->prepare_for_resend();
	protocol->store(global.ptr(),global.length(),global.charset());
	if (protocol->write())
	{
	  error= -1;
	  break;
	}
      }
    }
  }
4559
  return error;
unknown's avatar
unknown committed
4560 4561
}

unknown's avatar
unknown committed
4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589
/*
  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;
}


4590
void get_mqh(const char *user, const char *host, USER_CONN *uc)
unknown's avatar
unknown committed
4591 4592
{
  ACL_USER *acl_user;
4593 4594 4595

  pthread_mutex_lock(&acl_cache->lock);

4596
  if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
4597 4598 4599
    uc->user_resources= acl_user->user_resource;
  else
    bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
4600 4601

  pthread_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
4602 4603
}

4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624
/*
  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.
*/

4625
#define GRANT_TABLES 5
4626 4627 4628 4629 4630 4631
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("open_grant_tables");

  if (!initialized)
  {
unknown's avatar
unknown committed
4632
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
4633 4634 4635
    DBUG_RETURN(-1);
  }

4636
  bzero((char*) tables, GRANT_TABLES*sizeof(*tables));
4637 4638 4639 4640 4641
  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
4642 4643 4644
  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;
4645
  (tables+3)->next_local= (tables+3)->next_global= tables+4;
4646
  tables->lock_type= (tables+1)->lock_type=
4647 4648 4649 4650
    (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";
4651 4652 4653 4654 4655 4656

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
4657 4658
  if (thd->slave_thread && table_rules_on)
  {
unknown's avatar
unknown committed
4659 4660 4661
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
4662
    */
4663 4664
    tables[0].updating=tables[1].updating=tables[2].updating=
      tables[3].updating=tables[4].updating=1;
4665
    if (!tables_ok(thd, tables))
4666
      DBUG_RETURN(1);
4667 4668
    tables[0].updating=tables[1].updating=tables[2].updating=
      tables[3].updating=tables[4].updating=0;;
4669
  }
4670 4671
#endif

4672
  if (simple_open_n_lock_tables(thd, tables))
4673 4674 4675 4676 4677 4678 4679 4680 4681
  {						// 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
4682
			 uint *acl_acl_userdx)
4683 4684 4685 4686
{
  ACL_USER *acl_user= 0;
  uint counter;

4687 4688
  safe_mutex_assert_owner(&acl_cache->lock);

4689 4690 4691 4692 4693
  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
4694
      user= "";
4695
    if (!(host=acl_user->host.hostname))
4696
      host= "";
4697 4698 4699 4700 4701 4702 4703
    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
4704
  *acl_acl_userdx= counter;
unknown's avatar
unknown committed
4705
  return acl_user;
4706
}
unknown's avatar
unknown committed
4707

4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729
/*
  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)
unknown's avatar
unknown committed
4730
{
4731 4732
  int error;
  DBUG_ENTER("modify_grant_table");
4733

4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750
  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));
  }
unknown's avatar
unknown committed
4751

4752 4753
  DBUG_RETURN(error);
}
unknown's avatar
unknown committed
4754 4755


4756 4757 4758 4759 4760 4761
/*
  Handle a privilege table.

  SYNOPSIS
    handle_grant_table()
    tables                      The array with the four open tables.
4762
    table_no                    The number of the table to handle (0..4).
4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779
    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
4780
    4 procs_priv
4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799

  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;
4800
  byte user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
4801
  uint key_prefix_length;
4802 4803
  DBUG_ENTER("handle_grant_table");

unknown's avatar
unknown committed
4804
  if (! table_no) // mysql.user table
4805
  {
4806 4807 4808 4809 4810 4811 4812 4813 4814 4815
    /*
      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'",
4816
                       table->s->table_name, user_str, host_str));
4817 4818
    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
4819 4820 4821 4822 4823

    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);

4824
    if ((error= table->file->index_read_idx(table->record[0], 0,
unknown's avatar
unknown committed
4825
                                            user_key, key_prefix_length,
4826
                                            HA_READ_KEY_EXACT)))
4827
    {
4828 4829 4830 4831 4832
      if (error != HA_ERR_KEY_NOT_FOUND)
      {
        table->file->print_error(error, MYF(0));
        result= -1;
      }
4833
    }
4834 4835 4836 4837 4838 4839 4840 4841 4842 4843
    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
unknown's avatar
unknown committed
4844
  {
4845 4846 4847 4848 4849 4850
    /*
      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)))
unknown's avatar
unknown committed
4851
    {
4852
      table->file->print_error(error, MYF(0));
4853
      result= -1;
4854 4855 4856 4857 4858
    }
    else
    {
#ifdef EXTRA_DEBUG
      DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
4859
                         table->s->table_name, user_str, host_str));
4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 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
#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()
4908
    struct_no                   The number of the structure to handle (0..3).
4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923
    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
4924
    3 procs_priv_hash
4925 4926 4927 4928

  RETURN
    > 0         At least one element matched.
    0           OK, but no element matched.
4929
    -1		Wrong arguments to function
4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941
*/

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;
4942
  GRANT_NAME *grant_name;
4943
  DBUG_ENTER("handle_grant_struct");
unknown's avatar
unknown committed
4944 4945 4946
  DBUG_PRINT("info",("scan struct: %u  search: '%s'@'%s'",
                     struct_no, user_from->user.str, user_from->host.str));

4947 4948
  LINT_INIT(acl_user);
  LINT_INIT(acl_db);
4949
  LINT_INIT(grant_name);
4950

4951 4952
  safe_mutex_assert_owner(&acl_cache->lock);

4953
  /* Get the number of elements in the in-memory structure. */
4954
  switch (struct_no) {
4955 4956 4957 4958 4959 4960
  case 0:
    elements= acl_users.elements;
    break;
  case 1:
    elements= acl_dbs.elements;
    break;
4961
  case 2:
4962
    elements= column_priv_hash.records;
4963 4964 4965 4966 4967 4968
    break;
  case 3:
    elements= proc_priv_hash.records;
    break;
  default:
    return -1;
4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980
  }

#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.
    */
4981
    switch (struct_no) {
4982 4983 4984
    case 0:
      acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
      user= acl_user->user;
4985 4986
      host= acl_user->host.hostname;
    break;
4987 4988 4989 4990

    case 1:
      acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
      user= acl_db->user;
4991
      host= acl_db->host.hostname;
4992 4993
      break;

4994 4995 4996
    case 2:
      grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx);
      user= grant_name->user;
4997
      host= grant_name->host.hostname;
4998 4999 5000 5001 5002
      break;

    case 3:
      grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx);
      user= grant_name->user;
5003
      host= grant_name->host.hostname;
5004
      break;
5005 5006
    }
    if (! user)
5007
      user= "";
unknown's avatar
unknown committed
5008 5009 5010
    if (! host)
      host= "";

5011 5012 5013 5014 5015 5016
#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))
5017
      continue;
5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031

    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;

5032 5033 5034 5035 5036 5037 5038
      case 2:
        hash_delete(&column_priv_hash, (byte*) grant_name);
	break;

      case 3:
        hash_delete(&proc_priv_hash, (byte*) grant_name);
	break;
5039 5040 5041
      }
      elements--;
      idx--;
5042
    }
5043 5044
    else if ( user_to )
    {
5045
      switch ( struct_no ) {
5046 5047 5048 5049
      case 0:
        acl_user->user= strdup_root(&mem, user_to->user.str);
        acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
        break;
5050

5051 5052 5053 5054
      case 1:
        acl_db->user= strdup_root(&mem, user_to->user.str);
        acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
        break;
unknown's avatar
unknown committed
5055

5056 5057 5058
      case 2:
      case 3:
        grant_name->user= strdup_root(&mem, user_to->user.str);
5059 5060
        update_hostname(&grant_name->host,
                        strdup_root(&mem, user_to->host.str));
5061
	break;
5062 5063 5064
      }
    }
    else
unknown's avatar
unknown committed
5065
    {
5066 5067 5068 5069 5070 5071 5072
      /* 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
unknown's avatar
unknown committed
5073

5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 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
  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
5118 5119
    if ((handle_grant_struct(0, drop, user_from, user_to) && ! result) ||
        found)
5120 5121 5122 5123 5124
    {
      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;
unknown's avatar
unknown committed
5125
    }
5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146
  }

  /* 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;
    }
  }

5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165
  /* 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;
    }
  }

5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179
  /* 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;
5180
    }
5181 5182 5183

    /* Handle columns table. */
    if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
unknown's avatar
unknown committed
5184
    {
5185
      /* Handle of table failed, don't touch the in-memory array. */
5186
      result= -1;
unknown's avatar
unknown committed
5187
    }
5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199
    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
5200

unknown's avatar
unknown committed
5201 5202 5203 5204 5205 5206
static void append_user(String *str, LEX_USER *user)
{
  if (str->length())
    str->append(',');
  str->append('\'');
  str->append(user->user.str);
5207
  str->append(STRING_WITH_LEN("'@'"));
unknown's avatar
unknown committed
5208 5209 5210
  str->append(user->host.str);
  str->append('\'');
}
5211

unknown's avatar
unknown committed
5212

5213 5214 5215 5216 5217 5218 5219
/*
  Create a list of users.

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

5221 5222 5223 5224 5225 5226 5227 5228
  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_create_user(THD *thd, List <LEX_USER> &list)
{
  int result;
unknown's avatar
unknown committed
5229
  String wrong_users;
5230
  ulong sql_mode;
5231
  LEX_USER *user_name, *tmp_user_name;
5232
  List_iterator <LEX_USER> user_list(list);
5233
  TABLE_LIST tables[GRANT_TABLES];
5234
  DBUG_ENTER("mysql_create_user");
unknown's avatar
unknown committed
5235

5236 5237 5238 5239 5240 5241 5242
  /* 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));

5243
  while ((tmp_user_name= user_list++))
5244
  {
5245 5246 5247 5248
    if (!(user_name= get_current_user(thd, tmp_user_name)))
    {
      result= TRUE;
      continue;
5249 5250 5251 5252 5253 5254 5255 5256 5257 5258
    }

    if (user_name->host.length > HOSTNAME_LENGTH ||
	user_name->user.length > USERNAME_LENGTH)
    {
      append_user(&wrong_users, user_name);
      result= TRUE;
      continue;
    }

5259 5260 5261 5262
    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
5263
    if (handle_grant_data(tables, 0, user_name, NULL))
unknown's avatar
unknown committed
5264
    {
unknown's avatar
unknown committed
5265
      append_user(&wrong_users, user_name);
5266
      result= TRUE;
unknown's avatar
unknown committed
5267
      continue;
5268
    }
unknown's avatar
unknown committed
5269

5270
    sql_mode= thd->variables.sql_mode;
unknown's avatar
unknown committed
5271
    if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
5272
    {
unknown's avatar
unknown committed
5273
      append_user(&wrong_users, user_name);
5274
      result= TRUE;
unknown's avatar
unknown committed
5275
    }
5276 5277 5278 5279 5280 5281
  }

  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
5282
    my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302
  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
5303
  String wrong_users;
5304
  LEX_USER *user_name, *tmp_user_name;
5305
  List_iterator <LEX_USER> user_list(list);
5306
  TABLE_LIST tables[GRANT_TABLES];
5307 5308
  DBUG_ENTER("mysql_drop_user");

unknown's avatar
unknown committed
5309
  /* DROP USER may be skipped on replication client. */
5310 5311 5312 5313 5314 5315
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

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

5316
  while ((tmp_user_name= user_list++))
5317
  {
5318 5319 5320 5321 5322 5323
    user_name= get_current_user(thd, tmp_user_name);
    if (!(user_name= get_current_user(thd, tmp_user_name)))
    {
      result= TRUE;
      continue;
    }  
5324
    if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
unknown's avatar
unknown committed
5325
    {
unknown's avatar
unknown committed
5326
      append_user(&wrong_users, user_name);
5327
      result= TRUE;
unknown's avatar
unknown committed
5328
    }
5329
  }
unknown's avatar
unknown committed
5330

5331 5332 5333
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

5334 5335 5336 5337
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
unknown's avatar
unknown committed
5338
    my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357
  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)
{
5358
  int result;
unknown's avatar
unknown committed
5359
  String wrong_users;
5360 5361
  LEX_USER *user_from, *tmp_user_from;
  LEX_USER *user_to, *tmp_user_to;
5362
  List_iterator <LEX_USER> user_list(list);
5363
  TABLE_LIST tables[GRANT_TABLES];
5364 5365
  DBUG_ENTER("mysql_rename_user");

unknown's avatar
unknown committed
5366
  /* RENAME USER may be skipped on replication client. */
5367 5368 5369 5370 5371 5372
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

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

5373
  while ((tmp_user_from= user_list++))
5374
  {
5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385
    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;
    }  
5386
    DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
5387 5388 5389 5390 5391

    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
unknown's avatar
unknown committed
5392 5393
    if (handle_grant_data(tables, 0, user_to, NULL) ||
        handle_grant_data(tables, 0, user_from, user_to) <= 0)
5394
    {
unknown's avatar
unknown committed
5395
      append_user(&wrong_users, user_from);
5396 5397
      result= TRUE;
    }
unknown's avatar
unknown committed
5398 5399
  }
  
5400 5401 5402
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

5403 5404 5405 5406
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
5407
    my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
5408 5409 5410
  DBUG_RETURN(result);
}

5411

5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425
/*
  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
5426
bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
5427
{
5428
  uint counter, revoked, is_proc;
5429
  int result;
unknown's avatar
unknown committed
5430
  ACL_DB *acl_db;
5431
  TABLE_LIST tables[GRANT_TABLES];
5432 5433 5434
  DBUG_ENTER("mysql_revoke_all");

  if ((result= open_grant_tables(thd, tables)))
unknown's avatar
unknown committed
5435
    DBUG_RETURN(result != 1);
5436 5437 5438 5439

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

5440
  LEX_USER *lex_user, *tmp_lex_user;
5441
  List_iterator <LEX_USER> user_list(list);
5442
  while ((tmp_lex_user= user_list++))
5443
  {
5444 5445 5446 5447 5448
    if (!(lex_user= get_current_user(thd, tmp_lex_user)))
    {
      result= -1;
      continue;
    }  
5449
    if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
5450
    {
unknown's avatar
unknown committed
5451 5452
      sql_print_error("REVOKE ALL PRIVILEGES, GRANT: User '%s'@'%s' does not "
                      "exists", lex_user->user.str, lex_user->host.str);
5453 5454 5455
      result= -1;
      continue;
    }
unknown's avatar
unknown committed
5456

5457
    if (replace_user_table(thd, tables[0].table,
5458
			   *lex_user, ~(ulong)0, 1, 0, 0))
5459 5460 5461 5462 5463 5464
    {
      result= -1;
      continue;
    }

    /* Remove db access privileges */
unknown's avatar
unknown committed
5465 5466 5467 5468 5469
    /*
      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
5470
    do
5471
    {
unknown's avatar
unknown committed
5472
      for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
5473
      {
unknown's avatar
unknown committed
5474
	const char *user,*host;
unknown's avatar
unknown committed
5475

unknown's avatar
unknown committed
5476 5477 5478 5479 5480
	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
5481

unknown's avatar
unknown committed
5482 5483 5484
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
5485
	  if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, ~(ulong)0, 1))
unknown's avatar
unknown committed
5486
	  {
unknown's avatar
unknown committed
5487 5488 5489 5490 5491
	    /*
	      Don't increment counter as replace_db_table deleted the
	      current element in acl_dbs.
	     */
	    revoked= 1;
unknown's avatar
unknown committed
5492 5493
	    continue;
	  }
unknown's avatar
unknown committed
5494
	  result= -1; // Something went wrong
unknown's avatar
unknown committed
5495
	}
unknown's avatar
unknown committed
5496
	counter++;
5497
      }
unknown's avatar
unknown committed
5498
    } while (revoked);
5499 5500

    /* Remove column access */
unknown's avatar
unknown committed
5501
    do
5502
    {
unknown's avatar
unknown committed
5503
      for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
5504
      {
unknown's avatar
unknown committed
5505 5506 5507 5508 5509
	const char *user,*host;
	GRANT_TABLE *grant_table= (GRANT_TABLE*)hash_element(&column_priv_hash,
							     counter);
	if (!(user=grant_table->user))
	  user= "";
5510
	if (!(host=grant_table->host.hostname))
unknown's avatar
unknown committed
5511
	  host= "";
unknown's avatar
unknown committed
5512

unknown's avatar
unknown committed
5513 5514
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
5515
	{
unknown's avatar
unknown committed
5516 5517 5518
	  if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
				  grant_table->db,
				  grant_table->tname,
5519
				  ~(ulong)0, 0, 1))
unknown's avatar
unknown committed
5520
	  {
unknown's avatar
unknown committed
5521
	    result= -1;
unknown's avatar
unknown committed
5522
	  }
unknown's avatar
unknown committed
5523
	  else
unknown's avatar
unknown committed
5524
	  {
unknown's avatar
unknown committed
5525
	    if (!grant_table->cols)
unknown's avatar
unknown committed
5526
	    {
unknown's avatar
unknown committed
5527 5528
	      revoked= 1;
	      continue;
unknown's avatar
unknown committed
5529
	    }
unknown's avatar
unknown committed
5530 5531
	    List<LEX_COLUMN> columns;
	    if (!replace_column_table(grant_table,tables[3].table, *lex_user,
unknown's avatar
unknown committed
5532 5533 5534
				      columns,
				      grant_table->db,
				      grant_table->tname,
5535
				      ~(ulong)0, 1))
unknown's avatar
unknown committed
5536
	    {
unknown's avatar
unknown committed
5537
	      revoked= 1;
5538
	      continue;
unknown's avatar
unknown committed
5539
	    }
5540
	    result= -1;
unknown's avatar
unknown committed
5541
	  }
5542
	}
unknown's avatar
unknown committed
5543
	counter++;
5544
      }
unknown's avatar
unknown committed
5545
    } while (revoked);
5546 5547

    /* Remove procedure access */
5548 5549 5550
    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 ; )
5551 5552
      {
	const char *user,*host;
5553
	GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
5554 5555
	if (!(user=grant_proc->user))
	  user= "";
5556
	if (!(host=grant_proc->host.hostname))
5557 5558 5559 5560 5561
	  host= "";

	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
5562
	  if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
5563 5564
				  grant_proc->db,
				  grant_proc->tname,
5565
                                  is_proc,
unknown's avatar
unknown committed
5566
				  ~(ulong)0, 1))
5567 5568 5569 5570 5571 5572 5573 5574 5575
	  {
	    revoked= 1;
	    continue;
	  }
	  result= -1;	// Something went wrong
	}
	counter++;
      }
    } while (revoked);
5576
  }
unknown's avatar
unknown committed
5577

5578 5579 5580
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
unknown's avatar
unknown committed
5581

5582
  if (result)
unknown's avatar
unknown committed
5583
    my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
unknown's avatar
unknown committed
5584

5585 5586
  DBUG_RETURN(result);
}
unknown's avatar
unknown committed
5587

5588

5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602
/*
  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.
*/

5603 5604
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
                          bool is_proc)
5605 5606 5607 5608
{
  uint counter, revoked;
  int result;
  TABLE_LIST tables[GRANT_TABLES];
5609
  HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
5610 5611 5612 5613 5614 5615 5616 5617 5618
  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 */
5619 5620
  do
  {
5621
    for (counter= 0, revoked= 0 ; counter < hash->records ; )
5622
    {
5623
      GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
5624 5625 5626 5627 5628 5629
      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);
5630 5631 5632 5633
	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;
5634 5635
	if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
				   grant_proc->db, grant_proc->tname,
unknown's avatar
unknown committed
5636
                                   is_proc, ~(ulong)0, 1))
5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671
	{
	  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.
*/

5672 5673
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
                         bool is_proc)
5674
{
5675
  Security_context *sctx= thd->security_ctx;
5676 5677 5678 5679
  LEX_USER *combo;
  TABLE_LIST tables[1];
  List<LEX_USER> user_list;
  bool result;
5680 5681
  ACL_USER *au;
  char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
5682
  DBUG_ENTER("sp_grant_privileges");
5683 5684 5685 5686

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

5687
  combo->user.str= sctx->user;
5688

5689
  VOID(pthread_mutex_lock(&acl_cache->lock));
5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703

  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:
5704
  VOID(pthread_mutex_unlock(&acl_cache->lock));
5705 5706 5707 5708 5709

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

  tables->db= (char*)sp_db;
5710
  tables->table_name= tables->alias= (char*)sp_name;
5711

5712 5713 5714 5715
  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);
5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741


  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;
  }
5742 5743 5744 5745 5746

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

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

5749
  result= mysql_routine_grant(thd, tables, is_proc, user_list,
5750 5751 5752 5753 5754
  				DEFAULT_CREATE_PROC_ACLS, 0, 1);
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
5755
/*****************************************************************************
unknown's avatar
unknown committed
5756
  Instantiate used templates
unknown's avatar
unknown committed
5757 5758
*****************************************************************************/

5759
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
unknown's avatar
unknown committed
5760 5761 5762 5763 5764
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
unknown committed
5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811

#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');
}

5812 5813 5814 5815 5816 5817 5818 5819

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;
5820
  restore_record(table, s->default_values);
5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841
  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;
5842
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
5843
  char *curr_host= thd->security_ctx->priv_host_name();
5844
  DBUG_ENTER("fill_schema_user_privileges");
5845

5846 5847
  pthread_mutex_lock(&acl_cache->lock);

5848 5849 5850 5851 5852 5853 5854 5855
  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= "";
5856 5857

    if (no_global_access &&
5858
        (strcmp(thd->security_ctx->priv_user, user) ||
5859 5860 5861
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;
      
5862 5863 5864 5865 5866 5867
    want_access= acl_user->access;
    if (!(want_access & GRANT_ACL))
      is_grantable= "NO";

    strxmov(buff,"'",user,"'@'",host,"'",NullS);
    if (!(want_access & ~GRANT_ACL))
5868 5869
      update_schema_privilege(table, buff, 0, 0, 0, 0,
                              STRING_WITH_LEN("USAGE"), is_grantable);
5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882
    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);
      }
    }
  }
5883 5884 5885

  pthread_mutex_unlock(&acl_cache->lock);

5886
  DBUG_RETURN(0);
5887 5888 5889
#else
  return(0);
#endif
5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900
}


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;
5901
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
5902
  char *curr_host= thd->security_ctx->priv_host_name();
5903 5904
  DBUG_ENTER("fill_schema_schema_privileges");

5905 5906
  pthread_mutex_lock(&acl_cache->lock);

5907 5908 5909 5910 5911 5912 5913 5914 5915 5916
  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= "";

5917
    if (no_global_access &&
5918
        (strcmp(thd->security_ctx->priv_user, user) ||
5919 5920 5921
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;

5922 5923 5924 5925 5926 5927 5928 5929 5930 5931
    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,
5932
                                0, STRING_WITH_LEN("USAGE"), is_grantable);
5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944
      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);
      }
    }
  }
5945 5946 5947

  pthread_mutex_unlock(&acl_cache->lock);

5948
  DBUG_RETURN(0);
5949 5950 5951
#else
  return (0);
#endif
5952 5953 5954 5955 5956 5957 5958 5959 5960
}


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;
5961
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
5962
  char *curr_host= thd->security_ctx->priv_host_name();
5963 5964
  DBUG_ENTER("fill_schema_table_privileges");

5965 5966
  rw_rdlock(&LOCK_grant);

5967 5968
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
5969
    const char *user, *host, *is_grantable= "YES";
5970 5971 5972 5973
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
5974 5975
    if (!(host= grant_table->host.hostname))
      host= "";
5976 5977

    if (no_global_access &&
5978
        (strcmp(thd->security_ctx->priv_user, user) ||
5979
         my_strcasecmp(system_charset_info, curr_host, host)))
5980 5981
      continue;

5982
    ulong table_access= grant_table->privs;
5983
    if (table_access)
5984 5985
    {
      ulong test_access= table_access & ~GRANT_ACL;
unknown's avatar
unknown committed
5986 5987 5988 5989
      /*
        We should skip 'usage' privilege on table if
        we have any privileges on column(s) of this table
      */
5990 5991
      if (!test_access && grant_table->cols)
        continue;
5992 5993 5994
      if (!(table_access & GRANT_ACL))
        is_grantable= "NO";

5995
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
5996 5997
      if (!test_access)
        update_schema_privilege(table, buff, grant_table->db, grant_table->tname,
5998
                                0, 0, STRING_WITH_LEN("USAGE"), is_grantable);
5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012
      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);
        }
      }
    }
  }
6013 6014 6015

  rw_unlock(&LOCK_grant);

6016
  DBUG_RETURN(0);
6017 6018 6019
#else
  return (0);
#endif
6020 6021 6022 6023 6024 6025 6026 6027 6028
}


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;
6029
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
6030
  char *curr_host= thd->security_ctx->priv_host_name();
6031 6032
  DBUG_ENTER("fill_schema_table_privileges");

6033 6034
  rw_rdlock(&LOCK_grant);

6035 6036
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
6037
    const char *user, *host, *is_grantable= "YES";
6038 6039 6040 6041
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
6042 6043
    if (!(host= grant_table->host.hostname))
      host= "";
6044 6045

    if (no_global_access &&
6046
        (strcmp(thd->security_ctx->priv_user, user) ||
6047
         my_strcasecmp(system_charset_info, curr_host, host)))
6048 6049
      continue;

6050 6051 6052
    ulong table_access= grant_table->cols;
    if (table_access != 0)
    {
unknown's avatar
unknown committed
6053
      if (!(grant_table->privs & GRANT_ACL))
6054 6055
        is_grantable= "NO";

unknown's avatar
unknown committed
6056
      ulong test_access= table_access & ~GRANT_ACL;
6057
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086
      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);
            }
          }
        }
      }
    }
  }
6087 6088 6089

  rw_unlock(&LOCK_grant);

6090
  DBUG_RETURN(0);
6091 6092 6093
#else
  return (0);
#endif
6094 6095 6096
}


unknown's avatar
VIEW  
unknown committed
6097 6098 6099 6100 6101
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
  fill effective privileges for table

  SYNOPSIS
6102 6103
    fill_effective_table_privileges()
    thd     thread handler
unknown's avatar
VIEW  
unknown committed
6104 6105 6106 6107 6108 6109 6110 6111
    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)
{
6112
  Security_context *sctx= thd->security_ctx;
6113 6114 6115 6116 6117
  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));
6118 6119 6120
  /* --skip-grants */
  if (!initialized)
  {
6121
    DBUG_PRINT("info", ("skip grants"));
6122
    grant->privilege= ~NO_ACCESS;             // everything is allowed
6123 6124
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;
6125 6126
  }

unknown's avatar
VIEW  
unknown committed
6127
  /* global privileges */
6128
  grant->privilege= sctx->master_access;
6129

6130
  if (!sctx->priv_user)
6131 6132 6133 6134
  {
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;                         // it is slave
  }
6135

6136
  /* db privileges */
6137
  grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
6138

6139
  if (!grant_option)
6140 6141 6142 6143
  {
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;
  }
6144

unknown's avatar
VIEW  
unknown committed
6145
  /* table privileges */
6146
  rw_rdlock(&LOCK_grant);
unknown's avatar
VIEW  
unknown committed
6147 6148 6149
  if (grant->version != grant_version)
  {
    grant->grant_table=
6150 6151
      table_hash_search(sctx->host, sctx->ip, db,
			sctx->priv_user,
unknown's avatar
VIEW  
unknown committed
6152 6153 6154 6155 6156 6157 6158
			table, 0);              /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
  }
  if (grant->grant_table != 0)
  {
    grant->privilege|= grant->grant_table->privs;
  }
6159 6160
  rw_unlock(&LOCK_grant);

6161 6162
  DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
  DBUG_VOID_RETURN;
unknown's avatar
VIEW  
unknown committed
6163
}
6164 6165 6166 6167 6168 6169 6170

#else /* NO_EMBEDDED_ACCESS_CHECKS */

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

unknown's avatar
unknown committed
6171 6172
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
                             bool is_proc)
6173 6174 6175 6176
{
  return FALSE;
}

unknown's avatar
VIEW  
unknown committed
6177
#endif