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

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

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

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


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

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

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

hf@deer.(none)'s avatar
hf@deer.(none) committed
38
#ifndef NO_EMBEDDED_ACCESS_CHECKS
39

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

48

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

serg@serg.mylan's avatar
serg@serg.mylan committed
56
#define IP_ADDR_STRLEN (3+1+3+1+3+1+3)
57
#define ACL_KEY_LENGTH (IP_ADDR_STRLEN+1+NAME_LEN+1+USERNAME_LENGTH+1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
58 59 60 61 62

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

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

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

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

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

124

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

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

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

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

143
my_bool acl_init(bool dont_read_acl_tables)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
144
{
145
  THD  *thd;
146
  my_bool return_val;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
147 148
  DBUG_ENTER("acl_init");

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

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


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

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

  RETURN VALUES
    FALSE  Success
    TRUE   Error
*/

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

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
207 208
  acl_cache->clear(1);				// Clear locked hostname cache

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

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

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

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

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

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

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

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

362 363 364
      user.sort= get_sort(2,user.host.hostname,user.user);
      user.hostname_length= (user.host.hostname ?
                             (uint) strlen(user.host.hostname) : 0);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
365

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

379 380 381
        user.ssl_cipher=   get_field(&mem, table->field[next_field++]);
        user.x509_issuer=  get_field(&mem, table->field[next_field++]);
        user.x509_subject= get_field(&mem, table->field[next_field++]);
382

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

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

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

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

end:
  DBUG_RETURN(return_val);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
495 496 497 498 499
}


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

515 516

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

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

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

  RETURN VALUE
    FALSE  Success
    TRUE   Failure
533
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
534

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

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

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

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
571 572 573 574 575 576 577 578 579 580
  if ((old_initialized=initialized))
    VOID(pthread_mutex_lock(&acl_cache->lock));

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

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


606 607
/*
  Get all access bits from table after fieldnr
608 609

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

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

  RETURN VALUE
    privilege mask
623
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
624

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

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


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

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

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

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

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


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

697

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

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
702 703 704 705
  IMPLEMENTATION
   This function does not check if the user has any sensible privileges:
   only user's existence and  validity is checked.
   Note, that entire operation is protected by acl_cache_lock.
peter@mysql.com's avatar
peter@mysql.com committed
706

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

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

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
749
  VOID(pthread_mutex_lock(&acl_cache->lock));
peter@mysql.com's avatar
peter@mysql.com committed
750

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

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

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

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

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

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


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

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

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

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

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

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

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

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1025 1026 1027 1028 1029 1030 1031
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;
}

1032

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

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


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

  safe_mutex_assert_owner(&acl_cache->lock);

1096
  acl_user.user=*user ? strdup_root(&mem,user) : 0;
monty@mysql.com's avatar
monty@mysql.com committed
1097
  update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1098
  acl_user.access=privileges;
1099
  acl_user.user_resource = *mqh;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1100
  acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
1101
  acl_user.hostname_length=(uint) strlen(host);
1102 1103 1104 1105 1106
  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;
1107 1108

  set_user_salt(&acl_user, password, password_len);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1109 1110

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

1117 1118
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1119 1120 1121 1122
}


static void acl_update_db(const char *user, const char *host, const char *db,
1123
			  ulong privileges)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1124
{
1125 1126
  safe_mutex_assert_owner(&acl_cache->lock);

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


1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
/*
  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
*/

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1166
static void acl_insert_db(const char *user, const char *host, const char *db,
1167
			  ulong privileges)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1168 1169
{
  ACL_DB acl_db;
1170
  safe_mutex_assert_owner(&acl_cache->lock);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1171
  acl_db.user=strdup_root(&mem,user);
1172
  update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1173 1174 1175 1176 1177 1178 1179 1180 1181
  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);
}


1182 1183 1184

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

  as db_is_pattern changes the semantics of comparison,
  acl_cache is not used if db_is_pattern is set.
1188
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1189

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

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

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

1269 1270 1271 1272 1273 1274 1275
/*
  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
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1276 1277 1278 1279

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


1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337
/*
  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();
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
1338 1339 1340 1341 1342 1343 1344 1345
/* 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));

1346 1347
  if (host && hash_search(&acl_check_hosts,(byte*) host,(uint) strlen(host)) ||
      ip && hash_search(&acl_check_hosts,(byte*) ip,(uint) strlen(ip)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365
  {
    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
}


1366 1367 1368 1369 1370 1371 1372 1373
/*
  Check if the user is allowed to change password

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

  NOTE:
    new_password cannot be NULL
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1378

1379
    RETURN VALUE
1380 1381
      0		OK
      1		ERROR  ; In this case the error is sent to the client.
1382 1383
*/

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


1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429
/*
  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.
peter@mysql.com's avatar
peter@mysql.com committed
1430
*/
1431

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

1447
  if (check_change_password(thd, host, user, new_password, new_password_len))
1448 1449
    DBUG_RETURN(1);

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

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

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

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

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

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


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

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

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


bk@work.mysql.com's avatar
bk@work.mysql.com committed
1544 1545 1546 1547 1548
/*
  Find first entry that matches the current user
*/

static ACL_USER *
1549
find_acl_user(const char *host, const char *user, my_bool exact)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1550
{
1551
  DBUG_ENTER("find_acl_user");
1552
  DBUG_PRINT("enter",("host: '%s'  user: '%s'",host,user));
1553 1554 1555

  safe_mutex_assert_owner(&acl_cache->lock);

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


1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590
/*
  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.
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612

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


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 ||
1632
	  (hostname && !wild_case_compare(system_charset_info,
1633 1634
                                          hostname, host->hostname)) ||
	  (ip && !wild_compare(ip, host->hostname, 0)));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1635 1636
}

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

1657

1658
/*
1659 1660 1661 1662 1663 1664 1665 1666 1667 1668
  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
1669
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1670

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

1680 1681
  table->field[0]->store(host,(uint) strlen(host), system_charset_info);
  table->field[1]->store(user,(uint) strlen(user), system_charset_info);
1682
  key_copy((byte *) user_key, table->record[0], table->key_info,
1683
           table->key_info->key_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1684

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

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1704

1705 1706 1707 1708 1709 1710
/*
  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.
*/
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1711 1712 1713

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

1727 1728
    db_access=acl_get(sctx->host, sctx->ip,
		      sctx->priv_user, tl.db, 0);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1729 1730
    if (!(db_access & INSERT_ACL))
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1731
      if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1732 1733 1734 1735 1736 1737 1738
	create_new_users=0;
    }
  }
  return create_new_users;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
1739
/****************************************************************************
1740
  Handle GRANT commands
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1741 1742
****************************************************************************/

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

1756
  safe_mutex_assert_owner(&acl_cache->lock);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1757 1758

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

1770 1771
  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);
1772 1773 1774
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

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

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

1833 1834 1835 1836
  /* Update table columns with new privileges */

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

1893
    USER_RESOURCES mqh= lex->mqh;
1894
    if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1895
      table->field[next_field]->store((longlong) mqh.questions, TRUE);
1896
    if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1897
      table->field[next_field+1]->store((longlong) mqh.updates, TRUE);
1898
    if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
1899
      table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE);
