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

unknown's avatar
unknown committed
3 4
   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
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6

unknown's avatar
unknown committed
7 8 9 10
   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
11

unknown's avatar
unknown committed
12 13 14 15 16 17 18
   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:
19 20
  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
21 22 23 24 25 26 27 28 29 30
  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"
#include <m_ctype.h>
#include <stdarg.h>
31 32
#include "sp_head.h"
#include "sp.h"
unknown's avatar
unknown committed
33

34 35 36 37
time_t mysql_db_table_last_check= 0L;

TABLE_FIELD_W_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
  {
unknown's avatar
unknown committed
38 39
    { C_STRING_WITH_LEN("Host") },            
    { C_STRING_WITH_LEN("char(60)") },
40 41 42
    {NULL, 0}
  }, 
  {
unknown's avatar
unknown committed
43 44
    { C_STRING_WITH_LEN("Db") },            
    { C_STRING_WITH_LEN("char(64)") },
45 46 47
    {NULL, 0}
  }, 
  {
unknown's avatar
unknown committed
48 49
    { C_STRING_WITH_LEN("User") },
    { C_STRING_WITH_LEN("char(16)") },
50 51 52
    {NULL, 0}
  },
  {
unknown's avatar
unknown committed
53 54 55
    { C_STRING_WITH_LEN("Select_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
56 57
  },
  {
unknown's avatar
unknown committed
58 59 60
    { C_STRING_WITH_LEN("Insert_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
61 62
  },
  {
unknown's avatar
unknown committed
63 64 65
    { C_STRING_WITH_LEN("Update_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
66 67
  },
  {
unknown's avatar
unknown committed
68 69 70
    { C_STRING_WITH_LEN("Delete_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
71 72
  },
  {
unknown's avatar
unknown committed
73 74 75
    { C_STRING_WITH_LEN("Create_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
76 77
  },
  {
unknown's avatar
unknown committed
78 79 80
    { C_STRING_WITH_LEN("Drop_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
81 82
  },
  {
unknown's avatar
unknown committed
83 84 85
    { C_STRING_WITH_LEN("Grant_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
86 87
  },
  {
unknown's avatar
unknown committed
88 89 90
    { C_STRING_WITH_LEN("References_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
91 92
  },
  {
unknown's avatar
unknown committed
93 94 95
    { C_STRING_WITH_LEN("Index_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
96 97
  },
  {
unknown's avatar
unknown committed
98 99 100
    { C_STRING_WITH_LEN("Alter_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
101 102
  },
  {
unknown's avatar
unknown committed
103 104 105
    { C_STRING_WITH_LEN("Create_tmp_table_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
106 107
  },
  {
unknown's avatar
unknown committed
108 109 110
    { C_STRING_WITH_LEN("Lock_tables_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
111 112
  },
  {
unknown's avatar
unknown committed
113 114 115
    { C_STRING_WITH_LEN("Create_view_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
116 117
  },
  {
unknown's avatar
unknown committed
118 119 120
    { C_STRING_WITH_LEN("Show_view_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
121 122
  },
  {
unknown's avatar
unknown committed
123 124 125
    { C_STRING_WITH_LEN("Create_routine_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
126 127
  },
  {
unknown's avatar
unknown committed
128 129 130
    { C_STRING_WITH_LEN("Alter_routine_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
131 132
  },
  {
unknown's avatar
unknown committed
133 134 135
    { C_STRING_WITH_LEN("Execute_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
136 137
  },
  {
unknown's avatar
unknown committed
138 139 140
    { C_STRING_WITH_LEN("Event_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
141 142
  },
  {
unknown's avatar
unknown committed
143 144 145
    { C_STRING_WITH_LEN("Trigger_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
146 147 148 149
  }
};


150 151 152 153
#ifndef NO_EMBEDDED_ACCESS_CHECKS

#define FIRST_NON_YN_FIELD 26

unknown's avatar
unknown committed
154 155 156
class acl_entry :public hash_filo_element
{
public:
unknown's avatar
unknown committed
157
  ulong access;
unknown's avatar
unknown committed
158 159 160 161
  uint16 length;
  char key[1];					// Key will be stored here
};

unknown's avatar
unknown committed
162

unknown's avatar
unknown committed
163 164 165 166 167 168 169
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
170
#define IP_ADDR_STRLEN (3+1+3+1+3+1+3)
unknown's avatar
unknown committed
171
#define ACL_KEY_LENGTH (IP_ADDR_STRLEN+1+NAME_LEN+1+USERNAME_LENGTH+1)
unknown's avatar
unknown committed
172 173 174 175 176

static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
177
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
unknown's avatar
unknown committed
178 179
static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
180
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
unknown's avatar
unknown committed
181
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
unknown's avatar
unknown committed
182 183 184
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
185
static void rebuild_check_host(void);
186 187
static ACL_USER *find_acl_user(const char *host, const char *user,
                               my_bool exact);
188 189
static bool update_user_table(THD *thd, TABLE *table,
                              const char *host, const char *user,
190
			      const char *new_password, uint new_password_len);
unknown's avatar
unknown committed
191
static void update_hostname(acl_host_and_ip *host, const char *hostname);
192
static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
unknown's avatar
unknown committed
193
			     const char *ip);
194 195
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
static my_bool grant_load(TABLE_LIST *tables);
unknown's avatar
unknown committed
196

197 198 199 200 201 202 203 204 205 206 207 208 209 210
/*
  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;
  }
211
  else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
212 213
  {
    get_salt_from_password_323((ulong *) acl_user->salt, password);
214
    acl_user->salt_len= SCRAMBLE_LENGTH_323;
215 216 217 218 219
  }
  else
    acl_user->salt_len= 0;
}

220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
/*
  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;
}

238

239
/*
240 241
  Initialize structures responsible for user/db-level privilege checking and
  load privilege information for them from tables in the 'mysql' database.
242 243 244

  SYNOPSIS
    acl_init()
245 246 247 248 249 250
      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().
251 252 253 254 255 256

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

257
my_bool acl_init(bool dont_read_acl_tables)
unknown's avatar
unknown committed
258
{
unknown's avatar
unknown committed
259
  THD  *thd;
260
  my_bool return_val;
unknown's avatar
unknown committed
261 262
  DBUG_ENTER("acl_init");

263 264
  acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
                           (hash_get_key) acl_entry_get_key,
265
                           (hash_free_key) free,
266 267
                           lower_case_file_system ?
                           system_charset_info : &my_charset_bin);
unknown's avatar
unknown committed
268
  if (dont_read_acl_tables)
269
  {
unknown's avatar
unknown committed
270
    DBUG_RETURN(0); /* purecov: tested */
unknown's avatar
unknown committed
271 272
  }

273 274 275
  /*
    To be able to run this from boot, we allocate a temporary THD
  */
unknown's avatar
unknown committed
276 277
  if (!(thd=new THD))
    DBUG_RETURN(1); /* purecov: inspected */
278
  thd->thread_stack= (char*) &thd;
279
  thd->store_globals();
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
  /*
    It is safe to call acl_reload() since acl_* arrays and hashes which
    will be freed there are global static objects and thus are initialized
    by zeros at startup.
  */
  return_val= acl_reload(thd);
  delete thd;
  /* Remember that we don't have a THD */
  my_pthread_setspecific_ptr(THR_THD,  0);
  DBUG_RETURN(return_val);
}


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

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

  RETURN VALUES
    FALSE  Success
    TRUE   Error
*/

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

318
  grant_version++; /* Privileges updated */
319

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

322
  init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
unknown's avatar
unknown committed
323
  init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0);
324
  table->use_all_columns();
325
  VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
unknown's avatar
unknown committed
326 327 328
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_HOST host;
329 330
    update_hostname(&host.host,get_field(&mem, table->field[0]));
    host.db=	 get_field(&mem, table->field[1]);
331
    if (lower_case_table_names && host.db)
332 333
    {
      /*
334 335
        convert db to lower case and give a warning if the db wasn't
        already in lower case
336
      */
337 338
      (void) strmov(tmp_name, host.db);
      my_casedn_str(files_charset_info, host.db);
339 340 341
      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 "
342 343
                          "lower_case_table_names is set. It will not be "
                          "possible to remove this privilege using REVOKE.",
344 345
                          host.host.hostname ? host.host.hostname : "",
                          host.db ? host.db : "");
346
    }
unknown's avatar
unknown committed
347 348
    host.access= get_access(table,2);
    host.access= fix_rights_for_db(host.access);
349
    host.sort=	 get_sort(2,host.host.hostname,host.db);
unknown's avatar
SCRUM  
unknown committed
350 351
    if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
    {
unknown's avatar
unknown committed
352
      sql_print_warning("'host' entry '%s|%s' "
unknown's avatar
SCRUM  
unknown committed
353
		      "ignored in --skip-name-resolve mode.",
354 355
			host.host.hostname ? host.host.hostname : "",
			host.db ? host.db : "");
unknown's avatar
SCRUM  
unknown committed
356 357
      continue;
    }
unknown's avatar
unknown committed
358
#ifndef TO_BE_REMOVED
359
    if (table->s->fields == 8)
unknown's avatar
unknown committed
360 361
    {						// Without grant
      if (host.access & CREATE_ACL)
362
	host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
unknown's avatar
unknown committed
363 364 365 366 367 368 369 370 371 372
    }
#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);
373
  table->use_all_columns();
374
  VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
375 376 377
  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
378
  {
379 380 381
    sql_print_error("Fatal error: mysql.user table is damaged or in "
                    "unsupported 3.20 format.");
    goto end;
unknown's avatar
unknown committed
382 383
  }

384
  DBUG_PRINT("info",("user table fields: %d, password length: %d",
385
		     table->s->fields, password_length));
unknown's avatar
unknown committed
386

387
  pthread_mutex_lock(&LOCK_global_system_variables);
388
  if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
389
  {
390 391 392 393 394 395 396 397 398 399 400 401 402 403
    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);
404 405 406
      sql_print_warning("mysql.user table is not updated to new password format; "
                        "Disabling new password usage until "
                        "mysql_fix_privilege_tables is run");
407 408 409 410
    }
    thd->variables.old_passwords= 1;
  }
  else
411
  {
412 413
    sys_old_passwords.after_update= 0;
    pthread_mutex_unlock(&LOCK_global_system_variables);
414 415
  }

unknown's avatar
unknown committed
416 417 418 419
  allow_all_hosts=0;
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_USER user;
420 421
    update_hostname(&user.host, get_field(&mem, table->field[0]));
    user.user= get_field(&mem, table->field[1]);
unknown's avatar
SCRUM  
unknown committed
422 423
    if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
    {
unknown's avatar
unknown committed
424 425
      sql_print_warning("'user' entry '%s@%s' "
                        "ignored in --skip-name-resolve mode.",
426 427
			user.user ? user.user : "",
			user.host.hostname ? user.host.hostname : "");
unknown's avatar
SCRUM  
unknown committed
428 429 430
      continue;
    }

431 432 433 434
    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
435
    {
436 437
      switch (password_len) {
      case 45: /* 4.1: to be removed */
unknown's avatar
unknown committed
438 439 440 441 442
        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 : "");
443 444
        break;
      default:
unknown's avatar
unknown committed
445 446 447
        sql_print_warning("Found invalid password for user: '%s@%s'; "
                          "Ignoring user", user.user ? user.user : "",
                           user.host.hostname ? user.host.hostname : "");
448 449
        break;
      }
unknown's avatar
unknown committed
450
    }
451
    else                                        // password is correct
unknown's avatar
unknown committed
452
    {
unknown's avatar
unknown committed
453 454
      uint next_field;
      user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
455 456 457 458
      /*
        if it is pre 5.0.1 privilege table then map CREATE privilege on
        CREATE VIEW & SHOW VIEW privileges
      */
459
      if (table->s->fields <= 31 && (user.access & CREATE_ACL))
460
        user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
461 462 463 464 465

      /*
        if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
        CREATE PROCEDURE & ALTER PROCEDURE privileges
      */
466
      if (table->s->fields <= 33 && (user.access & CREATE_ACL))
467
        user.access|= CREATE_PROC_ACL;
468
      if (table->s->fields <= 33 && (user.access & ALTER_ACL))
469 470
        user.access|= ALTER_PROC_ACL;

unknown's avatar
unknown committed
471 472 473 474 475 476
      /*
        pre 5.0.3 did not have CREATE_USER_ACL
      */
      if (table->s->fields <= 36 && (user.access & GRANT_ACL))
        user.access|= CREATE_USER_ACL;

unknown's avatar
unknown committed
477 478

      /*
unknown's avatar
unknown committed
479
        if it is pre 5.1.6 privilege table then map CREATE privilege on
unknown's avatar
unknown committed
480 481
        CREATE|ALTER|DROP|EXECUTE EVENT
      */
unknown's avatar
unknown committed
482
      if (table->s->fields <= 37 && (user.access & SUPER_ACL))
unknown's avatar
unknown committed
483 484
        user.access|= EVENT_ACL;

485 486 487 488 489 490
      /*
        if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
      */
      if (table->s->fields <= 38 && (user.access & SUPER_ACL))
        user.access|= TRIGGER_ACL;

491 492 493
      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
494

495 496
      /* Starting from 4.0.2 we have more fields */
      if (table->s->fields >= 31)
497
      {
unknown's avatar
unknown committed
498
        char *ssl_type=get_field(&mem, table->field[next_field++]);
499 500 501 502 503 504 505 506 507
        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
508 509 510
        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++]);
511

unknown's avatar
unknown committed
512 513 514 515 516
        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++]);
517
        user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
518
        if (user.user_resource.questions || user.user_resource.updates ||
519
            user.user_resource.conn_per_hour)
520
          mqh_used=1;
521

522
        if (table->s->fields >= 36)
523 524 525 526 527 528 529
        {
          /* 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;
530
      }
531 532 533
      else
      {
        user.ssl_type=SSL_TYPE_NONE;
534
        bzero((char *)&(user.user_resource),sizeof(user.user_resource));
unknown's avatar
unknown committed
535
#ifndef TO_BE_REMOVED
536
        if (table->s->fields <= 13)
537 538 539 540 541 542 543 544 545 546
        {						// 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
547
#endif
548 549
      }
      VOID(push_dynamic(&acl_users,(gptr) &user));
550 551
      if (!user.host.hostname ||
	  (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
552
        allow_all_hosts=1;			// Anyone can connect
unknown's avatar
unknown committed
553
    }
unknown's avatar
unknown committed
554 555 556 557 558
  }
  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
559

unknown's avatar
unknown committed
560
  init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0);
561
  table->use_all_columns();
562
  VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100));
unknown's avatar
unknown committed
563 564 565
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_DB db;
566 567
    update_hostname(&db.host,get_field(&mem, table->field[MYSQL_DB_FIELD_HOST]));
    db.db=get_field(&mem, table->field[MYSQL_DB_FIELD_DB]);
568 569
    if (!db.db)
    {
unknown's avatar
unknown committed
570
      sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
571
      continue;
572
    }
573
    db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]);
unknown's avatar
SCRUM  
unknown committed
574 575
    if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
    {
unknown's avatar
unknown committed
576 577
      sql_print_warning("'db' entry '%s %s@%s' "
		        "ignored in --skip-name-resolve mode.",
578 579 580
		        db.db,
			db.user ? db.user : "",
			db.host.hostname ? db.host.hostname : "");
unknown's avatar
SCRUM  
unknown committed
581 582
      continue;
    }
unknown's avatar
unknown committed
583 584
    db.access=get_access(table,3);
    db.access=fix_rights_for_db(db.access);
585 586 587
    if (lower_case_table_names)
    {
      /*
588 589
        convert db to lower case and give a warning if the db wasn't
        already in lower case
590 591
      */
      (void)strmov(tmp_name, db.db);
592
      my_casedn_str(files_charset_info, db.db);
593 594 595 596
      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 "
597 598
                          "lower_case_table_names is set. It will not be "
                          "possible to remove this privilege using REVOKE.",
599 600 601
		          db.db,
			  db.user ? db.user : "",
			  db.host.hostname ? db.host.hostname : "");
602 603
      }
    }
unknown's avatar
unknown committed
604 605
    db.sort=get_sort(3,db.host.hostname,db.db,db.user);
#ifndef TO_BE_REMOVED
606
    if (table->s->fields <=  9)
unknown's avatar
unknown committed
607 608 609 610 611 612 613 614 615 616 617 618 619
    {						// 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();

620 621 622 623 624
  initialized=1;
  return_val=0;

end:
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
625 626 627 628 629
}


void acl_free(bool end)
{
630
  free_root(&mem,MYF(0));
unknown's avatar
unknown committed
631 632 633 634 635 636 637 638 639 640 641 642 643 644
  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;
  }
}

645 646

/*
647 648
  Forget current user/db-level privileges and read new privileges
  from the privilege tables.
649 650 651

  SYNOPSIS
    acl_reload()
652 653 654 655 656 657 658 659 660 661 662
      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
663
*/
unknown's avatar
unknown committed
664

665
my_bool acl_reload(THD *thd)
unknown's avatar
unknown committed
666
{
667
  TABLE_LIST tables[3];
unknown's avatar
unknown committed
668 669 670
  DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs;
  MEM_ROOT old_mem;
  bool old_initialized;
671
  my_bool return_val= 1;
unknown's avatar
unknown committed
672 673
  DBUG_ENTER("acl_reload");

674
  if (thd->locked_tables)
unknown's avatar
unknown committed
675
  {					// Can't have locked tables here
676 677 678
    thd->lock=thd->locked_tables;
    thd->locked_tables=0;
    close_thread_tables(thd);
unknown's avatar
unknown committed
679
  }
680 681 682 683 684 685

  /*
    To avoid deadlocks we should obtain table locks before
    obtaining acl_cache->lock mutex.
  */
  bzero((char*) tables, sizeof(tables));
686 687 688 689 690 691
  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;
692 693 694 695 696 697 698 699 700
  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
701 702 703 704 705 706 707 708 709 710
  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);

711
  if ((return_val= acl_load(thd, tables)))
unknown's avatar
unknown committed
712
  {					// Error. Revert to old list
713
    DBUG_PRINT("error",("Reverting to old privileges"));
714
    acl_free();				/* purecov: inspected */
unknown's avatar
unknown committed
715 716 717 718 719 720 721 722
    acl_hosts=old_acl_hosts;
    acl_users=old_acl_users;
    acl_dbs=old_acl_dbs;
    mem=old_mem;
    init_check_host();
  }
  else
  {
723
    free_root(&old_mem,MYF(0));
unknown's avatar
unknown committed
724 725 726 727 728 729
    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));
730 731 732
end:
  close_thread_tables(thd);
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
733 734 735
}


unknown's avatar
unknown committed
736 737
/*
  Get all access bits from table after fieldnr
unknown's avatar
unknown committed
738 739

  IMPLEMENTATION
unknown's avatar
unknown committed
740 741
  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
742 743 744 745 746 747 748 749 750 751 752

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

unknown's avatar
unknown committed
755
static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
unknown's avatar
unknown committed
756
{
unknown's avatar
unknown committed
757
  ulong access_bits=0,bit;
unknown's avatar
unknown committed
758
  char buff[2];
unknown's avatar
unknown committed
759
  String res(buff,sizeof(buff),&my_charset_latin1);
unknown's avatar
unknown committed
760 761
  Field **pos;

unknown's avatar
unknown committed
762
  for (pos=form->field+fieldnr, bit=1;
763
       *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
unknown's avatar
unknown committed
764
	 ((Field_enum*) (*pos))->typelib->count == 2 ;
unknown's avatar
unknown committed
765
       pos++, fieldnr++, bit<<=1)
unknown's avatar
unknown committed
766
  {
767
    (*pos)->val_str(&res);
unknown's avatar
unknown committed
768
    if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
unknown's avatar
unknown committed
769
      access_bits|= bit;
unknown's avatar
unknown committed
770
  }
unknown's avatar
unknown committed
771 772
  if (next_field)
    *next_field=fieldnr;
unknown's avatar
unknown committed
773 774 775 776 777
  return access_bits;
}


/*
unknown's avatar
unknown committed
778 779 780 781 782
  Return a number which, if sorted 'desc', puts strings in this order:
    no wildcards
    wildcards
    empty string
*/
unknown's avatar
unknown committed
783 784 785 786 787 788 789

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

790 791 792
  /* Should not use this function with more than 4 arguments for compare. */
  DBUG_ASSERT(count <= 4);

unknown's avatar
unknown committed
793 794
  while (count--)
  {
795 796 797
    char *start, *str= va_arg(args,char*);
    uint chars= 0;
    uint wild_pos= 0;           /* first wildcard position */
unknown's avatar
unknown committed
798

unknown's avatar
unknown committed
799
    if ((start= str))
unknown's avatar
unknown committed
800 801 802 803
    {
      for (; *str ; str++)
      {
	if (*str == wild_many || *str == wild_one || *str == wild_prefix)
804
        {
unknown's avatar
unknown committed
805
          wild_pos= (uint) (str - start) + 1;
806 807
          break;
        }
unknown's avatar
unknown committed
808
        chars= 128;                             // Marker that chars existed
unknown's avatar
unknown committed
809 810
      }
    }
unknown's avatar
unknown committed
811
    sort= (sort << 8) + (wild_pos ? min(wild_pos, 127) : chars);
unknown's avatar
unknown committed
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
  }
  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;
}

827

828
/*
unknown's avatar
unknown committed
829 830
  Seek ACL entry for a user, check password, SSL cypher, and if
  everything is OK, update THD user data and USER_RESOURCES struct.
831

unknown's avatar
unknown committed
832 833 834 835
  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
836

837
  SYNOPSIS
838 839
    acl_getroot()
    thd         thread handle. If all checks are OK,
840 841
                thd->security_ctx->priv_user/master_access are updated.
                thd->security_ctx->host/ip/user are used for checks.
842 843
    mqh         user resources; on success mqh is reset, else
                unchanged
844
    passwd      scrambled & crypted password, received from client
845 846 847 848 849 850 851
                (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
852
  RETURN VALUE
853 854
    0  success: thd->priv_user, thd->priv_host, thd->master_access, mqh are
       updated
855
    1  user not found or authentication failure
unknown's avatar
unknown committed
856
    2  user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
857
   -1  user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
unknown's avatar
unknown committed
858 859
*/

860 861
int acl_getroot(THD *thd, USER_RESOURCES  *mqh,
                const char *passwd, uint passwd_len)
unknown's avatar
unknown committed
862
{
unknown's avatar
merge  
unknown committed
863 864 865
  ulong user_access= NO_ACCESS;
  int res= 1;
  ACL_USER *acl_user= 0;
866
  Security_context *sctx= thd->security_ctx;
867
  DBUG_ENTER("acl_getroot");
unknown's avatar
unknown committed
868 869

  if (!initialized)
870
  {
871 872 873
    /* 
      here if mysqld's been started with --skip-grant-tables option.
    */
874
    sctx->skip_grants();
unknown's avatar
merge  
unknown committed
875
    bzero((char*) mqh, sizeof(*mqh));
876
    DBUG_RETURN(0);
877
  }
878

unknown's avatar
unknown committed
879
  VOID(pthread_mutex_lock(&acl_cache->lock));
unknown's avatar
unknown committed
880

unknown's avatar
unknown committed
881
  /*
882 883 884
    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
885
  */
unknown's avatar
unknown committed
886

887
  for (uint i=0 ; i < acl_users.elements ; i++)
888
  {
unknown's avatar
unknown committed
889
    ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
890
    if (!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user))
unknown's avatar
unknown committed
891
    {
892
      if (compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
unknown's avatar
unknown committed
893
      {
894
        /* check password: it should be empty or valid */
unknown's avatar
unknown committed
895
        if (passwd_len == acl_user_tmp->salt_len)
unknown's avatar
unknown committed
896
        {
unknown's avatar
unknown committed
897
          if (acl_user_tmp->salt_len == 0 ||
unknown's avatar
cleanup  
unknown committed
898 899
              (acl_user_tmp->salt_len == SCRAMBLE_LENGTH ?
              check_scramble(passwd, thd->scramble, acl_user_tmp->salt) :
900
              check_scramble_323(passwd, thd->scramble,
unknown's avatar
cleanup  
unknown committed
901
                                 (ulong *) acl_user_tmp->salt)) == 0)
902
          {
unknown's avatar
unknown committed
903
            acl_user= acl_user_tmp;
904 905
            res= 0;
          }
unknown's avatar
unknown committed
906
        }
907
        else if (passwd_len == SCRAMBLE_LENGTH &&
unknown's avatar
unknown committed
908
                 acl_user_tmp->salt_len == SCRAMBLE_LENGTH_323)
909
          res= -1;
unknown's avatar
unknown committed
910
        else if (passwd_len == SCRAMBLE_LENGTH_323 &&
unknown's avatar
unknown committed
911
                 acl_user_tmp->salt_len == SCRAMBLE_LENGTH)
unknown's avatar
unknown committed
912
          res= 2;
913 914
        /* linear search complete: */
        break;
unknown's avatar
unknown committed
915
      }
unknown's avatar
unknown committed
916
    }
917
  }
918 919 920 921
  /*
    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
922 923 924

  if (acl_user)
  {
925
    /* OK. User found and password checked continue validation */
926
#ifdef HAVE_OPENSSL
unknown's avatar
unknown committed
927
    Vio *vio=thd->net.vio;
unknown's avatar
unknown committed
928
    SSL *ssl= (SSL*) vio->ssl_arg;
929
#endif
unknown's avatar
merge  
unknown committed
930

931
    /*
unknown's avatar
unknown committed
932
      At this point we know that user is allowed to connect
933 934 935 936 937 938
      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
939 940
    case SSL_TYPE_NONE:				// SSL is not required
      user_access= acl_user->access;
941
      break;
942
#ifdef HAVE_OPENSSL
unknown's avatar
merge  
unknown committed
943
    case SSL_TYPE_ANY:				// Any kind of SSL is ok
944
      if (vio_type(vio) == VIO_TYPE_SSL)
unknown's avatar
merge  
unknown committed
945
	user_access= acl_user->access;
946 947 948 949 950
      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
951

unknown's avatar
unknown committed
952 953
	We need to check for absence of SSL because without SSL
	we should reject connection.
954
      */
unknown's avatar
unknown committed
955
      if (vio_type(vio) == VIO_TYPE_SSL &&
unknown's avatar
unknown committed
956 957
	  SSL_get_verify_result(ssl) == X509_V_OK &&
	  SSL_get_peer_certificate(ssl))
unknown's avatar
merge  
unknown committed
958
	user_access= acl_user->access;
959 960 961 962 963 964 965 966
      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
967
      X509 *cert;
unknown's avatar
unknown committed
968
      if (vio_type(vio) != VIO_TYPE_SSL ||
unknown's avatar
unknown committed
969
	  SSL_get_verify_result(ssl) != X509_V_OK)
unknown's avatar
unknown committed
970
	break;
971
      if (acl_user->ssl_cipher)
unknown's avatar
unknown committed
972
      {
973
	DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
unknown's avatar
unknown committed
974 975
			   acl_user->ssl_cipher,SSL_get_cipher(ssl)));
	if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl)))
unknown's avatar
merge  
unknown committed
976
	  user_access= acl_user->access;
977 978
	else
	{
unknown's avatar
unknown committed
979
	  if (global_system_variables.log_warnings)
unknown's avatar
unknown committed
980 981 982
	    sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
			      acl_user->ssl_cipher,
			      SSL_get_cipher(ssl));
983 984
	  break;
	}
unknown's avatar
unknown committed
985
      }
986 987
      /* Prepare certificate (if exists) */
      DBUG_PRINT("info",("checkpoint 1"));
unknown's avatar
unknown committed
988 989 990 991 992
      if (!(cert= SSL_get_peer_certificate(ssl)))
      {
	user_access=NO_ACCESS;
	break;
      }
993
      DBUG_PRINT("info",("checkpoint 2"));
994
      /* If X509 issuer is specified, we check it... */
995
      if (acl_user->x509_issuer)
unknown's avatar
unknown committed
996
      {
unknown's avatar
unknown committed
997
        DBUG_PRINT("info",("checkpoint 3"));
unknown's avatar
unknown committed
998 999
        char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
1000
			   acl_user->x509_issuer, ptr));
unknown's avatar
unknown committed
1001
        if (strcmp(acl_user->x509_issuer, ptr))
1002
        {
unknown's avatar
unknown committed
1003
          if (global_system_variables.log_warnings)
unknown's avatar
unknown committed
1004 1005
            sql_print_information("X509 issuer mismatch: should be '%s' "
			      "but is '%s'", acl_user->x509_issuer, ptr);
1006
          free(ptr);
1007
          user_access=NO_ACCESS;
unknown's avatar
unknown committed
1008
          break;
1009
        }
unknown's avatar
merge  
unknown committed
1010
        user_access= acl_user->access;
unknown's avatar
unknown committed
1011
        free(ptr);
unknown's avatar
unknown committed
1012
      }
1013 1014 1015 1016
      DBUG_PRINT("info",("checkpoint 4"));
      /* X509 subject is specified, we check it .. */
      if (acl_user->x509_subject)
      {
unknown's avatar
unknown committed
1017 1018 1019 1020
        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))
1021
        {
unknown's avatar
unknown committed
1022
          if (global_system_variables.log_warnings)
1023
            sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
unknown's avatar
unknown committed
1024
                            acl_user->x509_subject, ptr);
1025 1026 1027
          free(ptr);
          user_access=NO_ACCESS;
          break;
1028
        }
1029
        user_access= acl_user->access;
unknown's avatar
unknown committed
1030
        free(ptr);
1031 1032
      }
      break;
unknown's avatar
unknown committed
1033
#else  /* HAVE_OPENSSL */
unknown's avatar
unknown committed
1034
    default:
1035
      /*
unknown's avatar
unknown committed
1036 1037 1038
        If we don't have SSL but SSL is required for this user the 
        authentication should fail.
      */
1039 1040
      break;
#endif /* HAVE_OPENSSL */
unknown's avatar
unknown committed
1041
    }
1042 1043
    sctx->master_access= user_access;
    sctx->priv_user= acl_user->user ? sctx->user : (char *) "";
1044
    *mqh= acl_user->user_resource;
1045

1046
    if (acl_user->host.hostname)
1047
      strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
1048
    else
1049
      *sctx->priv_host= 0;
1050
  }
unknown's avatar
unknown committed
1051
  VOID(pthread_mutex_unlock(&acl_cache->lock));
1052
  DBUG_RETURN(res);
unknown's avatar
unknown committed
1053 1054 1055
}


1056
/*
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074
  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)
1075 1076
{
  int res= 1;
1077
  uint i;
1078 1079 1080
  ACL_USER *acl_user= 0;
  DBUG_ENTER("acl_getroot_no_password");

1081 1082
  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
                       (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
1083
                       user, (db ? db : "(NULL)")));
1084 1085 1086 1087 1088
  sctx->user= user;
  sctx->host= host;
  sctx->ip= ip;
  sctx->host_or_ip= host ? host : (ip ? ip : "");

1089 1090
  if (!initialized)
  {
1091
    /*
1092 1093
      here if mysqld's been started with --skip-grant-tables option.
    */
1094
    sctx->skip_grants();
1095
    DBUG_RETURN(FALSE);
1096 1097 1098 1099
  }

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

1100 1101
  sctx->master_access= 0;
  sctx->db_access= 0;
1102 1103
  sctx->priv_user= (char *) "";
  *sctx->priv_host= 0;
1104

1105 1106 1107
  /*
     Find acl entry in user database.
     This is specially tailored to suit the check we do for CALL of
1108
     a stored procedure; user is set to what is actually a
1109 1110
     priv_user, which can be ''.
  */
1111
  for (i=0 ; i < acl_users.elements ; i++)
1112 1113
  {
    acl_user= dynamic_element(&acl_users,i,ACL_USER*);
1114
    if ((!acl_user->user && !user[0]) ||
1115
	(acl_user->user && strcmp(user, acl_user->user) == 0))
1116
    {
1117
      if (compare_hostname(&acl_user->host, host, ip))
1118 1119 1120 1121 1122 1123 1124 1125 1126
      {
	res= 0;
	break;
      }
    }
  }

  if (acl_user)
  {
1127 1128 1129 1130
    for (i=0 ; i < acl_dbs.elements ; i++)
    {
      ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
      if (!acl_db->user ||
1131
	  (user && user[0] && !strcmp(user, acl_db->user)))
1132
      {
1133
	if (compare_hostname(&acl_db->host, host, ip))
1134
	{
1135
	  if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
1136
	  {
1137
	    sctx->db_access= acl_db->access;
1138 1139 1140 1141 1142
	    break;
	  }
	}
      }
    }
1143 1144
    sctx->master_access= acl_user->access;
    sctx->priv_user= acl_user->user ? user : (char *) "";
1145 1146

    if (acl_user->host.hostname)
1147
      strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
1148
    else
1149
      *sctx->priv_host= 0;
1150 1151 1152 1153 1154
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  DBUG_RETURN(res);
}

unknown's avatar
unknown committed
1155 1156 1157 1158 1159 1160 1161
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;
}

1162

unknown's avatar
unknown committed
1163
static void acl_update_user(const char *user, const char *host,
1164
			    const char *password, uint password_len,
1165 1166 1167 1168
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
unknown's avatar
unknown committed
1169
			    USER_RESOURCES  *mqh,
unknown's avatar
unknown committed
1170
			    ulong privileges)
unknown's avatar
unknown committed
1171
{
1172 1173
  safe_mutex_assert_owner(&acl_cache->lock);

unknown's avatar
unknown committed
1174 1175 1176 1177
  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] ||
1178
	acl_user->user && !strcmp(user,acl_user->user))
unknown's avatar
unknown committed
1179 1180
    {
      if (!acl_user->host.hostname && !host[0] ||
unknown's avatar
unknown committed
1181
	  acl_user->host.hostname &&
1182
	  !my_strcasecmp(system_charset_info, host, acl_user->host.hostname))
unknown's avatar
unknown committed
1183 1184
      {
	acl_user->access=privileges;
1185
	if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1186
	  acl_user->user_resource.questions=mqh->questions;
1187
	if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1188
	  acl_user->user_resource.updates=mqh->updates;
1189 1190 1191 1192
	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;
1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
	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
1203 1204
	if (password)
	  set_user_salt(acl_user, password, password_len);
1205
        /* search complete: */
unknown's avatar
unknown committed
1206 1207 1208 1209 1210 1211 1212 1213
	break;
      }
    }
  }
}


static void acl_insert_user(const char *user, const char *host,
1214
			    const char *password, uint password_len,
1215 1216 1217 1218
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
1219
			    USER_RESOURCES *mqh,
unknown's avatar
unknown committed
1220
			    ulong privileges)
unknown's avatar
unknown committed
1221 1222
{
  ACL_USER acl_user;
1223 1224 1225

  safe_mutex_assert_owner(&acl_cache->lock);

1226
  acl_user.user=*user ? strdup_root(&mem,user) : 0;
unknown's avatar
unknown committed
1227
  update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
unknown's avatar
unknown committed
1228
  acl_user.access=privileges;
1229
  acl_user.user_resource = *mqh;
unknown's avatar
unknown committed
1230
  acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
1231
  acl_user.hostname_length=(uint) strlen(host);
1232 1233 1234 1235 1236
  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;
1237 1238

  set_user_salt(&acl_user, password, password_len);
unknown's avatar
unknown committed
1239 1240

  VOID(push_dynamic(&acl_users,(gptr) &acl_user));
1241 1242
  if (!acl_user.host.hostname ||
      (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
unknown's avatar
unknown committed
1243
    allow_all_hosts=1;		// Anyone can connect /* purecov: tested */
unknown's avatar
unknown committed
1244 1245 1246
  qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
	sizeof(ACL_USER),(qsort_cmp) acl_compare);

1247 1248
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();
unknown's avatar
unknown committed
1249 1250 1251 1252
}


static void acl_update_db(const char *user, const char *host, const char *db,
unknown's avatar
unknown committed
1253
			  ulong privileges)
unknown's avatar
unknown committed
1254
{
1255 1256
  safe_mutex_assert_owner(&acl_cache->lock);

unknown's avatar
unknown committed
1257 1258 1259 1260 1261 1262 1263 1264
  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] ||
1265
	  acl_db->host.hostname &&
1266
	  !my_strcasecmp(system_charset_info, host, acl_db->host.hostname))
unknown's avatar
unknown committed
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
      {
	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);
	}
      }
    }
  }
}


