sql_acl.cc 178 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 30 31
  mysql/db	 ; database privileges / user

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

#include "mysql_priv.h"
#include "hash_filo.h"
#include <m_ctype.h>
#include <stdarg.h>
32 33
#include "sp_head.h"
#include "sp.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
34

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
time_t mysql_db_table_last_check= 0L;

TABLE_FIELD_W_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
  {
    {(char *) STRING_WITH_LEN("Host")},            
    {(char *) STRING_WITH_LEN("char(60)")},
    {NULL, 0}
  }, 
  {
    {(char *) STRING_WITH_LEN("Db")},            
    {(char *) STRING_WITH_LEN("char(64)")},
    {NULL, 0}
  }, 
  {
    {(char *) STRING_WITH_LEN("User")},
    {(char *) STRING_WITH_LEN("char(16)")},
    {NULL, 0}
  },
  {
    {(char *) STRING_WITH_LEN("Select_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Insert_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Update_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Delete_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Create_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Drop_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Grant_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("References_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Index_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Alter_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Create_tmp_table_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Lock_tables_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Create_view_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Show_view_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Create_routine_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Alter_routine_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Execute_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Event_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  },
  {
    {(char *) STRING_WITH_LEN("Trigger_priv")},
    {(char *) STRING_WITH_LEN("enum('N','Y')")},
    {(char *) STRING_WITH_LEN("utf8")}
  }
};


151 152 153 154
#ifndef NO_EMBEDDED_ACCESS_CHECKS

#define FIRST_NON_YN_FIELD 26

bk@work.mysql.com's avatar
bk@work.mysql.com committed
155 156 157
class acl_entry :public hash_filo_element
{
public:
158
  ulong access;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
159 160 161 162
  uint16 length;
  char key[1];					// Key will be stored here
};

163

bk@work.mysql.com's avatar
bk@work.mysql.com committed
164 165 166 167 168 169 170
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
171
#define IP_ADDR_STRLEN (3+1+3+1+3+1+3)
172
#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
173 174 175 176 177

static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
178
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
179 180
static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
181
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
182
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
183 184 185
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
186
static void rebuild_check_host(void);
187 188
static ACL_USER *find_acl_user(const char *host, const char *user,
                               my_bool exact);
189 190
static bool update_user_table(THD *thd, TABLE *table,
                              const char *host, const char *user,
191
			      const char *new_password, uint new_password_len);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
192
static void update_hostname(acl_host_and_ip *host, const char *hostname);
193
static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
194
			     const char *ip);
195 196
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
197

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

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

239

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

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

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

258
my_bool acl_init(bool dont_read_acl_tables)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
259
{
260
  THD  *thd;
261
  my_bool return_val;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
262 263
  DBUG_ENTER("acl_init");

264 265
  acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
                           (hash_get_key) acl_entry_get_key,
266 267 268
                           (hash_free_key) free,
                           /* Use the case sensitive "binary" charset */
                           &my_charset_bin);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
269
  if (dont_read_acl_tables)
270
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
271
    DBUG_RETURN(0); /* purecov: tested */
peter@mysql.com's avatar
peter@mysql.com committed
272 273
  }

274 275 276
  /*
    To be able to run this from boot, we allocate a temporary THD
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
277 278
  if (!(thd=new THD))
    DBUG_RETURN(1); /* purecov: inspected */
279
  thd->thread_stack= (char*) &thd;
280
  thd->store_globals();
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
  /*
    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];
316
  int password_length;
317 318
  DBUG_ENTER("acl_load");

319 320
  grant_version++; /* Privileges updated */
  mysql_proc_table_exists= 1;			// Assume mysql.proc exists
321

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

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

382
  DBUG_PRINT("info",("user table fields: %d, password length: %d",
383
		     table->s->fields, password_length));
384

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
414 415 416 417
  allow_all_hosts=0;
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_USER user;
418 419
    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
420 421
    if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
    {
serg@serg.mylan's avatar
serg@serg.mylan committed
422 423
      sql_print_warning("'user' entry '%s@%s' "
                        "ignored in --skip-name-resolve mode.",
424
		      user.user, user.host.hostname);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
425 426 427
      continue;
    }

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

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

468 469 470 471 472 473
      /*
        pre 5.0.3 did not have CREATE_USER_ACL
      */
      if (table->s->fields <= 36 && (user.access & GRANT_ACL))
        user.access|= CREATE_USER_ACL;

andrey@lmy004's avatar
andrey@lmy004 committed
474 475

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

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

488 489 490
      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
491

492 493
      /* Starting from 4.0.2 we have more fields */
      if (table->s->fields >= 31)
494
      {
495
        char *ssl_type=get_field(&mem, table->field[next_field++]);
496 497 498 499 500 501 502 503 504
        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;

505 506 507
        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++]);
508

509 510 511 512 513
        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++]);
514
        user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
515
        if (user.user_resource.questions || user.user_resource.updates ||
516
            user.user_resource.conn_per_hour)
517
          mqh_used=1;
518

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

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

612 613 614 615 616
  initialized=1;
  return_val=0;

end:
  DBUG_RETURN(return_val);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
617 618 619 620 621
}


void acl_free(bool end)
{
622
  free_root(&mem,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
623 624 625 626 627 628 629 630 631 632 633 634 635 636
  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;
  }
}

637 638

/*
639 640
  Forget current user/db-level privileges and read new privileges
  from the privilege tables.
641 642 643

  SYNOPSIS
    acl_reload()
644 645 646 647 648 649 650 651 652 653 654
      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
655
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
656

657
my_bool acl_reload(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
658
{
659
  TABLE_LIST tables[3];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
660 661 662
  DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs;
  MEM_ROOT old_mem;
  bool old_initialized;
663
  my_bool return_val= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
664 665
  DBUG_ENTER("acl_reload");

666
  if (thd->locked_tables)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
667
  {					// Can't have locked tables here
668 669 670
    thd->lock=thd->locked_tables;
    thd->locked_tables=0;
    close_thread_tables(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
671
  }
672 673 674 675 676 677

  /*
    To avoid deadlocks we should obtain table locks before
    obtaining acl_cache->lock mutex.
  */
  bzero((char*) tables, sizeof(tables));
678 679 680 681 682 683
  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;
684 685 686 687 688 689 690 691 692
  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
693 694 695 696 697 698 699 700 701 702
  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);

703
  if ((return_val= acl_load(thd, tables)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
704
  {					// Error. Revert to old list
705
    DBUG_PRINT("error",("Reverting to old privileges"));
706
    acl_free();				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
707 708 709 710 711 712 713 714
    acl_hosts=old_acl_hosts;
    acl_users=old_acl_users;
    acl_dbs=old_acl_dbs;
    mem=old_mem;
    init_check_host();
  }
  else
  {
715
    free_root(&old_mem,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
716 717 718 719 720 721
    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));
722 723 724
end:
  close_thread_tables(thd);
  DBUG_RETURN(return_val);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
725 726 727
}


728 729
/*
  Get all access bits from table after fieldnr
730 731

  IMPLEMENTATION
732 733
  We know that the access privileges ends when there is no more fields
  or the field is not an enum with two elements.
734 735 736 737 738 739 740 741 742 743 744

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

747
static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
748
{
749
  ulong access_bits=0,bit;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
750
  char buff[2];
751
  String res(buff,sizeof(buff),&my_charset_latin1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
752 753
  Field **pos;

754 755 756
  for (pos=form->field+fieldnr, bit=1;
       *pos && (*pos)->real_type() == FIELD_TYPE_ENUM &&
	 ((Field_enum*) (*pos))->typelib->count == 2 ;
757
       pos++, fieldnr++, bit<<=1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
758
  {
759
    (*pos)->val_str(&res);
760
    if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
761
      access_bits|= bit;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
762
  }
763 764
  if (next_field)
    *next_field=fieldnr;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
765 766 767 768 769
  return access_bits;
}


/*
770 771 772 773 774
  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
775 776 777 778 779 780 781

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

782 783 784
  /* 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
785 786
  while (count--)
  {
787 788 789
    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
790

monty@mysql.com's avatar
monty@mysql.com committed
791
    if ((start= str))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
792 793 794 795
    {
      for (; *str ; str++)
      {
	if (*str == wild_many || *str == wild_one || *str == wild_prefix)
796
        {
monty@mysql.com's avatar
monty@mysql.com committed
797
          wild_pos= (uint) (str - start) + 1;
798 799
          break;
        }
monty@mysql.com's avatar
monty@mysql.com committed
800
        chars= 128;                             // Marker that chars existed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
801 802
      }
    }
monty@mysql.com's avatar
monty@mysql.com committed
803
    sort= (sort << 8) + (wild_pos ? min(wild_pos, 127) : chars);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
  }
  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;
}

819

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

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
824 825 826 827
  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
828

829
  SYNOPSIS
830 831
    acl_getroot()
    thd         thread handle. If all checks are OK,
832 833
                thd->security_ctx->priv_user/master_access are updated.
                thd->security_ctx->host/ip/user are used for checks.
834 835
    mqh         user resources; on success mqh is reset, else
                unchanged
836
    passwd      scrambled & crypted password, received from client
837 838 839 840 841 842 843
                (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
844
  RETURN VALUE
845 846
    0  success: thd->priv_user, thd->priv_host, thd->master_access, mqh are
       updated
847
    1  user not found or authentication failure
848
    2  user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
849
   -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
850 851
*/

852 853
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
854
{
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
855 856 857
  ulong user_access= NO_ACCESS;
  int res= 1;
  ACL_USER *acl_user= 0;
858
  Security_context *sctx= thd->security_ctx;
859
  DBUG_ENTER("acl_getroot");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
860 861

  if (!initialized)
862
  {
863 864 865
    /* 
      here if mysqld's been started with --skip-grant-tables option.
    */
866
    sctx->skip_grants();
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
867
    bzero((char*) mqh, sizeof(*mqh));
868
    DBUG_RETURN(0);
869
  }
870

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
873
  /*
874 875 876
    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
877
  */
peter@mysql.com's avatar
peter@mysql.com committed
878

879
  for (uint i=0 ; i < acl_users.elements ; i++)
880
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
881
    ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
882
    if (!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
883
    {
884
      if (compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
885
      {
886
        /* check password: it should be empty or valid */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
887
        if (passwd_len == acl_user_tmp->salt_len)
peter@mysql.com's avatar
peter@mysql.com committed
888
        {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
889
          if (acl_user_tmp->salt_len == 0 ||
serg@serg.mylan's avatar
serg@serg.mylan committed
890 891
              (acl_user_tmp->salt_len == SCRAMBLE_LENGTH ?
              check_scramble(passwd, thd->scramble, acl_user_tmp->salt) :
892
              check_scramble_323(passwd, thd->scramble,
serg@serg.mylan's avatar
serg@serg.mylan committed
893
                                 (ulong *) acl_user_tmp->salt)) == 0)
894
          {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
895
            acl_user= acl_user_tmp;
896 897
            res= 0;
          }
peter@mysql.com's avatar
peter@mysql.com committed
898
        }
899
        else if (passwd_len == SCRAMBLE_LENGTH &&
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
900
                 acl_user_tmp->salt_len == SCRAMBLE_LENGTH_323)
901
          res= -1;
902
        else if (passwd_len == SCRAMBLE_LENGTH_323 &&
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
903
                 acl_user_tmp->salt_len == SCRAMBLE_LENGTH)
904
          res= 2;
905 906
        /* linear search complete: */
        break;
peter@mysql.com's avatar
peter@mysql.com committed
907
      }
peter@mysql.com's avatar
peter@mysql.com committed
908
    }
909
  }
910 911 912 913
  /*
    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
914 915 916

  if (acl_user)
  {
917
    /* OK. User found and password checked continue validation */
918
#ifdef HAVE_OPENSSL
919
    Vio *vio=thd->net.vio;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
920
    SSL *ssl= (SSL*) vio->ssl_arg;
921
#endif
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
922

923
    /*
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
924
      At this point we know that user is allowed to connect
925 926 927 928 929 930
      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
931 932
    case SSL_TYPE_NONE:				// SSL is not required
      user_access= acl_user->access;
933
      break;
934
#ifdef HAVE_OPENSSL
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
935
    case SSL_TYPE_ANY:				// Any kind of SSL is ok
936
      if (vio_type(vio) == VIO_TYPE_SSL)
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
937
	user_access= acl_user->access;
938 939 940 941 942
      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
943

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

1035
    if (acl_user->host.hostname)
1036
      strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
1037
    else
1038
      *sctx->priv_host= 0;
1039
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1040
  VOID(pthread_mutex_unlock(&acl_cache->lock));
1041
  DBUG_RETURN(res);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1042 1043 1044
}


1045
/*
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
  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)
1064 1065
{
  int res= 1;
1066
  uint i;
1067 1068 1069
  ACL_USER *acl_user= 0;
  DBUG_ENTER("acl_getroot_no_password");

1070 1071
  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
                       (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
1072
                       user, (db ? db : "(NULL)")));
1073 1074 1075 1076 1077
  sctx->user= user;
  sctx->host= host;
  sctx->ip= ip;
  sctx->host_or_ip= host ? host : (ip ? ip : "");

1078 1079
  if (!initialized)
  {
1080
    /*
1081 1082
      here if mysqld's been started with --skip-grant-tables option.
    */
1083
    sctx->skip_grants();
1084
    DBUG_RETURN(FALSE);
1085 1086 1087 1088
  }

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

1089 1090
  sctx->master_access= 0;
  sctx->db_access= 0;
1091

1092 1093 1094
  /*
     Find acl entry in user database.
     This is specially tailored to suit the check we do for CALL of
1095
     a stored procedure; user is set to what is actually a
1096 1097
     priv_user, which can be ''.
  */
1098
  for (i=0 ; i < acl_users.elements ; i++)
1099 1100
  {
    acl_user= dynamic_element(&acl_users,i,ACL_USER*);
1101
    if ((!acl_user->user && !user[0]) ||
1102
	(acl_user->user && strcmp(user, acl_user->user) == 0))
1103
    {
1104
      if (compare_hostname(&acl_user->host, host, ip))
1105 1106 1107 1108 1109 1110 1111 1112 1113
      {
	res= 0;
	break;
      }
    }
  }

  if (acl_user)
  {
1114 1115 1116 1117
    for (i=0 ; i < acl_dbs.elements ; i++)
    {
      ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
      if (!acl_db->user ||
1118
	  (user && user[0] && !strcmp(user, acl_db->user)))
1119
      {
1120
	if (compare_hostname(&acl_db->host, host, ip))
1121
	{
1122
	  if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
1123
	  {
1124
	    sctx->db_access= acl_db->access;
1125 1126 1127 1128 1129
	    break;
	  }
	}
      }
    }
1130 1131
    sctx->master_access= acl_user->access;
    sctx->priv_user= acl_user->user ? user : (char *) "";
1132 1133

    if (acl_user->host.hostname)
1134
      strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
1135
    else
1136
      *sctx->priv_host= 0;
1137 1138 1139 1140 1141
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  DBUG_RETURN(res);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1142 1143 1144 1145 1146 1147 1148
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;
}

1149

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1150
static void acl_update_user(const char *user, const char *host,
1151
			    const char *password, uint password_len,
1152 1153 1154 1155
			    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
1156
			    USER_RESOURCES  *mqh,
1157
			    ulong privileges)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1158 1159 1160 1161 1162 1163 1164 1165 1166
{
  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] ||
	acl_user->user &&
	!strcmp(user,acl_user->user))
    {
      if (!acl_user->host.hostname && !host[0] ||
1167
	  acl_user->host.hostname &&
1168
	  !my_strcasecmp(system_charset_info, host, acl_user->host.hostname))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1169 1170
      {
	acl_user->access=privileges;
1171
	if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1172
	  acl_user->user_resource.questions=mqh->questions;
1173
	if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1174
	  acl_user->user_resource.updates=mqh->updates;
1175 1176 1177 1178
	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;
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
	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);
	}
1189 1190
	if (password)
	  set_user_salt(acl_user, password, password_len);
1191
        /* search complete: */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1192 1193 1194 1195 1196 1197 1198 1199
	break;
      }
    }
  }
}