1900
    if (table->s->fields >= 36 &&
1901
        (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1902
      table->field[next_field+3]->store((longlong) mqh.user_conn);
1903
    mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
1904
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1905
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1906 1907 1908 1909 1910
  {
    /*
      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!
    */
1911
    table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
1912
    if (cmp_record(table,record[1]) &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931
	(error=table->file->update_row(table->record[1],table->record[0])))
    {						// This should never happen
      table->file->print_error(error,MYF(0));	/* purecov: deadcode */
      error= -1;				/* purecov: deadcode */
      goto end;					/* purecov: deadcode */
    }
  }
  else if ((error=table->file->write_row(table->record[0]))) // insert
  {						// This should never happen
    if (error && error != HA_ERR_FOUND_DUPP_KEY &&
	error != HA_ERR_FOUND_DUPP_UNIQUE)	/* purecov: inspected */
    {
      table->file->print_error(error,MYF(0));	/* purecov: deadcode */
      error= -1;				/* purecov: deadcode */
      goto end;					/* purecov: deadcode */
    }
  }
  error=0;					// Privileges granted / revoked

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


/*
1959
  change grants in the mysql.db table
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1960 1961 1962 1963
*/

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

1974 1975
  if (!initialized)
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1976
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1977 1978 1979
    DBUG_RETURN(-1);
  }

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

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

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

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

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

  acl_cache->clear(1);				// Clear privilege cache
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2046
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2047 2048
    acl_update_db(combo.user.str,combo.host.str,db,rights);
  else
2049
  if (rights)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2050 2051 2052 2053
    acl_insert_db(combo.user.str,combo.host.str,db,rights);
  DBUG_RETURN(0);

  /* This could only happen if the grant tables got corrupted */
2054
table_error:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2055 2056
  table->file->print_error(error,MYF(0));	/* purecov: deadcode */

2057
abort:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2058 2059 2060 2061 2062 2063 2064 2065
  DBUG_RETURN(-1);
}


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

2074

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2075 2076 2077 2078 2079 2080 2081
static byte* get_key_column(GRANT_COLUMN *buff,uint *length,
			    my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
  return (byte*) buff->column;
}

2082

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

  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);
2108
  ~GRANT_TABLE();
2109 2110
  bool ok() { return privs != 0 || cols != 0; }
};
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2111

2112

monty@mysql.com's avatar
monty@mysql.com committed
2113

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


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)
{
2139
  (void) hash_init(&hash_columns,system_charset_info,
monty@mysql.com's avatar
monty@mysql.com committed
2140
                   0,0,0, (hash_get_key) get_key_column,0,0);
2141
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2142

2143

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


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();
2186 2187
  cols =  fix_rights_for_column(cols);

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

    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);
monty@mysql.com's avatar
monty@mysql.com committed
2206
    col_privs->field[4]->store("",0, &my_charset_latin1);
2207

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

2238

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


2245
static byte* get_grant_table(GRANT_NAME *buff,uint *length,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2246 2247 2248 2249 2250 2251
			     my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
  return (byte*) buff->hash_key;
}

2252

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2253 2254 2255 2256 2257
void free_grant_table(GRANT_TABLE *grant_table)
{
  hash_free(&grant_table->hash_columns);
}

2258

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2259 2260
/* Search after a matching grant. Prefer exact grants before not exact ones */

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

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


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


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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2317

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

2337 2338 2339 2340 2341 2342 2343 2344
  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);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2345

2346 2347 2348 2349
  /* 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);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2350

2351
  rights&= COL_ACLS;				// Only ACL for columns
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2352 2353 2354 2355

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

  List_iterator <LEX_COLUMN> iter(columns);
2356
  class LEX_COLUMN *column;
2357
  table->file->ha_index_init(0);
2358
  while ((column= iter++))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2359
  {
2360
    ulong privileges= column->rights;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2361
    bool old_row_exists=0;
2362 2363 2364 2365
    byte user_key[MAX_KEY_LENGTH];

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

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

      if (revoke_grant)
	privileges = tmp & ~(privileges | rights);
      else
	privileges |= tmp;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2401
      old_row_exists = 1;
2402
      store_record(table,record[1]);			// copy original row
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2403 2404
    }

2405
    table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2406

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

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

  if (revoke_grant)
  {
2446 2447
    byte user_key[MAX_KEY_LENGTH];
    key_copy(user_key, table->record[0], table->key_info,
2448 2449
             key_prefix_length);

2450
    table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
2451
    if (table->file->index_read(table->record[0], user_key,
2452
				key_prefix_length,
2453
                                HA_READ_KEY_EXACT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2454 2455
      goto end;

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

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

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

2507
end:
2508
  table->file->ha_index_end();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2509 2510 2511 2512 2513 2514 2515
  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,
2516 2517
			       ulong rights, ulong col_rights,
			       bool revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2518
{
2519
  char grantor[USER_HOST_BUFF_SIZE];
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2520
  int old_row_exists = 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2521
  int error=0;
2522
  ulong store_table_rights, store_col_rights;
2523
  byte user_key[MAX_KEY_LENGTH];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2524 2525
  DBUG_ENTER("replace_table_table");

2526 2527
  strxmov(grantor, thd->security_ctx->user, "@",
          thd->security_ctx->host_or_ip, NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2528

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

2540
  restore_record(table, s->default_values);     // Get empty record
2541 2542 2543 2544
  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);
2545
  store_record(table,record[1]);			// store at pos 1
2546 2547
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2548

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

2570 2571
  store_table_rights= get_rights_for_table(rights);
  store_col_rights=   get_rights_for_column(col_rights);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2572
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2573
  {
2574
    ulong j,k;
2575
    store_record(table,record[1]);
2576 2577
    j = (ulong) table->field[6]->val_int();
    k = (ulong) table->field[7]->val_int();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2578 2579 2580

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

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

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

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

2625 2626
  /* This should never happen */
table_error:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2627 2628 2629 2630 2631
  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
  DBUG_RETURN(-1); /* purecov: deadcode */
}


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

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

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

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

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

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

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

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

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


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

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

  if (!initialized)
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2780 2781
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: inspected */
2782
    DBUG_RETURN(TRUE);				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2783 2784 2785
  }
  if (rights & ~TABLE_ACLS)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2786 2787
    my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
               MYF(0));
2788
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2789 2790
  }