1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295
/*
  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
1296
static void acl_insert_db(const char *user, const char *host, const char *db,
unknown's avatar
unknown committed
1297
			  ulong privileges)
unknown's avatar
unknown committed
1298 1299
{
  ACL_DB acl_db;
1300
  safe_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
1301
  acl_db.user=strdup_root(&mem,user);
1302
  update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
unknown's avatar
unknown committed
1303 1304 1305 1306 1307 1308 1309 1310 1311
  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);
}


1312 1313 1314

/*
  Get privilege for a host, user and db combination
1315 1316 1317

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

1320
ulong acl_get(const char *host, const char *ip,
1321
              const char *user, const char *db, my_bool db_is_pattern)
unknown's avatar
unknown committed
1322
{
1323
  ulong host_access= ~(ulong)0, db_access= 0;
unknown's avatar
unknown committed
1324
  uint i,key_length;
unknown's avatar
unknown committed
1325
  char key[ACL_KEY_LENGTH],*tmp_db,*end;
unknown's avatar
unknown committed
1326
  acl_entry *entry;
unknown's avatar
unknown committed
1327
  DBUG_ENTER("acl_get");
unknown's avatar
unknown committed
1328 1329

  VOID(pthread_mutex_lock(&acl_cache->lock));
1330
  end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db);
unknown's avatar
unknown committed
1331 1332
  if (lower_case_table_names)
  {
1333
    my_casedn_str(files_charset_info, tmp_db);
unknown's avatar
unknown committed
1334 1335
    db=tmp_db;
  }
unknown's avatar
unknown committed
1336
  key_length=(uint) (end-key);
1337
  if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search(key,key_length)))
unknown's avatar
unknown committed
1338 1339 1340
  {
    db_access=entry->access;
    VOID(pthread_mutex_unlock(&acl_cache->lock));
unknown's avatar
unknown committed
1341 1342
    DBUG_PRINT("exit", ("access: 0x%lx", db_access));
    DBUG_RETURN(db_access);
unknown's avatar
unknown committed
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
  }

  /*
    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))
      {
1355
	if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
unknown's avatar
unknown committed
1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
	{
	  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))
    {
1377
      if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
unknown's avatar
unknown committed
1378 1379 1380 1381 1382 1383 1384 1385
      {
	host_access=acl_host->access;		// Fully specified. Take it
	break;
      }
    }
  }
exit:
  /* Save entry in cache for quick retrieval */
1386 1387
  if (!db_is_pattern &&
      (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
unknown's avatar
unknown committed
1388 1389 1390 1391 1392 1393 1394
  {
    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
1395 1396
  DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
  DBUG_RETURN(db_access & host_access);
unknown's avatar
unknown committed
1397 1398
}

1399 1400 1401 1402 1403 1404 1405
/*
  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
1406 1407 1408 1409

static void init_check_host(void)
{
  DBUG_ENTER("init_check_host");
1410
  VOID(my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
unknown's avatar
unknown committed
1411
			  acl_users.elements,1));
1412
  VOID(hash_init(&acl_check_hosts,system_charset_info,acl_users.elements,0,0,
unknown's avatar
unknown committed
1413
		 (hash_get_key) check_get_key,0,0));
unknown's avatar
unknown committed
1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427
  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 *);
1428
	  if (!my_strcasecmp(system_charset_info,
1429
                             acl_user->host.hostname, acl->hostname))
unknown's avatar
unknown committed
1430 1431 1432 1433 1434
	    break;				// already stored
	}
	if (j == acl_wild_hosts.elements)	// If new
	  (void) push_dynamic(&acl_wild_hosts,(char*) &acl_user->host);
      }
1435
      else if (!hash_search(&acl_check_hosts,(byte*) acl_user->host.hostname,
unknown's avatar
unknown committed
1436
			    (uint) strlen(acl_user->host.hostname)))
unknown's avatar
unknown committed
1437
      {
unknown's avatar
SCRUM  
unknown committed
1438
	if (my_hash_insert(&acl_check_hosts,(byte*) acl_user))
unknown's avatar
unknown committed
1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451
	{					// 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;
}


1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467
/*
  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
1468 1469 1470 1471 1472 1473 1474 1475
/* 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
1476 1477
  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
1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495
  {
    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
1496 1497 1498 1499 1500 1501 1502 1503
/*
  Check if the user is allowed to change password

  SYNOPSIS:
    check_change_password()
    thd		THD
    host	hostname for the user
    user	user name
1504 1505 1506 1507
    new_password new password

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

unknown's avatar
unknown committed
1509
    RETURN VALUE
1510 1511
      0		OK
      1		ERROR  ; In this case the error is sent to the client.
unknown's avatar
unknown committed
1512 1513
*/

1514
bool check_change_password(THD *thd, const char *host, const char *user,
1515
                           char *new_password, uint new_password_len)
unknown's avatar
unknown committed
1516
{
unknown's avatar
unknown committed
1517 1518
  if (!initialized)
  {
unknown's avatar
unknown committed
1519
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1520
    return(1);
unknown's avatar
unknown committed
1521
  }
unknown's avatar
unknown committed
1522
  if (!thd->slave_thread &&
1523 1524 1525
      (strcmp(thd->security_ctx->user, user) ||
       my_strcasecmp(system_charset_info, host,
                     thd->security_ctx->priv_host)))
unknown's avatar
unknown committed
1526
  {
1527
    if (check_access(thd, UPDATE_ACL, "mysql",0,1,0,0))
unknown's avatar
unknown committed
1528
      return(1);
unknown's avatar
unknown committed
1529
  }
1530
  if (!thd->slave_thread && !thd->security_ctx->user[0])
unknown's avatar
unknown committed
1531
  {
unknown's avatar
unknown committed
1532 1533
    my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
               MYF(0));
unknown's avatar
unknown committed
1534
    return(1);
unknown's avatar
unknown committed
1535
  }
1536
  uint len=strlen(new_password);
unknown's avatar
unknown committed
1537
  if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
1538 1539
      len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
  {
unknown's avatar
unknown committed
1540
    my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
1541 1542
    return -1;
  }
unknown's avatar
unknown committed
1543 1544 1545 1546
  return(0);
}


1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
/*
  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
1560
*/
1561

unknown's avatar
unknown committed
1562 1563 1564
bool change_password(THD *thd, const char *host, const char *user,
		     char *new_password)
{
1565 1566 1567 1568 1569
  TABLE_LIST tables;
  TABLE *table;
  /* Buffer should be extended when password length is extended. */
  char buff[512];
  ulong query_length;
1570
  uint new_password_len= strlen(new_password);
1571
  bool result= 1;
unknown's avatar
unknown committed
1572 1573 1574 1575 1576
  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

1577
  if (check_change_password(thd, host, user, new_password, new_password_len))
unknown's avatar
unknown committed
1578 1579
    DBUG_RETURN(1);

1580
  bzero((char*) &tables, sizeof(tables));
1581
  tables.alias= tables.table_name= (char*) "user";
1582 1583 1584 1585 1586 1587 1588
  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.
  */
1589
  if (thd->slave_thread && rpl_filter->is_on())
1590 1591 1592 1593 1594 1595 1596
  {
    /*
      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 */
1597
    if (!(thd->spcont || rpl_filter->tables_ok(0, &tables)))
1598 1599 1600 1601 1602 1603 1604
      DBUG_RETURN(0);
  }
#endif

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

unknown's avatar
unknown committed
1605 1606
  VOID(pthread_mutex_lock(&acl_cache->lock));
  ACL_USER *acl_user;
1607
  if (!(acl_user= find_acl_user(host, user, TRUE)))
unknown's avatar
unknown committed
1608 1609
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock));
unknown's avatar
unknown committed
1610
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
1611
    goto end;
unknown's avatar
unknown committed
1612
  }