static void acl_insert_user(const char *user, const char *host,
1200
			    const char *password, uint password_len,
1201 1202 1203 1204
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
1205
			    USER_RESOURCES *mqh,
1206
			    ulong privileges)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1207 1208
{
  ACL_USER acl_user;
1209
  acl_user.user=*user ? strdup_root(&mem,user) : 0;
monty@mysql.com's avatar
monty@mysql.com committed
1210
  update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1211
  acl_user.access=privileges;
1212
  acl_user.user_resource = *mqh;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1213
  acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
1214
  acl_user.hostname_length=(uint) strlen(host);
1215 1216 1217 1218 1219
  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;
1220 1221

  set_user_salt(&acl_user, password, password_len);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1222 1223 1224 1225

  VOID(push_dynamic(&acl_users,(gptr) &acl_user));
  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
1226
    allow_all_hosts=1;		// Anyone can connect /* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1227 1228 1229
  qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
	sizeof(ACL_USER),(qsort_cmp) acl_compare);

1230 1231
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1232 1233 1234 1235
}


static void acl_update_db(const char *user, const char *host, const char *db,
1236
			  ulong privileges)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1237 1238 1239 1240 1241 1242 1243 1244 1245
{
  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] ||
1246
	  acl_db->host.hostname &&
1247
	  !my_strcasecmp(system_charset_info, host, acl_db->host.hostname))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
      {
	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);
	}
      }
    }
  }
}


1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276
/*
  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
1277
static void acl_insert_db(const char *user, const char *host, const char *db,
1278
			  ulong privileges)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1279 1280
{
  ACL_DB acl_db;
1281
  safe_mutex_assert_owner(&acl_cache->lock);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292
  acl_db.user=strdup_root(&mem,user);
  update_hostname(&acl_db.host,strdup_root(&mem,host));
  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);
}


1293 1294 1295

/*
  Get privilege for a host, user and db combination
1296 1297 1298

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

1301
ulong acl_get(const char *host, const char *ip,
1302
              const char *user, const char *db, my_bool db_is_pattern)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1303
{
1304
  ulong host_access= ~(ulong)0, db_access= 0;
1305
  uint i,key_length;
1306
  char key[ACL_KEY_LENGTH],*tmp_db,*end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1307
  acl_entry *entry;
monty@mysql.com's avatar
monty@mysql.com committed
1308
  DBUG_ENTER("acl_get");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1309 1310

  VOID(pthread_mutex_lock(&acl_cache->lock));
1311
  end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db);
1312 1313
  if (lower_case_table_names)
  {
1314
    my_casedn_str(files_charset_info, tmp_db);
1315 1316
    db=tmp_db;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1317
  key_length=(uint) (end-key);
1318
  if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search(key,key_length)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1319 1320 1321
  {
    db_access=entry->access;
    VOID(pthread_mutex_unlock(&acl_cache->lock));
monty@mysql.com's avatar
monty@mysql.com committed
1322 1323
    DBUG_PRINT("exit", ("access: 0x%lx", db_access));
    DBUG_RETURN(db_access);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
  }

  /*
    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))
      {
1336
	if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357
	{
	  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))
    {
1358
      if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1359 1360 1361 1362 1363 1364 1365 1366
      {
	host_access=acl_host->access;		// Fully specified. Take it
	break;
      }
    }
  }
exit:
  /* Save entry in cache for quick retrieval */