2791
  if (!revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2792
  {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2793
    if (columns.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2794
    {
2795 2796
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2797 2798 2799

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

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

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

  bzero((char*) &tables,sizeof(tables));
2852 2853 2854
  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";
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2855
  tables[0].next_local= tables[0].next_global= tables+1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2856
  /* Don't open column table if we don't need it ! */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2857 2858 2859 2860 2861
  tables[1].next_local=
    tables[1].next_global= ((column_priv ||
			     (revoke_grant &&
			      ((rights & COL_ACLS) || columns.elements)))
			    ? tables+2 : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2862 2863 2864
  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";

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

2882
  if (simple_open_n_lock_tables(thd,tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2883 2884
  {						// Should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
2885
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2886 2887
  }

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

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2925 2926 2927
    db_name= (table_list->view_db.length ?
	      table_list->view_db.str :
	      table_list->db);
2928
    table_name= (table_list->view_name.length ?
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2929
		table_list->view_name.str :
2930
		table_list->table_name);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2931

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

    /* If revoke_grant, calculate the new column privilege for tables_priv */
    if (revoke_grant)
    {
2959 2960
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2961 2962 2963
      GRANT_COLUMN *grant_column;

      /* Fix old grants */
2964
      while ((column = column_iter++))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2965 2966
      {
	grant_column = column_hash_search(grant_table,
2967 2968
					  column->column.ptr(),
					  column->column.length());
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2969
	if (grant_column)
2970
	  grant_column->rights&= ~(column->rights | rights);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2971 2972
      }
      /* scan trough all columns to get new column grant */
2973
      column_priv= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989
      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 */

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2990
    if (replace_table_table(thd, grant_table, tables[1].table, *Str,
2991
			    db_name, table_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2992
			    rights, column_priv, revoke_grant))
2993 2994
    {
      /* Should only happen if table is crashed */
2995
      result= TRUE;			       /* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2996 2997 2998
    }
    else if (tables[2].table)
    {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2999
      if ((replace_column_table(grant_table, tables[2].table, *Str,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3000
				columns,
3001
				db_name, table_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3002 3003
				rights, revoke_grant)))
      {
3004
	result= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3005 3006 3007 3008
      }
    }
  }
  grant_option=TRUE;
3009
  thd->mem_root= old_root;
3010
  pthread_mutex_unlock(&acl_cache->lock);
3011
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3012
  if (!result)
3013
    send_ok(thd);
3014
  /* Tables are automatically closed */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3015 3016 3017 3018
  DBUG_RETURN(result);
}


3019
/*
3020
  Store routine level grants in the privilege tables
3021 3022

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

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

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

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

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

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
  if (thd->slave_thread && table_rules_on)
  {
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
    */
    tables[0].updating= tables[1].updating= 1;
3089
    if (!tables_ok(thd, tables))
3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102
      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);
3103
  pthread_mutex_lock(&acl_cache->lock);
3104 3105 3106 3107 3108
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;

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

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

    db_name= table_list->db;
3139
    table_name= table_list->table_name;
3140

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

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


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

3198 3199 3200
  if (lower_case_table_names && db)
  {
    strmov(tmp_db,db);
3201
    my_casedn_str(files_charset_info, tmp_db);
3202 3203
    db=tmp_db;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3204 3205

  /* open the mysql.user and mysql.db tables */
3206
  bzero((char*) &tables,sizeof(tables));
3207 3208
  tables[0].alias=tables[0].table_name=(char*) "user";
  tables[1].alias=tables[1].table_name=(char*) "db";
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3209
  tables[0].next_local= tables[0].next_global= tables+1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3210 3211
  tables[0].lock_type=tables[1].lock_type=TL_WRITE;
  tables[0].db=tables[1].db=(char*) "mysql";
3212 3213 3214 3215 3216 3217

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

3230
  if (simple_open_n_lock_tables(thd,tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3231 3232
  {						// This should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
3233
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3234 3235
  }

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3236 3237
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3238

3239
  /* go through users in user_list */
3240
  rw_wrlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3241 3242 3243 3244
  VOID(pthread_mutex_lock(&acl_cache->lock));
  grant_version++;

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

  if (!result)
3286
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3287 3288 3289
  DBUG_RETURN(result);
}

3290 3291

/* Free grant array if possible */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3292 3293 3294 3295 3296

void  grant_free(void)
{
  DBUG_ENTER("grant_free");
  grant_option = FALSE;
3297
  hash_free(&column_priv_hash);
3298
  hash_free(&proc_priv_hash);
monty@mysql.com's avatar
monty@mysql.com committed
3299
  hash_free(&func_priv_hash);
3300
  free_root(&memex,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3301 3302 3303 3304
  DBUG_VOID_RETURN;
}


3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315
/*
  Initialize structures responsible for table/column-level privilege checking
  and load information for them from tables in the 'mysql' database.

  SYNOPSIS
    grant_init()

  RETURN VALUES
    0	ok
    1	Could not initialize grant's
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3316

3317
my_bool grant_init()
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3318
{
3319
  THD  *thd;
3320 3321 3322 3323 3324
  my_bool return_val;
  DBUG_ENTER("grant_init");

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

  grant_option = FALSE;
3361
  (void) hash_init(&column_priv_hash,system_charset_info,
3362
		   0,0,0, (hash_get_key) get_grant_table,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3363
		   (hash_free_key) free_grant_table,0);
3364 3365 3366
  (void) hash_init(&proc_priv_hash,system_charset_info,
		   0,0,0, (hash_get_key) get_grant_table,
		   0,0);
3367 3368 3369
  (void) hash_init(&func_priv_hash,system_charset_info,
		   0,0,0, (hash_get_key) get_grant_table,
		   0,0);
3370
  init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3371 3372

  t_table = tables[0].table; c_table = tables[1].table;
3373
  p_table= tables[2].table;
3374
  t_table->file->ha_index_init(0);
3375 3376
  p_table->file->ha_index_init(0);
  if (!t_table->file->index_first(t_table->record[0]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3377
  {
3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
    {
      GRANT_TABLE *mem_check;
      if (!(mem_check=new GRANT_TABLE(t_table,c_table)))
      {
	/* This could only happen if we are out memory */
	grant_option= FALSE;
	goto end_unlock;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3389

3390 3391
      if (check_no_resolve)
      {
3392
	if (hostname_requires_resolving(mem_check->host.hostname))
3393 3394 3395
	{
          sql_print_warning("'tables_priv' entry '%s %s@%s' "
                            "ignored in --skip-name-resolve mode.",
3396 3397 3398 3399
                            mem_check->tname,
                            mem_check->user ? mem_check->user : "",
                            mem_check->host.hostname ?
                            mem_check->host.hostname : "");
3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415
	  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]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3416
  {
3417 3418 3419
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3420
    {
3421
      GRANT_NAME *mem_check;
3422
      HASH *hash;
3423 3424 3425 3426 3427 3428
      if (!(mem_check=new GRANT_NAME(p_table)))
      {
	/* This could only happen if we are out memory */
	grant_option= FALSE;
	goto end_unlock;
      }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3429

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

3459 3460 3461
      mem_check->privs= fix_rights_for_procedure(mem_check->privs);
      if (! mem_check->ok())
	delete mem_check;
3462
      else if (my_hash_insert(hash, (byte*) mem_check))
3463 3464 3465 3466 3467
      {
	delete mem_check;
	grant_option= FALSE;
	goto end_unlock;
      }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3468
    }
3469
    while (!p_table->file->index_next(p_table->record[0]));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3470
  }
3471
  grant_option= TRUE;
3472 3473 3474
  return_val=0;					// Return ok

end_unlock:
3475
  t_table->file->ha_index_end();
3476
  p_table->file->ha_index_end();
3477
  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
3478
  DBUG_RETURN(return_val);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3479 3480 3481
}


3482
/*
3483
  Reload information about table and column level privileges if possible.
3484 3485 3486

  SYNOPSIS
    grant_reload()
3487
      thd  Current thread
3488 3489

  NOTES
3490 3491 3492 3493 3494 3495 3496 3497
    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
3498
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3499

3500
my_bool grant_reload(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3501
{
3502
  TABLE_LIST tables[3];
3503
  HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
3504
  bool old_grant_option;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3505
  MEM_ROOT old_mem;
3506
  my_bool return_val= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3507 3508
  DBUG_ENTER("grant_reload");

3509 3510 3511 3512 3513
  /* Don't do anything if running with --skip-grant-tables */
  if (!initialized)
    DBUG_RETURN(0);

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

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

3529
  rw_wrlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3530
  grant_version++;
3531
  old_column_priv_hash= column_priv_hash;
3532
  old_proc_priv_hash= proc_priv_hash;
3533
  old_func_priv_hash= func_priv_hash;
3534
  old_grant_option= grant_option;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3535
  old_mem= memex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3536

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


/****************************************************************************
3562
  Check table level grants
3563

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

   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).
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3586 3587
****************************************************************************/

3588
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3589
		 uint show_table, uint number, bool no_errors)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3590
{
3591
  TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table();
3592
  Security_context *sctx= thd->security_ctx;
3593
  uint i;
3594
  ulong orig_want_access= want_access;
3595 3596
  DBUG_ENTER("check_grant");
  DBUG_ASSERT(number > 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3597

3598
  /*
3599 3600 3601 3602 3603 3604 3605 3606
    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.
3607 3608
  */
  for (i= 0, table= tables;
3609
       table != first_not_own_table && i < number;
3610 3611 3612 3613 3614 3615
       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);
  }

3616
  rw_rdlock(&LOCK_grant);
3617 3618 3619
  for (table= tables;
       table && number-- && table != first_not_own_table;
       table= table->next_global)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3620
  {
3621
    GRANT_TABLE *grant_table;
3622 3623 3624 3625 3626 3627 3628 3629
    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

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

    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
    }
  }
3673
  rw_unlock(&LOCK_grant);
3674
  DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3675

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


3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709
/*
  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
*/

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3710
bool check_grant_column(THD *thd, GRANT_INFO *grant,
3711
			const char *db_name, const char *table_name,
3712
			const char *name, uint length,  Security_context *sctx)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3713 3714 3715
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3716
  ulong want_access= grant->want_privilege & ~grant->privilege;
monty@mysql.com's avatar
monty@mysql.com committed
3717 3718 3719
  DBUG_ENTER("check_grant_column");
  DBUG_PRINT("enter", ("table: %s  want_access: %u", table_name, want_access));

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3720
  if (!want_access)
monty@mysql.com's avatar
monty@mysql.com committed
3721
    DBUG_RETURN(0);				// Already checked
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3722

3723
  rw_rdlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3724

3725
  /* reload table if someone has modified any grants */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3726

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3727
  if (grant->version != grant_version)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3728
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3729
    grant->grant_table=
3730 3731
      table_hash_search(sctx->host, sctx->ip, db_name,
			sctx->priv_user,
monty@mysql.com's avatar
monty@mysql.com committed
3732
			table_name, 0);         /* purecov: inspected */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3733
    grant->version= grant_version;		/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3734
  }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3735
  if (!(grant_table= grant->grant_table))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3736 3737 3738 3739 3740
    goto err;					/* purecov: deadcode */

  grant_column=column_hash_search(grant_table, name, length);
  if (grant_column && !(~grant_column->rights & want_access))
  {
3741
    rw_unlock(&LOCK_grant);
monty@mysql.com's avatar
monty@mysql.com committed
3742
    DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3743 3744
  }

3745
err:
3746
  rw_unlock(&LOCK_grant);
3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790
  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)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3791
  {
3792
    /* View or derived information schema table. */
3793
    ulong view_privs;
3794 3795 3796
    grant= &(table_ref->grant);
    db_name= table_ref->view_db.str;
    table_name= table_ref->view_name.str;
3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810
    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;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3811
  }
3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826
  else
  {
    /* Normal or temporary table. */
    TABLE *table= table_ref->table;
    grant= &(table->grant);
    db_name= table->s->db;
    table_name= table->s->table_name;
  }

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3827 3828 3829
}


bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3830
bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant,
3831
                             const char* db_name, const char *table_name,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3832
                             Field_iterator *fields)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3833
{
3834
  Security_context *sctx= thd->security_ctx;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3835 3836 3837
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3838
  want_access &= ~grant->privilege;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3839
  if (!want_access)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3840
    return 0;				// Already checked
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3841 3842
  if (!grant_option)
    goto err2;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3843

3844
  rw_rdlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3845

3846
  /* reload table if someone has modified any grants */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3847

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3848
  if (grant->version != grant_version)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3849
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3850
    grant->grant_table=
3851 3852
      table_hash_search(sctx->host, sctx->ip, db_name,
			sctx->priv_user,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3853 3854
			table_name, 0);	/* purecov: inspected */
    grant->version= grant_version;		/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3855
  }
3856
  /* The following should always be true */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3857
  if (!(grant_table= grant->grant_table))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3858 3859
    goto err;					/* purecov: inspected */

3860
  for (; !fields->end_of_fields(); fields->next())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3861
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3862 3863 3864
    const char *field_name= fields->name();
    grant_column= column_hash_search(grant_table, field_name,
				    (uint) strlen(field_name));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3865 3866 3867
    if (!grant_column || (~grant_column->rights & want_access))
      goto err;
  }
3868
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3869 3870
  return 0;

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3871
err:
3872
  rw_unlock(&LOCK_grant);
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3873
err2:
3874 3875
  char command[128];
  get_privilege_desc(command, sizeof(command), want_access);
3876 3877
  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
           command,
3878 3879
           sctx->priv_user,
           sctx->host_or_ip,
3880 3881
           fields->name(),
           table_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3882 3883 3884 3885
  return 1;
}


3886
/*
3887
  Check if a user has the right to access a database
3888
  Access is accepted if he has a grant for any table/routine in the database
3889
  Return 1 if access is denied
3890
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3891 3892 3893

bool check_grant_db(THD *thd,const char *db)
{
3894
  Security_context *sctx= thd->security_ctx;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3895 3896
  char helping [NAME_LEN+USERNAME_LENGTH+2];
  uint len;
3897
  bool error= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3898

3899
  len= (uint) (strmov(strmov(helping, sctx->priv_user) + 1, db) - helping) + 1;
3900
  rw_rdlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3901

3902
  for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3903
  {
3904 3905
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  idx);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3906 3907
    if (len < grant_table->key_length &&
	!memcmp(grant_table->hash_key,helping,len) &&
3908
        compare_hostname(&grant_table->host, sctx->host, sctx->ip))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3909 3910 3911 3912 3913
    {
      error=0;					// Found match
      break;
    }
  }
3914
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3915 3916 3917
  return error;
}

3918 3919

/****************************************************************************
3920
  Check routine level grants
3921 3922

  SYNPOSIS
3923
   bool check_grant_routine()
3924 3925
   thd		Thread handler
   want_access  Bits of privileges user needs to have
3926 3927
   procs	List of routines to check. The user should have 'want_access'
   is_proc	True if the list is all procedures, else functions
3928 3929 3930 3931 3932 3933 3934 3935
   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
****************************************************************************/

3936
bool check_grant_routine(THD *thd, ulong want_access,
3937
			 TABLE_LIST *procs, bool is_proc, bool no_errors)
3938 3939
{
  TABLE_LIST *table;
3940
  Security_context *sctx= thd->security_ctx;
3941 3942
  char *user= sctx->priv_user;
  char *host= sctx->priv_host;
3943
  DBUG_ENTER("check_grant_routine");
3944

3945
  want_access&= ~sctx->master_access;
3946 3947 3948 3949 3950 3951 3952
  if (!want_access)
    DBUG_RETURN(0);                             // ok

  rw_rdlock(&LOCK_grant);
  for (table= procs; table; table= table->next_global)
  {
    GRANT_NAME *grant_proc;
3953
    if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
3954
					 table->table_name, is_proc, 0)))
3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971
      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)
3972
      strxmov(buff, table->db, ".", table->table_name, NullS);
3973 3974 3975
    if (want_access & EXECUTE_ACL)
      command= "execute";
    else if (want_access & ALTER_PROC_ACL)
3976
      command= "alter routine";
3977 3978 3979 3980 3981 3982 3983 3984 3985
    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);
}