1613 1614 1615
  /* update loaded acl entry: */
  set_user_salt(acl_user, new_password, new_password_len);

1616
  if (update_user_table(thd, table,
unknown's avatar
unknown committed
1617
			acl_user->host.hostname ? acl_user->host.hostname : "",
unknown's avatar
unknown committed
1618
			acl_user->user ? acl_user->user : "",
1619
			new_password, new_password_len))
unknown's avatar
unknown committed
1620 1621
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
1622
    goto end;
unknown's avatar
unknown committed
1623
  }
unknown's avatar
unknown committed
1624

unknown's avatar
unknown committed
1625 1626
  acl_cache->clear(1);				// Clear locked hostname cache
  VOID(pthread_mutex_unlock(&acl_cache->lock));
1627 1628 1629
  result= 0;
  if (mysql_bin_log.is_open())
  {
1630 1631
    query_length=
      my_sprintf(buff,
1632
                 (buff,"SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
1633 1634 1635
                  acl_user->user ? acl_user->user : "",
                  acl_user->host.hostname ? acl_user->host.hostname : "",
                  new_password));
1636
    thd->clear_error();
1637
    thd->binlog_query(THD::MYSQL_QUERY_TYPE, buff, query_length, FALSE, FALSE);
1638 1639 1640 1641
  }
end:
  close_thread_tables(thd);
  DBUG_RETURN(result);
unknown's avatar
unknown committed
1642 1643 1644
}


1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660
/*
  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;
1661 1662 1663 1664 1665

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

1666
  VOID(pthread_mutex_lock(&acl_cache->lock));
1667
  res= find_acl_user(host, user, TRUE) != NULL;
1668 1669 1670 1671 1672
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  return res;
}


unknown's avatar
unknown committed
1673 1674 1675 1676 1677
/*
  Find first entry that matches the current user
*/

static ACL_USER *
1678
find_acl_user(const char *host, const char *user, my_bool exact)
unknown's avatar
unknown committed
1679
{
unknown's avatar
unknown committed
1680
  DBUG_ENTER("find_acl_user");
1681
  DBUG_PRINT("enter",("host: '%s'  user: '%s'",host,user));
1682 1683 1684

  safe_mutex_assert_owner(&acl_cache->lock);

unknown's avatar
unknown committed
1685 1686 1687
  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
1688
    DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
1689 1690 1691 1692
                       user, acl_user->user ? acl_user->user : "",
                       host,
                       acl_user->host.hostname ? acl_user->host.hostname :
                       ""));
unknown's avatar
unknown committed
1693 1694 1695
    if (!acl_user->user && !user[0] ||
	acl_user->user && !strcmp(user,acl_user->user))
    {
1696
      if (exact ? !my_strcasecmp(system_charset_info, host,
1697 1698
                                 acl_user->host.hostname ?
				 acl_user->host.hostname : "") :
1699
          compare_hostname(&acl_user->host,host,host))
unknown's avatar
unknown committed
1700 1701 1702
      {
	DBUG_RETURN(acl_user);
      }
unknown's avatar
unknown committed
1703 1704
    }
  }
unknown's avatar
unknown committed
1705
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1706 1707 1708
}


1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719
/*
  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
1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741

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)
{
1742
  host->hostname=(char*) hostname;             // This will not be modified!
1743
  if (!hostname ||
unknown's avatar
unknown committed
1744 1745 1746
      (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
       !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
  {
1747
    host->ip= host->ip_mask=0;			// Not a masked ip
unknown's avatar
unknown committed
1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760
  }
}


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 ||
1761
	  (hostname && !wild_case_compare(system_charset_info,
1762 1763
                                          hostname, host->hostname)) ||
	  (ip && !wild_compare(ip, host->hostname, 0)));
unknown's avatar
unknown committed
1764 1765
}

unknown's avatar
SCRUM  
unknown committed
1766 1767 1768 1769
bool hostname_requires_resolving(const char *hostname)
{
  char cur;
  if (!hostname)
unknown's avatar
unknown committed
1770
    return FALSE;
unknown's avatar
SCRUM  
unknown committed
1771 1772 1773
  int namelen= strlen(hostname);
  int lhlen= strlen(my_localhost);
  if ((namelen == lhlen) &&
1774
      !my_strnncoll(system_charset_info, (const uchar *)hostname,  namelen,
unknown's avatar
SCRUM  
unknown committed
1775
		    (const uchar *)my_localhost, strlen(my_localhost)))
unknown's avatar
unknown committed
1776
    return FALSE;
unknown's avatar
SCRUM  
unknown committed
1777 1778
  for (; (cur=*hostname); hostname++)
  {
1779
    if ((cur != '%') && (cur != '_') && (cur != '.') && (cur != '/') &&
unknown's avatar
SCRUM  
unknown committed
1780
	((cur < '0') || (cur > '9')))
unknown's avatar
unknown committed
1781
      return TRUE;
unknown's avatar
SCRUM  
unknown committed
1782
  }
unknown's avatar
unknown committed
1783
  return FALSE;
unknown's avatar
SCRUM  
unknown committed
1784
}
unknown's avatar
unknown committed
1785

1786

1787
/*
1788 1789 1790 1791 1792 1793 1794 1795 1796 1797
  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
1798
*/
unknown's avatar
unknown committed
1799

1800 1801
static bool update_user_table(THD *thd, TABLE *table,
                              const char *host, const char *user,
1802
			      const char *new_password, uint new_password_len)
unknown's avatar
unknown committed
1803
{
1804
  char user_key[MAX_KEY_LENGTH];
1805
  int error;
unknown's avatar
unknown committed
1806 1807 1808
  DBUG_ENTER("update_user_table");
  DBUG_PRINT("enter",("user: %s  host: %s",user,host));

1809
  table->use_all_columns();
1810 1811
  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
1812
  key_copy((byte *) user_key, table->record[0], table->key_info,
1813
           table->key_info->key_length);
unknown's avatar
unknown committed
1814

1815
  if (table->file->index_read_idx(table->record[0], 0,
1816 1817
				  (byte *) user_key,
                                  table->key_info->key_length,
unknown's avatar
unknown committed
1818 1819
				  HA_READ_KEY_EXACT))
  {
unknown's avatar
unknown committed
1820 1821
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
               MYF(0));	/* purecov: deadcode */
unknown's avatar
unknown committed
1822 1823
    DBUG_RETURN(1);				/* purecov: deadcode */
  }
unknown's avatar
unknown committed
1824
  store_record(table,record[1]);
1825
  table->field[2]->store(new_password, new_password_len, system_charset_info);
1826
  if ((error=table->file->ha_update_row(table->record[1],table->record[0])))
unknown's avatar
unknown committed
1827 1828
  {
    table->file->print_error(error,MYF(0));	/* purecov: deadcode */
1829
    DBUG_RETURN(1);
unknown's avatar
unknown committed
1830
  }
1831
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1832 1833
}

unknown's avatar
unknown committed
1834

unknown's avatar
unknown committed
1835 1836 1837 1838 1839 1840
/*
  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
1841 1842 1843

static bool test_if_create_new_users(THD *thd)
{
1844
  Security_context *sctx= thd->security_ctx;
1845
  bool create_new_users= test(sctx->master_access & INSERT_ACL) ||
unknown's avatar
unknown committed
1846
                         (!opt_safe_user_create &&
1847
                          test(sctx->master_access & CREATE_USER_ACL));
unknown's avatar
unknown committed
1848
  if (!create_new_users)
unknown's avatar
unknown committed
1849 1850
  {
    TABLE_LIST tl;
unknown's avatar
unknown committed
1851
    ulong db_access;
unknown's avatar
unknown committed
1852 1853
    bzero((char*) &tl,sizeof(tl));
    tl.db=	   (char*) "mysql";
1854
    tl.table_name=  (char*) "user";
unknown's avatar
unknown committed
1855
    create_new_users= 1;
unknown's avatar
unknown committed
1856

1857 1858
    db_access=acl_get(sctx->host, sctx->ip,
		      sctx->priv_user, tl.db, 0);
unknown's avatar
unknown committed
1859 1860
    if (!(db_access & INSERT_ACL))
    {
unknown's avatar
unknown committed
1861
      if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1))
unknown's avatar
unknown committed
1862 1863 1864 1865 1866 1867 1868
	create_new_users=0;
    }
  }
  return create_new_users;
}


unknown's avatar
unknown committed
1869
/****************************************************************************
1870
  Handle GRANT commands
unknown's avatar
unknown committed
1871 1872
****************************************************************************/

1873
static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
unknown's avatar
unknown committed
1874
			      ulong rights, bool revoke_grant,
unknown's avatar
unknown committed
1875
			      bool can_create_user, bool no_auto_create)
unknown's avatar
unknown committed
1876 1877
{
  int error = -1;
unknown's avatar
unknown committed
1878
  bool old_row_exists=0;
1879
  const char *password= "";
1880
  uint password_len= 0;
unknown's avatar
unknown committed
1881
  char what= (revoke_grant) ? 'N' : 'Y';
1882
  byte user_key[MAX_KEY_LENGTH];
1883
  LEX *lex= thd->lex;
unknown's avatar
unknown committed
1884
  DBUG_ENTER("replace_user_table");
unknown's avatar
unknown committed
1885

1886
  safe_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
1887 1888

  if (combo.password.str && combo.password.str[0])
1889
  {
1890 1891
    if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
        combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1892
    {
1893
      my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
unknown's avatar
unknown committed
1894
      DBUG_RETURN(-1);
1895
    }
1896
    password_len= combo.password.length;
unknown's avatar
unknown committed
1897
    password=combo.password.str;
1898
  }
unknown's avatar
unknown committed
1899

1900 1901 1902 1903 1904
  table->use_all_columns();
  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);
1905 1906 1907
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

unknown's avatar
unknown committed
1908
  if (table->file->index_read_idx(table->record[0], 0,
1909 1910
                                  user_key, table->key_info->key_length,
                                  HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
1911
  {
1912 1913
    /* what == 'N' means revoke */
    if (what == 'N')
unknown's avatar
unknown committed
1914
    {
1915 1916 1917 1918
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
      goto end;
    }
    /*
unknown's avatar
unknown committed
1919 1920
      There are four options which affect the process of creation of
      a new user (mysqld option --safe-create-user, 'insert' privilege
1921 1922 1923 1924 1925 1926 1927
      on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
      SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
      how it should work.
      if (safe-user-create && ! INSERT_priv) => reject
      else if (identified_by) => create
      else if (no_auto_create_user) => reject
      else create
unknown's avatar
unknown committed
1928 1929

      see also test_if_create_new_users()
1930
    */
unknown's avatar
unknown committed
1931 1932 1933 1934 1935 1936
    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)
1937
    {
unknown's avatar
unknown committed
1938
      my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0),
1939
               thd->security_ctx->user, thd->security_ctx->host_or_ip);
unknown's avatar
unknown committed
1940 1941
      goto end;
    }
unknown's avatar
unknown committed
1942
    old_row_exists = 0;
1943
    restore_record(table,s->default_values);
1944
    table->field[0]->store(combo.host.str,combo.host.length,
1945
                           system_charset_info);
1946
    table->field[1]->store(combo.user.str,combo.user.length,
1947
                           system_charset_info);
1948
    table->field[2]->store(password, password_len,
1949
                           system_charset_info);
unknown's avatar
unknown committed
1950 1951 1952
  }
  else
  {
unknown's avatar
unknown committed
1953
    old_row_exists = 1;
unknown's avatar
unknown committed
1954
    store_record(table,record[1]);			// Save copy for update
unknown's avatar
unknown committed
1955
    if (combo.password.str)			// If password given
1956
      table->field[2]->store(password, password_len, system_charset_info);
1957
    else if (!rights && !revoke_grant &&
1958 1959
             lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
             !lex->mqh.specified_limits)
unknown's avatar
unknown committed
1960 1961 1962
    {
      DBUG_RETURN(0);
    }
unknown's avatar
unknown committed
1963 1964
  }

unknown's avatar
unknown committed
1965 1966 1967 1968
  /* Update table columns with new privileges */

  Field **tmp_field;
  ulong priv;
1969
  uint next_field;
unknown's avatar
unknown committed
1970
  for (tmp_field= table->field+3, priv = SELECT_ACL;
1971
       *tmp_field && (*tmp_field)->real_type() == MYSQL_TYPE_ENUM &&
unknown's avatar
unknown committed
1972 1973
	 ((Field_enum*) (*tmp_field))->typelib->count == 2 ;
       tmp_field++, priv <<= 1)
unknown's avatar
unknown committed
1974
  {
unknown's avatar
unknown committed
1975
    if (priv & rights)				 // set requested privileges
unknown's avatar
unknown committed
1976
      (*tmp_field)->store(&what, 1, &my_charset_latin1);
unknown's avatar
unknown committed
1977
  }
1978
  rights= get_access(table, 3, &next_field);
1979 1980
  DBUG_PRINT("info",("table fields: %d",table->s->fields));
  if (table->s->fields >= 31)		/* From 4.0.0 we have more fields */
1981
  {
unknown's avatar
unknown committed
1982
    /* We write down SSL related ACL stuff */
1983
    switch (lex->ssl_type) {
unknown's avatar
unknown committed
1984
    case SSL_TYPE_ANY:
1985 1986
      table->field[next_field]->store(STRING_WITH_LEN("ANY"),
                                      &my_charset_latin1);
1987 1988 1989
      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
1990 1991
      break;
    case SSL_TYPE_X509:
1992 1993
      table->field[next_field]->store(STRING_WITH_LEN("X509"),
                                      &my_charset_latin1);
1994 1995 1996
      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
1997 1998
      break;
    case SSL_TYPE_SPECIFIED:
1999 2000
      table->field[next_field]->store(STRING_WITH_LEN("SPECIFIED"),
                                      &my_charset_latin1);
2001 2002 2003
      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);
2004
      if (lex->ssl_cipher)
unknown's avatar
unknown committed
2005 2006
        table->field[next_field+1]->store(lex->ssl_cipher,
                                strlen(lex->ssl_cipher), system_charset_info);
2007
      if (lex->x509_issuer)
unknown's avatar
unknown committed
2008 2009
        table->field[next_field+2]->store(lex->x509_issuer,
                                strlen(lex->x509_issuer), system_charset_info);
2010
      if (lex->x509_subject)
unknown's avatar
unknown committed
2011 2012
        table->field[next_field+3]->store(lex->x509_subject,
                                strlen(lex->x509_subject), system_charset_info);
unknown's avatar
unknown committed
2013
      break;
unknown's avatar
unknown committed
2014
    case SSL_TYPE_NOT_SPECIFIED:
unknown's avatar
unknown committed
2015 2016
      break;
    case SSL_TYPE_NONE:
2017 2018 2019 2020
      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
2021
      break;
2022
    }
unknown's avatar
unknown committed
2023
    next_field+=4;
unknown's avatar
unknown committed
2024

2025
    USER_RESOURCES mqh= lex->mqh;
2026
    if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
2027
      table->field[next_field]->store((longlong) mqh.questions, TRUE);
2028
    if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
2029
      table->field[next_field+1]->store((longlong) mqh.updates, TRUE);
2030
    if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
2031
      table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE);
2032
    if (table->s->fields >= 36 &&
2033
        (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
2034
      table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE);
2035
    mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
unknown's avatar
unknown committed
2036
  }
unknown's avatar
unknown committed
2037
  if (old_row_exists)
unknown's avatar
unknown committed
2038 2039 2040 2041 2042
  {
    /*
      We should NEVER delete from the user table, as a uses can still
      use mysqld even if he doesn't have any privileges in the user table!
    */
unknown's avatar
unknown committed
2043
    if (cmp_record(table,record[1]) &&
2044
	(error=table->file->ha_update_row(table->record[1],table->record[0])))
unknown's avatar
unknown committed
2045 2046 2047 2048 2049 2050
    {						// This should never happen
      table->file->print_error(error,MYF(0));	/* purecov: deadcode */
      error= -1;				/* purecov: deadcode */
      goto end;					/* purecov: deadcode */
    }
  }
2051
  else if ((error=table->file->ha_write_row(table->record[0]))) // insert
unknown's avatar
unknown committed
2052
  {						// This should never happen
2053
    if (table->file->is_fatal_error(error, HA_CHECK_DUP))
unknown's avatar
unknown committed
2054 2055 2056 2057 2058 2059 2060 2061
    {
      table->file->print_error(error,MYF(0));	/* purecov: deadcode */
      error= -1;				/* purecov: deadcode */
      goto end;					/* purecov: deadcode */
    }
  }
  error=0;					// Privileges granted / revoked

unknown's avatar
unknown committed
2062
end:
unknown's avatar
unknown committed
2063 2064 2065
  if (!error)
  {
    acl_cache->clear(1);			// Clear privilege cache
unknown's avatar
unknown committed
2066
    if (old_row_exists)
2067 2068
      acl_update_user(combo.user.str, combo.host.str,
                      combo.password.str, password_len,
2069 2070 2071 2072 2073
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
unknown's avatar
unknown committed
2074
		      rights);
unknown's avatar
unknown committed
2075
    else
2076
      acl_insert_user(combo.user.str, combo.host.str, password, password_len,
2077 2078 2079 2080 2081
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
unknown's avatar
unknown committed
2082
		      rights);
unknown's avatar
unknown committed
2083 2084 2085 2086 2087 2088
  }
  DBUG_RETURN(error);
}


/*
unknown's avatar
unknown committed
2089
  change grants in the mysql.db table
unknown's avatar
unknown committed
2090 2091 2092 2093
*/

static int replace_db_table(TABLE *table, const char *db,
			    const LEX_USER &combo,
unknown's avatar
unknown committed
2094
			    ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
2095
{
unknown's avatar
unknown committed
2096 2097
  uint i;
  ulong priv,store_rights;
unknown's avatar
unknown committed
2098
  bool old_row_exists=0;
unknown's avatar
unknown committed
2099
  int error;
unknown's avatar
unknown committed
2100
  char what= (revoke_grant) ? 'N' : 'Y';
2101
  byte user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
2102 2103
  DBUG_ENTER("replace_db_table");

2104 2105
  if (!initialized)
  {
unknown's avatar
unknown committed
2106
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2107 2108 2109
    DBUG_RETURN(-1);
  }

2110
  /* Check if there is such a user in user table in memory? */
2111
  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
unknown's avatar
unknown committed
2112
  {
unknown's avatar
unknown committed
2113
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
unknown's avatar
unknown committed
2114 2115 2116
    DBUG_RETURN(-1);
  }

2117 2118 2119
  table->use_all_columns();
  table->field[0]->store(combo.host.str,combo.host.length,
                         system_charset_info);
2120
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
2121 2122
  table->field[2]->store(combo.user.str,combo.user.length,
                         system_charset_info);
2123 2124 2125
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

unknown's avatar
unknown committed
2126
  if (table->file->index_read_idx(table->record[0],0,
2127 2128
                                  user_key, table->key_info->key_length,
                                  HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
2129 2130 2131
  {
    if (what == 'N')
    { // no row, no revoke
unknown's avatar
unknown committed
2132
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
unknown's avatar
unknown committed
2133 2134
      goto abort;
    }
unknown's avatar
unknown committed
2135
    old_row_exists = 0;
2136
    restore_record(table, s->default_values);
2137 2138
    table->field[0]->store(combo.host.str,combo.host.length,
                           system_charset_info);
2139
    table->field[1]->store(db,(uint) strlen(db), system_charset_info);
2140 2141
    table->field[2]->store(combo.user.str,combo.user.length,
                           system_charset_info);
unknown's avatar
unknown committed
2142 2143 2144
  }
  else
  {
unknown's avatar
unknown committed
2145
    old_row_exists = 1;
unknown's avatar
unknown committed
2146
    store_record(table,record[1]);
unknown's avatar
unknown committed
2147 2148 2149
  }

  store_rights=get_rights_for_db(rights);
2150
  for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
unknown's avatar
unknown committed
2151
  {
unknown's avatar
unknown committed
2152
    if (priv & store_rights)			// do it if priv is chosen
unknown's avatar
unknown committed
2153
      table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
unknown's avatar
unknown committed
2154 2155 2156 2157
  }
  rights=get_access(table,3);
  rights=fix_rights_for_db(rights);

unknown's avatar
unknown committed
2158
  if (old_row_exists)
unknown's avatar
unknown committed
2159
  {
2160
    /* update old existing row */
unknown's avatar
unknown committed
2161 2162
    if (rights)
    {
2163 2164
      if ((error= table->file->ha_update_row(table->record[1],
                                             table->record[0])))
unknown's avatar
unknown committed
2165 2166 2167 2168
	goto table_error;			/* purecov: deadcode */
    }
    else	/* must have been a revoke of all privileges */
    {
2169
      if ((error= table->file->ha_delete_row(table->record[1])))
unknown's avatar
unknown committed
2170 2171 2172
	goto table_error;			/* purecov: deadcode */
    }
  }
2173
  else if (rights && (error= table->file->ha_write_row(table->record[0])))
unknown's avatar
unknown committed
2174
  {
2175
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
unknown's avatar
unknown committed
2176 2177 2178 2179
      goto table_error; /* purecov: deadcode */
  }

  acl_cache->clear(1);				// Clear privilege cache
unknown's avatar
unknown committed
2180
  if (old_row_exists)
unknown's avatar
unknown committed
2181 2182
    acl_update_db(combo.user.str,combo.host.str,db,rights);
  else
2183
  if (rights)
unknown's avatar
unknown committed
2184 2185 2186 2187
    acl_insert_db(combo.user.str,combo.host.str,db,rights);
  DBUG_RETURN(0);

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

2191
abort:
unknown's avatar
unknown committed
2192 2193 2194 2195 2196 2197 2198 2199
  DBUG_RETURN(-1);
}


class GRANT_COLUMN :public Sql_alloc
{
public:
  char *column;
unknown's avatar
unknown committed
2200 2201 2202
  ulong rights;
  uint key_length;
  GRANT_COLUMN(String &c,  ulong y) :rights (y)
unknown's avatar
unknown committed
2203
  {
unknown's avatar
unknown committed
2204
    column= memdup_root(&memex,c.ptr(), key_length=c.length());
unknown's avatar
unknown committed
2205 2206 2207
  }
};

unknown's avatar
unknown committed
2208

unknown's avatar
unknown committed
2209 2210 2211 2212 2213 2214 2215
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
2216

2217
class GRANT_NAME :public Sql_alloc
unknown's avatar
unknown committed
2218 2219
{
public:
2220 2221
  acl_host_and_ip host;
  char *db, *user, *tname, *hash_key;
2222
  ulong privs;
2223
  ulong sort;
unknown's avatar
unknown committed
2224
  uint key_length;
2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236
  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
2237
  HASH hash_columns;
unknown's avatar
unknown committed
2238 2239 2240 2241

  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);
2242
  ~GRANT_TABLE();
2243 2244
  bool ok() { return privs != 0 || cols != 0; }
};
unknown's avatar
unknown committed
2245

2246

unknown's avatar
unknown committed
2247

2248 2249 2250
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
                       const char *t, ulong p)
  :privs(p)
2251 2252
{
  /* Host given by user */
2253
  update_hostname(&host, strdup_root(&memex, h));
2254 2255
  db =   strdup_root(&memex,d);
  user = strdup_root(&memex,u);
2256
  sort=  get_sort(3,host.hostname,db,user);
2257 2258
  tname= strdup_root(&memex,t);
  if (lower_case_table_names)
unknown's avatar
unknown committed
2259
  {
2260 2261
    my_casedn_str(files_charset_info, db);
    my_casedn_str(files_charset_info, tname);
2262 2263 2264 2265
  }
  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);
2266 2267 2268 2269 2270 2271 2272
}


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)
{
2273
  (void) hash_init(&hash_columns,system_charset_info,
unknown's avatar
unknown committed
2274
                   0,0,0, (hash_get_key) get_key_column,0,0);
2275
}
unknown's avatar
unknown committed
2276