1367 1368
  if (!db_is_pattern &&
      (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1369 1370 1371 1372 1373 1374 1375
  {
    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
1376 1377
  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
1378 1379
}

1380 1381 1382 1383 1384 1385 1386
/*
  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
1387 1388 1389 1390

static void init_check_host(void)
{
  DBUG_ENTER("init_check_host");
1391
  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
1392
			  acl_users.elements,1));
1393
  VOID(hash_init(&acl_check_hosts,system_charset_info,acl_users.elements,0,0,
1394
		 (hash_get_key) check_get_key,0,0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
  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 *);
1409
	  if (!my_strcasecmp(system_charset_info,
1410
                             acl_user->host.hostname, acl->hostname))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1411 1412 1413 1414 1415
	    break;				// already stored
	}
	if (j == acl_wild_hosts.elements)	// If new
	  (void) push_dynamic(&acl_wild_hosts,(char*) &acl_user->host);
      }
1416
      else if (!hash_search(&acl_check_hosts,(byte*) acl_user->host.hostname,
1417
			    (uint) strlen(acl_user->host.hostname)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1418
      {
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1419
	if (my_hash_insert(&acl_check_hosts,(byte*) acl_user))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432
	{					// 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;
}


1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448
/*
  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
1449 1450 1451 1452 1453 1454 1455 1456
/* 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));

1457 1458
  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
1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476
  {
    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
}


1477 1478 1479 1480 1481 1482 1483 1484
/*
  Check if the user is allowed to change password

  SYNOPSIS:
    check_change_password()
    thd		THD
    host	hostname for the user
    user	user name
1485 1486 1487 1488
    new_password new password

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

1490
    RETURN VALUE
1491 1492
      0		OK
      1		ERROR  ; In this case the error is sent to the client.
1493 1494
*/

1495
bool check_change_password(THD *thd, const char *host, const char *user,
1496
                           char *new_password, uint new_password_len)
1497
{
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1498 1499
  if (!initialized)
  {
1500
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1501
    return(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1502
  }
1503
  if (!thd->slave_thread &&
1504 1505 1506
      (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
1507
  {
1508
    if (check_access(thd, UPDATE_ACL, "mysql",0,1,0,0))
1509
      return(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1510
  }
1511
  if (!thd->slave_thread && !thd->security_ctx->user[0])
1512
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1513 1514
    my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
               MYF(0));
1515
    return(1);
1516
  }
1517
  uint len=strlen(new_password);
1518
  if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
1519 1520
      len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
  {
1521
    my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
1522 1523
    return -1;
  }
1524 1525 1526 1527
  return(0);
}


1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540
/*
  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
1541
*/
1542

1543 1544 1545
bool change_password(THD *thd, const char *host, const char *user,
		     char *new_password)
{
1546 1547 1548 1549 1550
  TABLE_LIST tables;
  TABLE *table;
  /* Buffer should be extended when password length is extended. */
  char buff[512];
  ulong query_length;
1551
  uint new_password_len= strlen(new_password);
1552
  bool result= 1;
1553 1554 1555 1556 1557
  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

1558
  if (check_change_password(thd, host, user, new_password, new_password_len))
1559 1560
    DBUG_RETURN(1);

1561
  bzero((char*) &tables, sizeof(tables));
1562
  tables.alias= tables.table_name= (char*) "user";
1563 1564 1565 1566 1567 1568 1569
  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.
  */
1570
  if (thd->slave_thread && rpl_filter->is_on())
1571 1572 1573 1574 1575 1576 1577
  {
    /*
      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 */
1578
    if (!(thd->spcont || rpl_filter->tables_ok(0, &tables)))
1579 1580 1581 1582 1583 1584 1585
      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
1586 1587
  VOID(pthread_mutex_lock(&acl_cache->lock));
  ACL_USER *acl_user;
1588
  if (!(acl_user= find_acl_user(host, user, TRUE)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1589 1590
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1591
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
1592
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1593
  }
1594 1595 1596
  /* update loaded acl entry: */
  set_user_salt(acl_user, new_password, new_password_len);

1597
  if (update_user_table(thd, table,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1598
			acl_user->host.hostname ? acl_user->host.hostname : "",
1599
			acl_user->user ? acl_user->user : "",
1600
			new_password, new_password_len))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1601 1602
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
1603
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1604
  }
peter@mysql.com's avatar
peter@mysql.com committed
1605

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1606 1607
  acl_cache->clear(1);				// Clear locked hostname cache
  VOID(pthread_mutex_unlock(&acl_cache->lock));
1608 1609 1610
  result= 0;
  if (mysql_bin_log.is_open())
  {
1611 1612 1613 1614 1615 1616
    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));
1617
    thd->clear_error();
1618
    thd->binlog_query(THD::MYSQL_QUERY_TYPE, buff, query_length, FALSE, FALSE);
1619 1620 1621 1622
  }
end:
  close_thread_tables(thd);
  DBUG_RETURN(result);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1623 1624 1625
}


1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641
/*
  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;
1642 1643 1644 1645 1646

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

1647
  VOID(pthread_mutex_lock(&acl_cache->lock));
1648
  res= find_acl_user(host, user, TRUE) != NULL;
1649 1650 1651 1652 1653
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  return res;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
1654 1655 1656 1657 1658
/*
  Find first entry that matches the current user
*/

static ACL_USER *
1659
find_acl_user(const char *host, const char *user, my_bool exact)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1660
{
1661
  DBUG_ENTER("find_acl_user");
1662
  DBUG_PRINT("enter",("host: '%s'  user: '%s'",host,user));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1663 1664 1665
  for (uint i=0 ; i < acl_users.elements ; i++)
  {
    ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
1666
    DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
1667 1668 1669 1670 1671
		       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
1672 1673 1674
    if (!acl_user->user && !user[0] ||
	acl_user->user && !strcmp(user,acl_user->user))
    {
1675
      if (exact ? !my_strcasecmp(&my_charset_latin1, host,
1676 1677
                                 acl_user->host.hostname ?
				 acl_user->host.hostname : "") :
1678
          compare_hostname(&acl_user->host,host,host))
1679 1680 1681
      {
	DBUG_RETURN(acl_user);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1682 1683
    }
  }
1684
  DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1685 1686 1687
}


1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698
/*
  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
1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721

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)
{
  host->hostname=(char*) hostname;		// This will not be modified!
1722
  if (!hostname ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1723 1724 1725
      (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
       !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
  {
1726
    host->ip= host->ip_mask=0;			// Not a masked ip
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739
  }
}


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 ||
1740
	  (hostname && !wild_case_compare(system_charset_info,
1741
                                          hostname,host->hostname)) ||
1742
	  (ip && !wild_compare(ip,host->hostname,0)));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1743 1744
}

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1745 1746 1747 1748
bool hostname_requires_resolving(const char *hostname)
{
  char cur;
  if (!hostname)
monty@mysql.com's avatar
monty@mysql.com committed
1749
    return FALSE;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1750 1751 1752
  int namelen= strlen(hostname);
  int lhlen= strlen(my_localhost);
  if ((namelen == lhlen) &&
1753
      !my_strnncoll(system_charset_info, (const uchar *)hostname,  namelen,
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1754
		    (const uchar *)my_localhost, strlen(my_localhost)))
monty@mysql.com's avatar
monty@mysql.com committed
1755
    return FALSE;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1756 1757
  for (; (cur=*hostname); hostname++)
  {
1758
    if ((cur != '%') && (cur != '_') && (cur != '.') && (cur != '/') &&
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1759
	((cur < '0') || (cur > '9')))
monty@mysql.com's avatar
monty@mysql.com committed
1760
      return TRUE;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1761
  }
monty@mysql.com's avatar
monty@mysql.com committed
1762
  return FALSE;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1763
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1764

1765

1766
/*
1767 1768 1769 1770 1771 1772 1773 1774 1775 1776
  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
1777
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1778

1779 1780
static bool update_user_table(THD *thd, TABLE *table,
                              const char *host, const char *user,
1781
			      const char *new_password, uint new_password_len)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1782
{
1783
  char user_key[MAX_KEY_LENGTH];
1784
  int error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1785 1786 1787
  DBUG_ENTER("update_user_table");
  DBUG_PRINT("enter",("user: %s  host: %s",user,host));

1788 1789
  table->field[0]->store(host,(uint) strlen(host), system_charset_info);
  table->field[1]->store(user,(uint) strlen(user), system_charset_info);
1790
  key_copy((byte *) user_key, table->record[0], table->key_info,
1791
           table->key_info->key_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1792

1793
  table->file->ha_retrieve_all_cols();
1794
  if (table->file->index_read_idx(table->record[0], 0,
1795
				  (byte *) user_key, table->key_info->key_length,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1796 1797
				  HA_READ_KEY_EXACT))
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1798 1799
    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
1800 1801
    DBUG_RETURN(1);				/* purecov: deadcode */
  }
1802
  store_record(table,record[1]);
1803
  table->field[2]->store(new_password, new_password_len, system_charset_info);
1804
  if ((error=table->file->ha_update_row(table->record[1],table->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1805 1806
  {
    table->file->print_error(error,MYF(0));	/* purecov: deadcode */
1807
    DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1808
  }
1809
  DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1810 1811
}

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

1813 1814 1815 1816 1817 1818
/*
  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
1819 1820 1821

static bool test_if_create_new_users(THD *thd)
{
1822
  Security_context *sctx= thd->security_ctx;
1823
  bool create_new_users= test(sctx->master_access & INSERT_ACL) ||
1824
                         (!opt_safe_user_create &&
1825
                          test(sctx->master_access & CREATE_USER_ACL));
1826
  if (!create_new_users)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1827 1828
  {
    TABLE_LIST tl;
1829
    ulong db_access;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1830 1831
    bzero((char*) &tl,sizeof(tl));
    tl.db=	   (char*) "mysql";
1832
    tl.table_name=  (char*) "user";
1833
    create_new_users= 1;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1834

1835 1836
    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
1837 1838
    if (!(db_access & INSERT_ACL))
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1839
      if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1840 1841 1842 1843 1844 1845 1846
	create_new_users=0;
    }
  }
  return create_new_users;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
1847
/****************************************************************************
1848
  Handle GRANT commands
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1849 1850
****************************************************************************/

1851
static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
1852
			      ulong rights, bool revoke_grant,
serg@serg.mylan's avatar
serg@serg.mylan committed
1853
			      bool can_create_user, bool no_auto_create)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1854 1855
{
  int error = -1;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1856
  bool old_row_exists=0;
1857
  const char *password= "";
1858
  uint password_len= 0;
1859
  char what= (revoke_grant) ? 'N' : 'Y';
1860
  byte user_key[MAX_KEY_LENGTH];
1861
  LEX *lex= thd->lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1862
  DBUG_ENTER("replace_user_table");
1863

1864
  safe_mutex_assert_owner(&acl_cache->lock);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1865 1866

  if (combo.password.str && combo.password.str[0])
1867
  {
1868 1869
    if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
        combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1870
    {
1871
      my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
1872
      DBUG_RETURN(-1);
1873
    }
1874
    password_len= combo.password.length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1875
    password=combo.password.str;
1876
  }
peter@mysql.com's avatar
peter@mysql.com committed
1877

1878 1879
  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);
1880 1881 1882
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

1883
  table->file->ha_retrieve_all_cols();
1884
  if (table->file->index_read_idx(table->record[0], 0,
1885 1886
                                  user_key, table->key_info->key_length,
                                  HA_READ_KEY_EXACT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1887
  {
1888 1889
    /* what == 'N' means revoke */
    if (what == 'N')
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1890
    {
1891 1892 1893 1894
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
      goto end;
    }
    /*
1895 1896
      There are four options which affect the process of creation of
      a new user (mysqld option --safe-create-user, 'insert' privilege
1897 1898 1899 1900 1901 1902 1903
      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
1904 1905

      see also test_if_create_new_users()
1906
    */
serg@serg.mylan's avatar
serg@serg.mylan committed
1907 1908 1909 1910 1911 1912
    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)
1913
    {
1914
      my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0),
1915
               thd->security_ctx->user, thd->security_ctx->host_or_ip);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1916 1917
      goto end;
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1918
    old_row_exists = 0;
1919
    restore_record(table,s->default_values);
1920
    table->field[0]->store(combo.host.str,combo.host.length,
1921
                           system_charset_info);
1922
    table->field[1]->store(combo.user.str,combo.user.length,
1923
                           system_charset_info);
1924
    table->field[2]->store(password, password_len,
1925
                           system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1926 1927 1928
  }
  else
  {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1929
    old_row_exists = 1;
1930
    store_record(table,record[1]);			// Save copy for update
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1931
    if (combo.password.str)			// If password given
1932
      table->field[2]->store(password, password_len, system_charset_info);
1933
    else if (!rights && !revoke_grant &&
1934 1935
             lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
             !lex->mqh.specified_limits)
1936 1937 1938
    {
      DBUG_RETURN(0);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1939 1940
  }

1941 1942 1943 1944
  /* Update table columns with new privileges */

  Field **tmp_field;
  ulong priv;
1945
  uint next_field;
1946 1947 1948 1949
  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
1950
  {
1951
    if (priv & rights)				 // set requested privileges
1952
      (*tmp_field)->store(&what, 1, &my_charset_latin1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1953
  }
1954
  rights= get_access(table, 3, &next_field);
1955 1956
  DBUG_PRINT("info",("table fields: %d",table->s->fields));
  if (table->s->fields >= 31)		/* From 4.0.0 we have more fields */
1957
  {
1958
    /* We write down SSL related ACL stuff */
1959
    switch (lex->ssl_type) {
1960
    case SSL_TYPE_ANY:
1961 1962
      table->field[next_field]->store(STRING_WITH_LEN("ANY"),
                                      &my_charset_latin1);
1963 1964 1965
      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);
1966 1967
      break;
    case SSL_TYPE_X509:
1968 1969
      table->field[next_field]->store(STRING_WITH_LEN("X509"),
                                      &my_charset_latin1);
1970 1971 1972
      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);
1973 1974
      break;
    case SSL_TYPE_SPECIFIED:
1975 1976
      table->field[next_field]->store(STRING_WITH_LEN("SPECIFIED"),
                                      &my_charset_latin1);
1977 1978 1979
      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);
1980
      if (lex->ssl_cipher)
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1981 1982
        table->field[next_field+1]->store(lex->ssl_cipher,
                                strlen(lex->ssl_cipher), system_charset_info);
1983
      if (lex->x509_issuer)
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1984 1985
        table->field[next_field+2]->store(lex->x509_issuer,
                                strlen(lex->x509_issuer), system_charset_info);
1986
      if (lex->x509_subject)
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1987 1988
        table->field[next_field+3]->store(lex->x509_subject,
                                strlen(lex->x509_subject), system_charset_info);
1989
      break;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1990
    case SSL_TYPE_NOT_SPECIFIED:
gluh@gluh.(none)'s avatar
gluh@gluh.(none) committed
1991 1992
      break;
    case SSL_TYPE_NONE:
1993 1994 1995 1996
      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
1997
      break;
1998
    }
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1999
    next_field+=4;
2000

2001
    USER_RESOURCES mqh= lex->mqh;
2002
    if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
2003
      table->field[next_field]->store((longlong) mqh.questions, TRUE);
2004
    if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
2005
      table->field[next_field+1]->store((longlong) mqh.updates, TRUE);
2006
    if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
2007
      table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE);
2008
    if (table->s->fields >= 36 &&
2009
        (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2010
      table->field[next_field+3]->store((longlong) mqh.user_conn);
2011
    mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
2012
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2013
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2014 2015 2016 2017 2018
  {
    /*
      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!
    */
2019
    table->file->ha_retrieve_all_cols();
2020
    if (cmp_record(table,record[1]) &&
2021
	(error=table->file->ha_update_row(table->record[1],table->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2022 2023 2024 2025 2026 2027
    {						// This should never happen
      table->file->print_error(error,MYF(0));	/* purecov: deadcode */
      error= -1;				/* purecov: deadcode */
      goto end;					/* purecov: deadcode */
    }
  }
2028
  else if ((error=table->file->ha_write_row(table->record[0]))) // insert
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039
  {						// 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

2040
end:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2041 2042 2043
  if (!error)
  {
    acl_cache->clear(1);			// Clear privilege cache
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2044
    if (old_row_exists)
2045 2046
      acl_update_user(combo.user.str, combo.host.str,
                      combo.password.str, password_len,
2047 2048 2049 2050 2051
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
2052
		      rights);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2053
    else
2054
      acl_insert_user(combo.user.str, combo.host.str, password, password_len,
2055 2056 2057 2058 2059
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
2060
		      rights);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2061 2062 2063 2064 2065 2066
  }
  DBUG_RETURN(error);
}


/*
2067
  change grants in the mysql.db table
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2068 2069 2070 2071
*/

static int replace_db_table(TABLE *table, const char *db,
			    const LEX_USER &combo,
2072
			    ulong rights, bool revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2073
{
2074 2075
  uint i;
  ulong priv,store_rights;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2076
  bool old_row_exists=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2077
  int error;
2078
  char what= (revoke_grant) ? 'N' : 'Y';
2079
  byte user_key[MAX_KEY_LENGTH];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2080 2081
  DBUG_ENTER("replace_db_table");

2082 2083
  if (!initialized)
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2084
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2085 2086 2087
    DBUG_RETURN(-1);
  }

2088
  /* Check if there is such a user in user table in memory? */
2089
  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2090
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2091
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2092 2093 2094
    DBUG_RETURN(-1);
  }

2095 2096 2097
  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);
2098 2099 2100
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

2101
  table->file->ha_retrieve_all_cols();
2102
  if (table->file->index_read_idx(table->record[0],0,
2103 2104
                                  user_key, table->key_info->key_length,
                                  HA_READ_KEY_EXACT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2105 2106 2107
  {
    if (what == 'N')
    { // no row, no revoke
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2108
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2109 2110
      goto abort;
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2111
    old_row_exists = 0;
2112
    restore_record(table, s->default_values);
2113 2114 2115
    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
2116 2117 2118
  }
  else
  {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2119
    old_row_exists = 1;
2120
    store_record(table,record[1]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2121 2122 2123
  }

  store_rights=get_rights_for_db(rights);
2124
  for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2125
  {
2126
    if (priv & store_rights)			// do it if priv is chosen
2127
      table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2128 2129 2130 2131
  }
  rights=get_access(table,3);
  rights=fix_rights_for_db(rights);

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2132
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2133
  {
2134
    /* update old existing row */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2135 2136
    if (rights)
    {
2137
      table->file->ha_retrieve_all_cols();
2138 2139
      if ((error=table->file->ha_update_row(table->record[1],
                                            table->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2140 2141 2142 2143
	goto table_error;			/* purecov: deadcode */
    }
    else	/* must have been a revoke of all privileges */
    {
2144
      if ((error = table->file->ha_delete_row(table->record[1])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2145 2146 2147
	goto table_error;			/* purecov: deadcode */
    }
  }
2148
  else if (rights && (error=table->file->ha_write_row(table->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2149 2150 2151 2152 2153 2154
  {
    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
2155
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2156 2157
    acl_update_db(combo.user.str,combo.host.str,db,rights);
  else
2158
  if (rights)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2159 2160 2161 2162
    acl_insert_db(combo.user.str,combo.host.str,db,rights);
  DBUG_RETURN(0);

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

2166
abort:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2167 2168 2169 2170 2171 2172 2173 2174
  DBUG_RETURN(-1);
}


class GRANT_COLUMN :public Sql_alloc
{
public:
  char *column;
2175 2176 2177
  ulong rights;
  uint key_length;
  GRANT_COLUMN(String &c,  ulong y) :rights (y)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2178
  {
2179
    column= memdup_root(&memex,c.ptr(), key_length=c.length());
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2180 2181 2182
  }
};

2183

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2184 2185 2186 2187 2188 2189 2190
static byte* get_key_column(GRANT_COLUMN *buff,uint *length,
			    my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
  return (byte*) buff->column;
}

2191

2192
class GRANT_NAME :public Sql_alloc
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2193 2194
{
public:
2195 2196
  acl_host_and_ip host;
  char *db, *user, *tname, *hash_key;
2197
  ulong privs;
2198
  ulong sort;
2199
  uint key_length;
2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211
  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
2212
  HASH hash_columns;
monty@mysql.com's avatar
monty@mysql.com committed
2213 2214 2215 2216

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

2221

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

2223 2224 2225
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
                       const char *t, ulong p)
  :privs(p)
2226 2227
{
  /* Host given by user */
2228
  update_hostname(&host, strdup_root(&memex, h));
2229 2230
  db =   strdup_root(&memex,d);
  user = strdup_root(&memex,u);
2231
  sort=  get_sort(3,host.hostname,db,user);
2232 2233
  tname= strdup_root(&memex,t);
  if (lower_case_table_names)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2234
  {
2235 2236
    my_casedn_str(files_charset_info, db);
    my_casedn_str(files_charset_info, tname);
2237 2238 2239 2240
  }
  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);
2241 2242 2243 2244 2245 2246 2247
}


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

2252

2253
GRANT_NAME::GRANT_NAME(TABLE *form)
2254
{
2255
  update_hostname(&host, get_field(&memex, form->field[0]));
monty@mysql.com's avatar
monty@mysql.com committed
2256 2257
  db=    get_field(&memex,form->field[1]);
  user=  get_field(&memex,form->field[2]);
2258 2259
  if (!user)
    user= (char*) "";
2260
  sort=  get_sort(3, host.hostname, db, user);
monty@mysql.com's avatar
monty@mysql.com committed
2261
  tname= get_field(&memex,form->field[3]);
2262 2263 2264
  if (!db || !tname)
  {
    /* Wrong table row; Ignore it */
2265
    privs= 0;
2266 2267 2268 2269
    return;					/* purecov: inspected */
  }
  if (lower_case_table_names)
  {
2270 2271
    my_casedn_str(files_charset_info, db);
    my_casedn_str(files_charset_info, tname);
2272 2273 2274 2275 2276 2277 2278
  }
  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);
2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294
}


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();
2295 2296
  cols =  fix_rights_for_column(cols);

2297
  (void) hash_init(&hash_columns,system_charset_info,
monty@mysql.com's avatar
monty@mysql.com committed
2298
                   0,0,0, (hash_get_key) get_key_column,0,0);
2299 2300
  if (cols)
  {
2301 2302
    uint key_prefix_len;
    KEY_PART_INFO *key_part= col_privs->key_info->key_part;
2303 2304
    col_privs->field[0]->store(host.hostname,
                               host.hostname ? (uint) strlen(host.hostname) : 0,
2305 2306 2307 2308
                               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);
2309 2310 2311 2312 2313 2314

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

2317
    col_privs->file->ha_index_init(0, 1);
2318
    if (col_privs->file->index_read(col_privs->record[0],
2319 2320
                                    (byte*) key,
                                    key_prefix_len, HA_READ_KEY_EXACT))
2321
    {
2322
      cols = 0; /* purecov: deadcode */
2323
      col_privs->file->ha_index_end();
2324
      return;
2325
    }
2326
    do
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2327
    {
2328 2329 2330
      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
2331
      res=col_privs->field[4]->val_str(&column_name);
2332 2333 2334
      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
2335
      {
2336 2337 2338
        /* Don't use this entry */
        privs = cols = 0;			/* purecov: deadcode */
        return;				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2339
      }
monty@mysql.com's avatar
monty@mysql.com committed
2340
      my_hash_insert(&hash_columns, (byte *) mem_check);
2341
    } while (!col_privs->file->index_next(col_privs->record[0]) &&
2342
             !key_cmp_if_same(col_privs,key,0,key_prefix_len));
2343
    col_privs->file->ha_index_end();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2344
  }
2345
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2346

2347

2348 2349 2350 2351 2352 2353
GRANT_TABLE::~GRANT_TABLE()
{
  hash_free(&hash_columns);
}


2354
static byte* get_grant_table(GRANT_NAME *buff,uint *length,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2355 2356 2357 2358 2359 2360
			     my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
  return (byte*) buff->hash_key;
}

2361

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2362 2363 2364 2365 2366
void free_grant_table(GRANT_TABLE *grant_table)
{
  hash_free(&grant_table->hash_columns);
}

2367

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

2370
static GRANT_NAME *name_hash_search(HASH *name_hash,
2371 2372 2373 2374
                                    const char *host,const char* ip,
                                    const char *db,
                                    const char *user, const char *tname,
                                    bool exact)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2375 2376 2377
{
  char helping [NAME_LEN*2+USERNAME_LENGTH+3];
  uint len;
2378
  GRANT_NAME *grant_name,*found=0;
2379
  HASH_SEARCH_STATE state;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2380 2381

  len  = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1;
2382 2383
  for (grant_name= (GRANT_NAME*) hash_first(name_hash, (byte*) helping,
                                            len, &state);
2384 2385
       grant_name ;
       grant_name= (GRANT_NAME*) hash_next(name_hash,(byte*) helping,
2386
                                           len, &state))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2387 2388 2389
  {
    if (exact)
    {
2390 2391
      if ((host &&
	   !my_strcasecmp(system_charset_info, host,
gluh@mysql.com's avatar
gluh@mysql.com committed
2392 2393
                          grant_name->host.hostname)) ||
	  (ip && !strcmp(ip, grant_name->host.hostname)))
2394
	return grant_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2395 2396 2397
    }
    else
    {
2398
      if (compare_hostname(&grant_name->host, host, ip) &&
2399 2400
          (!found || found->sort < grant_name->sort))
	found=grant_name;					// Host ok
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2401 2402 2403 2404 2405 2406
    }
  }
  return found;
}


2407
inline GRANT_NAME *
2408 2409
routine_hash_search(const char *host, const char *ip, const char *db,
                 const char *user, const char *tname, bool proc, bool exact)
2410
{
2411 2412 2413
  return (GRANT_TABLE*)
    name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
		     host, ip, db, user, tname, exact);
2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424
}


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
2425

2426
inline GRANT_COLUMN *
2427
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2428 2429 2430 2431 2432 2433 2434 2435 2436
{
  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,
2437
				ulong rights, bool revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2438 2439 2440
{
  int error=0,result=0;
  byte key[MAX_KEY_LENGTH];
2441 2442
  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
2443 2444
  DBUG_ENTER("replace_column_table");

2445 2446 2447 2448 2449 2450 2451 2452
  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
2453

2454 2455 2456 2457
  /* 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
2458

2459
  rights&= COL_ACLS;				// Only ACL for columns
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2460 2461 2462 2463

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

  List_iterator <LEX_COLUMN> iter(columns);
2464
  class LEX_COLUMN *column;
2465
  table->file->ha_index_init(0, 1);
2466
  while ((column= iter++))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2467
  {
2468
    ulong privileges= column->rights;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2469
    bool old_row_exists=0;
2470 2471 2472 2473
    byte user_key[MAX_KEY_LENGTH];

    key_restore(table->record[0],key,table->key_info,
                key_prefix_length);
2474
    table->field[4]->store(column->column.ptr(), column->column.length(),
2475
                           system_charset_info);
2476 2477 2478
    /* 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
2479

2480
    table->file->ha_retrieve_all_cols();
2481 2482 2483
    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
2484 2485 2486
    {
      if (revoke_grant)
      {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2487
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
2488
                 combo.user.str, combo.host.str,
2489 2490 2491
                 table_name);                   /* purecov: inspected */
	result= -1;                             /* purecov: inspected */
	continue;                               /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2492
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2493
      old_row_exists = 0;
2494
      restore_record(table, s->default_values);		// Get empty record
2495 2496
      key_restore(table->record[0],key,table->key_info,
                  key_prefix_length);
2497
      table->field[4]->store(column->column.ptr(),column->column.length(),
2498
                             system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2499 2500 2501
    }
    else
    {
2502
      ulong tmp= (ulong) table->field[6]->val_int();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2503 2504 2505 2506 2507 2508
      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
2509
      old_row_exists = 1;
2510
      store_record(table,record[1]);			// copy original row
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2511 2512
    }

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

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2515
    if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2516
    {
2517
      GRANT_COLUMN *grant_column;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2518
      if (privileges)
2519
	error=table->file->ha_update_row(table->record[1],table->record[0]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2520
      else
2521
	error=table->file->ha_delete_row(table->record[1]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2522 2523 2524 2525 2526 2527
      if (error)
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
2528 2529
      grant_column= column_hash_search(g_t, column->column.ptr(),
                                       column->column.length());
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2530
      if (grant_column)				// Should always be true
2531
	grant_column->rights= privileges;	// Update hash
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2532 2533 2534
    }
    else					// new grant
    {
2535
      GRANT_COLUMN *grant_column;
2536
      if ((error=table->file->ha_write_row(table->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2537 2538 2539 2540 2541
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
2542
      grant_column= new GRANT_COLUMN(column->column,privileges);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2543
      my_hash_insert(&g_t->hash_columns,(byte*) grant_column);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2544 2545 2546 2547 2548 2549 2550 2551 2552 2553
    }
  }

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

  if (revoke_grant)
  {
2554 2555
    byte user_key[MAX_KEY_LENGTH];
    key_copy(user_key, table->record[0], table->key_info,
2556 2557
             key_prefix_length);

2558
    table->file->ha_retrieve_all_cols();
2559
    if (table->file->index_read(table->record[0], user_key,
2560
				key_prefix_length,
2561
                                HA_READ_KEY_EXACT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2562 2563
      goto end;

2564
    /* Scan through all rows with the same host,db,user and table */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2565 2566
    do
    {
2567
      ulong privileges = (ulong) table->field[6]->val_int();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2568
      privileges=fix_rights_for_column(privileges);
2569
      store_record(table,record[1]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2570 2571 2572 2573 2574

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

	privileges&= ~rights;
	table->field[6]->store((longlong)
2580
			       get_rights_for_column(privileges), TRUE);
2581
	table->field[4]->val_str(&column_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2582 2583 2584 2585 2586 2587
	grant_column = column_hash_search(g_t,
					  column_name.ptr(),
					  column_name.length());
	if (privileges)
	{
	  int tmp_error;
2588 2589
	  if ((tmp_error=table->file->ha_update_row(table->record[1],
						    table->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600
	  {					/* 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;
2601
	  if ((tmp_error = table->file->ha_delete_row(table->record[1])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2602 2603 2604 2605 2606 2607 2608 2609 2610 2611
	  {					/* 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]) &&
2612
	     !key_cmp_if_same(table, key, 0, key_prefix_length));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2613 2614
  }

2615
end:
2616
  table->file->ha_index_end();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2617 2618 2619 2620 2621 2622 2623
  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,
2624 2625
			       ulong rights, ulong col_rights,
			       bool revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2626
{
2627
  char grantor[USER_HOST_BUFF_SIZE];
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2628
  int old_row_exists = 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2629
  int error=0;
2630
  ulong store_table_rights, store_col_rights;
2631
  byte user_key[MAX_KEY_LENGTH];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2632 2633
  DBUG_ENTER("replace_table_table");

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

2637 2638 2639 2640
  /*
    The following should always succeed as new users are created before
    this function is called!
  */
2641
  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2642
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2643 2644
    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
2645 2646 2647
    DBUG_RETURN(-1);				/* purecov: deadcode */
  }

2648
  restore_record(table, s->default_values);     // Get empty record
2649 2650 2651 2652
  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);
2653
  store_record(table,record[1]);			// store at pos 1
2654 2655
  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
2656

2657
  table->file->ha_retrieve_all_cols();
2658 2659
  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
2660 2661 2662 2663 2664 2665 2666 2667 2668
				  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
2669 2670
      my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
               combo.user.str, combo.host.str,
2671
               table_name);		        /* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2672 2673
      DBUG_RETURN(-1);				/* purecov: deadcode */
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2674
    old_row_exists = 0;
2675
    restore_record(table,record[1]);			// Get saved record
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2676 2677
  }

2678 2679
  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
2680
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2681
  {
2682
    ulong j,k;
2683
    store_record(table,record[1]);
2684 2685
    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
2686 2687 2688

    if (revoke_grant)
    {
2689
      /* column rights are already fixed in mysql_table_grant */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2690 2691 2692 2693
      store_table_rights=j & ~store_table_rights;
    }
    else
    {
2694 2695
      store_table_rights|= j;
      store_col_rights|=   k;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2696 2697 2698
    }
  }

2699
  table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
2700 2701
  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
2702
  rights=fix_rights_for_table(store_table_rights);
2703
  col_rights=fix_rights_for_column(store_col_rights);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2704

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2705
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2706 2707 2708
  {
    if (store_table_rights || store_col_rights)
    {
2709
      if ((error=table->file->ha_update_row(table->record[1],table->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2710 2711
	goto table_error;			/* purecov: deadcode */
    }
2712
    else if ((error = table->file->ha_delete_row(table->record[1])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2713 2714 2715 2716
      goto table_error;				/* purecov: deadcode */
  }
  else
  {
2717
    error=table->file->ha_write_row(table->record[0]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2718 2719 2720 2721
    if (error && error != HA_ERR_FOUND_DUPP_KEY)
      goto table_error;				/* purecov: deadcode */
  }

2722
  if (rights | col_rights)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2723
  {
2724
    grant_table->privs= rights;
2725
    grant_table->cols=	col_rights;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2726 2727 2728
  }
  else
  {
2729
    hash_delete(&column_priv_hash,(byte*) grant_table);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2730 2731 2732
  }
  DBUG_RETURN(0);

2733 2734
  /* This should never happen */
table_error:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2735 2736 2737 2738 2739
  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
  DBUG_RETURN(-1); /* purecov: deadcode */
}


2740
static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
2741
			      TABLE *table, const LEX_USER &combo,
2742 2743
			      const char *db, const char *routine_name,
			      bool is_proc, ulong rights, bool revoke_grant)
2744
{
2745
  char grantor[USER_HOST_BUFF_SIZE];
2746 2747 2748
  int old_row_exists= 1;
  int error=0;
  ulong store_proc_rights;
2749
  DBUG_ENTER("replace_routine_table");
2750 2751 2752 2753 2754 2755 2756

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

2757 2758
  strxmov(grantor, thd->security_ctx->user, "@",
          thd->security_ctx->host_or_ip, NullS);
2759 2760 2761 2762 2763

  /*
    The following should always succeed as new users are created before
    this function is called!
  */
monty@mishka.local's avatar
monty@mishka.local committed
2764
  if (!find_acl_user(combo.host.str, combo.user.str, FALSE))
2765 2766 2767 2768 2769
  {
    my_error(ER_PASSWORD_NO_MATCH,MYF(0));
    DBUG_RETURN(-1);
  }

2770
  restore_record(table, s->default_values);		// Get empty record
2771 2772 2773
  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);
2774 2775 2776
  table->field[3]->store(routine_name,(uint) strlen(routine_name),
                         &my_charset_latin1);
  table->field[4]->store((longlong)(is_proc ? 
2777 2778
                                    TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION),
                         TRUE);
2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792
  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),
2793
               combo.user.str, combo.host.str, routine_name);
2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817
      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;
    }
  }

2818
  table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
2819
  table->field[6]->store((longlong) store_proc_rights, TRUE);
2820 2821 2822 2823 2824 2825
  rights=fix_rights_for_procedure(store_proc_rights);

  if (old_row_exists)
  {
    if (store_proc_rights)
    {
2826
      if ((error=table->file->ha_update_row(table->record[1],table->record[0])))
2827 2828
	goto table_error;
    }
2829
    else if ((error= table->file->ha_delete_row(table->record[1])))
2830 2831 2832 2833
      goto table_error;
  }
  else
  {
2834
    error=table->file->ha_write_row(table->record[0]);
2835 2836 2837 2838 2839 2840 2841 2842 2843 2844
    if (error && error != HA_ERR_FOUND_DUPP_KEY)
      goto table_error;
  }

  if (rights)
  {
    grant_name->privs= rights;
  }
  else
  {
2845
    hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
2846 2847 2848 2849 2850 2851 2852 2853 2854 2855
  }
  DBUG_RETURN(0);

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


2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868
/*
  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
2869 2870
    FALSE ok
    TRUE  error
2871 2872
*/

2873
bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
2874 2875 2876
		      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
2877
{
2878
  ulong column_priv= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2879 2880 2881
  List_iterator <LEX_USER> str_list (user_list);
  LEX_USER *Str;
  TABLE_LIST tables[3];
2882
  bool create_new_users=0;
2883
  char *db_name, *table_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2884 2885 2886 2887
  DBUG_ENTER("mysql_table_grant");

  if (!initialized)
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2888 2889
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: inspected */
2890
    DBUG_RETURN(TRUE);				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2891 2892 2893
  }
  if (rights & ~TABLE_ACLS)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2894 2895
    my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
               MYF(0));
2896
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2897 2898
  }

2899
  if (!revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2900
  {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2901
    if (columns.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2902
    {
2903 2904
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2905 2906 2907

      if (open_and_lock_tables(thd, table_list))
        DBUG_RETURN(TRUE);
2908 2909

      while ((column = column_iter++))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2910
      {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2911
        uint unused_field_idx= NO_CACHED_FIELD_INDEX;
2912 2913
        TABLE_LIST *dummy;
        Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
2914
                                         column->column.length(),
2915
                                         column->column.ptr(), NULL, NULL,
2916
                                         NULL, TRUE, FALSE,
2917
                                         &unused_field_idx, FALSE, &dummy);
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2918
        if (f == (Field*)0)
2919
        {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2920 2921
          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
2922
          DBUG_RETURN(TRUE);
2923
        }
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2924 2925
        if (f == (Field *)-1)
          DBUG_RETURN(TRUE);
2926
        column_priv|= column->rights;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2927
      }
2928
      close_thread_tables(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2929
    }
2930
    else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2931
    {
2932 2933 2934
      if (!(rights & CREATE_ACL))
      {
        char buf[FN_REFLEN];
2935 2936 2937 2938
        build_table_filename(buf, sizeof(buf), table_list->db,
                             table_list->table_name, reg_ext);
        fn_format(buf, buf, "", "", MY_UNPACK_FILENAME  | MY_RESOLVE_SYMLINKS |
                                    MY_RETURN_REAL_PATH | MY_APPEND_EXT);
2939 2940
        if (access(buf,F_OK))
        {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2941
          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
2942
          DBUG_RETURN(TRUE);
2943 2944 2945 2946 2947 2948 2949 2950
        }
      }
      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),
2951 2952
                 command, thd->security_ctx->priv_user,
                 thd->security_ctx->host_or_ip, table_list->alias);
2953 2954
        DBUG_RETURN(-1);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2955 2956 2957 2958 2959 2960
    }
  }

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

  bzero((char*) &tables,sizeof(tables));
2961 2962 2963
  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
2964
  tables[0].next_local= tables[0].next_global= tables+1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2965
  /* 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
2966 2967 2968 2969 2970
  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
2971 2972 2973
  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";

2974 2975 2976 2977 2978
#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
2979
  if (thd->slave_thread && rpl_filter->is_on())
lars@mysql.com's avatar
lars@mysql.com committed
2980
  if (thd->slave_thread && rpl_filter->is_on())
2981
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2982 2983 2984
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
2985
    */
2986
    tables[0].updating= tables[1].updating= tables[2].updating= 1;
lars@mysql.com's avatar
lars@mysql.com committed
2987
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
2988
      DBUG_RETURN(FALSE);
2989
  }
2990 2991
#endif

2992
  if (simple_open_n_lock_tables(thd,tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2993 2994
  {						// Should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
2995
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2996 2997
  }

2998 2999
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
3000
  bool result= FALSE;
3001
  rw_wrlock(&LOCK_grant);
3002 3003
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;
3004
  grant_version++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3005 3006 3007

  while ((Str = str_list++))
  {
3008
    int error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3009 3010 3011 3012
    GRANT_TABLE *grant_table;
    if (Str->host.length > HOSTNAME_LENGTH ||
	Str->user.length > USERNAME_LENGTH)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3013 3014
      my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
                 MYF(0));
3015
      result= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3016 3017 3018
      continue;
    }
    /* Create user if needed */
3019
    pthread_mutex_lock(&acl_cache->lock);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3020
    error=replace_user_table(thd, tables[0].table, *Str,
serg@serg.mylan's avatar
serg@serg.mylan committed
3021
			     0, revoke_grant, create_new_users,
monty@mysql.com's avatar
monty@mysql.com committed
3022 3023
                             test(thd->variables.sql_mode &
                                  MODE_NO_AUTO_CREATE_USER));
3024 3025
    pthread_mutex_unlock(&acl_cache->lock);
    if (error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3026
    {
3027
      result= TRUE;				// Remember error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3028 3029 3030
      continue;					// Add next user
    }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3031 3032 3033
    db_name= (table_list->view_db.length ?
	      table_list->view_db.str :
	      table_list->db);
3034
    table_name= (table_list->view_name.length ?
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3035
		table_list->view_name.str :
3036
		table_list->table_name);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3037

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3038
    /* Find/create cached table grant */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3039
    grant_table= table_hash_search(Str->host.str, NullS, db_name,
3040
				   Str->user.str, table_name, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3041 3042 3043 3044
    if (!grant_table)
    {
      if (revoke_grant)
      {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3045
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
3046
                 Str->user.str, Str->host.str, table_list->table_name);
3047
	result= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3048 3049
	continue;
      }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3050
      grant_table = new GRANT_TABLE (Str->host.str, db_name,
3051
				     Str->user.str, table_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3052 3053 3054 3055
				     rights,
				     column_priv);
      if (!grant_table)				// end of memory
      {
3056
	result= TRUE;				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3057 3058
	continue;				/* purecov: deadcode */
      }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3059
      my_hash_insert(&column_priv_hash,(byte*) grant_table);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3060 3061 3062 3063 3064
    }

    /* If revoke_grant, calculate the new column privilege for tables_priv */
    if (revoke_grant)
    {
3065 3066
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3067 3068 3069
      GRANT_COLUMN *grant_column;

      /* Fix old grants */
3070
      while ((column = column_iter++))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3071 3072
      {
	grant_column = column_hash_search(grant_table,
3073 3074
					  column->column.ptr(),
					  column->column.length());
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3075
	if (grant_column)
3076
	  grant_column->rights&= ~(column->rights | rights);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3077 3078
      }
      /* scan trough all columns to get new column grant */
3079
      column_priv= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095
      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
3096
    if (replace_table_table(thd, grant_table, tables[1].table, *Str,
3097
			    db_name, table_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3098
			    rights, column_priv, revoke_grant))
3099 3100
    {
      /* Should only happen if table is crashed */
3101
      result= TRUE;			       /* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3102 3103 3104
    }
    else if (tables[2].table)
    {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3105
      if ((replace_column_table(grant_table, tables[2].table, *Str,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3106
				columns,
3107
				db_name, table_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3108 3109
				rights, revoke_grant)))
      {
3110
	result= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3111 3112 3113 3114
      }
    }
  }
  grant_option=TRUE;
3115
  thd->mem_root= old_root;
3116
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3117
  if (!result)
3118
    send_ok(thd);
3119
  /* Tables are automatically closed */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3120 3121 3122 3123
  DBUG_RETURN(result);
}


3124
/*
3125
  Store routine level grants in the privilege tables
3126 3127

  SYNOPSIS
3128
    mysql_routine_grant()
3129
    thd			Thread handle
3130 3131
    table_list		List of routines to give grant
    is_proc             true indicates routine list are procedures
3132 3133 3134 3135 3136 3137 3138 3139 3140
    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
*/

3141 3142 3143
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)
3144 3145 3146 3147 3148
{
  List_iterator <LEX_USER> str_list (user_list);
  LEX_USER *Str;
  TABLE_LIST tables[2];
  bool create_new_users=0, result=0;
3149
  char *db_name, *table_name;
3150
  DBUG_ENTER("mysql_routine_grant");
3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168

  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)
  {
3169
    if (sp_exist_routines(thd, table_list, is_proc, no_error)<0)
3170 3171 3172 3173 3174 3175
      DBUG_RETURN(TRUE);
  }

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

  bzero((char*) &tables,sizeof(tables));
3176 3177
  tables[0].alias=tables[0].table_name= (char*) "user";
  tables[1].alias=tables[1].table_name= (char*) "procs_priv";
3178 3179 3180 3181 3182 3183 3184 3185 3186
  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.
  */
3187
  if (thd->slave_thread && rpl_filter->is_on())
3188 3189 3190 3191 3192 3193
  {
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
    */
    tables[0].updating= tables[1].updating= 1;
lars@mysql.com's avatar
lars@mysql.com committed
3194
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228
      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);
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;

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

  while ((Str= str_list++))
  {
    int error;
    GRANT_NAME *grant_name;
    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 */
    pthread_mutex_lock(&acl_cache->lock);
    error=replace_user_table(thd, tables[0].table, *Str,
serg@serg.mylan's avatar
serg@serg.mylan committed
3229
			     0, revoke_grant, create_new_users,
monty@mysql.com's avatar
monty@mysql.com committed
3230 3231
                             test(thd->variables.sql_mode &
                                  MODE_NO_AUTO_CREATE_USER));
3232 3233 3234 3235 3236 3237 3238 3239
    pthread_mutex_unlock(&acl_cache->lock);
    if (error)
    {
      result= TRUE;				// Remember error
      continue;					// Add next user
    }

    db_name= table_list->db;
3240
    table_name= table_list->table_name;
3241

3242 3243
    grant_name= routine_hash_search(Str->host.str, NullS, db_name,
                                    Str->user.str, table_name, is_proc, 1);
3244 3245 3246 3247 3248 3249
    if (!grant_name)
    {
      if (revoke_grant)
      {
        if (!no_error)
          my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
3250
		   Str->user.str, Str->host.str, table_name);
3251 3252 3253 3254
	result= TRUE;
	continue;
      }
      grant_name= new GRANT_NAME(Str->host.str, db_name,
3255
				 Str->user.str, table_name,
3256 3257 3258 3259 3260 3261
				 rights);
      if (!grant_name)
      {
        result= TRUE;
	continue;
      }
3262
      my_hash_insert(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
3263
    }
3264

3265 3266
    if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
			   db_name, table_name, is_proc, rights, revoke_grant))
3267 3268 3269 3270 3271 3272 3273
    {
      result= TRUE;
      continue;
    }
  }
  grant_option=TRUE;
  thd->mem_root= old_root;
3274 3275 3276 3277 3278 3279 3280 3281 3282 3283
  /*
    This flush is here only becuase there is code that writes rows to
    system tables after executing a binlog_query().

    TODO: Ensure that no writes are executed after a binlog_query() by
    moving the writes to before calling binlog_query(). Then remove
    this line (and add an assert inside send_ok() that checks that
    everything is in a consistent state).
   */
  thd->binlog_flush_pending_rows_event(true);
3284 3285 3286 3287 3288 3289 3290 3291
  rw_unlock(&LOCK_grant);
  if (!result && !no_error)
    send_ok(thd);
  /* Tables are automatically closed */
  DBUG_RETURN(result);
}


3292 3293
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
3294 3295 3296
{
  List_iterator <LEX_USER> str_list (list);
  LEX_USER *Str;
3297
  char tmp_db[NAME_LEN+1];
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3298
  bool create_new_users=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3299 3300 3301 3302
  TABLE_LIST tables[2];
  DBUG_ENTER("mysql_grant");
  if (!initialized)
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3303 3304
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: tested */
3305
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3306 3307
  }

3308 3309 3310
  if (lower_case_table_names && db)
  {
    strmov(tmp_db,db);
3311
    my_casedn_str(files_charset_info, tmp_db);
3312 3313
    db=tmp_db;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3314 3315

  /* open the mysql.user and mysql.db tables */
3316
  bzero((char*) &tables,sizeof(tables));
3317 3318
  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
3319
  tables[0].next_local= tables[0].next_global= tables+1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3320 3321
  tables[0].lock_type=tables[1].lock_type=TL_WRITE;
  tables[0].db=tables[1].db=(char*) "mysql";
3322 3323 3324 3325 3326 3327

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
3328
  if (thd->slave_thread && rpl_filter->is_on())
3329
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3330 3331 3332
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
3333
    */
3334
    tables[0].updating= tables[1].updating= 1;
lars@mysql.com's avatar
lars@mysql.com committed
3335
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
3336
      DBUG_RETURN(FALSE);
3337
  }
3338 3339
#endif

3340
  if (simple_open_n_lock_tables(thd,tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3341 3342
  {						// This should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
3343
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3344 3345
  }

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3346 3347
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3348

3349
  /* go through users in user_list */
3350
  rw_wrlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3351 3352 3353 3354 3355 3356 3357 3358 3359
  VOID(pthread_mutex_lock(&acl_cache->lock));
  grant_version++;

  int result=0;
  while ((Str = str_list++))
  {
    if (Str->host.length > HOSTNAME_LENGTH ||
	Str->user.length > USERNAME_LENGTH)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3360 3361
      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
3362 3363 3364
      result= -1;
      continue;
    }
serg@serg.mylan's avatar
serg@serg.mylan committed
3365 3366
    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
3367 3368
                           test(thd->variables.sql_mode &
                                MODE_NO_AUTO_CREATE_USER)))
3369
      result= -1;
3370
    else if (db)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3371
    {
3372 3373 3374 3375 3376 3377 3378 3379 3380
      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
3381
	my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3382
	result= -1;
3383
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3384
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3385 3386
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
3387
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3388 3389 3390
  close_thread_tables(thd);

  if (!result)
3391
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3392 3393 3394
  DBUG_RETURN(result);
}

3395 3396

/* Free grant array if possible */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3397 3398 3399 3400 3401

void  grant_free(void)
{
  DBUG_ENTER("grant_free");
  grant_option = FALSE;
3402
  hash_free(&column_priv_hash);
3403
  hash_free(&proc_priv_hash);
monty@mysql.com's avatar
monty@mysql.com committed
3404
  hash_free(&func_priv_hash);
3405
  free_root(&memex,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3406 3407 3408 3409
  DBUG_VOID_RETURN;
}


3410 3411 3412 3413 3414 3415
/*
  Initialize structures responsible for table/column-level privilege checking
  and load information for them from tables in the 'mysql' database.

  SYNOPSIS
    grant_init()
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3416

3417 3418 3419 3420
  RETURN VALUES
    0	ok
    1	Could not initialize grant's
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3421

3422
my_bool grant_init()
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3423
{
3424
  THD  *thd;
3425 3426 3427 3428 3429
  my_bool return_val;
  DBUG_ENTER("grant_init");

  if (!(thd= new THD))
    DBUG_RETURN(1);				/* purecov: deadcode */
3430
  thd->thread_stack= (char*) &thd;
3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456
  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)
{
3457
  MEM_ROOT *memex_ptr;
3458
  my_bool return_val= 1;
3459
  TABLE *t_table, *c_table, *p_table;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3460
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
3461 3462 3463
  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
3464 3465

  grant_option = FALSE;
3466
  (void) hash_init(&column_priv_hash,system_charset_info,
3467
		   0,0,0, (hash_get_key) get_grant_table,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3468
		   (hash_free_key) free_grant_table,0);
3469 3470 3471
  (void) hash_init(&proc_priv_hash,system_charset_info,
		   0,0,0, (hash_get_key) get_grant_table,
		   0,0);
3472 3473 3474
  (void) hash_init(&func_priv_hash,system_charset_info,
		   0,0,0, (hash_get_key) get_grant_table,
		   0,0);
3475
  init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3476 3477

  t_table = tables[0].table; c_table = tables[1].table;
3478
  p_table= tables[2].table;
3479 3480
  t_table->file->ha_index_init(0, 1);
  p_table->file->ha_index_init(0, 1);
3481
  if (!t_table->file->index_first(t_table->record[0]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3482
  {
3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493
    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
3494

3495 3496
      if (check_no_resolve)
      {
3497
	if (hostname_requires_resolving(mem_check->host.hostname))
3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518
	{
          sql_print_warning("'tables_priv' entry '%s %s@%s' "
                            "ignored in --skip-name-resolve mode.",
                            mem_check->tname, mem_check->user,
                            mem_check->host, mem_check->host);
	  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
3519
  {
3520 3521 3522
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3523
    {
3524
      GRANT_NAME *mem_check;
3525
      HASH *hash;
3526 3527 3528 3529 3530 3531
      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
3532

3533
      if (check_no_resolve)
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3534
      {
jimw@mysql.com's avatar
jimw@mysql.com committed
3535
	if (hostname_requires_resolving(mem_check->host.hostname))
3536 3537 3538 3539
	{
          sql_print_warning("'procs_priv' entry '%s %s@%s' "
                            "ignored in --skip-name-resolve mode.",
                            mem_check->tname, mem_check->user,
acurtis@xiphis.org's avatar
Merge  
acurtis@xiphis.org committed
3540
                            mem_check->host);
3541 3542
	  continue;
	}
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3543
      }
3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559
      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
3560

3561 3562 3563
      mem_check->privs= fix_rights_for_procedure(mem_check->privs);
      if (! mem_check->ok())
	delete mem_check;
3564
      else if (my_hash_insert(hash, (byte*) mem_check))
3565 3566 3567 3568 3569
      {
	delete mem_check;
	grant_option= FALSE;
	goto end_unlock;
      }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3570
    }
3571
    while (!p_table->file->index_next(p_table->record[0]));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3572
  }
3573
  grant_option= TRUE;
3574 3575 3576
  return_val=0;					// Return ok

end_unlock:
3577
  t_table->file->ha_index_end();
3578
  p_table->file->ha_index_end();
3579
  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
3580
  DBUG_RETURN(return_val);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3581 3582 3583
}


3584
/*
3585
  Reload information about table and column level privileges if possible.
3586 3587 3588

  SYNOPSIS
    grant_reload()
3589
      thd  Current thread
3590 3591

  NOTES
3592 3593 3594 3595 3596 3597 3598 3599
    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
3600
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3601

3602
my_bool grant_reload(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3603
{
3604
  TABLE_LIST tables[3];
3605
  HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
3606
  bool old_grant_option;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3607
  MEM_ROOT old_mem;
3608
  my_bool return_val= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3609 3610
  DBUG_ENTER("grant_reload");

3611 3612 3613 3614 3615
  /* Don't do anything if running with --skip-grant-tables */
  if (!initialized)
    DBUG_RETURN(0);

  bzero((char*) tables, sizeof(tables));
3616 3617 3618 3619 3620 3621 3622
  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;
3623 3624 3625 3626 3627 3628 3629 3630

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

3631
  rw_wrlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3632
  grant_version++;
3633
  old_column_priv_hash= column_priv_hash;
3634
  old_proc_priv_hash= proc_priv_hash;
3635
  old_func_priv_hash= func_priv_hash;
3636
  old_grant_option= grant_option;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3637
  old_mem= memex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3638

3639
  if ((return_val= grant_load(tables)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3640
  {						// Error. Revert to old hash
3641
    DBUG_PRINT("error",("Reverting to old privileges"));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3642
    grant_free();				/* purecov: deadcode */
3643
    column_priv_hash= old_column_priv_hash;	/* purecov: deadcode */
3644
    proc_priv_hash= old_proc_priv_hash;
3645
    func_priv_hash= old_func_priv_hash;
3646
    grant_option= old_grant_option;		/* purecov: deadcode */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3647
    memex= old_mem;				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3648 3649 3650
  }
  else
  {
3651
    hash_free(&old_column_priv_hash);
3652
    hash_free(&old_proc_priv_hash);
3653
    hash_free(&old_func_priv_hash);
3654
    free_root(&old_mem,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3655
  }
3656
  rw_unlock(&LOCK_grant);
3657 3658 3659
end:
  close_thread_tables(thd);
  DBUG_RETURN(return_val);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3660 3661 3662 3663
}


/****************************************************************************
3664
  Check table level grants
3665

3666
  SYNOPSIS
3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679
   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
3680
     1  Error: User did not have the requested privileges
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3681 3682
****************************************************************************/

3683
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3684
		 uint show_table, uint number, bool no_errors)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3685
{
3686
  TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table();
3687
  Security_context *sctx= thd->security_ctx;
3688
  uint i;
3689 3690
  DBUG_ENTER("check_grant");
  DBUG_ASSERT(number > 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3691

3692
  /*
3693 3694 3695 3696 3697 3698 3699 3700
    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.
3701 3702
  */
  for (i= 0, table= tables;
3703
       table != first_not_own_table && i < number;
3704 3705 3706 3707 3708 3709
       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);
  }

3710
  want_access&= ~sctx->master_access;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3711
  if (!want_access)
3712
    DBUG_RETURN(0);                             // ok
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3713

3714
  rw_rdlock(&LOCK_grant);
3715 3716 3717
  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
3718
  {
3719
    GRANT_TABLE *grant_table;
3720
    if (!(~table->grant.privilege & want_access) || 
3721
        table->derived || table->schema_table || table->belong_to_view)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3722
    {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3723 3724 3725 3726
      /*
        It is subquery in the FROM clause. VIEW set table->derived after
        table opening, but this function always called before table opening.
      */
3727 3728 3729 3730 3731 3732 3733 3734 3735 3736
      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
3737
    }
3738 3739 3740
    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
3741 3742 3743 3744
    {
      want_access &= ~table->grant.privilege;
      goto err;					// No grants
    }
monty@tramp.mysql.fi's avatar
monty@tramp.mysql.fi committed
3745 3746
    if (show_table)
      continue;					// We have some priv on this
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762

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

3766
err:
3767
  rw_unlock(&LOCK_grant);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3768
  if (!no_errors)				// Not a silent skip of table
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3769
  {
3770 3771
    char command[128];
    get_privilege_desc(command, sizeof(command), want_access);
3772 3773
    my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
             command,
3774 3775
             sctx->priv_user,
             sctx->host_or_ip,
3776
             table ? table->table_name : "unknown");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3777
  }
3778
  DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3779 3780 3781
}


3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799
/*
  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
3800
bool check_grant_column(THD *thd, GRANT_INFO *grant,
3801
			const char *db_name, const char *table_name,
3802
			const char *name, uint length,  Security_context *sctx)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3803 3804 3805
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3806
  ulong want_access= grant->want_privilege & ~grant->privilege;
monty@mysql.com's avatar
monty@mysql.com committed
3807 3808 3809
  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
3810
  if (!want_access)
monty@mysql.com's avatar
monty@mysql.com committed
3811
    DBUG_RETURN(0);				// Already checked
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3812

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

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3817
  if (grant->version != grant_version)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3818
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3819
    grant->grant_table=
3820 3821
      table_hash_search(sctx->host, sctx->ip, db_name,
			sctx->priv_user,
monty@mysql.com's avatar
monty@mysql.com committed
3822
			table_name, 0);         /* purecov: inspected */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3823
    grant->version= grant_version;		/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3824
  }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3825
  if (!(grant_table= grant->grant_table))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3826 3827 3828 3829 3830
    goto err;					/* purecov: deadcode */

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

3835
err:
3836
  rw_unlock(&LOCK_grant);
3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880
  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
3881
  {
3882 3883 3884 3885
    /* View or derived information schema table. */
    grant= &(table_ref->grant);
    db_name= table_ref->view_db.str;
    table_name= table_ref->view_name.str;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3886
  }
3887 3888 3889 3890 3891
  else
  {
    /* Normal or temporary table. */
    TABLE *table= table_ref->table;
    grant= &(table->grant);
3892 3893
    db_name= table->s->db.str;
    table_name= table->s->table_name.str;
3894 3895 3896 3897 3898 3899 3900 3901
  }

  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
3902 3903 3904
}


bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3905
bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant,
3906
                             const char* db_name, const char *table_name,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3907
                             Field_iterator *fields)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3908
{
3909
  Security_context *sctx= thd->security_ctx;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3910 3911 3912
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3913
  want_access &= ~grant->privilege;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3914
  if (!want_access)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3915
    return 0;				// Already checked
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3916 3917
  if (!grant_option)
    goto err2;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3918

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

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3923
  if (grant->version != grant_version)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3924
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3925
    grant->grant_table=
3926 3927
      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
3928 3929
			table_name, 0);	/* purecov: inspected */
    grant->version= grant_version;		/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3930
  }
3931
  /* The following should always be true */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3932
  if (!(grant_table= grant->grant_table))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3933 3934
    goto err;					/* purecov: inspected */

3935
  for (; !fields->end_of_fields(); fields->next())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3936
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3937 3938 3939
    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
3940 3941 3942
    if (!grant_column || (~grant_column->rights & want_access))
      goto err;
  }
3943
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3944 3945
  return 0;

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3946
err:
3947
  rw_unlock(&LOCK_grant);
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3948
err2:
3949 3950
  char command[128];
  get_privilege_desc(command, sizeof(command), want_access);
3951 3952
  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
           command,
3953 3954
           sctx->priv_user,
           sctx->host_or_ip,
3955 3956
           fields->name(),
           table_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3957 3958 3959 3960
  return 1;
}


3961
/*
3962
  Check if a user has the right to access a database
3963
  Access is accepted if he has a grant for any table/routine in the database
3964
  Return 1 if access is denied
3965
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3966 3967 3968

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

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

3977
  for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3978
  {
3979 3980
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  idx);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3981 3982
    if (len < grant_table->key_length &&
	!memcmp(grant_table->hash_key,helping,len) &&
3983
        compare_hostname(&grant_table->host, sctx->host, sctx->ip))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3984 3985 3986 3987 3988
    {
      error=0;					// Found match
      break;
    }
  }
3989
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3990 3991 3992
  return error;
}

3993 3994

/****************************************************************************
3995
  Check routine level grants
3996 3997

  SYNPOSIS
3998
   bool check_grant_routine()
3999 4000
   thd		Thread handler
   want_access  Bits of privileges user needs to have
4001 4002
   procs	List of routines to check. The user should have 'want_access'
   is_proc	True if the list is all procedures, else functions
4003 4004 4005 4006 4007 4008 4009 4010
   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
****************************************************************************/

4011
bool check_grant_routine(THD *thd, ulong want_access,
4012
			 TABLE_LIST *procs, bool is_proc, bool no_errors)
4013 4014
{
  TABLE_LIST *table;
4015
  Security_context *sctx= thd->security_ctx;
4016 4017
  char *user= sctx->priv_user;
  char *host= sctx->priv_host;
4018
  DBUG_ENTER("check_grant_routine");
4019

4020
  want_access&= ~sctx->master_access;
4021 4022 4023 4024 4025 4026 4027
  if (!want_access)
    DBUG_RETURN(0);                             // ok

  rw_rdlock(&LOCK_grant);
  for (table= procs; table; table= table->next_global)
  {
    GRANT_NAME *grant_proc;
4028
    if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
4029
					 table->table_name, is_proc, 0)))
4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046
      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)
4047
      strxmov(buff, table->db, ".", table->table_name, NullS);
4048 4049 4050
    if (want_access & EXECUTE_ACL)
      command= "execute";
    else if (want_access & ALTER_PROC_ACL)
4051
      command= "alter routine";
4052 4053 4054 4055 4056 4057 4058 4059 4060
    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);
}


4061 4062
/*
  Check if routine has any of the 
4063
  routine level grants
4064 4065 4066 4067 4068 4069 4070 4071 4072
  
  SYNPOSIS
   bool    check_routine_level_acl()
   thd	        Thread handler
   db           Database name
   name         Routine name

  RETURN
   0            Ok 
4073
   1            error
4074 4075
*/

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
4076 4077
bool check_routine_level_acl(THD *thd, const char *db, const char *name, 
                             bool is_proc)
4078 4079 4080 4081 4082
{
  bool no_routine_acl= 1;
  if (grant_option)
  {
    GRANT_NAME *grant_proc;
4083
    Security_context *sctx= thd->security_ctx;
4084
    rw_rdlock(&LOCK_grant);
4085 4086 4087
    if ((grant_proc= routine_hash_search(sctx->priv_host,
                                         sctx->ip, db,
                                         sctx->priv_user,
4088
                                         name, is_proc, 0)))
4089 4090 4091 4092 4093 4094 4095
      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
4096
/*****************************************************************************
4097
  Functions to retrieve the grant for a table/column  (for SHOW functions)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4098 4099
*****************************************************************************/

4100
ulong get_table_grant(THD *thd, TABLE_LIST *table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4101
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4102
  ulong privilege;
4103
  Security_context *sctx= thd->security_ctx;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4104 4105 4106
  const char *db = table->db ? table->db : thd->db;
  GRANT_TABLE *grant_table;

4107
  rw_rdlock(&LOCK_grant);
4108 4109 4110
#ifdef EMBEDDED_LIBRARY
  grant_table= NULL;
#else
4111
  grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
4112
				 table->table_name, 0);
4113
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4114 4115 4116 4117
  table->grant.grant_table=grant_table; // Remember for column test
  table->grant.version=grant_version;
  if (grant_table)
    table->grant.privilege|= grant_table->privs;
4118
  privilege= table->grant.privilege;
4119
  rw_unlock(&LOCK_grant);
4120
  return privilege;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4121 4122 4123
}


4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141
/*
  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
4142 4143 4144
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
4145 4146 4147
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
4148
  ulong priv;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4149

4150
  rw_rdlock(&LOCK_grant);
4151
  /* reload table if someone has modified any grants */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4152
  if (grant->version != grant_version)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4153
  {
4154
    Security_context *sctx= thd->security_ctx;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4155
    grant->grant_table=
4156 4157
      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
4158 4159
			table_name, 0);	        /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4160 4161
  }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4162 4163
  if (!(grant_table= grant->grant_table))
    priv= grant->privilege;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4164 4165
  else
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4166 4167
    grant_column= column_hash_search(grant_table, field_name,
                                     (uint) strlen(field_name));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4168
    if (!grant_column)
4169
      priv= (grant->privilege | grant_table->privs);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4170
    else
4171
      priv= (grant->privilege | grant_table->privs | grant_column->rights);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4172
  }
4173
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4174 4175 4176
  return priv;
}

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

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

4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191
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
4192 4193

static const char *command_array[]=
4194
{
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4195 4196 4197 4198
  "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",
4199
  "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
4200
  "CREATE USER", "EVENT", "TRIGGER"
4201
};
4202

4203 4204
static uint command_lengths[]=
{
4205
  6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
4206
  14, 13, 11, 5, 7
4207 4208
};

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

4210 4211 4212 4213 4214
static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
                               const char *type, int typelen,
                               char *buff, int buffsize);


4215 4216 4217 4218 4219 4220 4221
/*
  SHOW GRANTS;  Send grants for a user to the client

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

4222
bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4223
{
4224 4225
  ulong want_access;
  uint counter,index;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4226
  int  error = 0;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4227 4228
  ACL_USER *acl_user;
  ACL_DB *acl_db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4229
  char buff[1024];
4230
  Protocol *protocol= thd->protocol;
tonu@x153.internalnet's avatar
tonu@x153.internalnet committed
4231
  DBUG_ENTER("mysql_show_grants");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4232 4233 4234 4235

  LINT_INIT(acl_user);
  if (!initialized)
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
4236
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
4237
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4238
  }
monty@mysql.com's avatar
monty@mysql.com committed
4239 4240 4241 4242 4243 4244

  if (!lex_user->host.str)
  {
    lex_user->host.str= (char*) "%";
    lex_user->host.length=1;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4245 4246 4247
  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
4248 4249
    my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
               MYF(0));
4250
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4251 4252 4253 4254 4255 4256 4257
  }

  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
4258
      user= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4259
    if (!(host=acl_user->host.hostname))
monty@mysql.com's avatar
monty@mysql.com committed
4260
      host= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4261
    if (!strcmp(lex_user->user.str,user) &&
4262
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4263 4264
      break;
  }
peter@mysql.com's avatar
peter@mysql.com committed
4265
  if (counter == acl_users.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4266
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
4267 4268
    my_error(ER_NONEXISTING_GRANT, MYF(0),
             lex_user->user.str, lex_user->host.str);
4269
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4270 4271
  }

4272
  Item_string *field=new Item_string("",0,&my_charset_latin1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4273 4274 4275 4276 4277 4278
  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);
4279 4280
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
4281
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4282

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4283
  rw_wrlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4284 4285 4286 4287
  VOID(pthread_mutex_lock(&acl_cache->lock));

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

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4292
    want_access= acl_user->access;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4293
    if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
4294
      global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4295
    else if (!(want_access & ~GRANT_ACL))
4296
      global.append(STRING_WITH_LEN("USAGE"));
peter@mysql.com's avatar
peter@mysql.com committed
4297
    else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4298 4299
    {
      bool found=0;
4300
      ulong j,test_access= want_access & ~GRANT_ACL;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4301 4302
      for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
      {
peter@mysql.com's avatar
peter@mysql.com committed
4303
	if (test_access & j)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4304 4305
	{
	  if (found)
4306
	    global.append(STRING_WITH_LEN(", "));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4307 4308 4309 4310 4311
	  found=1;
	  global.append(command_array[counter],command_lengths[counter]);
	}
      }
    }
4312
    global.append (STRING_WITH_LEN(" ON *.* TO '"));
4313 4314
    global.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
4315
    global.append (STRING_WITH_LEN("'@'"));
4316 4317
    global.append(lex_user->host.str,lex_user->host.length,
		  system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4318
    global.append ('\'');
4319
    if (acl_user->salt_len)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4320
    {
4321 4322 4323 4324 4325
      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);
4326
      global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
4327
      global.append(passwd_buff);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4328 4329
      global.append('\'');
    }
4330 4331
    /* "show grants" SSL related stuff */
    if (acl_user->ssl_type == SSL_TYPE_ANY)
4332
      global.append(STRING_WITH_LEN(" REQUIRE SSL"));
4333
    else if (acl_user->ssl_type == SSL_TYPE_X509)
4334
      global.append(STRING_WITH_LEN(" REQUIRE X509"));
4335
    else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
4336
    {
4337
      int ssl_options = 0;
4338
      global.append(STRING_WITH_LEN(" REQUIRE "));
4339 4340
      if (acl_user->x509_issuer)
      {
4341
	ssl_options++;
4342
	global.append(STRING_WITH_LEN("ISSUER \'"));
4343
	global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
4344
	global.append('\'');
4345
      }
4346 4347
      if (acl_user->x509_subject)
      {
4348 4349
	if (ssl_options++)
	  global.append(' ');
4350
	global.append(STRING_WITH_LEN("SUBJECT \'"));
4351 4352
	global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
                      system_charset_info);
4353
	global.append('\'');
tonu@x153.internalnet's avatar
tonu@x153.internalnet committed
4354
      }
4355 4356
      if (acl_user->ssl_cipher)
      {
4357 4358
	if (ssl_options++)
	  global.append(' ');
4359
	global.append(STRING_WITH_LEN("CIPHER '"));
4360 4361
	global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
                      system_charset_info);
4362
	global.append('\'');
4363 4364
      }
    }
4365
    if ((want_access & GRANT_ACL) ||
4366 4367 4368 4369
	(acl_user->user_resource.questions ||
         acl_user->user_resource.updates ||
         acl_user->user_resource.conn_per_hour ||
         acl_user->user_resource.user_conn))
4370
    {
4371
      global.append(STRING_WITH_LEN(" WITH"));
4372
      if (want_access & GRANT_ACL)
4373
	global.append(STRING_WITH_LEN(" GRANT OPTION"));
4374 4375 4376 4377
      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");
4378
      add_user_option(&global, acl_user->user_resource.conn_per_hour,
4379
		      "MAX_CONNECTIONS_PER_HOUR");
4380 4381
      add_user_option(&global, acl_user->user_resource.user_conn,
		      "MAX_USER_CONNECTIONS");
4382
    }
4383
    protocol->prepare_for_resend();
4384
    protocol->store(global.ptr(),global.length(),global.charset());
4385
    if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4386
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4387
      error= -1;
4388
      goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4389 4390 4391 4392 4393 4394
    }
  }

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

    acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
    if (!(user=acl_db->user))
monty@mysql.com's avatar
monty@mysql.com committed
4399
      user= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4400
    if (!(host=acl_db->host.hostname))
monty@mysql.com's avatar
monty@mysql.com committed
4401
      host= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4402 4403

    if (!strcmp(lex_user->user.str,user) &&
4404
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4405 4406
    {
      want_access=acl_db->access;
peter@mysql.com's avatar
peter@mysql.com committed
4407
      if (want_access)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4408
      {
4409
	String db(buff,sizeof(buff),system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4410
	db.length(0);
4411
	db.append(STRING_WITH_LEN("GRANT "));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4412 4413

	if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
4414
	  db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
4415
	else if (!(want_access & ~GRANT_ACL))
4416
	  db.append(STRING_WITH_LEN("USAGE"));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4417 4418 4419
	else
	{
	  int found=0, cnt;
4420
	  ulong j,test_access= want_access & ~GRANT_ACL;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4421 4422 4423 4424 4425
	  for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
	  {
	    if (test_access & j)
	    {
	      if (found)
4426
		db.append(STRING_WITH_LEN(", "));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4427 4428 4429 4430 4431
	      found = 1;
	      db.append(command_array[cnt],command_lengths[cnt]);
	    }
	  }
	}
4432
	db.append (STRING_WITH_LEN(" ON "));
4433
	append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
4434
	db.append (STRING_WITH_LEN(".* TO '"));
4435 4436
	db.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
4437
	db.append (STRING_WITH_LEN("'@'"));
4438 4439
	db.append(lex_user->host.str, lex_user->host.length,
                  system_charset_info);
peter@mysql.com's avatar
peter@mysql.com committed
4440
	db.append ('\'');
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4441
	if (want_access & GRANT_ACL)
4442
	  db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
4443
	protocol->prepare_for_resend();
4444
	protocol->store(db.ptr(),db.length(),db.charset());
4445
	if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4446
	{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4447
	  error= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4448 4449 4450 4451 4452 4453
	  goto end;
	}
      }
    }
  }

4454
  /* Add table & column access */
4455
  for (index=0 ; index < column_priv_hash.records ; index++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4456
  {
monty@mysql.com's avatar
monty@mysql.com committed
4457
    const char *user;
4458 4459
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4460 4461

    if (!(user=grant_table->user))
4462
      user= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4463 4464

    if (!strcmp(lex_user->user.str,user) &&
4465
	!my_strcasecmp(system_charset_info, lex_user->host.str,
4466
                       grant_table->host.hostname))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4467
    {
4468 4469
      ulong table_access= grant_table->privs;
      if ((table_access | grant_table->cols) != 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4470
      {
4471
	String global(buff, sizeof(buff), system_charset_info);
4472 4473
	ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
4474
	global.length(0);
4475
	global.append(STRING_WITH_LEN("GRANT "));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4476

4477
	if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
4478
	  global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4479
	else if (!test_access)
4480
	  global.append(STRING_WITH_LEN("USAGE"));
peter@mysql.com's avatar
peter@mysql.com committed
4481
	else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4482
	{
4483
          /* Add specific column access */
4484
	  int found= 0;
4485
	  ulong j;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4486

4487
	  for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4488
	  {
peter@mysql.com's avatar
peter@mysql.com committed
4489
	    if (test_access & j)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4490 4491
	    {
	      if (found)
4492
		global.append(STRING_WITH_LEN(", "));
4493
	      found= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4494 4495
	      global.append(command_array[counter],command_lengths[counter]);

peter@mysql.com's avatar
peter@mysql.com committed
4496
	      if (grant_table->cols)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4497
	      {
4498
		uint found_col= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4499 4500 4501 4502 4503 4504
		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
4505
		  if (grant_column->rights & j)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4506
		  {
peter@mysql.com's avatar
peter@mysql.com committed
4507
		    if (!found_col)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4508
		    {
4509 4510 4511 4512 4513 4514 4515
		      found_col= 1;
		      /*
			If we have a duplicated table level privilege, we
			must write the access privilege name again.
		      */
		      if (table_access & j)
		      {
4516
			global.append(STRING_WITH_LEN(", "));
4517 4518 4519
			global.append(command_array[counter],
				      command_lengths[counter]);
		      }
4520
		      global.append(STRING_WITH_LEN(" ("));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4521 4522
		    }
		    else
4523
		      global.append(STRING_WITH_LEN(", "));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4524
		    global.append(grant_column->column,
4525 4526
				  grant_column->key_length,
				  system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4527 4528 4529 4530 4531 4532 4533 4534
		  }
		}
		if (found_col)
		  global.append(')');
	      }
	    }
	  }
	}
4535
	global.append(STRING_WITH_LEN(" ON "));
4536 4537 4538 4539 4540
	append_identifier(thd, &global, grant_table->db,
			  strlen(grant_table->db));
	global.append('.');
	append_identifier(thd, &global, grant_table->tname,
			  strlen(grant_table->tname));
4541
	global.append(STRING_WITH_LEN(" TO '"));
4542 4543
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
4544
	global.append(STRING_WITH_LEN("'@'"));
4545 4546
	global.append(lex_user->host.str,lex_user->host.length,
		      system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4547
	global.append('\'');
4548
	if (table_access & GRANT_ACL)
4549
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
4550
	protocol->prepare_for_resend();
4551
	protocol->store(global.ptr(),global.length(),global.charset());
4552
	if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4553
	{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4554
	  error= -1;
4555
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4556 4557 4558 4559
	}
      }
    }
  }
4560

4561
  if (show_routine_grants(thd, lex_user, &proc_priv_hash, 
4562
                          STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
4563 4564 4565 4566 4567 4568
  {
    error= -1;
    goto end;
  }

  if (show_routine_grants(thd, lex_user, &func_priv_hash,
4569
                          STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591
  {
    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++)
4592 4593
  {
    const char *user;
4594
    GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, index);
4595 4596 4597 4598 4599 4600

    if (!(user=grant_proc->user))
      user= "";

    if (!strcmp(lex_user->user.str,user) &&
	!my_strcasecmp(system_charset_info, lex_user->host.str,
4601
                       grant_proc->host.hostname))
4602 4603 4604 4605
    {
      ulong proc_access= grant_proc->privs;
      if (proc_access != 0)
      {
4606
	String global(buff, buffsize, system_charset_info);
4607 4608 4609
	ulong test_access= proc_access & ~GRANT_ACL;

	global.length(0);
4610
	global.append(STRING_WITH_LEN("GRANT "));
4611 4612

	if (!test_access)
4613
 	  global.append(STRING_WITH_LEN("USAGE"));
4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624
	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)
4625
		global.append(STRING_WITH_LEN(", "));
4626 4627 4628 4629 4630
	      found= 1;
	      global.append(command_array[counter],command_lengths[counter]);
	    }
	  }
	}
4631
	global.append(STRING_WITH_LEN(" ON "));
4632 4633
        global.append(type,typelen);
        global.append(' ');
4634 4635 4636 4637 4638
	append_identifier(thd, &global, grant_proc->db,
			  strlen(grant_proc->db));
	global.append('.');
	append_identifier(thd, &global, grant_proc->tname,
			  strlen(grant_proc->tname));
4639
	global.append(STRING_WITH_LEN(" TO '"));
4640 4641
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
4642
	global.append(STRING_WITH_LEN("'@'"));
4643 4644 4645 4646
	global.append(lex_user->host.str,lex_user->host.length,
		      system_charset_info);
	global.append('\'');
	if (proc_access & GRANT_ACL)
4647
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
4648 4649 4650 4651 4652 4653 4654 4655 4656 4657
	protocol->prepare_for_resend();
	protocol->store(global.ptr(),global.length(),global.charset());
	if (protocol->write())
	{
	  error= -1;
	  break;
	}
      }
    }
  }
4658
  return error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4659 4660
}

4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688
/*
  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;
}


4689
void get_mqh(const char *user, const char *host, USER_CONN *uc)
4690 4691
{
  ACL_USER *acl_user;
4692
  if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
4693 4694 4695
    uc->user_resources= acl_user->user_resource;
  else
    bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
4696 4697
}

4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718
/*
  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.
*/

4719
#define GRANT_TABLES 5
4720 4721 4722 4723 4724 4725
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("open_grant_tables");

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

4730
  bzero((char*) tables, GRANT_TABLES*sizeof(*tables));
4731 4732 4733 4734 4735
  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
4736 4737 4738
  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;
4739
  (tables+3)->next_local= (tables+3)->next_global= tables+4;
4740
  tables->lock_type= (tables+1)->lock_type=
4741 4742 4743 4744
    (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";
4745 4746 4747 4748 4749 4750

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
4751
  if (thd->slave_thread && rpl_filter->is_on())
4752
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4753 4754 4755
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
4756
    */
4757 4758
    tables[0].updating=tables[1].updating=tables[2].updating=
      tables[3].updating=tables[4].updating=1;
lars@mysql.com's avatar
lars@mysql.com committed
4759
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
4760
      DBUG_RETURN(1);
4761 4762
    tables[0].updating=tables[1].updating=tables[2].updating=
      tables[3].updating=tables[4].updating=0;;
4763
  }
4764 4765
#endif

4766
  if (simple_open_n_lock_tables(thd, tables))
4767 4768 4769 4770 4771 4772 4773 4774 4775
  {						// 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
4776
			 uint *acl_acl_userdx)
4777 4778 4779 4780 4781 4782 4783 4784 4785
{
  ACL_USER *acl_user= 0;
  uint counter;

  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
4786
      user= "";
4787
    if (!(host=acl_user->host.hostname))
4788
      host= "";
4789 4790 4791 4792 4793 4794 4795
    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
4796
  *acl_acl_userdx= counter;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4797
  return acl_user;
4798 4799
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4800

4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822
/*
  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)
4823
{
4824 4825
  int error;
  DBUG_ENTER("modify_grant_table");
4826

4827 4828 4829 4830 4831 4832 4833 4834
  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);
4835
    if ((error= table->file->ha_update_row(table->record[1], table->record[0])))
4836 4837 4838 4839 4840
      table->file->print_error(error, MYF(0));
  }
  else
  {
    /* delete */
4841
    if ((error=table->file->ha_delete_row(table->record[0])))
4842 4843
      table->file->print_error(error, MYF(0));
  }
4844

4845 4846
  DBUG_RETURN(error);
}
4847 4848


4849 4850 4851 4852 4853 4854
/*
  Handle a privilege table.

  SYNOPSIS
    handle_grant_table()
    tables                      The array with the four open tables.
4855
    table_no                    The number of the table to handle (0..4).
4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872
    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
4873
    4 procs_priv
4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892

  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;
4893
  byte user_key[MAX_KEY_LENGTH];
4894
  uint key_prefix_length;
4895 4896
  DBUG_ENTER("handle_grant_table");

4897
  if (! table_no) // mysql.user table
4898
  {
4899 4900 4901 4902 4903 4904 4905 4906 4907 4908
    /*
      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'",
4909
                       table->s->table_name.str, user_str, host_str));
4910 4911
    host_field->store(host_str, user_from->host.length, system_charset_info);
    user_field->store(user_str, user_from->user.length, system_charset_info);
4912 4913 4914 4915 4916

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

4917
    if ((error= table->file->index_read_idx(table->record[0], 0,
4918
                                            user_key, key_prefix_length,
4919
                                            HA_READ_KEY_EXACT)))
4920
    {
4921
      if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
4922 4923 4924 4925
      {
        table->file->print_error(error, MYF(0));
        result= -1;
      }
4926
    }
4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943
    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)))
4944
    {
4945
      table->file->print_error(error, MYF(0));
4946
      result= -1;
4947 4948 4949 4950 4951
    }
    else
    {
#ifdef EXTRA_DEBUG
      DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
4952
                         table->s->table_name.str, user_str, host_str));
4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000
#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()
5001
    struct_no                   The number of the structure to handle (0..3).
5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016
    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
5017
    3 procs_priv_hash
5018 5019 5020 5021

  RETURN
    > 0         At least one element matched.
    0           OK, but no element matched.
5022
    -1		Wrong arguments to function
5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034
*/

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

5040 5041
  LINT_INIT(acl_user);
  LINT_INIT(acl_db);
5042
  LINT_INIT(grant_name);
5043 5044

  /* Get the number of elements in the in-memory structure. */
5045
  switch (struct_no) {
5046 5047 5048 5049 5050 5051
  case 0:
    elements= acl_users.elements;
    break;
  case 1:
    elements= acl_dbs.elements;
    break;
5052
  case 2:
5053
    elements= column_priv_hash.records;
5054 5055 5056 5057 5058 5059
    break;
  case 3:
    elements= proc_priv_hash.records;
    break;
  default:
    return -1;
5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072
  }

#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.
      Unfortunaltely, the host default differs for the structures.
    */
5073
    switch (struct_no) {
5074 5075 5076 5077 5078 5079 5080 5081 5082 5083
    case 0:
      acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
      user= acl_user->user;
      if (!(host= acl_user->host.hostname))
        host= "%";
      break;

    case 1:
      acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
      user= acl_db->user;
5084 5085
      if (!(host= acl_db->host.hostname))
        host= "%";
5086 5087
      break;

5088 5089 5090
    case 2:
      grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx);
      user= grant_name->user;
5091 5092
      if (!(host= grant_name->host.hostname))
        host= "%";
5093 5094 5095 5096 5097
      break;

    case 3:
      grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx);
      user= grant_name->user;
5098 5099
      if (!(host= grant_name->host.hostname))
        host= "%";
5100
      break;
5101 5102
    }
    if (! user)
5103
      user= "";
5104 5105 5106 5107 5108 5109
#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))
5110
      continue;
5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124

    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;

5125 5126 5127 5128 5129 5130 5131
      case 2:
        hash_delete(&column_priv_hash, (byte*) grant_name);
	break;

      case 3:
        hash_delete(&proc_priv_hash, (byte*) grant_name);
	break;
5132 5133 5134
      }
      elements--;
      idx--;
5135
    }
5136 5137
    else if ( user_to )
    {
5138
      switch ( struct_no ) {
5139 5140 5141 5142
      case 0:
        acl_user->user= strdup_root(&mem, user_to->user.str);
        acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
        break;
5143

5144 5145 5146 5147 5148
      case 1:
        acl_db->user= strdup_root(&mem, user_to->user.str);
        acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
        break;

5149 5150 5151
      case 2:
      case 3:
        grant_name->user= strdup_root(&mem, user_to->user.str);
5152 5153
        update_hostname(&grant_name->host,
                        strdup_root(&mem, user_to->host.str));
5154
	break;
5155 5156 5157
      }
    }
    else
5158
    {
5159 5160 5161 5162 5163 5164 5165
      /* 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
5166

5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210
  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. */
5211 5212
    if ((handle_grant_struct(0, drop, user_from, user_to) && ! result) ||
        found)
5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239
    {
      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;
    }
  }

5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258
  /* 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;
    }
  }

5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272
  /* 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;
5273
    }
5274 5275 5276

    /* Handle columns table. */
    if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
5277
    {
5278
      /* Handle of table failed, don't touch the in-memory array. */
5279 5280
      result= -1;
    }
5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292
    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);
}

5293

5294 5295 5296 5297 5298 5299
static void append_user(String *str, LEX_USER *user)
{
  if (str->length())
    str->append(',');
  str->append('\'');
  str->append(user->user.str);
5300
  str->append(STRING_WITH_LEN("'@'"));
5301 5302 5303
  str->append(user->host.str);
  str->append('\'');
}
5304

5305

5306 5307 5308 5309 5310 5311 5312
/*
  Create a list of users.

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

5314 5315 5316 5317 5318 5319 5320 5321
  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_create_user(THD *thd, List <LEX_USER> &list)
{
  int result;
5322
  String wrong_users;
5323 5324 5325
  ulong sql_mode;
  LEX_USER *user_name;
  List_iterator <LEX_USER> user_list(list);
5326
  TABLE_LIST tables[GRANT_TABLES];
5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341
  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));

  while ((user_name= user_list++))
  {
    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
5342
    if (handle_grant_data(tables, 0, user_name, NULL))
5343
    {
5344
      append_user(&wrong_users, user_name);
5345
      result= TRUE;
5346
      continue;
5347
    }
5348

5349
    sql_mode= thd->variables.sql_mode;
serg@serg.mylan's avatar
serg@serg.mylan committed
5350
    if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
5351
    {
5352
      append_user(&wrong_users, user_name);
5353 5354 5355 5356 5357 5358 5359 5360
      result= TRUE;
    }
  }

  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
5361
    my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381
  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;
5382
  String wrong_users;
5383 5384
  LEX_USER *user_name;
  List_iterator <LEX_USER> user_list(list);
5385
  TABLE_LIST tables[GRANT_TABLES];
5386 5387
  DBUG_ENTER("mysql_drop_user");

5388
  /* DROP USER may be skipped on replication client. */
5389 5390 5391 5392 5393 5394 5395 5396
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

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

  while ((user_name= user_list++))
  {
5397
    if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
5398
    {
5399
      append_user(&wrong_users, user_name);
5400
      result= TRUE;
5401
    }
5402
  }
5403

5404 5405 5406
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

5407 5408 5409 5410
  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
5411
    my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430
  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)
{
5431
  int result;
5432
  String wrong_users;
5433 5434 5435
  LEX_USER *user_from;
  LEX_USER *user_to;
  List_iterator <LEX_USER> user_list(list);
5436
  TABLE_LIST tables[GRANT_TABLES];
5437 5438
  DBUG_ENTER("mysql_rename_user");

5439
  /* RENAME USER may be skipped on replication client. */
5440 5441 5442 5443 5444 5445 5446 5447 5448
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

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

  while ((user_from= user_list++))
  {
    user_to= user_list++;
5449
    DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
5450 5451 5452 5453 5454

    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
5455 5456
    if (handle_grant_data(tables, 0, user_to, NULL) ||
        handle_grant_data(tables, 0, user_from, user_to) <= 0)
5457
    {
5458
      append_user(&wrong_users, user_from);
5459 5460
      result= TRUE;
    }
5461
  }
5462

5463 5464 5465
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

5466 5467 5468 5469
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
5470
    my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
5471 5472 5473
  DBUG_RETURN(result);
}

5474

5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488
/*
  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.
*/

5489
bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
5490
{
5491
  uint counter, revoked, is_proc;
5492
  int result;
5493
  ACL_DB *acl_db;
5494
  TABLE_LIST tables[GRANT_TABLES];
5495 5496 5497
  DBUG_ENTER("mysql_revoke_all");

  if ((result= open_grant_tables(thd, tables)))
5498
    DBUG_RETURN(result != 1);
5499 5500 5501 5502 5503 5504 5505 5506

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

  LEX_USER *lex_user;
  List_iterator <LEX_USER> user_list(list);
  while ((lex_user=user_list++))
  {
5507
    if (!check_acl_user(lex_user, &counter))
5508
    {
5509 5510
      sql_print_error("REVOKE ALL PRIVILEGES, GRANT: User '%s'@'%s' does not "
                      "exists", lex_user->user.str, lex_user->host.str);
5511 5512 5513
      result= -1;
      continue;
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
5514

5515
    if (replace_user_table(thd, tables[0].table,
5516
			   *lex_user, ~(ulong)0, 1, 0, 0))
5517 5518 5519 5520 5521 5522
    {
      result= -1;
      continue;
    }

    /* Remove db access privileges */
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5523 5524 5525 5526 5527
    /*
      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
5528
    do
5529
    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5530
      for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
5531
      {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5532
	const char *user,*host;
5533

dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5534 5535 5536 5537 5538
	acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
	if (!(user=acl_db->user))
	  user= "";
	if (!(host=acl_db->host.hostname))
	  host= "";
5539

dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5540 5541 5542
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
5543
	  if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, ~(ulong)0, 1))
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5544
	  {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5545 5546 5547 5548 5549
	    /*
	      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
5550 5551
	    continue;
	  }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5552
	  result= -1; // Something went wrong
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5553
	}
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5554
	counter++;
5555
      }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5556
    } while (revoked);
5557 5558

    /* Remove column access */
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5559
    do
5560
    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5561
      for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
5562
      {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5563 5564 5565 5566 5567
	const char *user,*host;
	GRANT_TABLE *grant_table= (GRANT_TABLE*)hash_element(&column_priv_hash,
							     counter);
	if (!(user=grant_table->user))
	  user= "";
5568
	if (!(host=grant_table->host.hostname))
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5569
	  host= "";
5570

dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5571 5572
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
5573
	{
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5574 5575 5576
	  if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
				  grant_table->db,
				  grant_table->tname,
5577
				  ~(ulong)0, 0, 1))
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5578
	  {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5579
	    result= -1;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5580
	  }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5581
	  else
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5582
	  {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5583
	    if (!grant_table->cols)
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5584
	    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5585 5586
	      revoked= 1;
	      continue;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5587
	    }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5588 5589
	    List<LEX_COLUMN> columns;
	    if (!replace_column_table(grant_table,tables[3].table, *lex_user,
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5590 5591 5592
				      columns,
				      grant_table->db,
				      grant_table->tname,
5593
				      ~(ulong)0, 1))
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5594
	    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5595
	      revoked= 1;
5596
	      continue;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5597
	    }
5598
	    result= -1;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5599
	  }
5600
	}
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5601
	counter++;
5602
      }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5603
    } while (revoked);
5604 5605

    /* Remove procedure access */
5606 5607 5608
    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 ; )
5609 5610
      {
	const char *user,*host;
5611
	GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
5612 5613
	if (!(user=grant_proc->user))
	  user= "";
5614
	if (!(host=grant_proc->host.hostname))
5615 5616 5617 5618 5619
	  host= "";

	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
5620
	  if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
5621 5622
				  grant_proc->db,
				  grant_proc->tname,
5623
                                  is_proc,
5624
				  ~(ulong)0, 1))
5625 5626 5627 5628 5629 5630 5631 5632 5633
	  {
	    revoked= 1;
	    continue;
	  }
	  result= -1;	// Something went wrong
	}
	counter++;
      }
    } while (revoked);
5634
  }
5635

5636 5637 5638
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
5639

5640
  if (result)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5641
    my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
5642

5643 5644
  DBUG_RETURN(result);
}
5645