3986 3987
/*
  Check if routine has any of the 
3988
  routine level grants
3989 3990 3991 3992 3993 3994 3995 3996 3997
  
  SYNPOSIS
   bool    check_routine_level_acl()
   thd	        Thread handler
   db           Database name
   name         Routine name

  RETURN
   0            Ok 
3998
   1            error
3999 4000
*/

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
4001 4002
bool check_routine_level_acl(THD *thd, const char *db, const char *name, 
                             bool is_proc)
4003 4004 4005 4006 4007
{
  bool no_routine_acl= 1;
  if (grant_option)
  {
    GRANT_NAME *grant_proc;
4008
    Security_context *sctx= thd->security_ctx;
4009
    rw_rdlock(&LOCK_grant);
4010 4011 4012
    if ((grant_proc= routine_hash_search(sctx->priv_host,
                                         sctx->ip, db,
                                         sctx->priv_user,
4013
                                         name, is_proc, 0)))
4014 4015 4016 4017 4018 4019 4020
      no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
    rw_unlock(&LOCK_grant);
  }
  return no_routine_acl;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
4021
/*****************************************************************************
4022
  Functions to retrieve the grant for a table/column  (for SHOW functions)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4023 4024
*****************************************************************************/

4025
ulong get_table_grant(THD *thd, TABLE_LIST *table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4026
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4027
  ulong privilege;
4028
  Security_context *sctx= thd->security_ctx;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4029 4030 4031
  const char *db = table->db ? table->db : thd->db;
  GRANT_TABLE *grant_table;

4032
  rw_rdlock(&LOCK_grant);
4033 4034 4035
#ifdef EMBEDDED_LIBRARY
  grant_table= NULL;
#else
4036
  grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
4037
				 table->table_name, 0);
4038
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4039 4040 4041 4042
  table->grant.grant_table=grant_table; // Remember for column test
  table->grant.version=grant_version;
  if (grant_table)
    table->grant.privilege|= grant_table->privs;
4043
  privilege= table->grant.privilege;
4044
  rw_unlock(&LOCK_grant);
4045
  return privilege;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4046 4047 4048
}