2277

2278
GRANT_NAME::GRANT_NAME(TABLE *form)
2279
{
2280
  update_hostname(&host, get_field(&memex, form->field[0]));
unknown's avatar
unknown committed
2281 2282
  db=    get_field(&memex,form->field[1]);
  user=  get_field(&memex,form->field[2]);
2283 2284
  if (!user)
    user= (char*) "";
2285
  sort=  get_sort(3, host.hostname, db, user);
unknown's avatar
unknown committed
2286
  tname= get_field(&memex,form->field[3]);
2287 2288 2289
  if (!db || !tname)
  {
    /* Wrong table row; Ignore it */
2290
    privs= 0;
2291 2292 2293 2294
    return;					/* purecov: inspected */
  }
  if (lower_case_table_names)
  {
2295 2296
    my_casedn_str(files_charset_info, db);
    my_casedn_str(files_charset_info, tname);
2297 2298 2299 2300 2301 2302 2303
  }
  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);
2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319
}


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();
2320 2321
  cols =  fix_rights_for_column(cols);

2322
  (void) hash_init(&hash_columns,system_charset_info,
unknown's avatar
unknown committed
2323
                   0,0,0, (hash_get_key) get_key_column,0,0);
2324 2325
  if (cols)
  {
2326 2327
    uint key_prefix_len;
    KEY_PART_INFO *key_part= col_privs->key_info->key_part;
2328
    col_privs->field[0]->store(host.hostname,
2329 2330
                               host.hostname ? (uint) strlen(host.hostname) :
                               0,
2331 2332 2333 2334
                               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);
2335 2336 2337 2338 2339 2340

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

2343
    col_privs->file->ha_index_init(0, 1);
unknown's avatar
unknown committed
2344
    if (col_privs->file->index_read(col_privs->record[0],
2345 2346
                                    (byte*) key,
                                    key_prefix_len, HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
2347
    {
2348
      cols = 0; /* purecov: deadcode */
unknown's avatar
unknown committed
2349
      col_privs->file->ha_index_end();
2350
      return;
unknown's avatar
unknown committed
2351
    }
2352
    do
unknown's avatar
unknown committed
2353
    {
2354 2355 2356
      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
2357
      res=col_privs->field[4]->val_str(&column_name);
2358 2359 2360
      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
2361
      {
2362 2363 2364
        /* Don't use this entry */
        privs = cols = 0;			/* purecov: deadcode */
        return;				/* purecov: deadcode */
unknown's avatar
unknown committed
2365
      }
unknown's avatar
unknown committed
2366
      my_hash_insert(&hash_columns, (byte *) mem_check);
2367
    } while (!col_privs->file->index_next(col_privs->record[0]) &&
2368
             !key_cmp_if_same(col_privs,key,0,key_prefix_len));
unknown's avatar
unknown committed
2369
    col_privs->file->ha_index_end();
unknown's avatar
unknown committed
2370
  }
2371
}
unknown's avatar
unknown committed
2372

unknown's avatar
unknown committed
2373

2374 2375 2376 2377 2378 2379
GRANT_TABLE::~GRANT_TABLE()
{
  hash_free(&hash_columns);
}


2380
static byte* get_grant_table(GRANT_NAME *buff,uint *length,
unknown's avatar
unknown committed
2381 2382 2383 2384 2385 2386
			     my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
  return (byte*) buff->hash_key;
}

unknown's avatar
unknown committed
2387

unknown's avatar
unknown committed
2388 2389 2390 2391 2392
void free_grant_table(GRANT_TABLE *grant_table)
{
  hash_free(&grant_table->hash_columns);
}

unknown's avatar
unknown committed
2393

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

2396
static GRANT_NAME *name_hash_search(HASH *name_hash,
unknown's avatar
unknown committed
2397 2398 2399 2400
                                    const char *host,const char* ip,
                                    const char *db,
                                    const char *user, const char *tname,
                                    bool exact)
unknown's avatar
unknown committed
2401
{
unknown's avatar
unknown committed
2402
  char helping [NAME_LEN*2+USERNAME_LENGTH+3];
unknown's avatar
unknown committed
2403
  uint len;
2404
  GRANT_NAME *grant_name,*found=0;
2405
  HASH_SEARCH_STATE state;
unknown's avatar
unknown committed
2406 2407

  len  = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1;
2408 2409
  for (grant_name= (GRANT_NAME*) hash_first(name_hash, (byte*) helping,
                                            len, &state);
2410 2411
       grant_name ;
       grant_name= (GRANT_NAME*) hash_next(name_hash,(byte*) helping,
2412
                                           len, &state))
unknown's avatar
unknown committed
2413 2414 2415
  {
    if (exact)
    {
2416 2417
      if (!grant_name->host.hostname ||
          (host &&
2418
	   !my_strcasecmp(system_charset_info, host,
unknown's avatar
unknown committed
2419 2420
                          grant_name->host.hostname)) ||
	  (ip && !strcmp(ip, grant_name->host.hostname)))
2421
	return grant_name;
unknown's avatar
unknown committed
2422 2423 2424
    }
    else
    {
unknown's avatar
unknown committed
2425
      if (compare_hostname(&grant_name->host, host, ip) &&
2426 2427
          (!found || found->sort < grant_name->sort))
	found=grant_name;					// Host ok
unknown's avatar
unknown committed
2428 2429 2430 2431 2432 2433
    }
  }
  return found;
}


2434
inline GRANT_NAME *
2435 2436
routine_hash_search(const char *host, const char *ip, const char *db,
                 const char *user, const char *tname, bool proc, bool exact)
2437
{
2438 2439 2440
  return (GRANT_TABLE*)
    name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
		     host, ip, db, user, tname, exact);
2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451
}


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
2452

unknown's avatar
unknown committed
2453
inline GRANT_COLUMN *
unknown's avatar
unknown committed
2454
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
unknown's avatar
unknown committed
2455 2456 2457 2458 2459 2460 2461 2462 2463
{
  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
2464
				ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
2465 2466 2467
{
  int error=0,result=0;
  byte key[MAX_KEY_LENGTH];
2468 2469
  uint key_prefix_length;
  KEY_PART_INFO *key_part= table->key_info->key_part;
unknown's avatar
unknown committed
2470 2471
  DBUG_ENTER("replace_column_table");

2472
  table->use_all_columns();
unknown's avatar
unknown committed
2473 2474 2475 2476 2477 2478 2479 2480
  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
2481

2482 2483 2484 2485
  /* 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
2486

2487
  rights&= COL_ACLS;				// Only ACL for columns
unknown's avatar
unknown committed
2488 2489 2490 2491

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

  List_iterator <LEX_COLUMN> iter(columns);
unknown's avatar
unknown committed
2492
  class LEX_COLUMN *column;
2493
  table->file->ha_index_init(0, 1);
unknown's avatar
unknown committed
2494
  while ((column= iter++))
unknown's avatar
unknown committed
2495
  {
unknown's avatar
unknown committed
2496
    ulong privileges= column->rights;
unknown's avatar
unknown committed
2497
    bool old_row_exists=0;
2498 2499 2500 2501
    byte user_key[MAX_KEY_LENGTH];

    key_restore(table->record[0],key,table->key_info,
                key_prefix_length);
unknown's avatar
unknown committed
2502
    table->field[4]->store(column->column.ptr(), column->column.length(),
2503
                           system_charset_info);
2504 2505 2506
    /* 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
2507

2508 2509 2510
    if (table->file->index_read(table->record[0], user_key,
				table->key_info->key_length,
                                HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
2511 2512 2513
    {
      if (revoke_grant)
      {
unknown's avatar
unknown committed
2514
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
2515
                 combo.user.str, combo.host.str,
unknown's avatar
unknown committed
2516 2517 2518
                 table_name);                   /* purecov: inspected */
	result= -1;                             /* purecov: inspected */
	continue;                               /* purecov: inspected */
unknown's avatar
unknown committed
2519
      }
unknown's avatar
unknown committed
2520
      old_row_exists = 0;
2521
      restore_record(table, s->default_values);		// Get empty record
2522 2523
      key_restore(table->record[0],key,table->key_info,
                  key_prefix_length);
unknown's avatar
unknown committed
2524
      table->field[4]->store(column->column.ptr(),column->column.length(),
2525
                             system_charset_info);
unknown's avatar
unknown committed
2526 2527 2528
    }
    else
    {
unknown's avatar
unknown committed
2529
      ulong tmp= (ulong) table->field[6]->val_int();
unknown's avatar
unknown committed
2530 2531 2532 2533 2534 2535
      tmp=fix_rights_for_column(tmp);

      if (revoke_grant)
	privileges = tmp & ~(privileges | rights);
      else
	privileges |= tmp;
unknown's avatar
unknown committed
2536
      old_row_exists = 1;
unknown's avatar
unknown committed
2537
      store_record(table,record[1]);			// copy original row
unknown's avatar
unknown committed
2538 2539
    }

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

unknown's avatar
unknown committed
2542
    if (old_row_exists)
unknown's avatar
unknown committed
2543
    {
unknown's avatar
unknown committed
2544
      GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
2545
      if (privileges)
2546
	error=table->file->ha_update_row(table->record[1],table->record[0]);
unknown's avatar
unknown committed
2547
      else
2548
	error=table->file->ha_delete_row(table->record[1]);
unknown's avatar
unknown committed
2549 2550 2551 2552 2553 2554
      if (error)
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
unknown's avatar
unknown committed
2555 2556
      grant_column= column_hash_search(g_t, column->column.ptr(),
                                       column->column.length());
unknown's avatar
unknown committed
2557
      if (grant_column)				// Should always be true
unknown's avatar
unknown committed
2558
	grant_column->rights= privileges;	// Update hash
unknown's avatar
unknown committed
2559 2560 2561
    }
    else					// new grant
    {
unknown's avatar
unknown committed
2562
      GRANT_COLUMN *grant_column;
2563
      if ((error=table->file->ha_write_row(table->record[0])))
unknown's avatar
unknown committed
2564 2565 2566 2567 2568
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
unknown's avatar
unknown committed
2569
      grant_column= new GRANT_COLUMN(column->column,privileges);
unknown's avatar
SCRUM  
unknown committed
2570
      my_hash_insert(&g_t->hash_columns,(byte*) grant_column);
unknown's avatar
unknown committed
2571 2572 2573 2574 2575 2576 2577 2578 2579 2580
    }
  }

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

  if (revoke_grant)
  {
2581 2582
    byte user_key[MAX_KEY_LENGTH];
    key_copy(user_key, table->record[0], table->key_info,
unknown's avatar
unknown committed
2583 2584
             key_prefix_length);

2585
    if (table->file->index_read(table->record[0], user_key,
unknown's avatar
unknown committed
2586
				key_prefix_length,
2587
                                HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
2588 2589
      goto end;

2590
    /* Scan through all rows with the same host,db,user and table */
unknown's avatar
unknown committed
2591 2592
    do
    {
unknown's avatar
unknown committed
2593
      ulong privileges = (ulong) table->field[6]->val_int();
unknown's avatar
unknown committed
2594
      privileges=fix_rights_for_column(privileges);
unknown's avatar
unknown committed
2595
      store_record(table,record[1]);
unknown's avatar
unknown committed
2596 2597 2598 2599 2600

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

	privileges&= ~rights;
	table->field[6]->store((longlong)
2606
			       get_rights_for_column(privileges), TRUE);
2607
	table->field[4]->val_str(&column_name);
unknown's avatar
unknown committed
2608 2609 2610 2611 2612 2613
	grant_column = column_hash_search(g_t,
					  column_name.ptr(),
					  column_name.length());
	if (privileges)
	{
	  int tmp_error;
2614 2615
	  if ((tmp_error=table->file->ha_update_row(table->record[1],
						    table->record[0])))
unknown's avatar
unknown committed
2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626
	  {					/* 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;
2627
	  if ((tmp_error = table->file->ha_delete_row(table->record[1])))
unknown's avatar
unknown committed
2628 2629 2630 2631 2632 2633 2634 2635 2636 2637
	  {					/* 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]) &&
2638
	     !key_cmp_if_same(table, key, 0, key_prefix_length));
unknown's avatar
unknown committed
2639 2640
  }

2641
end:
unknown's avatar
unknown committed
2642
  table->file->ha_index_end();
unknown's avatar
unknown committed
2643 2644 2645 2646 2647 2648 2649
  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
2650 2651
			       ulong rights, ulong col_rights,
			       bool revoke_grant)
unknown's avatar
unknown committed
2652
{
2653
  char grantor[USER_HOST_BUFF_SIZE];
unknown's avatar
unknown committed
2654
  int old_row_exists = 1;
unknown's avatar
unknown committed
2655
  int error=0;
unknown's avatar
unknown committed
2656
  ulong store_table_rights, store_col_rights;
2657
  byte user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
2658 2659
  DBUG_ENTER("replace_table_table");

2660 2661
  strxmov(grantor, thd->security_ctx->user, "@",
          thd->security_ctx->host_or_ip, NullS);
unknown's avatar
unknown committed
2662

unknown's avatar
unknown committed
2663 2664 2665 2666
  /*
    The following should always succeed as new users are created before
    this function is called!
  */
2667
  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
unknown's avatar
unknown committed
2668
  {
unknown's avatar
unknown committed
2669 2670
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
               MYF(0));	/* purecov: deadcode */
unknown's avatar
unknown committed
2671 2672 2673
    DBUG_RETURN(-1);				/* purecov: deadcode */
  }

2674
  table->use_all_columns();
2675
  restore_record(table, s->default_values);     // Get empty record
2676 2677
  table->field[0]->store(combo.host.str,combo.host.length,
                         system_charset_info);
2678
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
2679 2680 2681 2682
  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
2683
  store_record(table,record[1]);			// store at pos 1
2684 2685
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);
unknown's avatar
unknown committed
2686

2687 2688
  if (table->file->index_read_idx(table->record[0], 0,
                                  user_key, table->key_info->key_length,
unknown's avatar
unknown committed
2689 2690 2691 2692 2693 2694 2695 2696 2697
				  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
2698 2699
      my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
               combo.user.str, combo.host.str,
2700
               table_name);		        /* purecov: deadcode */
unknown's avatar
unknown committed
2701 2702
      DBUG_RETURN(-1);				/* purecov: deadcode */
    }
unknown's avatar
unknown committed
2703
    old_row_exists = 0;
unknown's avatar
unknown committed
2704
    restore_record(table,record[1]);			// Get saved record
unknown's avatar
unknown committed
2705 2706
  }

unknown's avatar
unknown committed
2707 2708
  store_table_rights= get_rights_for_table(rights);
  store_col_rights=   get_rights_for_column(col_rights);
unknown's avatar
unknown committed
2709
  if (old_row_exists)
unknown's avatar
unknown committed
2710
  {
unknown's avatar
unknown committed
2711
    ulong j,k;
unknown's avatar
unknown committed
2712
    store_record(table,record[1]);
unknown's avatar
unknown committed
2713 2714
    j = (ulong) table->field[6]->val_int();
    k = (ulong) table->field[7]->val_int();
unknown's avatar
unknown committed
2715 2716 2717

    if (revoke_grant)
    {
2718
      /* column rights are already fixed in mysql_table_grant */
unknown's avatar
unknown committed
2719 2720 2721 2722
      store_table_rights=j & ~store_table_rights;
    }
    else
    {
unknown's avatar
unknown committed
2723 2724
      store_table_rights|= j;
      store_col_rights|=   k;
unknown's avatar
unknown committed
2725 2726 2727
    }
  }

2728
  table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
2729 2730
  table->field[6]->store((longlong) store_table_rights, TRUE);
  table->field[7]->store((longlong) store_col_rights, TRUE);
unknown's avatar
unknown committed
2731
  rights=fix_rights_for_table(store_table_rights);
unknown's avatar
unknown committed
2732
  col_rights=fix_rights_for_column(store_col_rights);
unknown's avatar
unknown committed
2733

unknown's avatar
unknown committed
2734
  if (old_row_exists)
unknown's avatar
unknown committed
2735 2736 2737
  {
    if (store_table_rights || store_col_rights)
    {
2738
      if ((error=table->file->ha_update_row(table->record[1],table->record[0])))
unknown's avatar
unknown committed
2739 2740
	goto table_error;			/* purecov: deadcode */
    }
2741
    else if ((error = table->file->ha_delete_row(table->record[1])))
unknown's avatar
unknown committed
2742 2743 2744 2745
      goto table_error;				/* purecov: deadcode */
  }
  else
  {
2746
    error=table->file->ha_write_row(table->record[0]);
2747
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
unknown's avatar
unknown committed
2748 2749 2750
      goto table_error;				/* purecov: deadcode */
  }

unknown's avatar
unknown committed
2751
  if (rights | col_rights)
unknown's avatar
unknown committed
2752
  {
unknown's avatar
unknown committed
2753
    grant_table->privs= rights;
2754
    grant_table->cols=	col_rights;
unknown's avatar
unknown committed
2755 2756 2757
  }
  else
  {
2758
    hash_delete(&column_priv_hash,(byte*) grant_table);
unknown's avatar
unknown committed
2759 2760 2761
  }
  DBUG_RETURN(0);

2762 2763
  /* This should never happen */
table_error:
unknown's avatar
unknown committed
2764 2765 2766 2767 2768
  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
  DBUG_RETURN(-1); /* purecov: deadcode */
}


2769
static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
2770
			      TABLE *table, const LEX_USER &combo,
2771 2772
			      const char *db, const char *routine_name,
			      bool is_proc, ulong rights, bool revoke_grant)
2773
{
2774
  char grantor[USER_HOST_BUFF_SIZE];
2775 2776 2777
  int old_row_exists= 1;
  int error=0;
  ulong store_proc_rights;
2778
  DBUG_ENTER("replace_routine_table");
2779 2780 2781 2782 2783 2784 2785

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

2786 2787
  strxmov(grantor, thd->security_ctx->user, "@",
          thd->security_ctx->host_or_ip, NullS);
2788 2789 2790 2791 2792

  /*
    The following should always succeed as new users are created before
    this function is called!
  */
unknown's avatar
unknown committed
2793
  if (!find_acl_user(combo.host.str, combo.user.str, FALSE))
2794 2795 2796 2797 2798
  {
    my_error(ER_PASSWORD_NO_MATCH,MYF(0));
    DBUG_RETURN(-1);
  }

2799
  table->use_all_columns();
2800
  restore_record(table, s->default_values);		// Get empty record
2801 2802 2803
  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);
2804 2805 2806
  table->field[3]->store(routine_name,(uint) strlen(routine_name),
                         &my_charset_latin1);
  table->field[4]->store((longlong)(is_proc ? 
2807 2808
                                    TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION),
                         TRUE);
2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822
  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),
2823
               combo.user.str, combo.host.str, routine_name);
2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847
      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;
    }
  }

2848
  table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
2849
  table->field[6]->store((longlong) store_proc_rights, TRUE);
2850 2851 2852 2853 2854 2855
  rights=fix_rights_for_procedure(store_proc_rights);

  if (old_row_exists)
  {
    if (store_proc_rights)
    {
2856
      if ((error=table->file->ha_update_row(table->record[1],table->record[0])))
2857 2858
	goto table_error;
    }
2859
    else if ((error= table->file->ha_delete_row(table->record[1])))
2860 2861 2862 2863
      goto table_error;
  }
  else
  {
2864
    error=table->file->ha_write_row(table->record[0]);
2865
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
2866 2867 2868 2869 2870 2871 2872 2873 2874
      goto table_error;
  }

  if (rights)
  {
    grant_name->privs= rights;
  }
  else
  {
2875
    hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
2876 2877 2878 2879 2880 2881 2882 2883 2884 2885
  }
  DBUG_RETURN(0);

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


2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898
/*
  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
2899 2900
    FALSE ok
    TRUE  error
2901 2902
*/

unknown's avatar
unknown committed
2903
bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
unknown's avatar
unknown committed
2904 2905 2906
		      List <LEX_USER> &user_list,
		      List <LEX_COLUMN> &columns, ulong rights,
		      bool revoke_grant)