5646

5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660
/*
  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.
*/

5661 5662
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
                          bool is_proc)
5663 5664 5665 5666
{
  uint counter, revoked;
  int result;
  TABLE_LIST tables[GRANT_TABLES];
5667
  HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
5668 5669 5670 5671 5672 5673 5674 5675 5676
  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 */
5677 5678
  do
  {
5679
    for (counter= 0, revoked= 0 ; counter < hash->records ; )
5680
    {
5681
      GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
5682 5683 5684 5685 5686 5687
      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);
5688 5689
	lex_user.host.str= grant_proc->host.hostname;
	lex_user.host.length= strlen(grant_proc->host.hostname);
5690 5691
	if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
				   grant_proc->db, grant_proc->tname,
5692
                                   is_proc, ~(ulong)0, 1))
5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727
	{
	  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.
*/

5728 5729
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
                         bool is_proc)
5730
{
5731
  Security_context *sctx= thd->security_ctx;
5732 5733 5734 5735
  LEX_USER *combo;
  TABLE_LIST tables[1];
  List<LEX_USER> user_list;
  bool result;
5736
  DBUG_ENTER("sp_grant_privileges");
5737 5738 5739 5740

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

5741
  combo->user.str= sctx->user;
5742
  
5743 5744 5745
  if (!find_acl_user(combo->host.str=(char*)sctx->host_or_ip, combo->user.str,
                     FALSE) &&
      !find_acl_user(combo->host.str=(char*)sctx->host, combo->user.str,
monty@mishka.local's avatar
monty@mishka.local committed
5746
                     FALSE) &&
5747
      !find_acl_user(combo->host.str=(char*)sctx->ip, combo->user.str,
monty@mishka.local's avatar
monty@mishka.local committed
5748 5749
                     FALSE) &&
      !find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE))