4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066
/*
  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
*/

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4067 4068 4069
ulong get_column_grant(THD *thd, GRANT_INFO *grant,
                       const char *db_name, const char *table_name,
                       const char *field_name)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4070 4071 4072
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
4073
  ulong priv;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4074

4075
  rw_rdlock(&LOCK_grant);
4076
  /* reload table if someone has modified any grants */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4077
  if (grant->version != grant_version)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4078
  {
4079
    Security_context *sctx= thd->security_ctx;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4080
    grant->grant_table=
4081 4082
      table_hash_search(sctx->host, sctx->ip,
                        db_name, sctx->priv_user,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4083 4084
			table_name, 0);	        /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4085 4086
  }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4087 4088
  if (!(grant_table= grant->grant_table))
    priv= grant->privilege;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4089 4090
  else
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4091 4092
    grant_column= column_hash_search(grant_table, field_name,
                                     (uint) strlen(field_name));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4093
    if (!grant_column)
4094
      priv= (grant->privilege | grant_table->privs);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4095
    else
4096
      priv= (grant->privilege | grant_table->privs | grant_column->rights);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4097
  }
4098
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4099 4100 4101
  return priv;
}

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4102

4103
/* Help function for mysql_show_grants */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4104

4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116
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);
  }
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4117 4118

static const char *command_array[]=
4119
{
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4120 4121 4122 4123
  "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",
4124
  "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
4125
  "CREATE USER"
4126
};
4127

4128 4129
static uint command_lengths[]=
{
4130 4131
  6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
  14, 13, 11
4132 4133
};

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4134

4135 4136 4137 4138 4139
static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
                               const char *type, int typelen,
                               char *buff, int buffsize);