unknown's avatar
unknown committed
2907
{
2908
  ulong column_priv= 0;
unknown's avatar
unknown committed
2909
  List_iterator <LEX_USER> str_list (user_list);
2910
  LEX_USER *Str, *tmp_Str;
unknown's avatar
unknown committed
2911
  TABLE_LIST tables[3];
unknown's avatar
unknown committed
2912
  bool create_new_users=0;
2913
  char *db_name, *table_name;
unknown's avatar
unknown committed
2914 2915 2916 2917
  DBUG_ENTER("mysql_table_grant");

  if (!initialized)
  {
unknown's avatar
unknown committed
2918 2919
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: inspected */
unknown's avatar
unknown committed
2920
    DBUG_RETURN(TRUE);				/* purecov: inspected */
unknown's avatar
unknown committed
2921 2922 2923
  }
  if (rights & ~TABLE_ACLS)
  {
unknown's avatar
unknown committed
2924 2925
    my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
               MYF(0));
unknown's avatar
unknown committed
2926
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
2927 2928
  }

2929
  if (!revoke_grant)
unknown's avatar
unknown committed
2930
  {
unknown's avatar
unknown committed
2931
    if (columns.elements)
unknown's avatar
unknown committed
2932
    {
2933 2934
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
unknown's avatar
unknown committed
2935 2936 2937

      if (open_and_lock_tables(thd, table_list))
        DBUG_RETURN(TRUE);
2938 2939

      while ((column = column_iter++))
unknown's avatar
unknown committed
2940
      {
unknown's avatar
merged  
unknown committed
2941
        uint unused_field_idx= NO_CACHED_FIELD_INDEX;
unknown's avatar
unknown committed
2942 2943
        TABLE_LIST *dummy;
        Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
2944
                                         column->column.length(),
unknown's avatar
unknown committed
2945
                                         column->column.ptr(), NULL, NULL,
2946
                                         NULL, TRUE, FALSE,
unknown's avatar
unknown committed
2947
                                         &unused_field_idx, FALSE, &dummy);
unknown's avatar
unknown committed
2948
        if (f == (Field*)0)
2949
        {
unknown's avatar
merged  
unknown committed
2950 2951
          my_error(ER_BAD_FIELD_ERROR, MYF(0),
                   column->column.c_ptr(), table_list->alias);
unknown's avatar
unknown committed
2952
          DBUG_RETURN(TRUE);
2953
        }
unknown's avatar
unknown committed
2954 2955
        if (f == (Field *)-1)
          DBUG_RETURN(TRUE);
2956
        column_priv|= column->rights;
unknown's avatar
unknown committed
2957
      }
2958
      close_thread_tables(thd);
unknown's avatar
unknown committed
2959
    }
2960
    else
unknown's avatar
unknown committed
2961
    {
2962 2963 2964
      if (!(rights & CREATE_ACL))
      {
        char buf[FN_REFLEN];
2965
        build_table_filename(buf, sizeof(buf), table_list->db,
2966
                             table_list->table_name, reg_ext, 0);
2967 2968
        fn_format(buf, buf, "", "", MY_UNPACK_FILENAME  | MY_RESOLVE_SYMLINKS |
                                    MY_RETURN_REAL_PATH | MY_APPEND_EXT);
2969 2970
        if (access(buf,F_OK))
        {
unknown's avatar
merged  
unknown committed
2971
          my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
unknown's avatar
unknown committed
2972
          DBUG_RETURN(TRUE);
2973 2974 2975 2976 2977 2978 2979 2980
        }
      }
      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),
2981 2982
                 command, thd->security_ctx->priv_user,
                 thd->security_ctx->host_or_ip, table_list->alias);
2983 2984
        DBUG_RETURN(-1);
      }
unknown's avatar
unknown committed
2985 2986 2987 2988 2989 2990
    }
  }

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

  bzero((char*) &tables,sizeof(tables));
2991 2992 2993
  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
2994
  tables[0].next_local= tables[0].next_global= tables+1;
unknown's avatar
unknown committed
2995
  /* Don't open column table if we don't need it ! */
unknown's avatar
VIEW  
unknown committed
2996 2997 2998 2999 3000
  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
3001 3002 3003
  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";

3004 3005 3006 3007 3008 3009 3010
  /*
    This statement will be replicated as a statement, even when using
    row-based replication.  The flag will be reset at the end of the
    statement.
  */
  thd->clear_current_stmt_binlog_row_based();

3011 3012 3013 3014 3015
#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
3016
  if (thd->slave_thread && rpl_filter->is_on())
3017
  {
unknown's avatar
unknown committed
3018 3019 3020
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
3021
    */
3022
    tables[0].updating= tables[1].updating= tables[2].updating= 1;
unknown's avatar
unknown committed
3023
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
unknown's avatar
unknown committed
3024
      DBUG_RETURN(FALSE);
3025
  }
3026 3027
#endif

3028
  if (simple_open_n_lock_tables(thd,tables))
unknown's avatar
unknown committed
3029 3030
  {						// Should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
unknown's avatar
unknown committed
3031
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
unknown's avatar
unknown committed
3032 3033
  }

unknown's avatar
unknown committed
3034 3035
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
unknown's avatar
unknown committed
3036
  bool result= FALSE;
3037
  rw_wrlock(&LOCK_grant);
3038
  pthread_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
3039 3040
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;
3041
  grant_version++;
unknown's avatar
unknown committed
3042

3043
  while ((tmp_Str = str_list++))
unknown's avatar
unknown committed
3044
  {
unknown's avatar
unknown committed
3045
    int error;
unknown's avatar
unknown committed
3046
    GRANT_TABLE *grant_table;
3047 3048 3049 3050 3051
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
    }  
unknown's avatar
unknown committed
3052
    /* Create user if needed */
unknown's avatar
unknown committed
3053
    error=replace_user_table(thd, tables[0].table, *Str,
unknown's avatar
unknown committed
3054
			     0, revoke_grant, create_new_users,
unknown's avatar
unknown committed
3055 3056
                             test(thd->variables.sql_mode &
                                  MODE_NO_AUTO_CREATE_USER));
unknown's avatar
unknown committed
3057
    if (error)
unknown's avatar
unknown committed
3058
    {
unknown's avatar
unknown committed
3059
      result= TRUE;				// Remember error
unknown's avatar
unknown committed
3060 3061 3062
      continue;					// Add next user
    }

unknown's avatar
VIEW  
unknown committed
3063 3064 3065
    db_name= (table_list->view_db.length ?
	      table_list->view_db.str :
	      table_list->db);
3066
    table_name= (table_list->view_name.length ?
unknown's avatar
VIEW  
unknown committed
3067
		table_list->view_name.str :
3068
		table_list->table_name);
unknown's avatar
VIEW  
unknown committed
3069

unknown's avatar
unknown committed
3070
    /* Find/create cached table grant */
unknown's avatar
VIEW  
unknown committed
3071
    grant_table= table_hash_search(Str->host.str, NullS, db_name,
3072
				   Str->user.str, table_name, 1);
unknown's avatar
unknown committed
3073 3074 3075 3076
    if (!grant_table)
    {
      if (revoke_grant)
      {
unknown's avatar
unknown committed
3077
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
3078
                 Str->user.str, Str->host.str, table_list->table_name);
unknown's avatar
unknown committed
3079
	result= TRUE;
unknown's avatar
unknown committed
3080 3081
	continue;
      }
unknown's avatar
VIEW  
unknown committed
3082
      grant_table = new GRANT_TABLE (Str->host.str, db_name,
3083
				     Str->user.str, table_name,
unknown's avatar
unknown committed
3084 3085 3086 3087
				     rights,
				     column_priv);
      if (!grant_table)				// end of memory
      {
unknown's avatar
unknown committed
3088
	result= TRUE;				/* purecov: deadcode */
unknown's avatar
unknown committed
3089 3090
	continue;				/* purecov: deadcode */
      }
unknown's avatar
SCRUM  
unknown committed
3091
      my_hash_insert(&column_priv_hash,(byte*) grant_table);
unknown's avatar
unknown committed
3092 3093 3094 3095 3096
    }

    /* If revoke_grant, calculate the new column privilege for tables_priv */
    if (revoke_grant)
    {
3097 3098
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
unknown's avatar
unknown committed
3099 3100 3101
      GRANT_COLUMN *grant_column;

      /* Fix old grants */
3102
      while ((column = column_iter++))
unknown's avatar
unknown committed
3103 3104
      {
	grant_column = column_hash_search(grant_table,
3105 3106
					  column->column.ptr(),
					  column->column.length());
unknown's avatar
unknown committed
3107
	if (grant_column)
3108
	  grant_column->rights&= ~(column->rights | rights);
unknown's avatar
unknown committed
3109 3110
      }
      /* scan trough all columns to get new column grant */
3111
      column_priv= 0;
unknown's avatar
unknown committed
3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127
      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
3128
    if (replace_table_table(thd, grant_table, tables[1].table, *Str,
3129
			    db_name, table_name,
unknown's avatar
unknown committed
3130
			    rights, column_priv, revoke_grant))
3131 3132
    {
      /* Should only happen if table is crashed */
unknown's avatar
unknown committed
3133
      result= TRUE;			       /* purecov: deadcode */
unknown's avatar
unknown committed
3134 3135 3136
    }
    else if (tables[2].table)
    {
unknown's avatar
VIEW  
unknown committed
3137
      if ((replace_column_table(grant_table, tables[2].table, *Str,
unknown's avatar
unknown committed
3138
				columns,
3139
				db_name, table_name,
unknown's avatar
unknown committed
3140 3141
				rights, revoke_grant)))
      {
unknown's avatar
unknown committed
3142
	result= TRUE;
unknown's avatar
unknown committed
3143 3144 3145 3146
      }
    }
  }
  grant_option=TRUE;
unknown's avatar
unknown committed
3147
  thd->mem_root= old_root;
3148
  pthread_mutex_unlock(&acl_cache->lock);
3149 3150 3151 3152 3153 3154

  if (!result) /* success */
  {
    if (mysql_bin_log.is_open())
    {
      thd->clear_error();
3155 3156
      thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                        thd->query, thd->query_length, FALSE, FALSE);
3157 3158 3159
    }
  }

3160
  rw_unlock(&LOCK_grant);
3161 3162

  if (!result) /* success */
3163
    send_ok(thd);
3164

3165
  /* Tables are automatically closed */
unknown's avatar
unknown committed
3166 3167 3168 3169
  DBUG_RETURN(result);
}


3170
/*
3171
  Store routine level grants in the privilege tables
3172 3173

  SYNOPSIS
3174
    mysql_routine_grant()
3175
    thd			Thread handle
3176 3177
    table_list		List of routines to give grant
    is_proc             true indicates routine list are procedures
3178 3179 3180 3181 3182 3183 3184 3185 3186
    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
*/

3187 3188 3189
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)
3190 3191
{
  List_iterator <LEX_USER> str_list (user_list);
3192
  LEX_USER *Str, *tmp_Str;
3193 3194
  TABLE_LIST tables[2];
  bool create_new_users=0, result=0;
3195
  char *db_name, *table_name;
3196
  DBUG_ENTER("mysql_routine_grant");
3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214

  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)
  {
3215
    if (sp_exist_routines(thd, table_list, is_proc, no_error)<0)
3216 3217 3218 3219 3220 3221
      DBUG_RETURN(TRUE);
  }

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

  bzero((char*) &tables,sizeof(tables));
3222 3223
  tables[0].alias=tables[0].table_name= (char*) "user";
  tables[1].alias=tables[1].table_name= (char*) "procs_priv";
3224 3225 3226 3227
  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";

3228 3229 3230 3231 3232 3233 3234
  /*
    This statement will be replicated as a statement, even when using
    row-based replication.  The flag will be reset at the end of the
    statement.
  */
  thd->clear_current_stmt_binlog_row_based();

3235 3236 3237 3238 3239
#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
3240
  if (thd->slave_thread && rpl_filter->is_on())
3241 3242 3243 3244 3245 3246
  {
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
    */
    tables[0].updating= tables[1].updating= 1;
unknown's avatar
unknown committed
3247
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260
      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);
3261
  pthread_mutex_lock(&acl_cache->lock);
3262 3263 3264 3265 3266
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;

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

3267
  while ((tmp_Str= str_list++))
3268 3269 3270
  {
    int error;
    GRANT_NAME *grant_name;
3271 3272 3273 3274 3275
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
    }  
3276 3277
    /* Create user if needed */
    error=replace_user_table(thd, tables[0].table, *Str,
unknown's avatar
unknown committed
3278
			     0, revoke_grant, create_new_users,
unknown's avatar
unknown committed
3279 3280
                             test(thd->variables.sql_mode &
                                  MODE_NO_AUTO_CREATE_USER));
3281 3282 3283 3284 3285 3286 3287
    if (error)
    {
      result= TRUE;				// Remember error
      continue;					// Add next user
    }

    db_name= table_list->db;
3288
    table_name= table_list->table_name;
3289

3290 3291
    grant_name= routine_hash_search(Str->host.str, NullS, db_name,
                                    Str->user.str, table_name, is_proc, 1);
3292 3293 3294 3295 3296 3297
    if (!grant_name)
    {
      if (revoke_grant)
      {
        if (!no_error)
          my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
3298
		   Str->user.str, Str->host.str, table_name);
3299 3300 3301 3302
	result= TRUE;
	continue;
      }
      grant_name= new GRANT_NAME(Str->host.str, db_name,
3303
				 Str->user.str, table_name,
3304 3305 3306 3307 3308 3309
				 rights);
      if (!grant_name)
      {
        result= TRUE;
	continue;
      }
3310
      my_hash_insert(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
3311
    }
unknown's avatar
unknown committed
3312

3313 3314
    if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
			   db_name, table_name, is_proc, rights, revoke_grant))
3315 3316 3317 3318 3319 3320 3321
    {
      result= TRUE;
      continue;
    }
  }
  grant_option=TRUE;
  thd->mem_root= old_root;
3322
  pthread_mutex_unlock(&acl_cache->lock);
3323 3324 3325 3326 3327
  if (!result && !no_error)
  {
    if (mysql_bin_log.is_open())
    {
      thd->clear_error();
3328 3329
      thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                        thd->query, thd->query_length, FALSE, FALSE);
3330 3331 3332
    }
  }

3333
  rw_unlock(&LOCK_grant);
3334

3335 3336
  if (!result && !no_error)
    send_ok(thd);
3337

3338 3339 3340 3341 3342
  /* Tables are automatically closed */
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
3343 3344
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
                 ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
3345 3346
{
  List_iterator <LEX_USER> str_list (list);
3347
  LEX_USER *Str, *tmp_Str;
unknown's avatar
unknown committed
3348
  char tmp_db[NAME_LEN+1];
unknown's avatar
unknown committed
3349
  bool create_new_users=0;
unknown's avatar
unknown committed
3350 3351 3352 3353
  TABLE_LIST tables[2];
  DBUG_ENTER("mysql_grant");
  if (!initialized)
  {
unknown's avatar
unknown committed
3354 3355
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: tested */
unknown's avatar
unknown committed
3356
    DBUG_RETURN(TRUE);				/* purecov: tested */
unknown's avatar
unknown committed
3357 3358
  }

unknown's avatar
unknown committed
3359 3360 3361
  if (lower_case_table_names && db)
  {
    strmov(tmp_db,db);
3362
    my_casedn_str(files_charset_info, tmp_db);
unknown's avatar
unknown committed
3363 3364
    db=tmp_db;
  }
unknown's avatar
unknown committed
3365 3366

  /* open the mysql.user and mysql.db tables */
3367
  bzero((char*) &tables,sizeof(tables));
3368 3369
  tables[0].alias=tables[0].table_name=(char*) "user";
  tables[1].alias=tables[1].table_name=(char*) "db";
unknown's avatar
VIEW  
unknown committed
3370
  tables[0].next_local= tables[0].next_global= tables+1;
unknown's avatar
unknown committed
3371 3372
  tables[0].lock_type=tables[1].lock_type=TL_WRITE;
  tables[0].db=tables[1].db=(char*) "mysql";
3373

3374 3375 3376 3377 3378 3379 3380
  /*
    This statement will be replicated as a statement, even when using
    row-based replication.  The flag will be reset at the end of the
    statement.
  */
  thd->clear_current_stmt_binlog_row_based();

3381 3382 3383 3384 3385
#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
3386
  if (thd->slave_thread && rpl_filter->is_on())
3387
  {
unknown's avatar
unknown committed
3388 3389 3390
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
3391
    */
3392
    tables[0].updating= tables[1].updating= 1;
unknown's avatar
unknown committed
3393
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
unknown's avatar
unknown committed
3394
      DBUG_RETURN(FALSE);
3395
  }
3396 3397
#endif

3398
  if (simple_open_n_lock_tables(thd,tables))
unknown's avatar
unknown committed
3399 3400
  {						// This should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
unknown's avatar
unknown committed
3401
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
unknown's avatar
unknown committed
3402 3403
  }

unknown's avatar
unknown committed
3404 3405
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
unknown's avatar
unknown committed
3406

3407
  /* go through users in user_list */
3408
  rw_wrlock(&LOCK_grant);
unknown's avatar
unknown committed
3409 3410 3411 3412
  VOID(pthread_mutex_lock(&acl_cache->lock));
  grant_version++;

  int result=0;
3413
  while ((tmp_Str = str_list++))
unknown's avatar
unknown committed
3414
  {
3415 3416 3417 3418
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
unknown's avatar
unknown committed
3419
    }
unknown's avatar
unknown committed
3420 3421
    if (replace_user_table(thd, tables[0].table, *Str,
                           (!db ? rights : 0), revoke_grant, create_new_users,
unknown's avatar
unknown committed
3422 3423
                           test(thd->variables.sql_mode &
                                MODE_NO_AUTO_CREATE_USER)))
unknown's avatar
unknown committed
3424
      result= -1;
unknown's avatar
unknown committed
3425
    else if (db)
unknown's avatar
unknown committed
3426
    {
unknown's avatar
unknown committed
3427 3428 3429 3430 3431 3432 3433 3434 3435
      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
3436
	my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
unknown's avatar
unknown committed
3437
	result= -1;
unknown's avatar
unknown committed
3438
      }
unknown's avatar
unknown committed
3439
    }
unknown's avatar
unknown committed
3440 3441
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
3442 3443 3444 3445 3446 3447

  if (!result)
  {
    if (mysql_bin_log.is_open())
    {
      thd->clear_error();
3448 3449
      thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                        thd->query, thd->query_length, FALSE, FALSE);
3450 3451 3452
    }
  }

3453
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3454 3455 3456
  close_thread_tables(thd);

  if (!result)
3457
    send_ok(thd);
3458

unknown's avatar
unknown committed
3459 3460 3461
  DBUG_RETURN(result);
}

unknown's avatar
unknown committed
3462 3463

/* Free grant array if possible */
unknown's avatar
unknown committed
3464 3465 3466 3467 3468

void  grant_free(void)
{
  DBUG_ENTER("grant_free");
  grant_option = FALSE;
3469
  hash_free(&column_priv_hash);
3470
  hash_free(&proc_priv_hash);
unknown's avatar
unknown committed
3471
  hash_free(&func_priv_hash);
3472
  free_root(&memex,MYF(0));
unknown's avatar
unknown committed
3473 3474 3475 3476
  DBUG_VOID_RETURN;
}


3477 3478 3479 3480 3481 3482
/*
  Initialize structures responsible for table/column-level privilege checking
  and load information for them from tables in the 'mysql' database.

  SYNOPSIS
    grant_init()
unknown's avatar
unknown committed
3483

3484 3485 3486 3487
  RETURN VALUES
    0	ok
    1	Could not initialize grant's
*/
unknown's avatar
unknown committed
3488

3489
my_bool grant_init()
unknown's avatar
unknown committed
3490
{
unknown's avatar
unknown committed
3491
  THD  *thd;
3492 3493 3494 3495 3496
  my_bool return_val;
  DBUG_ENTER("grant_init");

  if (!(thd= new THD))
    DBUG_RETURN(1);				/* purecov: deadcode */
3497
  thd->thread_stack= (char*) &thd;
3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523
  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
3524
  MEM_ROOT *memex_ptr;
3525
  my_bool return_val= 1;
3526
  TABLE *t_table, *c_table, *p_table;
unknown's avatar
SCRUM  
unknown committed
3527
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
3528 3529 3530
  MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
                                                           THR_MALLOC);
  DBUG_ENTER("grant_load");
unknown's avatar
unknown committed
3531 3532

  grant_option = FALSE;
3533
  (void) hash_init(&column_priv_hash,system_charset_info,
unknown's avatar
unknown committed
3534
		   0,0,0, (hash_get_key) get_grant_table,
unknown's avatar
unknown committed
3535
		   (hash_free_key) free_grant_table,0);
3536 3537 3538
  (void) hash_init(&proc_priv_hash,system_charset_info,
		   0,0,0, (hash_get_key) get_grant_table,
		   0,0);
3539 3540 3541
  (void) hash_init(&func_priv_hash,system_charset_info,
		   0,0,0, (hash_get_key) get_grant_table,
		   0,0);
3542
  init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
unknown's avatar
unknown committed
3543

3544 3545
  t_table = tables[0].table;
  c_table = tables[1].table;
3546
  p_table= tables[2].table;
3547 3548
  t_table->file->ha_index_init(0, 1);
  p_table->file->ha_index_init(0, 1);
3549 3550 3551
  t_table->use_all_columns();
  c_table->use_all_columns();
  p_table->use_all_columns();
3552
  if (!t_table->file->index_first(t_table->record[0]))
unknown's avatar
unknown committed
3553
  {
3554 3555 3556 3557 3558
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
    {
      GRANT_TABLE *mem_check;
3559
      if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table)))
3560 3561 3562 3563 3564
      {
	/* This could only happen if we are out memory */
	grant_option= FALSE;
	goto end_unlock;
      }
unknown's avatar
unknown committed
3565