5750 5751 5752 5753 5754 5755
    DBUG_RETURN(TRUE);

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

  tables->db= (char*)sp_db;
5756
  tables->table_name= tables->alias= (char*)sp_name;
5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768
  
  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);
  combo->password.str= (char*)"";
  combo->password.length= 0;

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

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

5771
  result= mysql_routine_grant(thd, tables, is_proc, user_list,
5772 5773 5774 5775 5776
  				DEFAULT_CREATE_PROC_ACLS, 0, 1);
  DBUG_RETURN(result);
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
5777
/*****************************************************************************
5778
  Instantiate used templates
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5779 5780
*****************************************************************************/

5781
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5782 5783 5784 5785 5786
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
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 5827 5828 5829 5830 5831 5832 5833

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

5834 5835 5836 5837 5838 5839 5840 5841

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;
5842
  restore_record(table, s->default_values);
5843 5844 5845 5846 5847 5848 5849 5850 5851
  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);
5852
  table->file->ha_write_row(table->record[0]);
5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863
}


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;
5864
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
5865
  char *curr_host= thd->security_ctx->priv_host_name();
5866
  DBUG_ENTER("fill_schema_user_privileges");
5867

5868 5869 5870 5871 5872 5873 5874 5875
  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= "";
5876 5877

    if (no_global_access &&
5878
        (strcmp(thd->security_ctx->priv_user, user) ||
5879 5880 5881
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;
      
5882 5883 5884 5885 5886 5887
    want_access= acl_user->access;
    if (!(want_access & GRANT_ACL))
      is_grantable= "NO";

    strxmov(buff,"'",user,"'@'",host,"'",NullS);
    if (!(want_access & ~GRANT_ACL))
5888 5889
      update_schema_privilege(table, buff, 0, 0, 0, 0,
                              STRING_WITH_LEN("USAGE"), is_grantable);
5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903
    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);
      }
    }
  }
  DBUG_RETURN(0);