4140 4141 4142 4143 4144 4145 4146
/*
  SHOW GRANTS;  Send grants for a user to the client

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

4147
bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4148
{
4149 4150
  ulong want_access;
  uint counter,index;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4151
  int  error = 0;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4152 4153
  ACL_USER *acl_user;
  ACL_DB *acl_db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4154
  char buff[1024];
4155
  Protocol *protocol= thd->protocol;
tonu@x153.internalnet's avatar
tonu@x153.internalnet committed
4156
  DBUG_ENTER("mysql_show_grants");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4157 4158 4159 4160

  LINT_INIT(acl_user);
  if (!initialized)
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
4161
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
4162
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4163
  }
monty@mysql.com's avatar
monty@mysql.com committed
4164

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4165 4166 4167
  if (lex_user->host.length > HOSTNAME_LENGTH ||
      lex_user->user.length > USERNAME_LENGTH)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
4168 4169
    my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
               MYF(0));
4170
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4171 4172
  }

4173 4174 4175 4176 4177
  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)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4178
  {
4179 4180 4181
    VOID(pthread_mutex_unlock(&acl_cache->lock));
    rw_unlock(&LOCK_grant);

guilhem@mysql.com's avatar
guilhem@mysql.com committed
4182 4183
    my_error(ER_NONEXISTING_GRANT, MYF(0),
             lex_user->user.str, lex_user->host.str);
4184
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4185 4186
  }

4187
  Item_string *field=new Item_string("",0,&my_charset_latin1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4188 4189 4190 4191 4192 4193
  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);
4194 4195
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
4196 4197 4198
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock));
    rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4199

4200 4201
    DBUG_RETURN(TRUE);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4202 4203 4204

  /* Add first global access grants */
  {
4205
    String global(buff,sizeof(buff),system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4206
    global.length(0);
4207
    global.append(STRING_WITH_LEN("GRANT "));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4208

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

  /* Add database access */
  for (counter=0 ; counter < acl_dbs.elements ; counter++)
  {
monty@mysql.com's avatar
monty@mysql.com committed
4312
    const char *user, *host;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4313 4314 4315

    acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
    if (!(user=acl_db->user))
monty@mysql.com's avatar
monty@mysql.com committed
4316
      user= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4317
    if (!(host=acl_db->host.hostname))
monty@mysql.com's avatar
monty@mysql.com committed
4318
      host= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4319 4320

    if (!strcmp(lex_user->user.str,user) &&
4321
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4322 4323
    {
      want_access=acl_db->access;
peter@mysql.com's avatar
peter@mysql.com committed
4324
      if (want_access)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4325
      {
4326
	String db(buff,sizeof(buff),system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4327
	db.length(0);
4328
	db.append(STRING_WITH_LEN("GRANT "));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4329 4330

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

4371
  /* Add table & column access */
4372
  for (index=0 ; index < column_priv_hash.records ; index++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4373
  {
4374
    const char *user, *host;
4375 4376
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4377 4378

    if (!(user=grant_table->user))
4379
      user= "";
4380 4381
    if (!(host= grant_table->host.hostname))
      host= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4382 4383

    if (!strcmp(lex_user->user.str,user) &&
4384
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4385
    {
4386 4387
      ulong table_access= grant_table->privs;
      if ((table_access | grant_table->cols) != 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4388
      {
4389
	String global(buff, sizeof(buff), system_charset_info);
4390 4391
	ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4392
	global.length(0);
4393
	global.append(STRING_WITH_LEN("GRANT "));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4394

4395
	if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
4396
	  global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4397
	else if (!test_access)
4398
	  global.append(STRING_WITH_LEN("USAGE"));
peter@mysql.com's avatar
peter@mysql.com committed
4399
	else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4400
	{
4401
          /* Add specific column access */
4402
	  int found= 0;
4403
	  ulong j;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4404

4405
	  for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4406
	  {
peter@mysql.com's avatar
peter@mysql.com committed
4407
	    if (test_access & j)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4408 4409
	    {
	      if (found)
4410
		global.append(STRING_WITH_LEN(", "));
4411
	      found= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4412 4413
	      global.append(command_array[counter],command_lengths[counter]);

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

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

  if (show_routine_grants(thd, lex_user, &func_priv_hash,
4487
                          STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509
  {
    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++)
4510
  {
4511
    const char *user, *host;
4512
    GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, index);
4513 4514 4515

    if (!(user=grant_proc->user))
      user= "";
4516 4517
    if (!(host= grant_proc->host.hostname))
      host= "";
4518 4519

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

	global.length(0);
4529
	global.append(STRING_WITH_LEN("GRANT "));
4530 4531

	if (!test_access)
4532
 	  global.append(STRING_WITH_LEN("USAGE"));
4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543
	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)
4544
		global.append(STRING_WITH_LEN(", "));
4545 4546 4547 4548 4549
	      found= 1;
	      global.append(command_array[counter],command_lengths[counter]);
	    }
	  }
	}
4550
	global.append(STRING_WITH_LEN(" ON "));
4551 4552
        global.append(type,typelen);
        global.append(' ');
4553 4554 4555 4556 4557
	append_identifier(thd, &global, grant_proc->db,
			  strlen(grant_proc->db));
	global.append('.');
	append_identifier(thd, &global, grant_proc->tname,
			  strlen(grant_proc->tname));
4558
	global.append(STRING_WITH_LEN(" TO '"));
4559 4560
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
4561
	global.append(STRING_WITH_LEN("'@'"));
4562 4563 4564 4565
	global.append(lex_user->host.str,lex_user->host.length,
		      system_charset_info);
	global.append('\'');
	if (proc_access & GRANT_ACL)
4566
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
4567 4568 4569 4570 4571 4572 4573 4574 4575 4576
	protocol->prepare_for_resend();
	protocol->store(global.ptr(),global.length(),global.charset());
	if (protocol->write())
	{
	  error= -1;
	  break;
	}
      }
    }
  }
4577
  return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4578 4579
}

4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607
/*
  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;
}


4608
void get_mqh(const char *user, const char *host, USER_CONN *uc)
4609 4610
{
  ACL_USER *acl_user;
4611 4612 4613

  pthread_mutex_lock(&acl_cache->lock);

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

  pthread_mutex_unlock(&acl_cache->lock);
4620 4621
}

4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642
/*
  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.
*/

4643
#define GRANT_TABLES 5
4644 4645 4646 4647 4648 4649
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("open_grant_tables");

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

4654
  bzero((char*) tables, GRANT_TABLES*sizeof(*tables));
4655 4656 4657 4658 4659
  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";
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4660 4661 4662
  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;
4663
  (tables+3)->next_local= (tables+3)->next_global= tables+4;
4664
  tables->lock_type= (tables+1)->lock_type=
4665 4666 4667 4668
    (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";
4669 4670 4671 4672 4673 4674

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

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

  DBUG_RETURN(0);
}

ACL_USER *check_acl_user(LEX_USER *user_name,
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
4700
			 uint *acl_acl_userdx)
4701 4702 4703 4704
{
  ACL_USER *acl_user= 0;
  uint counter;

4705 4706
  safe_mutex_assert_owner(&acl_cache->lock);

4707 4708 4709 4710 4711
  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))
monty@mysql.com's avatar
monty@mysql.com committed
4712
      user= "";
4713
    if (!(host=acl_user->host.hostname))
4714
      host= "";
4715 4716 4717 4718 4719 4720 4721
    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;

monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
4722
  *acl_acl_userdx= counter;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4723
  return acl_user;
4724
}
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4725

4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747
/*
  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)
4748
{
4749 4750
  int error;
  DBUG_ENTER("modify_grant_table");
4751

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

4770 4771
  DBUG_RETURN(error);
}
4772

4773 4774 4775 4776 4777 4778
/*
  Handle a privilege table.

  SYNOPSIS
    handle_grant_table()
    tables                      The array with the four open tables.
4779
    table_no                    The number of the table to handle (0..4).
4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796
    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
4797
    4 procs_priv
4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816

  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;
4817
  byte user_key[MAX_KEY_LENGTH];
4818
  uint key_prefix_length;
4819 4820
  DBUG_ENTER("handle_grant_table");

4821
  if (! table_no) // mysql.user table
4822
  {
4823 4824 4825 4826 4827 4828 4829 4830 4831 4832
    /*
      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'",
4833
                       table->s->table_name, user_str, host_str));
4834 4835
    host_field->store(host_str, user_from->host.length, system_charset_info);
    user_field->store(user_str, user_from->user.length, system_charset_info);
4836 4837 4838 4839 4840

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

4841
    if ((error= table->file->index_read_idx(table->record[0], 0,
4842
                                            user_key, key_prefix_length,
4843
                                            HA_READ_KEY_EXACT)))
4844
    {
4845 4846 4847 4848 4849
      if (error != HA_ERR_KEY_NOT_FOUND)
      {
        table->file->print_error(error, MYF(0));
        result= -1;
      }
4850
    }
4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867
    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)))
4868
    {
4869
      table->file->print_error(error, MYF(0));
4870
      result= -1;
4871 4872 4873 4874 4875
    }
    else
    {
#ifdef EXTRA_DEBUG
      DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
4876
                         table->s->table_name, user_str, host_str));
4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924
#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()
4925
    struct_no                   The number of the structure to handle (0..3).
4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940
    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
4941
    3 procs_priv_hash
4942 4943 4944 4945

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

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;
4959
  GRANT_NAME *grant_name;
4960
  DBUG_ENTER("handle_grant_struct");
monty@mishka.local's avatar
monty@mishka.local committed
4961 4962 4963
  DBUG_PRINT("info",("scan struct: %u  search: '%s'@'%s'",
                     struct_no, user_from->user.str, user_from->host.str));

4964 4965
  LINT_INIT(acl_user);
  LINT_INIT(acl_db);
4966
  LINT_INIT(grant_name);
4967

4968 4969
  safe_mutex_assert_owner(&acl_cache->lock);

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

#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.
    */
4998
    switch (struct_no) {
4999 5000 5001
    case 0:
      acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
      user= acl_user->user;
5002 5003
      host= acl_user->host.hostname;
    break;
5004 5005 5006 5007

    case 1:
      acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
      user= acl_db->user;
5008
      host= acl_db->host.hostname;
5009 5010
      break;

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

    case 3:
      grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx);
      user= grant_name->user;
5020
      host= grant_name->host.hostname;
5021
      break;
5022 5023
    }
    if (! user)
5024
      user= "";
5025 5026 5027
    if (! host)
      host= "";

5028 5029 5030 5031 5032 5033
#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))
5034
      continue;
5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048

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

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

5049 5050 5051 5052 5053 5054 5055
      case 2:
        hash_delete(&column_priv_hash, (byte*) grant_name);
	break;

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

5068 5069 5070 5071 5072
      case 1:
        acl_db->user= strdup_root(&mem, user_to->user.str);
        acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
        break;

5073 5074 5075
      case 2:
      case 3:
        grant_name->user= strdup_root(&mem, user_to->user.str);
5076 5077
        update_hostname(&grant_name->host,
                        strdup_root(&mem, user_to->host.str));
5078
	break;
5079 5080 5081
      }
    }
    else
5082
    {
5083 5084 5085 5086 5087 5088 5089
      /* 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
5090

5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134
  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. */
5135 5136
    if ((handle_grant_struct(0, drop, user_from, user_to) && ! result) ||
        found)
5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163
    {
      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;
    }
  }

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

5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196
  /* 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;
5197
    }
5198 5199 5200

    /* Handle columns table. */
    if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
5201
    {
5202
      /* Handle of table failed, don't touch the in-memory array. */
5203 5204
      result= -1;
    }
5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216
    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);
}

5217

5218 5219 5220 5221 5222 5223
static void append_user(String *str, LEX_USER *user)
{
  if (str->length())
    str->append(',');
  str->append('\'');
  str->append(user->user.str);
5224
  str->append(STRING_WITH_LEN("'@'"));
5225 5226 5227
  str->append(user->host.str);
  str->append('\'');
}
5228

5229

5230 5231 5232 5233 5234 5235 5236
/*
  Create a list of users.

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

5238 5239 5240 5241 5242 5243 5244 5245
  RETURN
    FALSE       OK.
    TRUE        Error.
*/

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

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

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

5260
  while ((tmp_user_name= user_list++))
5261
  {
5262 5263 5264 5265
    if (!(user_name= get_current_user(thd, tmp_user_name)))
    {
      result= TRUE;
      continue;
5266 5267 5268 5269 5270 5271 5272 5273 5274 5275
    }

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

5276 5277 5278 5279
    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
5280
    if (handle_grant_data(tables, 0, user_name, NULL))
5281
    {
5282
      append_user(&wrong_users, user_name);
5283
      result= TRUE;
5284
      continue;
5285
    }
5286

5287
    sql_mode= thd->variables.sql_mode;
serg@serg.mylan's avatar
serg@serg.mylan committed
5288
    if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
5289
    {
5290
      append_user(&wrong_users, user_name);
5291 5292 5293 5294 5295 5296 5297 5298
      result= TRUE;
    }
  }

  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
5299
    my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319
  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;
5320
  String wrong_users;
5321
  LEX_USER *user_name, *tmp_user_name;
5322
  List_iterator <LEX_USER> user_list(list);
5323
  TABLE_LIST tables[GRANT_TABLES];
5324 5325
  DBUG_ENTER("mysql_drop_user");

5326
  /* DROP USER may be skipped on replication client. */
5327 5328 5329 5330 5331 5332
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

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

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

5348 5349 5350
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

5351 5352 5353 5354
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
monty@mysql.com's avatar
monty@mysql.com committed
5355
    my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374
  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)
{
5375
  int result;
5376
  String wrong_users;
5377 5378
  LEX_USER *user_from, *tmp_user_from;
  LEX_USER *user_to, *tmp_user_to;
5379
  List_iterator <LEX_USER> user_list(list);
5380
  TABLE_LIST tables[GRANT_TABLES];
5381 5382
  DBUG_ENTER("mysql_rename_user");

5383
  /* RENAME USER may be skipped on replication client. */
5384 5385 5386 5387 5388 5389
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

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

5390
  while ((tmp_user_from= user_list++))
5391
  {
5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402
    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;
    }  
5403
    DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
5404 5405 5406 5407 5408

    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
5409 5410
    if (handle_grant_data(tables, 0, user_to, NULL) ||
        handle_grant_data(tables, 0, user_from, user_to) <= 0)
5411
    {
5412
      append_user(&wrong_users, user_from);
5413 5414
      result= TRUE;
    }
5415
  }
5416
  
5417 5418 5419
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

5420 5421 5422 5423
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
5424
    my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
5425 5426 5427
  DBUG_RETURN(result);
}

5428

5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442
/*
  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.
*/

5443
bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
5444
{
5445
  uint counter, revoked, is_proc;
5446
  int result;
5447
  ACL_DB *acl_db;
5448
  TABLE_LIST tables[GRANT_TABLES];
5449 5450 5451
  DBUG_ENTER("mysql_revoke_all");

  if ((result= open_grant_tables(thd, tables)))
5452
    DBUG_RETURN(result != 1);
5453 5454 5455 5456

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

5457
  LEX_USER *lex_user, *tmp_lex_user;
5458
  List_iterator <LEX_USER> user_list(list);
5459
  while ((tmp_lex_user= user_list++))
5460
  {
5461 5462 5463 5464 5465
    if (!(lex_user= get_current_user(thd, tmp_lex_user)))
    {
      result= -1;
      continue;
    }  
5466
    if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
5467 5468 5469 5470
    {
      result= -1;
      continue;
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
5471

5472
    if (replace_user_table(thd, tables[0].table,
5473
			   *lex_user, ~(ulong)0, 1, 0, 0))
5474 5475 5476 5477 5478 5479
    {
      result= -1;
      continue;
    }

    /* Remove db access privileges */
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5480 5481 5482 5483 5484
    /*
      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.
     */
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5485
    do
5486
    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5487
      for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
5488
      {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5489
	const char *user,*host;
5490

dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5491 5492 5493 5494 5495
	acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
	if (!(user=acl_db->user))
	  user= "";
	if (!(host=acl_db->host.hostname))
	  host= "";
5496

dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5497 5498 5499
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
5500
	  if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, ~(ulong)0, 1))
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5501
	  {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5502 5503 5504 5505 5506
	    /*
	      Don't increment counter as replace_db_table deleted the
	      current element in acl_dbs.
	     */
	    revoked= 1;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5507 5508
	    continue;
	  }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5509
	  result= -1; // Something went wrong
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5510
	}
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5511
	counter++;
5512
      }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5513
    } while (revoked);