3566 3567
      if (check_no_resolve)
      {
unknown's avatar
unknown committed
3568
	if (hostname_requires_resolving(mem_check->host.hostname))
3569 3570 3571
	{
          sql_print_warning("'tables_priv' entry '%s %s@%s' "
                            "ignored in --skip-name-resolve mode.",
3572 3573 3574 3575
                            mem_check->tname,
                            mem_check->user ? mem_check->user : "",
                            mem_check->host.hostname ?
                            mem_check->host.hostname : "");
3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591
	  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
3592
  {
3593 3594 3595
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
unknown's avatar
unknown committed
3596
    {
3597
      GRANT_NAME *mem_check;
3598
      HASH *hash;
3599
      if (!(mem_check=new (&memex) GRANT_NAME(p_table)))
3600 3601 3602 3603 3604
      {
	/* This could only happen if we are out memory */
	grant_option= FALSE;
	goto end_unlock;
      }
unknown's avatar
SCRUM  
unknown committed
3605

3606
      if (check_no_resolve)
unknown's avatar
SCRUM  
unknown committed
3607
      {
unknown's avatar
unknown committed
3608
	if (hostname_requires_resolving(mem_check->host.hostname))
3609 3610 3611 3612
	{
          sql_print_warning("'procs_priv' entry '%s %s@%s' "
                            "ignored in --skip-name-resolve mode.",
                            mem_check->tname, mem_check->user,
3613 3614
                            mem_check->host.hostname ?
                            mem_check->host.hostname : "");
3615 3616
	  continue;
	}
unknown's avatar
SCRUM  
unknown committed
3617
      }
3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633
      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
3634

3635 3636 3637
      mem_check->privs= fix_rights_for_procedure(mem_check->privs);
      if (! mem_check->ok())
	delete mem_check;
3638
      else if (my_hash_insert(hash, (byte*) mem_check))
3639 3640 3641 3642 3643
      {
	delete mem_check;
	grant_option= FALSE;
	goto end_unlock;
      }
unknown's avatar
SCRUM  
unknown committed
3644
    }
3645
    while (!p_table->file->index_next(p_table->record[0]));
unknown's avatar
unknown committed
3646
  }
3647
  grant_option= TRUE;
3648 3649 3650
  return_val=0;					// Return ok

end_unlock:
unknown's avatar
unknown committed
3651
  t_table->file->ha_index_end();
3652
  p_table->file->ha_index_end();
3653
  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
3654
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
3655 3656 3657
}


3658
/*
3659
  Reload information about table and column level privileges if possible.
3660 3661 3662

  SYNOPSIS
    grant_reload()
3663
      thd  Current thread
3664 3665

  NOTES
3666 3667 3668 3669 3670 3671 3672 3673
    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
3674
*/
unknown's avatar
unknown committed
3675

3676
my_bool grant_reload(THD *thd)
unknown's avatar
unknown committed
3677
{
3678
  TABLE_LIST tables[3];
3679
  HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
3680
  bool old_grant_option;
unknown's avatar
unknown committed
3681
  MEM_ROOT old_mem;
3682
  my_bool return_val= 1;
unknown's avatar
unknown committed
3683 3684
  DBUG_ENTER("grant_reload");

3685 3686 3687 3688 3689
  /* Don't do anything if running with --skip-grant-tables */
  if (!initialized)
    DBUG_RETURN(0);

  bzero((char*) tables, sizeof(tables));
3690 3691 3692 3693 3694 3695 3696
  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;
3697 3698 3699 3700 3701 3702 3703 3704

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

3705
  rw_wrlock(&LOCK_grant);
unknown's avatar
unknown committed
3706
  grant_version++;
3707
  old_column_priv_hash= column_priv_hash;
3708
  old_proc_priv_hash= proc_priv_hash;
3709
  old_func_priv_hash= func_priv_hash;
unknown's avatar
unknown committed
3710
  old_grant_option= grant_option;
unknown's avatar
unknown committed
3711
  old_mem= memex;
unknown's avatar
unknown committed
3712

3713
  if ((return_val= grant_load(tables)))
unknown's avatar
unknown committed
3714
  {						// Error. Revert to old hash
3715
    DBUG_PRINT("error",("Reverting to old privileges"));
unknown's avatar
unknown committed
3716
    grant_free();				/* purecov: deadcode */
3717
    column_priv_hash= old_column_priv_hash;	/* purecov: deadcode */
3718
    proc_priv_hash= old_proc_priv_hash;
3719
    func_priv_hash= old_func_priv_hash;
unknown's avatar
unknown committed
3720
    grant_option= old_grant_option;		/* purecov: deadcode */
unknown's avatar
unknown committed
3721
    memex= old_mem;				/* purecov: deadcode */
unknown's avatar
unknown committed
3722 3723 3724
  }
  else
  {
3725
    hash_free(&old_column_priv_hash);
3726
    hash_free(&old_proc_priv_hash);
3727
    hash_free(&old_func_priv_hash);
3728
    free_root(&old_mem,MYF(0));
unknown's avatar
unknown committed
3729
  }
3730
  rw_unlock(&LOCK_grant);
3731 3732 3733
end:
  close_thread_tables(thd);
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
3734 3735 3736 3737
}


/****************************************************************************
3738
  Check table level grants
3739

3740
  SYNOPSIS
3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753
   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
3754
     1  Error: User did not have the requested privileges
3755 3756 3757 3758 3759 3760 3761

   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
3762 3763
****************************************************************************/

unknown's avatar
unknown committed
3764
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
unknown's avatar
unknown committed
3765
		 uint show_table, uint number, bool no_errors)
unknown's avatar
unknown committed
3766
{
3767
  TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table();
3768
  Security_context *sctx= thd->security_ctx;
3769
  uint i;
3770
  ulong orig_want_access= want_access;
3771 3772
  DBUG_ENTER("check_grant");
  DBUG_ASSERT(number > 0);
unknown's avatar
unknown committed
3773

3774
  /*
unknown's avatar
unknown committed
3775 3776 3777 3778 3779 3780 3781 3782
    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.
3783 3784
  */
  for (i= 0, table= tables;
unknown's avatar
unknown committed
3785
       table != first_not_own_table && i < number;
3786 3787 3788 3789 3790 3791
       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);
  }

3792
  rw_rdlock(&LOCK_grant);
3793 3794 3795
  for (table= tables;
       table && number-- && table != first_not_own_table;
       table= table->next_global)
unknown's avatar
unknown committed
3796
  {
3797
    GRANT_TABLE *grant_table;
3798 3799 3800 3801 3802 3803 3804 3805
    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

3806
    if (!(~table->grant.privilege & want_access) || 
3807
        table->derived || table->schema_table)
unknown's avatar
unknown committed
3808
    {
unknown's avatar
VIEW  
unknown committed
3809 3810 3811 3812
      /*
        It is subquery in the FROM clause. VIEW set table->derived after
        table opening, but this function always called before table opening.
      */
3813 3814 3815 3816 3817 3818 3819 3820 3821 3822
      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
3823
    }
3824 3825 3826
    if (!(grant_table= table_hash_search(sctx->host, sctx->ip,
                                         table->db, sctx->priv_user,
                                         table->table_name,0)))
unknown's avatar
unknown committed
3827 3828 3829 3830
    {
      want_access &= ~table->grant.privilege;
      goto err;					// No grants
    }
unknown's avatar
unknown committed
3831 3832
    if (show_table)
      continue;					// We have some priv on this
unknown's avatar
unknown committed
3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848

    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
    }
  }
3849
  rw_unlock(&LOCK_grant);
3850
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3851

3852
err:
3853
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3854
  if (!no_errors)				// Not a silent skip of table
unknown's avatar
unknown committed
3855
  {
3856 3857
    char command[128];
    get_privilege_desc(command, sizeof(command), want_access);
3858 3859
    my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
             command,
3860 3861
             sctx->priv_user,
             sctx->host_or_ip,
3862
             table ? table->table_name : "unknown");
unknown's avatar
unknown committed
3863
  }
3864
  DBUG_RETURN(1);
unknown's avatar
unknown committed
3865 3866 3867
}


3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885
/*
  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
3886
bool check_grant_column(THD *thd, GRANT_INFO *grant,
3887
			const char *db_name, const char *table_name,
3888
			const char *name, uint length,  Security_context *sctx)
unknown's avatar
unknown committed
3889 3890 3891
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
unknown's avatar
VIEW  
unknown committed
3892
  ulong want_access= grant->want_privilege & ~grant->privilege;
unknown's avatar
unknown committed
3893
  DBUG_ENTER("check_grant_column");
unknown's avatar
unknown committed
3894
  DBUG_PRINT("enter", ("table: %s  want_access: %lu", table_name, want_access));
unknown's avatar
unknown committed
3895

unknown's avatar
unknown committed
3896
  if (!want_access)
unknown's avatar
unknown committed
3897
    DBUG_RETURN(0);				// Already checked
unknown's avatar
unknown committed
3898

3899
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
3900

3901
  /* reload table if someone has modified any grants */
unknown's avatar
unknown committed
3902

unknown's avatar
VIEW  
unknown committed
3903
  if (grant->version != grant_version)
unknown's avatar
unknown committed
3904
  {
unknown's avatar
VIEW  
unknown committed
3905
    grant->grant_table=
3906 3907
      table_hash_search(sctx->host, sctx->ip, db_name,
			sctx->priv_user,
unknown's avatar
unknown committed
3908
			table_name, 0);         /* purecov: inspected */
unknown's avatar
VIEW  
unknown committed
3909
    grant->version= grant_version;		/* purecov: inspected */
unknown's avatar
unknown committed
3910
  }
unknown's avatar
VIEW  
unknown committed
3911
  if (!(grant_table= grant->grant_table))
unknown's avatar
unknown committed
3912 3913 3914 3915 3916
    goto err;					/* purecov: deadcode */

  grant_column=column_hash_search(grant_table, name, length);
  if (grant_column && !(~grant_column->rights & want_access))
  {
3917
    rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3918
    DBUG_RETURN(0);
unknown's avatar
unknown committed
3919 3920
  }

3921
err:
3922
  rw_unlock(&LOCK_grant);
3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966
  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
3967
  {
3968
    /* View or derived information schema table. */
3969
    ulong view_privs;
3970 3971 3972
    grant= &(table_ref->grant);
    db_name= table_ref->view_db.str;
    table_name= table_ref->view_name.str;
3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986
    if (table_ref->belong_to_view && 
        (thd->lex->sql_command == SQLCOM_SHOW_FIELDS ||
         thd->lex->sql_command == SQLCOM_SHOW_CREATE))
    {
      view_privs= get_column_grant(thd, grant, db_name, table_name, name);
      if (view_privs & VIEW_ANY_ACL)
      {
        table_ref->belong_to_view->allowed_show= TRUE;
        return FALSE;
      }
      table_ref->belong_to_view->allowed_show= FALSE;
      my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
      return TRUE;
    }
unknown's avatar
unknown committed
3987
  }
3988 3989 3990 3991 3992
  else
  {
    /* Normal or temporary table. */
    TABLE *table= table_ref->table;
    grant= &(table->grant);
unknown's avatar
unknown committed
3993 3994
    db_name= table->s->db.str;
    table_name= table->s->table_name.str;
3995 3996 3997 3998 3999 4000 4001 4002
  }

  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
4003 4004 4005
}


unknown's avatar
VIEW  
unknown committed
4006
bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant,
4007
                             const char* db_name, const char *table_name,
unknown's avatar
VIEW  
unknown committed
4008
                             Field_iterator *fields)
unknown's avatar
unknown committed
4009
{
4010
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
4011 4012 4013
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;

unknown's avatar
VIEW  
unknown committed
4014
  want_access &= ~grant->privilege;
unknown's avatar
unknown committed
4015
  if (!want_access)
unknown's avatar
unknown committed
4016
    return 0;				// Already checked
unknown's avatar
unknown committed
4017 4018
  if (!grant_option)
    goto err2;
unknown's avatar
unknown committed
4019

4020
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
4021

4022
  /* reload table if someone has modified any grants */
unknown's avatar
unknown committed
4023

unknown's avatar
VIEW  
unknown committed
4024
  if (grant->version != grant_version)
unknown's avatar
unknown committed
4025
  {
unknown's avatar
VIEW  
unknown committed
4026
    grant->grant_table=
4027 4028
      table_hash_search(sctx->host, sctx->ip, db_name,
			sctx->priv_user,
unknown's avatar
VIEW  
unknown committed
4029 4030
			table_name, 0);	/* purecov: inspected */
    grant->version= grant_version;		/* purecov: inspected */
unknown's avatar
unknown committed
4031
  }
4032
  /* The following should always be true */
unknown's avatar
VIEW  
unknown committed
4033
  if (!(grant_table= grant->grant_table))
unknown's avatar
unknown committed
4034 4035
    goto err;					/* purecov: inspected */

4036
  for (; !fields->end_of_fields(); fields->next())
unknown's avatar
unknown committed
4037
  {
unknown's avatar
VIEW  
unknown committed
4038 4039 4040
    const char *field_name= fields->name();
    grant_column= column_hash_search(grant_table, field_name,
				    (uint) strlen(field_name));
unknown's avatar
unknown committed
4041 4042 4043
    if (!grant_column || (~grant_column->rights & want_access))
      goto err;
  }
4044
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4045 4046
  return 0;

unknown's avatar
unknown committed
4047
err:
4048
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4049
err2:
4050 4051
  char command[128];
  get_privilege_desc(command, sizeof(command), want_access);
4052 4053
  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
           command,
4054 4055
           sctx->priv_user,
           sctx->host_or_ip,
4056 4057
           fields->name(),
           table_name);
unknown's avatar
unknown committed
4058 4059 4060 4061
  return 1;
}


4062
/*
unknown's avatar
unknown committed
4063
  Check if a user has the right to access a database
4064
  Access is accepted if he has a grant for any table/routine in the database
unknown's avatar
unknown committed
4065
  Return 1 if access is denied
4066
*/
unknown's avatar
unknown committed
4067 4068 4069

bool check_grant_db(THD *thd,const char *db)
{
4070
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
4071
  char helping [NAME_LEN+USERNAME_LENGTH+2];
unknown's avatar
unknown committed
4072
  uint len;
unknown's avatar
unknown committed
4073
  bool error= 1;
unknown's avatar
unknown committed
4074

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

4078
  for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
unknown's avatar
unknown committed
4079
  {
4080 4081
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  idx);
unknown's avatar
unknown committed
4082 4083
    if (len < grant_table->key_length &&
	!memcmp(grant_table->hash_key,helping,len) &&
4084
        compare_hostname(&grant_table->host, sctx->host, sctx->ip))
unknown's avatar
unknown committed
4085 4086 4087 4088 4089
    {
      error=0;					// Found match
      break;
    }
  }
4090
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4091 4092 4093
  return error;
}

4094 4095

/****************************************************************************
4096
  Check routine level grants
4097 4098

  SYNPOSIS
4099
   bool check_grant_routine()
4100 4101
   thd		Thread handler
   want_access  Bits of privileges user needs to have
4102 4103
   procs	List of routines to check. The user should have 'want_access'
   is_proc	True if the list is all procedures, else functions
4104 4105 4106 4107 4108 4109 4110 4111
   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
****************************************************************************/

4112
bool check_grant_routine(THD *thd, ulong want_access,
4113
			 TABLE_LIST *procs, bool is_proc, bool no_errors)
4114 4115
{
  TABLE_LIST *table;
4116
  Security_context *sctx= thd->security_ctx;
4117 4118
  char *user= sctx->priv_user;
  char *host= sctx->priv_host;
4119
  DBUG_ENTER("check_grant_routine");
4120

4121
  want_access&= ~sctx->master_access;
4122 4123 4124 4125 4126 4127 4128
  if (!want_access)
    DBUG_RETURN(0);                             // ok

  rw_rdlock(&LOCK_grant);
  for (table= procs; table; table= table->next_global)
  {
    GRANT_NAME *grant_proc;
4129
    if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
4130
					 table->table_name, is_proc, 0)))
4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147
      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)
4148
      strxmov(buff, table->db, ".", table->table_name, NullS);
4149 4150 4151
    if (want_access & EXECUTE_ACL)
      command= "execute";
    else if (want_access & ALTER_PROC_ACL)
4152
      command= "alter routine";
4153 4154 4155 4156 4157 4158 4159 4160 4161
    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);
}


4162 4163
/*
  Check if routine has any of the 
4164
  routine level grants
4165 4166 4167 4168 4169 4170 4171 4172 4173
  
  SYNPOSIS
   bool    check_routine_level_acl()
   thd	        Thread handler
   db           Database name
   name         Routine name

  RETURN
   0            Ok 
4174
   1            error
4175 4176
*/

unknown's avatar
unknown committed
4177 4178
bool check_routine_level_acl(THD *thd, const char *db, const char *name, 
                             bool is_proc)
4179 4180 4181 4182 4183
{
  bool no_routine_acl= 1;
  if (grant_option)
  {
    GRANT_NAME *grant_proc;
4184
    Security_context *sctx= thd->security_ctx;
4185
    rw_rdlock(&LOCK_grant);
4186 4187 4188
    if ((grant_proc= routine_hash_search(sctx->priv_host,
                                         sctx->ip, db,
                                         sctx->priv_user,
4189
                                         name, is_proc, 0)))
4190 4191 4192 4193 4194 4195 4196
      no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
    rw_unlock(&LOCK_grant);
  }
  return no_routine_acl;
}


unknown's avatar
unknown committed
4197
/*****************************************************************************
unknown's avatar
unknown committed
4198
  Functions to retrieve the grant for a table/column  (for SHOW functions)
unknown's avatar
unknown committed
4199 4200
*****************************************************************************/

unknown's avatar
unknown committed
4201
ulong get_table_grant(THD *thd, TABLE_LIST *table)
unknown's avatar
unknown committed
4202
{
unknown's avatar
unknown committed
4203
  ulong privilege;
4204
  Security_context *sctx= thd->security_ctx;
unknown's avatar
unknown committed
4205 4206 4207
  const char *db = table->db ? table->db : thd->db;
  GRANT_TABLE *grant_table;

4208
  rw_rdlock(&LOCK_grant);
4209 4210 4211
#ifdef EMBEDDED_LIBRARY
  grant_table= NULL;
#else
4212
  grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
4213
				 table->table_name, 0);
4214
#endif
unknown's avatar
unknown committed
4215 4216 4217 4218
  table->grant.grant_table=grant_table; // Remember for column test
  table->grant.version=grant_version;
  if (grant_table)
    table->grant.privilege|= grant_table->privs;
unknown's avatar
unknown committed
4219
  privilege= table->grant.privilege;
4220
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4221
  return privilege;
unknown's avatar
unknown committed
4222 4223 4224
}


unknown's avatar
unknown committed
4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242
/*
  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
4243 4244 4245
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
4246 4247 4248
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
4249
  ulong priv;
unknown's avatar
unknown committed
4250

4251
  rw_rdlock(&LOCK_grant);
4252
  /* reload table if someone has modified any grants */
unknown's avatar
VIEW  
unknown committed
4253
  if (grant->version != grant_version)
unknown's avatar
unknown committed
4254
  {
4255
    Security_context *sctx= thd->security_ctx;
unknown's avatar
VIEW  
unknown committed
4256
    grant->grant_table=
4257 4258
      table_hash_search(sctx->host, sctx->ip,
                        db_name, sctx->priv_user,
unknown's avatar
VIEW  
unknown committed
4259 4260
			table_name, 0);	        /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
unknown's avatar
unknown committed
4261 4262
  }

unknown's avatar
VIEW  
unknown committed
4263 4264
  if (!(grant_table= grant->grant_table))
    priv= grant->privilege;
unknown's avatar
unknown committed
4265 4266
  else
  {
unknown's avatar
VIEW  
unknown committed
4267 4268
    grant_column= column_hash_search(grant_table, field_name,
                                     (uint) strlen(field_name));
unknown's avatar
unknown committed
4269
    if (!grant_column)
4270
      priv= (grant->privilege | grant_table->privs);
unknown's avatar
unknown committed
4271
    else
4272
      priv= (grant->privilege | grant_table->privs | grant_column->rights);
unknown's avatar
unknown committed
4273
  }
4274
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4275 4276 4277
  return priv;
}

unknown's avatar
VIEW  
unknown committed
4278

4279
/* Help function for mysql_show_grants */
unknown's avatar
unknown committed
4280

4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292
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
4293 4294

static const char *command_array[]=
unknown's avatar
unknown committed
4295
{
unknown's avatar
VIEW  
unknown committed
4296 4297 4298 4299
  "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",
4300
  "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
4301
  "CREATE USER", "EVENT", "TRIGGER"
unknown's avatar
unknown committed
4302
};
4303

unknown's avatar
unknown committed
4304 4305
static uint command_lengths[]=
{
unknown's avatar
unknown committed
4306
  6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
4307
  14, 13, 11, 5, 7
unknown's avatar
unknown committed
4308 4309
};

unknown's avatar
unknown committed
4310

4311 4312 4313 4314 4315
static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
                               const char *type, int typelen,
                               char *buff, int buffsize);