5904 5905 5906
#else
  return(0);
#endif
5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917
}


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;
5918
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
5919
  char *curr_host= thd->security_ctx->priv_host_name();
5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931
  DBUG_ENTER("fill_schema_schema_privileges");

  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 5960
      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);
      }
    }
  }
  DBUG_RETURN(0);
5961 5962 5963
#else
  return (0);
#endif
5964 5965 5966 5967 5968 5969 5970 5971 5972
}


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

  for (index=0 ; index < column_priv_hash.records ; index++)
  {
    const char *user, *is_grantable= "YES";
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
5984 5985

    if (no_global_access &&
5986
        (strcmp(thd->security_ctx->priv_user, user) ||
5987 5988 5989 5990
         my_strcasecmp(system_charset_info, curr_host,
                       grant_table->host.hostname)))
      continue;

5991
    ulong table_access= grant_table->privs;
5992
    if (table_access)
5993 5994
    {
      ulong test_access= table_access & ~GRANT_ACL;
5995 5996 5997 5998
      /*
        We should skip 'usage' privilege on table if
        we have any privileges on column(s) of this table
      */
5999 6000
      if (!test_access && grant_table->cols)
        continue;
6001 6002 6003
      if (!(table_access & GRANT_ACL))
        is_grantable= "NO";

6004
      strxmov(buff,"'",user,"'@'",grant_table->host.hostname,"'",NullS);
6005 6006
      if (!test_access)
        update_schema_privilege(table, buff, grant_table->db, grant_table->tname,
6007
                                0, 0, STRING_WITH_LEN("USAGE"), is_grantable);
6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022
      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);
        }
      }
    }
  }
  DBUG_RETURN(0);