5514 5515

    /* Remove column access */
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5516
    do
5517
    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5518
      for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
5519
      {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5520 5521 5522 5523 5524
	const char *user,*host;
	GRANT_TABLE *grant_table= (GRANT_TABLE*)hash_element(&column_priv_hash,
							     counter);
	if (!(user=grant_table->user))
	  user= "";
5525
	if (!(host=grant_table->host.hostname))
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5526
	  host= "";
5527

dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5528 5529
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
5530
	{
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5531 5532 5533
	  if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
				  grant_table->db,
				  grant_table->tname,
5534
				  ~(ulong)0, 0, 1))
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5535
	  {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5536
	    result= -1;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5537
	  }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5538
	  else
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5539
	  {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5540
	    if (!grant_table->cols)
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5541
	    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5542 5543
	      revoked= 1;
	      continue;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5544
	    }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5545 5546
	    List<LEX_COLUMN> columns;
	    if (!replace_column_table(grant_table,tables[3].table, *lex_user,
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5547 5548 5549
				      columns,
				      grant_table->db,
				      grant_table->tname,
5550
				      ~(ulong)0, 1))
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5551
	    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5552
	      revoked= 1;
5553
	      continue;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5554
	    }
5555
	    result= -1;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5556
	  }
5557
	}
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5558
	counter++;
5559
      }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5560
    } while (revoked);
5561 5562

    /* Remove procedure access */
5563 5564 5565
    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 ; )
5566 5567
      {
	const char *user,*host;
5568
	GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
5569 5570
	if (!(user=grant_proc->user))
	  user= "";
5571
	if (!(host=grant_proc->host.hostname))
5572 5573 5574 5575 5576
	  host= "";

	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
5577
	  if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
5578 5579
				  grant_proc->db,
				  grant_proc->tname,
5580
                                  is_proc,
5581
				  ~(ulong)0, 1))
5582 5583 5584 5585 5586 5587 5588 5589 5590
	  {
	    revoked= 1;
	    continue;
	  }
	  result= -1;	// Something went wrong
	}
	counter++;
      }
    } while (revoked);
5591
  }
5592

5593 5594 5595
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
5596

5597
  if (result)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5598
    my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
5599

5600 5601
  DBUG_RETURN(result);
}
5602

5603

5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617
/*
  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.
*/

5618 5619
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
                          bool is_proc)
5620 5621 5622 5623
{
  uint counter, revoked;
  int result;
  TABLE_LIST tables[GRANT_TABLES];
5624
  HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
5625 5626 5627 5628 5629 5630 5631 5632 5633
  DBUG_ENTER("sp_revoke_privileges");

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

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

  /* Remove procedure access */
5634 5635
  do
  {
5636
    for (counter= 0, revoked= 0 ; counter < hash->records ; )
5637
    {
5638
      GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
5639 5640 5641 5642 5643 5644
      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);
5645 5646 5647 5648
	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;
5649 5650
	if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
				   grant_proc->db, grant_proc->tname,
5651
                                   is_proc, ~(ulong)0, 1))
5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686
	{
	  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.
*/

5687 5688
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
                         bool is_proc)
5689
{
5690
  Security_context *sctx= thd->security_ctx;
5691 5692 5693 5694
  LEX_USER *combo;
  TABLE_LIST tables[1];
  List<LEX_USER> user_list;
  bool result;
5695 5696
  ACL_USER *au;
  char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
5697
  DBUG_ENTER("sp_grant_privileges");
5698 5699 5700 5701

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

5702
  combo->user.str= sctx->user;
5703

5704
  VOID(pthread_mutex_lock(&acl_cache->lock));
5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718

  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:
5719
  VOID(pthread_mutex_unlock(&acl_cache->lock));
5720 5721 5722 5723 5724

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

  tables->db= (char*)sp_db;
5725
  tables->table_name= tables->alias= (char*)sp_name;
5726

5727 5728 5729 5730
  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);
5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756


  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;
  }