4316 4317 4318 4319 4320 4321 4322
/*
  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
4323
bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
unknown's avatar
unknown committed
4324
{
unknown's avatar
unknown committed
4325 4326
  ulong want_access;
  uint counter,index;
unknown's avatar
unknown committed
4327
  int  error = 0;
unknown's avatar
unknown committed
4328 4329
  ACL_USER *acl_user;
  ACL_DB *acl_db;
unknown's avatar
unknown committed
4330
  char buff[1024];
unknown's avatar
unknown committed
4331
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
4332
  DBUG_ENTER("mysql_show_grants");
unknown's avatar
unknown committed
4333 4334 4335 4336

  LINT_INIT(acl_user);
  if (!initialized)
  {
unknown's avatar
unknown committed
4337
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
unknown's avatar
unknown committed
4338
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4339
  }
unknown's avatar
unknown committed
4340

4341 4342 4343 4344 4345
  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
4346
  {
4347 4348 4349
    VOID(pthread_mutex_unlock(&acl_cache->lock));
    rw_unlock(&LOCK_grant);

unknown's avatar
unknown committed
4350 4351
    my_error(ER_NONEXISTING_GRANT, MYF(0),
             lex_user->user.str, lex_user->host.str);
unknown's avatar
unknown committed
4352
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4353 4354
  }

unknown's avatar
unknown committed
4355
  Item_string *field=new Item_string("",0,&my_charset_latin1);
unknown's avatar
unknown committed
4356 4357 4358 4359 4360 4361
  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);
4362 4363
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
4364 4365 4366
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock));
    rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4367

4368 4369
    DBUG_RETURN(TRUE);
  }
unknown's avatar
unknown committed
4370 4371 4372

  /* Add first global access grants */
  {
4373
    String global(buff,sizeof(buff),system_charset_info);
unknown's avatar
unknown committed
4374
    global.length(0);
4375
    global.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
4376

unknown's avatar
unknown committed
4377
    want_access= acl_user->access;
unknown's avatar
unknown committed
4378
    if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
4379
      global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
4380
    else if (!(want_access & ~GRANT_ACL))
4381
      global.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
4382
    else
unknown's avatar
unknown committed
4383 4384
    {
      bool found=0;
unknown's avatar
unknown committed
4385
      ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
4386 4387
      for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
      {
unknown's avatar
unknown committed
4388
	if (test_access & j)
unknown's avatar
unknown committed
4389 4390
	{
	  if (found)
4391
	    global.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
4392 4393 4394 4395 4396
	  found=1;
	  global.append(command_array[counter],command_lengths[counter]);
	}
      }
    }
4397
    global.append (STRING_WITH_LEN(" ON *.* TO '"));
4398 4399
    global.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
4400
    global.append (STRING_WITH_LEN("'@'"));
4401 4402
    global.append(lex_user->host.str,lex_user->host.length,
		  system_charset_info);
unknown's avatar
unknown committed
4403
    global.append ('\'');
4404
    if (acl_user->salt_len)
unknown's avatar
unknown committed
4405
    {
4406 4407 4408 4409 4410
      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);
4411
      global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
4412
      global.append(passwd_buff);
unknown's avatar
unknown committed
4413 4414
      global.append('\'');
    }
unknown's avatar
unknown committed
4415 4416
    /* "show grants" SSL related stuff */
    if (acl_user->ssl_type == SSL_TYPE_ANY)
4417
      global.append(STRING_WITH_LEN(" REQUIRE SSL"));
4418
    else if (acl_user->ssl_type == SSL_TYPE_X509)
4419
      global.append(STRING_WITH_LEN(" REQUIRE X509"));
4420
    else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
4421
    {
4422
      int ssl_options = 0;
4423
      global.append(STRING_WITH_LEN(" REQUIRE "));
unknown's avatar
unknown committed
4424 4425
      if (acl_user->x509_issuer)
      {
4426
	ssl_options++;
4427
	global.append(STRING_WITH_LEN("ISSUER \'"));
4428
	global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
4429
	global.append('\'');
4430
      }
unknown's avatar
unknown committed
4431 4432
      if (acl_user->x509_subject)
      {
4433 4434
	if (ssl_options++)
	  global.append(' ');
4435
	global.append(STRING_WITH_LEN("SUBJECT \'"));
4436 4437
	global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
                      system_charset_info);
4438
	global.append('\'');
unknown's avatar
unknown committed
4439
      }
unknown's avatar
unknown committed
4440 4441
      if (acl_user->ssl_cipher)
      {
4442 4443
	if (ssl_options++)
	  global.append(' ');
4444
	global.append(STRING_WITH_LEN("CIPHER '"));
4445 4446
	global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
                      system_charset_info);
4447
	global.append('\'');
4448 4449
      }
    }
unknown's avatar
unknown committed
4450
    if ((want_access & GRANT_ACL) ||
4451 4452 4453 4454
	(acl_user->user_resource.questions ||
         acl_user->user_resource.updates ||
         acl_user->user_resource.conn_per_hour ||
         acl_user->user_resource.user_conn))
4455
    {
4456
      global.append(STRING_WITH_LEN(" WITH"));
unknown's avatar
unknown committed
4457
      if (want_access & GRANT_ACL)
4458
	global.append(STRING_WITH_LEN(" GRANT OPTION"));
4459 4460 4461 4462
      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");
4463
      add_user_option(&global, acl_user->user_resource.conn_per_hour,
4464
		      "MAX_CONNECTIONS_PER_HOUR");
4465 4466
      add_user_option(&global, acl_user->user_resource.user_conn,
		      "MAX_USER_CONNECTIONS");
unknown's avatar
unknown committed
4467
    }
unknown's avatar
unknown committed
4468
    protocol->prepare_for_resend();
4469
    protocol->store(global.ptr(),global.length(),global.charset());
unknown's avatar
unknown committed
4470
    if (protocol->write())
unknown's avatar
unknown committed
4471
    {
unknown's avatar
unknown committed
4472
      error= -1;
unknown's avatar
unknown committed
4473
      goto end;
unknown's avatar
unknown committed
4474 4475 4476 4477 4478 4479
    }
  }

  /* Add database access */
  for (counter=0 ; counter < acl_dbs.elements ; counter++)
  {
unknown's avatar
unknown committed
4480
    const char *user, *host;
unknown's avatar
unknown committed
4481 4482 4483

    acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
    if (!(user=acl_db->user))
unknown's avatar
unknown committed
4484
      user= "";
unknown's avatar
unknown committed
4485
    if (!(host=acl_db->host.hostname))
unknown's avatar
unknown committed
4486
      host= "";
unknown's avatar
unknown committed
4487 4488

    if (!strcmp(lex_user->user.str,user) &&
4489
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
unknown's avatar
unknown committed
4490 4491
    {
      want_access=acl_db->access;
unknown's avatar
unknown committed
4492
      if (want_access)
unknown's avatar
unknown committed
4493
      {
4494
	String db(buff,sizeof(buff),system_charset_info);
unknown's avatar
unknown committed
4495
	db.length(0);
4496
	db.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
4497 4498

	if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
4499
	  db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
4500
	else if (!(want_access & ~GRANT_ACL))
4501
	  db.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
4502 4503 4504
	else
	{
	  int found=0, cnt;
unknown's avatar
unknown committed
4505
	  ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
4506 4507 4508 4509 4510
	  for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
	  {
	    if (test_access & j)
	    {
	      if (found)
4511
		db.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
4512 4513 4514 4515 4516
	      found = 1;
	      db.append(command_array[cnt],command_lengths[cnt]);
	    }
	  }
	}
4517
	db.append (STRING_WITH_LEN(" ON "));
4518
	append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
4519
	db.append (STRING_WITH_LEN(".* TO '"));
4520 4521
	db.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
4522
	db.append (STRING_WITH_LEN("'@'"));
4523 4524
	db.append(lex_user->host.str, lex_user->host.length,
                  system_charset_info);
unknown's avatar
unknown committed
4525
	db.append ('\'');
unknown's avatar
unknown committed
4526
	if (want_access & GRANT_ACL)
4527
	  db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
unknown's avatar
unknown committed
4528
	protocol->prepare_for_resend();
4529
	protocol->store(db.ptr(),db.length(),db.charset());
unknown's avatar
unknown committed
4530
	if (protocol->write())
unknown's avatar
unknown committed
4531
	{
unknown's avatar
unknown committed
4532
	  error= -1;
unknown's avatar
unknown committed
4533 4534 4535 4536 4537 4538
	  goto end;
	}
      }
    }
  }

4539
  /* Add table & column access */
4540
  for (index=0 ; index < column_priv_hash.records ; index++)
unknown's avatar
unknown committed
4541
  {
4542
    const char *user, *host;
4543 4544
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
unknown's avatar
unknown committed
4545 4546

    if (!(user=grant_table->user))
4547
      user= "";
4548 4549
    if (!(host= grant_table->host.hostname))
      host= "";
unknown's avatar
unknown committed
4550 4551

    if (!strcmp(lex_user->user.str,user) &&
4552
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
unknown's avatar
unknown committed
4553
    {
4554 4555
      ulong table_access= grant_table->privs;
      if ((table_access | grant_table->cols) != 0)
unknown's avatar
unknown committed
4556
      {
4557
	String global(buff, sizeof(buff), system_charset_info);
unknown's avatar
unknown committed
4558 4559
	ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;

unknown's avatar
unknown committed
4560
	global.length(0);
4561
	global.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
4562

4563
	if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
4564
	  global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
4565
	else if (!test_access)
4566
	  global.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
4567
	else
unknown's avatar
unknown committed
4568
	{
4569
          /* Add specific column access */
4570
	  int found= 0;
unknown's avatar
unknown committed
4571
	  ulong j;
unknown's avatar
unknown committed
4572

4573
	  for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
unknown's avatar
unknown committed
4574
	  {
unknown's avatar
unknown committed
4575
	    if (test_access & j)
unknown's avatar
unknown committed
4576 4577
	    {
	      if (found)
4578
		global.append(STRING_WITH_LEN(", "));
4579
	      found= 1;
unknown's avatar
unknown committed
4580 4581
	      global.append(command_array[counter],command_lengths[counter]);

unknown's avatar
unknown committed
4582
	      if (grant_table->cols)
unknown's avatar
unknown committed
4583
	      {
4584
		uint found_col= 0;
unknown's avatar
unknown committed
4585 4586 4587 4588 4589 4590
		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
4591
		  if (grant_column->rights & j)
unknown's avatar
unknown committed
4592
		  {
unknown's avatar
unknown committed
4593
		    if (!found_col)
unknown's avatar
unknown committed
4594
		    {
4595 4596 4597 4598 4599 4600 4601
		      found_col= 1;
		      /*
			If we have a duplicated table level privilege, we
			must write the access privilege name again.
		      */
		      if (table_access & j)
		      {
4602
			global.append(STRING_WITH_LEN(", "));
4603 4604 4605
			global.append(command_array[counter],
				      command_lengths[counter]);
		      }
4606
		      global.append(STRING_WITH_LEN(" ("));
unknown's avatar
unknown committed
4607 4608
		    }
		    else
4609
		      global.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
4610
		    global.append(grant_column->column,
4611 4612
				  grant_column->key_length,
				  system_charset_info);
unknown's avatar
unknown committed
4613 4614 4615 4616 4617 4618 4619 4620
		  }
		}
		if (found_col)
		  global.append(')');
	      }
	    }
	  }
	}
4621
	global.append(STRING_WITH_LEN(" ON "));
4622 4623 4624 4625 4626
	append_identifier(thd, &global, grant_table->db,
			  strlen(grant_table->db));
	global.append('.');
	append_identifier(thd, &global, grant_table->tname,
			  strlen(grant_table->tname));
4627
	global.append(STRING_WITH_LEN(" TO '"));
4628 4629
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
4630
	global.append(STRING_WITH_LEN("'@'"));
4631 4632
	global.append(lex_user->host.str,lex_user->host.length,
		      system_charset_info);
unknown's avatar
unknown committed
4633
	global.append('\'');
4634
	if (table_access & GRANT_ACL)
4635
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
unknown's avatar
unknown committed
4636
	protocol->prepare_for_resend();
4637
	protocol->store(global.ptr(),global.length(),global.charset());
unknown's avatar
unknown committed
4638
	if (protocol->write())
unknown's avatar
unknown committed
4639
	{
unknown's avatar
unknown committed
4640
	  error= -1;
unknown's avatar
unknown committed
4641
	  break;
unknown's avatar
unknown committed
4642 4643 4644 4645
	}
      }
    }
  }
4646

4647
  if (show_routine_grants(thd, lex_user, &proc_priv_hash, 
4648
                          STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
4649 4650 4651 4652 4653 4654
  {
    error= -1;
    goto end;
  }

  if (show_routine_grants(thd, lex_user, &func_priv_hash,
4655
                          STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677
  {
    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++)
4678
  {
4679
    const char *user, *host;
4680
    GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, index);
4681 4682 4683

    if (!(user=grant_proc->user))
      user= "";
4684 4685
    if (!(host= grant_proc->host.hostname))
      host= "";
4686 4687

    if (!strcmp(lex_user->user.str,user) &&
4688
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
4689 4690 4691 4692
    {
      ulong proc_access= grant_proc->privs;
      if (proc_access != 0)
      {
4693
	String global(buff, buffsize, system_charset_info);
4694 4695 4696
	ulong test_access= proc_access & ~GRANT_ACL;

	global.length(0);
4697
	global.append(STRING_WITH_LEN("GRANT "));
4698 4699

	if (!test_access)
4700
 	  global.append(STRING_WITH_LEN("USAGE"));
4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711
	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)
4712
		global.append(STRING_WITH_LEN(", "));
4713 4714 4715 4716 4717
	      found= 1;
	      global.append(command_array[counter],command_lengths[counter]);
	    }
	  }
	}
4718
	global.append(STRING_WITH_LEN(" ON "));
4719 4720
        global.append(type,typelen);
        global.append(' ');
4721 4722 4723 4724 4725
	append_identifier(thd, &global, grant_proc->db,
			  strlen(grant_proc->db));
	global.append('.');
	append_identifier(thd, &global, grant_proc->tname,
			  strlen(grant_proc->tname));
4726
	global.append(STRING_WITH_LEN(" TO '"));
4727 4728
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
4729
	global.append(STRING_WITH_LEN("'@'"));
4730 4731 4732 4733
	global.append(lex_user->host.str,lex_user->host.length,
		      system_charset_info);
	global.append('\'');
	if (proc_access & GRANT_ACL)
4734
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
4735 4736 4737 4738 4739 4740 4741 4742 4743 4744
	protocol->prepare_for_resend();
	protocol->store(global.ptr(),global.length(),global.charset());
	if (protocol->write())
	{
	  error= -1;
	  break;
	}
      }
    }
  }
4745
  return error;
unknown's avatar
unknown committed
4746 4747
}

unknown's avatar
unknown committed
4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775
/*
  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;
}


4776
void get_mqh(const char *user, const char *host, USER_CONN *uc)
unknown's avatar
unknown committed
4777 4778
{
  ACL_USER *acl_user;
4779 4780 4781

  pthread_mutex_lock(&acl_cache->lock);

4782
  if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
4783 4784 4785
    uc->user_resources= acl_user->user_resource;
  else
    bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
4786 4787

  pthread_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
4788 4789
}

4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810
/*
  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.
*/

4811
#define GRANT_TABLES 5
4812 4813 4814 4815 4816 4817
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("open_grant_tables");

  if (!initialized)
  {
unknown's avatar
unknown committed
4818
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
4819 4820 4821
    DBUG_RETURN(-1);
  }

4822
  bzero((char*) tables, GRANT_TABLES*sizeof(*tables));
4823 4824 4825 4826 4827
  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
4828 4829 4830
  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;
4831
  (tables+3)->next_local= (tables+3)->next_global= tables+4;
4832
  tables->lock_type= (tables+1)->lock_type=
4833 4834 4835 4836
    (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";
4837 4838 4839 4840 4841 4842

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
4843
  if (thd->slave_thread && rpl_filter->is_on())
4844
  {
unknown's avatar
unknown committed
4845 4846 4847
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
4848
    */
4849 4850
    tables[0].updating=tables[1].updating=tables[2].updating=
      tables[3].updating=tables[4].updating=1;
unknown's avatar
unknown committed
4851
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
4852
      DBUG_RETURN(1);
4853 4854
    tables[0].updating=tables[1].updating=tables[2].updating=
      tables[3].updating=tables[4].updating=0;;
4855
  }
4856 4857
#endif

4858
  if (simple_open_n_lock_tables(thd, tables))
4859 4860 4861 4862 4863 4864 4865 4866 4867
  {						// 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
4868
			 uint *acl_acl_userdx)
4869 4870 4871 4872
{
  ACL_USER *acl_user= 0;
  uint counter;

4873 4874
  safe_mutex_assert_owner(&acl_cache->lock);

4875 4876 4877 4878 4879
  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
4880
      user= "";
4881
    if (!(host=acl_user->host.hostname))
4882
      host= "";
4883 4884 4885 4886 4887 4888 4889
    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
4890
  *acl_acl_userdx= counter;
unknown's avatar
unknown committed
4891
  return acl_user;
4892
}
unknown's avatar
unknown committed
4893

4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915
/*
  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)
4916
{
4917 4918
  int error;
  DBUG_ENTER("modify_grant_table");
4919

4920 4921 4922 4923 4924 4925 4926 4927
  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);
4928
    if ((error= table->file->ha_update_row(table->record[1], table->record[0])))
4929 4930 4931 4932 4933
      table->file->print_error(error, MYF(0));
  }
  else
  {
    /* delete */
4934
    if ((error=table->file->ha_delete_row(table->record[0])))
4935 4936
      table->file->print_error(error, MYF(0));
  }
4937

4938 4939
  DBUG_RETURN(error);
}
4940

4941 4942 4943 4944 4945 4946
/*
  Handle a privilege table.

  SYNOPSIS
    handle_grant_table()
    tables                      The array with the four open tables.
4947
    table_no                    The number of the table to handle (0..4).
4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964
    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
4965
    4 procs_priv
4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984

  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;
4985
  byte user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
4986
  uint key_prefix_length;
4987 4988
  DBUG_ENTER("handle_grant_table");

4989
  table->use_all_columns();
unknown's avatar
unknown committed
4990
  if (! table_no) // mysql.user table
4991
  {
4992 4993 4994 4995 4996 4997 4998 4999 5000 5001
    /*
      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'",
unknown's avatar
unknown committed
5002
                       table->s->table_name.str, user_str, host_str));
5003 5004
    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
5005 5006 5007 5008 5009

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

5010
    if ((error= table->file->index_read_idx(table->record[0], 0,
unknown's avatar
unknown committed
5011
                                            user_key, key_prefix_length,
5012
                                            HA_READ_KEY_EXACT)))
5013
    {
5014
      if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
5015 5016 5017 5018
      {
        table->file->print_error(error, MYF(0));
        result= -1;
      }
5019
    }
5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036
    else
    {
      /* If requested, delete or update the record. */
      result= ((drop || user_to) &&
               modify_grant_table(table, host_field, user_field, user_to)) ?
        -1 : 1; /* Error or found. */
    }
    DBUG_PRINT("info",("read result: %d", result));
  }
  else
  {
    /*
      The non-'user' table do not have indexes on (host, user).
      And their host- and user fields are not consecutive.
      Thus, we need to do a table scan to find all matching records.
    */
    if ((error= table->file->ha_rnd_init(1)))
5037
    {
5038
      table->file->print_error(error, MYF(0));
5039
      result= -1;
5040 5041 5042 5043 5044
    }
    else
    {
#ifdef EXTRA_DEBUG
      DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
unknown's avatar
unknown committed
5045
                         table->s->table_name.str, user_str, host_str));
5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093
#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()
5094
    struct_no                   The number of the structure to handle (0..3).
5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109
    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
5110
    3 procs_priv_hash
5111 5112 5113 5114

  RETURN
    > 0         At least one element matched.
    0           OK, but no element matched.
5115
    -1		Wrong arguments to function
5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127
*/

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;
5128
  GRANT_NAME *grant_name;
5129
  DBUG_ENTER("handle_grant_struct");
unknown's avatar
unknown committed
5130 5131 5132
  DBUG_PRINT("info",("scan struct: %u  search: '%s'@'%s'",
                     struct_no, user_from->user.str, user_from->host.str));

5133 5134
  LINT_INIT(acl_user);
  LINT_INIT(acl_db);
5135
  LINT_INIT(grant_name);
unknown's avatar
unknown committed
5136 5137
  LINT_INIT(user);
  LINT_INIT(host);
5138

5139 5140
  safe_mutex_assert_owner(&acl_cache->lock);

5141
  /* Get the number of elements in the in-memory structure. */
5142
  switch (struct_no) {
5143 5144 5145 5146 5147 5148
  case 0:
    elements= acl_users.elements;
    break;
  case 1:
    elements= acl_dbs.elements;
    break;
5149
  case 2:
5150
    elements= column_priv_hash.records;
5151 5152 5153 5154 5155 5156
    break;
  case 3:
    elements= proc_priv_hash.records;
    break;
  default:
    return -1;
5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168
  }

#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.
    */
5169
    switch (struct_no) {
5170 5171 5172
    case 0:
      acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
      user= acl_user->user;
5173 5174
      host= acl_user->host.hostname;
    break;
5175 5176 5177 5178

    case 1:
      acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
      user= acl_db->user;
5179
      host= acl_db->host.hostname;
5180 5181
      break;

5182 5183 5184
    case 2:
      grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx);
      user= grant_name->user;
5185
      host= grant_name->host.hostname;
5186 5187 5188 5189 5190
      break;

    case 3:
      grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx);
      user= grant_name->user;
5191
      host= grant_name->host.hostname;
5192
      break;
unknown's avatar
unknown committed
5193 5194
    default:
      assert(0);
5195 5196
    }
    if (! user)
5197
      user= "";
5198 5199 5200
    if (! host)
      host= "";

5201 5202 5203 5204 5205 5206
#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))
5207
      continue;
5208 5209 5210 5211

    result= 1; /* At least one element found. */
    if ( drop )
    {
unknown's avatar
unknown committed
5212
      switch ( struct_no ) {
5213 5214 5215 5216 5217 5218 5219 5220
      case 0:
        delete_dynamic_element(&acl_users, idx);
        break;

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

5221 5222 5223 5224 5225 5226 5227
      case 2:
        hash_delete(&column_priv_hash, (byte*) grant_name);
	break;

      case 3:
        hash_delete(&proc_priv_hash, (byte*) grant_name);
	break;
5228 5229 5230
      }
      elements--;
      idx--;
5231
    }
5232 5233
    else if ( user_to )
    {
5234
      switch ( struct_no ) {
5235 5236 5237 5238
      case 0:
        acl_user->user= strdup_root(&mem, user_to->user.str);
        acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
        break;
5239

5240 5241 5242 5243 5244
      case 1:
        acl_db->user= strdup_root(&mem, user_to->user.str);
        acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
        break;

5245 5246 5247
      case 2:
      case 3:
        grant_name->user= strdup_root(&mem, user_to->user.str);
unknown's avatar
unknown committed
5248 5249
        update_hostname(&grant_name->host,
                        strdup_root(&mem, user_to->host.str));
5250
	break;
5251 5252 5253
      }
    }
    else
5254
    {
5255 5256 5257 5258 5259 5260 5261
      /* 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
5262

5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306
  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
5307 5308
    if ((handle_grant_struct(0, drop, user_from, user_to) && ! result) ||
        found)
5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335
    {
      result= 1; /* At least one record/element found. */
      /* If search is requested, we do not need to search further. */
      if (! drop && ! user_to)
        goto end;
    }
  }

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