6023 6024 6025
#else
  return (0);
#endif
6026 6027 6028 6029 6030 6031 6032 6033 6034
}


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;
6035
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
6036
  char *curr_host= thd->security_ctx->priv_host_name();
6037 6038 6039 6040 6041 6042 6043 6044 6045
  DBUG_ENTER("fill_schema_table_privileges");

  for (index=0 ; index < column_priv_hash.records ; index++)
  {
    const char *user, *is_grantable= "YES";
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
6046 6047

    if (no_global_access &&
6048
        (strcmp(thd->security_ctx->priv_user, user) ||
6049 6050 6051 6052
         my_strcasecmp(system_charset_info, curr_host,
                       grant_table->host.hostname)))
      continue;

6053 6054 6055
    ulong table_access= grant_table->cols;
    if (table_access != 0)
    {
6056
      if (!(grant_table->privs & GRANT_ACL))
6057 6058
        is_grantable= "NO";

6059
      ulong test_access= table_access & ~GRANT_ACL;
6060
      strxmov(buff,"'",user,"'@'",grant_table->host.hostname,"'",NullS);
6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090
      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);
            }
          }
        }
      }
    }
  }
  DBUG_RETURN(0);
6091 6092 6093
#else
  return (0);
#endif
6094 6095 6096
}


bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6097 6098 6099 6100 6101
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
  fill effective privileges for table

  SYNOPSIS
6102 6103
    fill_effective_table_privileges()
    thd     thread handler
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6104 6105 6106 6107 6108 6109 6110 6111
    grant   grants table descriptor
    db      db name
    table   table name
*/

void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
                                     const char *db, const char *table)
{
6112
  Security_context *sctx= thd->security_ctx;
6113 6114 6115 6116 6117
  DBUG_ENTER("fill_effective_table_privileges");
  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
                       sctx->priv_host, (sctx->ip ? sctx->ip : "(NULL)"),
                       (sctx->priv_user ? sctx->priv_user : "(NULL)"),
                       db, table));
6118 6119 6120
  /* --skip-grants */
  if (!initialized)
  {
6121
    DBUG_PRINT("info", ("skip grants"));
6122
    grant->privilege= ~NO_ACCESS;             // everything is allowed
6123 6124
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;
6125 6126
  }

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

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

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

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6145 6146 6147
  /* table privileges */
  if (grant->version != grant_version)
  {
6148
    rw_rdlock(&LOCK_grant);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6149
    grant->grant_table=
6150 6151
      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
6152 6153
			table, 0);              /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
6154
    rw_unlock(&LOCK_grant);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
6155 6156 6157 6158 6159
  }
  if (grant->grant_table != 0)
  {
    grant->privilege|= grant->grant_table->privs;
  }
6160 6161
  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
6162
}
6163 6164 6165 6166 6167 6168 6169

#else /* NO_EMBEDDED_ACCESS_CHECKS */

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

acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
6170 6171
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
                             bool is_proc)
6172 6173 6174 6175
{
  return FALSE;
}

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