5757 5758 5759 5760 5761

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

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

5764
  result= mysql_routine_grant(thd, tables, is_proc, user_list,
5765 5766 5767 5768 5769
  				DEFAULT_CREATE_PROC_ACLS, 0, 1);
  DBUG_RETURN(result);
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
5770
/*****************************************************************************
5771
  Instantiate used templates
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5772 5773
*****************************************************************************/

5774
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5775 5776 5777 5778 5779
template class List_iterator<LEX_COLUMN>;
template class List_iterator<LEX_USER>;
template class List<LEX_COLUMN>;
template class List<LEX_USER>;
#endif
hf@deer.(none)'s avatar
hf@deer.(none) committed
5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826

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

5827 5828 5829 5830 5831 5832 5833 5834

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;
5835
  restore_record(table, s->default_values);
5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856
  table->field[0]->store(buff, strlen(buff), cs);
  if (db)
    table->field[i++]->store(db, strlen(db), cs);
  if (t_name)
    table->field[i++]->store(t_name, strlen(t_name), cs);
  if (column)
    table->field[i++]->store(column, col_length, cs);
  table->field[i++]->store(priv, priv_length, cs);
  table->field[i]->store(is_grantable, strlen(is_grantable), cs);
  table->file->write_row(table->record[0]);
}


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

5861 5862
  pthread_mutex_lock(&acl_cache->lock);

5863 5864 5865 5866 5867 5868 5869 5870
  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= "";
5871 5872

    if (no_global_access &&
5873
        (strcmp(thd->security_ctx->priv_user, user) ||
5874 5875 5876
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;
      
5877 5878 5879 5880 5881 5882
    want_access= acl_user->access;
    if (!(want_access & GRANT_ACL))
      is_grantable= "NO";

    strxmov(buff,"'",user,"'@'",host,"'",NullS);
    if (!(want_access & ~GRANT_ACL))
5883 5884
      update_schema_privilege(table, buff, 0, 0, 0, 0,
                              STRING_WITH_LEN("USAGE"), is_grantable);
5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897
    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);
      }
    }
  }
5898 5899 5900

  pthread_mutex_unlock(&acl_cache->lock);

5901
  DBUG_RETURN(0);
5902 5903 5904
#else
  return(0);
#endif
5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915
}


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;
5916
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
5917
  char *curr_host= thd->security_ctx->priv_host_name();
5918 5919
  DBUG_ENTER("fill_schema_schema_privileges");

5920 5921
  pthread_mutex_lock(&acl_cache->lock);

5922 5923 5924 5925 5926 5927 5928 5929 5930 5931
  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= "";

5932
    if (no_global_access &&
5933
        (strcmp(thd->security_ctx->priv_user, user) ||
5934 5935 5936
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;

5937 5938 5939 5940 5941 5942 5943 5944 5945 5946
    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,
5947
                                0, STRING_WITH_LEN("USAGE"), is_grantable);
5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959
      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);
      }
    }
  }
5960 5961 5962

  pthread_mutex_unlock(&acl_cache->lock);

5963
  DBUG_RETURN(0);
5964 5965 5966
#else
  return (0);
#endif
5967 5968 5969 5970 5971 5972 5973 5974 5975
}


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;
5976
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
5977
  char *curr_host= thd->security_ctx->priv_host_name();
5978 5979
  DBUG_ENTER("fill_schema_table_privileges");

5980 5981
  rw_rdlock(&LOCK_grant);

5982 5983
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
5984
    const char *user, *host, *is_grantable= "YES";
5985 5986 5987 5988
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
5989 5990
    if (!(host= grant_table->host.hostname))
      host= "";
5991 5992

    if (no_global_access &&
5993
        (strcmp(thd->security_ctx->priv_user, user) ||
5994
         my_strcasecmp(system_charset_info, curr_host, host)))
5995 5996
      continue;

5997
    ulong table_access= grant_table->privs;
5998
    if (table_access)
5999 6000
    {
      ulong test_access= table_access & ~GRANT_ACL;
6001 6002 6003 6004
      /*
        We should skip 'usage' privilege on table if
        we have any privileges on column(s) of this table
      */
6005 6006
      if (!test_access && grant_table->cols)
        continue;
6007 6008 6009
      if (!(table_access & GRANT_ACL))
        is_grantable= "NO";

6010
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
6011 6012
      if (!test_access)
        update_schema_privilege(table, buff, grant_table->db, grant_table->tname,
6013
                                0, 0, STRING_WITH_LEN("USAGE"), is_grantable);
6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027
      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);
        }
      }
    }
  }
6028 6029 6030

  rw_unlock(&LOCK_grant);

6031
  DBUG_RETURN(0);
6032 6033 6034
#else
  return (0);
#endif
6035 6036 6037 6038 6039 6040 6041 6042 6043
}


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;
6044
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
6045
  char *curr_host= thd->security_ctx->priv_host_name();
6046 6047
  DBUG_ENTER("fill_schema_table_privileges");

6048 6049
  rw_rdlock(&LOCK_grant);

6050 6051
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
6052
    const char *user, *host, *is_grantable= "YES";
6053 6054 6055 6056
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
6057 6058
    if (!(host= grant_table->host.hostname))
      host= "";
6059 6060

    if (no_global_access &&
6061
        (strcmp(thd->security_ctx->priv_user, user) ||
6062
         my_strcasecmp(system_charset_info, curr_host, host)))
6063 6064
      continue;

6065 6066 6067
    ulong table_access= grant_table->cols;
    if (table_access != 0)
    {
6068
      if (!(grant_table->privs & GRANT_ACL))
6069 6070
        is_grantable= "NO";

6071
      ulong test_access= table_access & ~GRANT_ACL;
6072
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101
      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);
            }
          }
        }
      }
    }
  }
6102 6103 6104

  rw_unlock(&LOCK_grant);

6105
  DBUG_RETURN(0);
6106 6107 6108
#else
  return (0);
#endif
6109 6110 6111
}


bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6112 6113 6114 6115 6116
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
  fill effective privileges for table

  SYNOPSIS
6117 6118
    fill_effective_table_privileges()
    thd     thread handler
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6119 6120 6121 6122 6123 6124 6125 6126
    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)
{
6127
  Security_context *sctx= thd->security_ctx;
6128 6129 6130 6131 6132
  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));
6133 6134 6135
  /* --skip-grants */
  if (!initialized)
  {
6136
    DBUG_PRINT("info", ("skip grants"));
6137
    grant->privilege= ~NO_ACCESS;             // everything is allowed
6138 6139
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;
6140 6141
  }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6142
  /* global privileges */
6143
  grant->privilege= sctx->master_access;
6144

6145
  if (!sctx->priv_user)
6146 6147 6148 6149
  {
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;                         // it is slave
  }
6150

6151
  /* db privileges */
6152
  grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
6153

6154
  if (!grant_option)
6155 6156 6157 6158
  {
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;
  }
6159

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6160
  /* table privileges */
6161
  rw_rdlock(&LOCK_grant);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6162 6163 6164
  if (grant->version != grant_version)
  {
    grant->grant_table=
6165 6166
      table_hash_search(sctx->host, sctx->ip, db,
			sctx->priv_user,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6167 6168 6169 6170 6171 6172 6173
			table, 0);              /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
  }
  if (grant->grant_table != 0)
  {
    grant->privilege|= grant->grant_table->privs;
  }
6174 6175
  rw_unlock(&LOCK_grant);

6176 6177
  DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
  DBUG_VOID_RETURN;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6178
}
6179 6180 6181 6182 6183 6184 6185

#else /* NO_EMBEDDED_ACCESS_CHECKS */

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

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
6186 6187
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
                             bool is_proc)
6188 6189 6190 6191
{
  return FALSE;
}

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6192
#endif