5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354
  /* 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;
    }
  }

5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368
  /* 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;
5369
    }
5370 5371 5372

    /* Handle columns table. */
    if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
5373
    {
5374
      /* Handle of table failed, don't touch the in-memory array. */
5375 5376
      result= -1;
    }
5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388
    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
5389

unknown's avatar
unknown committed
5390 5391 5392 5393 5394 5395
static void append_user(String *str, LEX_USER *user)
{
  if (str->length())
    str->append(',');
  str->append('\'');
  str->append(user->user.str);
5396
  str->append(STRING_WITH_LEN("'@'"));
unknown's avatar
unknown committed
5397 5398 5399
  str->append(user->host.str);
  str->append('\'');
}
5400

unknown's avatar
unknown committed
5401

5402 5403 5404 5405 5406 5407 5408
/*
  Create a list of users.

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

5410 5411 5412 5413 5414 5415 5416 5417
  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_create_user(THD *thd, List <LEX_USER> &list)
{
  int result;
unknown's avatar
unknown committed
5418
  String wrong_users;
5419
  ulong sql_mode;
5420
  LEX_USER *user_name, *tmp_user_name;
5421
  List_iterator <LEX_USER> user_list(list);
5422
  TABLE_LIST tables[GRANT_TABLES];
5423 5424
  DBUG_ENTER("mysql_create_user");

5425 5426 5427 5428 5429 5430 5431
  /*
    This statement will be replicated as a statement, even when using
    row-based replication.  The flag will be reset at the end of the
    statement.
  */
  thd->clear_current_stmt_binlog_row_based();

5432 5433 5434 5435 5436 5437 5438
  /* 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));

5439
  while ((tmp_user_name= user_list++))
5440
  {
5441 5442 5443 5444
    if (!(user_name= get_current_user(thd, tmp_user_name)))
    {
      result= TRUE;
      continue;
5445 5446
    }

5447 5448 5449 5450
    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
5451
    if (handle_grant_data(tables, 0, user_name, NULL))
5452
    {
unknown's avatar
unknown committed
5453
      append_user(&wrong_users, user_name);
5454
      result= TRUE;
unknown's avatar
unknown committed
5455
      continue;
5456
    }
5457

5458
    sql_mode= thd->variables.sql_mode;
unknown's avatar
unknown committed
5459
    if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
5460
    {
unknown's avatar
unknown committed
5461
      append_user(&wrong_users, user_name);
5462 5463 5464 5465 5466
      result= TRUE;
    }
  }

  VOID(pthread_mutex_unlock(&acl_cache->lock));
5467 5468 5469

  if (mysql_bin_log.is_open())
  {
5470 5471
    thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                      thd->query, thd->query_length, FALSE, FALSE);
5472 5473
  }

5474 5475 5476
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
5477
    my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497
  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
5498
  String wrong_users;
5499
  LEX_USER *user_name, *tmp_user_name;
5500
  List_iterator <LEX_USER> user_list(list);
5501
  TABLE_LIST tables[GRANT_TABLES];
5502 5503
  DBUG_ENTER("mysql_drop_user");

5504 5505 5506 5507 5508 5509 5510
  /*
    This statement will be replicated as a statement, even when using
    row-based replication.  The flag will be reset at the end of the
    statement.
  */
  thd->clear_current_stmt_binlog_row_based();

unknown's avatar
unknown committed
5511
  /* DROP USER may be skipped on replication client. */
5512 5513 5514 5515 5516 5517
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

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

5518
  while ((tmp_user_name= user_list++))
5519
  {
5520 5521 5522 5523 5524 5525
    user_name= get_current_user(thd, tmp_user_name);
    if (!(user_name= get_current_user(thd, tmp_user_name)))
    {
      result= TRUE;
      continue;
    }  
5526
    if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
5527
    {
unknown's avatar
unknown committed
5528
      append_user(&wrong_users, user_name);
5529
      result= TRUE;
5530
    }
5531
  }
5532

5533 5534 5535
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

5536
  VOID(pthread_mutex_unlock(&acl_cache->lock));
5537 5538 5539

  if (mysql_bin_log.is_open())
  {
5540 5541
    thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                      thd->query, thd->query_length, FALSE, FALSE);
5542 5543
  }

5544 5545 5546
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
unknown's avatar
unknown committed
5547
    my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566
  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)
{
5567
  int result;
unknown's avatar
unknown committed
5568
  String wrong_users;
5569 5570
  LEX_USER *user_from, *tmp_user_from;
  LEX_USER *user_to, *tmp_user_to;
5571
  List_iterator <LEX_USER> user_list(list);
5572
  TABLE_LIST tables[GRANT_TABLES];
5573 5574
  DBUG_ENTER("mysql_rename_user");

5575 5576 5577 5578 5579 5580 5581
  /*
    This statement will be replicated as a statement, even when using
    row-based replication.  The flag will be reset at the end of the
    statement.
  */
  thd->clear_current_stmt_binlog_row_based();

unknown's avatar
unknown committed
5582
  /* RENAME USER may be skipped on replication client. */
5583 5584 5585 5586 5587 5588
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

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

5589
  while ((tmp_user_from= user_list++))
5590
  {
5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601
    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;
    }  
5602
    DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
5603 5604 5605 5606 5607

    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
unknown's avatar
unknown committed
5608 5609
    if (handle_grant_data(tables, 0, user_to, NULL) ||
        handle_grant_data(tables, 0, user_from, user_to) <= 0)
5610
    {
unknown's avatar
unknown committed
5611
      append_user(&wrong_users, user_from);
5612 5613
      result= TRUE;
    }
5614
  }
unknown's avatar
unknown committed
5615
  
5616 5617 5618
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

5619
  VOID(pthread_mutex_unlock(&acl_cache->lock));
5620 5621 5622

  if (mysql_bin_log.is_open())
  {
5623 5624
    thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                      thd->query, thd->query_length, FALSE, FALSE);
5625 5626
  }

5627 5628 5629
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
5630
    my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
5631 5632 5633
  DBUG_RETURN(result);
}

5634

5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648
/*
  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
5649
bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
5650
{
5651
  uint counter, revoked, is_proc;
5652
  int result;
unknown's avatar
unknown committed
5653
  ACL_DB *acl_db;
5654
  TABLE_LIST tables[GRANT_TABLES];
5655 5656
  DBUG_ENTER("mysql_revoke_all");

5657 5658 5659 5660 5661 5662 5663
  /*
    This statement will be replicated as a statement, even when using
    row-based replication.  The flag will be reset at the end of the
    statement.
  */
  thd->clear_current_stmt_binlog_row_based();

5664
  if ((result= open_grant_tables(thd, tables)))
unknown's avatar
unknown committed
5665
    DBUG_RETURN(result != 1);
5666 5667 5668 5669

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

5670
  LEX_USER *lex_user, *tmp_lex_user;
5671
  List_iterator <LEX_USER> user_list(list);
5672
  while ((tmp_lex_user= user_list++))
5673
  {
5674 5675 5676 5677 5678
    if (!(lex_user= get_current_user(thd, tmp_lex_user)))
    {
      result= -1;
      continue;
    }  
5679
    if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
5680 5681 5682 5683
    {
      result= -1;
      continue;
    }
unknown's avatar
unknown committed
5684

5685
    if (replace_user_table(thd, tables[0].table,
5686
			   *lex_user, ~(ulong)0, 1, 0, 0))
5687 5688 5689 5690 5691 5692
    {
      result= -1;
      continue;
    }

    /* Remove db access privileges */
unknown's avatar
unknown committed
5693 5694 5695 5696 5697
    /*
      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
5698
    do
5699
    {
unknown's avatar
unknown committed
5700
      for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
5701
      {
unknown's avatar
unknown committed
5702
	const char *user,*host;
unknown's avatar
unknown committed
5703

unknown's avatar
unknown committed
5704 5705 5706 5707 5708
	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
5709

unknown's avatar
unknown committed
5710 5711 5712
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
5713 5714
	  if (!replace_db_table(tables[1].table, acl_db->db, *lex_user,
                                ~(ulong)0, 1))
unknown's avatar
unknown committed
5715
	  {
unknown's avatar
unknown committed
5716 5717 5718 5719 5720
	    /*
	      Don't increment counter as replace_db_table deleted the
	      current element in acl_dbs.
	     */
	    revoked= 1;
unknown's avatar
unknown committed
5721 5722
	    continue;
	  }
unknown's avatar
unknown committed
5723
	  result= -1; // Something went wrong
unknown's avatar
unknown committed
5724
	}
unknown's avatar
unknown committed
5725
	counter++;
5726
      }
unknown's avatar
unknown committed
5727
    } while (revoked);
5728 5729

    /* Remove column access */
unknown's avatar
unknown committed
5730
    do
5731
    {
unknown's avatar
unknown committed
5732
      for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
5733
      {
unknown's avatar
unknown committed
5734 5735 5736 5737 5738
	const char *user,*host;
	GRANT_TABLE *grant_table= (GRANT_TABLE*)hash_element(&column_priv_hash,
							     counter);
	if (!(user=grant_table->user))
	  user= "";
5739
	if (!(host=grant_table->host.hostname))
unknown's avatar
unknown committed
5740
	  host= "";
unknown's avatar
unknown committed
5741

unknown's avatar
unknown committed
5742 5743
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
5744
	{
unknown's avatar
unknown committed
5745 5746 5747
	  if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
				  grant_table->db,
				  grant_table->tname,
5748
				  ~(ulong)0, 0, 1))
unknown's avatar
unknown committed
5749
	  {
unknown's avatar
unknown committed
5750
	    result= -1;
unknown's avatar
unknown committed
5751
	  }
unknown's avatar
unknown committed
5752
	  else
unknown's avatar
unknown committed
5753
	  {
unknown's avatar
unknown committed
5754
	    if (!grant_table->cols)
unknown's avatar
unknown committed
5755
	    {
unknown's avatar
unknown committed
5756 5757
	      revoked= 1;
	      continue;
unknown's avatar
unknown committed
5758
	    }
unknown's avatar
unknown committed
5759 5760
	    List<LEX_COLUMN> columns;
	    if (!replace_column_table(grant_table,tables[3].table, *lex_user,
unknown's avatar
unknown committed
5761 5762 5763
				      columns,
				      grant_table->db,
				      grant_table->tname,
5764
				      ~(ulong)0, 1))
unknown's avatar
unknown committed
5765
	    {
unknown's avatar
unknown committed
5766
	      revoked= 1;
5767
	      continue;
unknown's avatar
unknown committed
5768
	    }
5769
	    result= -1;
unknown's avatar
unknown committed
5770
	  }
5771
	}
unknown's avatar
unknown committed
5772
	counter++;
5773
      }
unknown's avatar
unknown committed
5774
    } while (revoked);
5775 5776

    /* Remove procedure access */
5777 5778 5779
    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 ; )
5780 5781
      {
	const char *user,*host;
5782
	GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
5783 5784
	if (!(user=grant_proc->user))
	  user= "";
unknown's avatar
unknown committed
5785
	if (!(host=grant_proc->host.hostname))
5786 5787 5788 5789 5790
	  host= "";

	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
5791
	  if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
5792 5793
				  grant_proc->db,
				  grant_proc->tname,
5794
                                  is_proc,
unknown's avatar
unknown committed
5795
				  ~(ulong)0, 1))
5796 5797 5798 5799 5800 5801 5802 5803 5804
	  {
	    revoked= 1;
	    continue;
	  }
	  result= -1;	// Something went wrong
	}
	counter++;
      }
    } while (revoked);
5805
  }
unknown's avatar
unknown committed
5806

5807
  VOID(pthread_mutex_unlock(&acl_cache->lock));
5808 5809 5810

  if (mysql_bin_log.is_open())
  {
5811 5812
    thd->binlog_query(THD::MYSQL_QUERY_TYPE,
                      thd->query, thd->query_length, FALSE, FALSE);
5813 5814
  }

5815 5816
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
unknown's avatar
unknown committed
5817

5818
  if (result)
unknown's avatar
unknown committed
5819
    my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
unknown's avatar
unknown committed
5820

5821 5822
  DBUG_RETURN(result);
}
unknown's avatar
unknown committed
5823

5824

5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838
/*
  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.
*/

5839 5840
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
                          bool is_proc)
5841 5842 5843 5844
{
  uint counter, revoked;
  int result;
  TABLE_LIST tables[GRANT_TABLES];
5845
  HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
5846 5847 5848 5849 5850 5851 5852 5853
  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));

5854 5855 5856 5857 5858 5859 5860
  /*
    This statement will be replicated as a statement, even when using
    row-based replication.  The flag will be reset at the end of the
    statement.
  */
  thd->clear_current_stmt_binlog_row_based();

5861
  /* Remove procedure access */
5862 5863
  do
  {
5864
    for (counter= 0, revoked= 0 ; counter < hash->records ; )
5865
    {
5866
      GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
5867 5868 5869 5870 5871 5872
      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);
5873 5874 5875 5876
	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;
5877 5878
	if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
				   grant_proc->db, grant_proc->tname,
unknown's avatar
unknown committed
5879
                                   is_proc, ~(ulong)0, 1))
5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914
	{
	  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.
*/

5915 5916
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
                         bool is_proc)
5917
{
5918
  Security_context *sctx= thd->security_ctx;
5919 5920 5921 5922
  LEX_USER *combo;
  TABLE_LIST tables[1];
  List<LEX_USER> user_list;
  bool result;
5923 5924
  ACL_USER *au;
  char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
5925
  DBUG_ENTER("sp_grant_privileges");
5926 5927 5928 5929

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

5930
  combo->user.str= sctx->user;
5931

5932
  VOID(pthread_mutex_lock(&acl_cache->lock));
5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946

  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:
5947
  VOID(pthread_mutex_unlock(&acl_cache->lock));
5948 5949 5950 5951 5952

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

  tables->db= (char*)sp_db;
5953
  tables->table_name= tables->alias= (char*)sp_name;
5954

5955 5956 5957 5958
  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);
5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984


  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;
  }
5985 5986 5987 5988 5989

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

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

5992
  result= mysql_routine_grant(thd, tables, is_proc, user_list,
5993 5994 5995 5996 5997
  				DEFAULT_CREATE_PROC_ACLS, 0, 1);
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
5998
/*****************************************************************************
unknown's avatar
unknown committed
5999
  Instantiate used templates
unknown's avatar
unknown committed
6000 6001
*****************************************************************************/

6002
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
unknown's avatar
unknown committed
6003 6004 6005 6006 6007
template class List_iterator<LEX_COLUMN>;
template class List_iterator<LEX_USER>;
template class List<LEX_COLUMN>;
template class List<LEX_USER>;
#endif
unknown's avatar
SCRUM:  
unknown committed
6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054

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

6055 6056 6057 6058 6059 6060 6061 6062

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;
6063
  restore_record(table, s->default_values);
6064 6065 6066 6067 6068 6069 6070 6071 6072
  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);
6073
  table->file->ha_write_row(table->record[0]);
6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084
}


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;
6085
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
6086
  char *curr_host= thd->security_ctx->priv_host_name();
6087
  DBUG_ENTER("fill_schema_user_privileges");
6088

6089 6090
  if (!initialized)
    DBUG_RETURN(0);
6091 6092
  pthread_mutex_lock(&acl_cache->lock);

6093 6094 6095 6096 6097 6098 6099 6100
  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= "";
6101 6102

    if (no_global_access &&
6103
        (strcmp(thd->security_ctx->priv_user, user) ||
6104 6105 6106
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;
      
6107 6108 6109 6110 6111 6112
    want_access= acl_user->access;
    if (!(want_access & GRANT_ACL))
      is_grantable= "NO";

    strxmov(buff,"'",user,"'@'",host,"'",NullS);
    if (!(want_access & ~GRANT_ACL))
6113 6114
      update_schema_privilege(table, buff, 0, 0, 0, 0,
                              STRING_WITH_LEN("USAGE"), is_grantable);
6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127
    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);
      }
    }
  }
6128 6129 6130

  pthread_mutex_unlock(&acl_cache->lock);

6131
  DBUG_RETURN(0);
6132 6133 6134
#else
  return(0);
#endif
6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145
}


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;
6146
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
6147
  char *curr_host= thd->security_ctx->priv_host_name();
6148 6149
  DBUG_ENTER("fill_schema_schema_privileges");

6150 6151
  if (!initialized)
    DBUG_RETURN(0);
6152 6153
  pthread_mutex_lock(&acl_cache->lock);

6154 6155 6156 6157 6158 6159 6160 6161 6162 6163
  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= "";

6164
    if (no_global_access &&
6165
        (strcmp(thd->security_ctx->priv_user, user) ||
6166 6167 6168
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;

6169 6170 6171 6172 6173 6174 6175 6176 6177 6178
    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,
6179
                                0, STRING_WITH_LEN("USAGE"), is_grantable);
6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191
      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);
      }
    }
  }
6192 6193 6194

  pthread_mutex_unlock(&acl_cache->lock);

6195
  DBUG_RETURN(0);
6196 6197 6198
#else
  return (0);
#endif
6199 6200 6201 6202 6203 6204 6205 6206 6207
}


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;
6208
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
6209
  char *curr_host= thd->security_ctx->priv_host_name();
6210 6211
  DBUG_ENTER("fill_schema_table_privileges");

6212 6213
  rw_rdlock(&LOCK_grant);

6214 6215
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
6216
    const char *user, *host, *is_grantable= "YES";
6217 6218 6219 6220
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
6221 6222
    if (!(host= grant_table->host.hostname))
      host= "";
6223 6224

    if (no_global_access &&
6225
        (strcmp(thd->security_ctx->priv_user, user) ||
6226
         my_strcasecmp(system_charset_info, curr_host, host)))
6227 6228
      continue;

6229
    ulong table_access= grant_table->privs;
6230
    if (table_access)
6231 6232
    {
      ulong test_access= table_access & ~GRANT_ACL;
unknown's avatar
unknown committed
6233 6234 6235 6236
      /*
        We should skip 'usage' privilege on table if
        we have any privileges on column(s) of this table
      */
6237 6238
      if (!test_access && grant_table->cols)
        continue;
6239 6240 6241
      if (!(table_access & GRANT_ACL))
        is_grantable= "NO";

6242
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
6243 6244
      if (!test_access)
        update_schema_privilege(table, buff, grant_table->db, grant_table->tname,
6245
                                0, 0, STRING_WITH_LEN("USAGE"), is_grantable);
6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259
      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);
        }
      }
    }
  }
6260 6261 6262

  rw_unlock(&LOCK_grant);

6263
  DBUG_RETURN(0);
6264 6265 6266
#else
  return (0);
#endif
6267 6268 6269 6270 6271 6272 6273 6274 6275
}


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;
6276
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
6277
  char *curr_host= thd->security_ctx->priv_host_name();
6278 6279
  DBUG_ENTER("fill_schema_table_privileges");

6280 6281
  rw_rdlock(&LOCK_grant);

6282 6283
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
6284
    const char *user, *host, *is_grantable= "YES";
6285 6286 6287 6288
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
6289 6290
    if (!(host= grant_table->host.hostname))
      host= "";
6291 6292

    if (no_global_access &&
6293
        (strcmp(thd->security_ctx->priv_user, user) ||
6294
         my_strcasecmp(system_charset_info, curr_host, host)))
6295 6296
      continue;

6297 6298 6299
    ulong table_access= grant_table->cols;
    if (table_access != 0)
    {
unknown's avatar
unknown committed
6300
      if (!(grant_table->privs & GRANT_ACL))
6301 6302
        is_grantable= "NO";

unknown's avatar
unknown committed
6303
      ulong test_access= table_access & ~GRANT_ACL;
6304
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333
      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);
            }
          }
        }
      }
    }
  }
6334 6335 6336

  rw_unlock(&LOCK_grant);

6337
  DBUG_RETURN(0);
6338 6339 6340
#else
  return (0);
#endif
6341 6342 6343
}


unknown's avatar
VIEW  
unknown committed
6344 6345 6346 6347 6348
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
  fill effective privileges for table

  SYNOPSIS
6349 6350
    fill_effective_table_privileges()
    thd     thread handler
unknown's avatar
VIEW  
unknown committed
6351 6352 6353 6354 6355 6356 6357 6358
    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)
{
6359
  Security_context *sctx= thd->security_ctx;
6360 6361 6362 6363 6364
  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));
6365 6366 6367
  /* --skip-grants */
  if (!initialized)
  {
6368
    DBUG_PRINT("info", ("skip grants"));
6369
    grant->privilege= ~NO_ACCESS;             // everything is allowed
6370 6371
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;
6372 6373
  }

unknown's avatar
VIEW  
unknown committed
6374
  /* global privileges */
6375
  grant->privilege= sctx->master_access;
6376

6377
  if (!sctx->priv_user)
6378 6379 6380 6381
  {
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;                         // it is slave
  }
6382

6383
  /* db privileges */
6384
  grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
6385

6386
  if (!grant_option)
6387 6388 6389 6390
  {
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;
  }
6391

unknown's avatar
VIEW  
unknown committed
6392
  /* table privileges */
6393
  rw_rdlock(&LOCK_grant);
unknown's avatar
VIEW  
unknown committed
6394 6395 6396
  if (grant->version != grant_version)
  {
    grant->grant_table=
6397 6398
      table_hash_search(sctx->host, sctx->ip, db,
			sctx->priv_user,
unknown's avatar
VIEW  
unknown committed
6399 6400 6401 6402 6403 6404 6405
			table, 0);              /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
  }
  if (grant->grant_table != 0)
  {
    grant->privilege|= grant->grant_table->privs;
  }
6406 6407
  rw_unlock(&LOCK_grant);

6408 6409
  DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
  DBUG_VOID_RETURN;
unknown's avatar
VIEW  
unknown committed
6410
}
6411 6412 6413 6414 6415 6416 6417

#else /* NO_EMBEDDED_ACCESS_CHECKS */

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

unknown's avatar
unknown committed
6418 6419
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
                             bool is_proc)
6420 6421 6422 6423
{
  return FALSE;
}

unknown's avatar
VIEW  
unknown committed
6424
#endif