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

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

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

unknown's avatar
unknown committed
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   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:
  mysql/user	 ; super user who are allowed to do almoust anything
  mysql/host	 ; host priviliges. This is used if host is empty in mysql/db.
  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 "sql_acl.h"
#include "hash_filo.h"
31 32 33
#ifdef HAVE_REPLICATION
#include "sql_repl.h" //for tables_ok()
#endif
unknown's avatar
unknown committed
34 35 36
#include <m_ctype.h>
#include <stdarg.h>

37

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

unknown's avatar
unknown committed
46

unknown's avatar
unknown committed
47 48 49 50 51 52 53 54 55 56 57 58 59
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;
}

#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+17)

static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
60
static HASH acl_check_hosts, column_priv_hash;
unknown's avatar
unknown committed
61 62 63
static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
static uint grant_version=0;
unknown's avatar
unknown committed
64
static uint priv_version=0; /* Version of priv tables. incremented by acl_init */
unknown's avatar
unknown committed
65
static ulong get_access(TABLE *form,uint fieldnr);
unknown's avatar
unknown committed
66 67 68 69 70 71 72 73 74 75
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
static ACL_USER *find_acl_user(const char *host, const char *user);
static bool update_user_table(THD *thd, const char *host, const char *user,
			      const char *new_password);
static void update_hostname(acl_host_and_ip *host, const char *hostname);
static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
			     const char *ip);

76 77 78 79 80
/*
  Read grant privileges from the privilege tables in the 'mysql' database.

  SYNOPSIS
    acl_init()
unknown's avatar
unknown committed
81
    thd				Thread handler
82 83 84 85 86 87 88 89
    dont_read_acl_tables	Set to 1 if run with --skip-grant

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


unknown's avatar
unknown committed
90
my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
unknown's avatar
unknown committed
91
{
unknown's avatar
unknown committed
92
  THD  *thd;
unknown's avatar
unknown committed
93 94 95
  TABLE_LIST tables[3];
  TABLE *table;
  READ_RECORD read_record_info;
96 97
  MYSQL_LOCK *lock;
  my_bool return_val=1;
unknown's avatar
unknown committed
98 99 100 101 102
  DBUG_ENTER("acl_init");

  if (!acl_cache)
    acl_cache=new hash_filo(ACL_CACHE_SIZE,0,0,
			    (hash_get_key) acl_entry_get_key,
unknown's avatar
unknown committed
103
			    (hash_free_key) free, system_charset_info);
unknown's avatar
unknown committed
104
  if (dont_read_acl_tables)
105
  {
unknown's avatar
unknown committed
106
    DBUG_RETURN(0); /* purecov: tested */
unknown's avatar
unknown committed
107 108
  }

109
  priv_version++; /* Privileges updated */
unknown's avatar
unknown committed
110

111 112 113
  /*
    To be able to run this from boot, we allocate a temporary THD
  */
unknown's avatar
unknown committed
114 115
  if (!(thd=new THD))
    DBUG_RETURN(1); /* purecov: inspected */
116
  thd->store_globals();
117 118
  /* Use passwords according to command line option */
  use_old_passwords= opt_old_passwords;
119

unknown's avatar
unknown committed
120
  acl_cache->clear(1);				// Clear locked hostname cache
unknown's avatar
unknown committed
121 122
  thd->db= my_strdup("mysql",MYF(0));
  thd->db_length=5;				// Safety
unknown's avatar
unknown committed
123
  bzero((char*) &tables,sizeof(tables));
124 125 126
  tables[0].alias=tables[0].real_name=(char*) "host";
  tables[1].alias=tables[1].real_name=(char*) "user";
  tables[2].alias=tables[2].real_name=(char*) "db";
unknown's avatar
unknown committed
127 128 129 130 131 132
  tables[0].next=tables+1;
  tables[1].next=tables+2;
  tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
  tables[0].db=tables[1].db=tables[2].db=thd->db;

  if (open_tables(thd,tables))
unknown's avatar
unknown committed
133 134 135
  {
    sql_print_error("Fatal error: Can't open privilege tables: %s",
		    thd->net.last_error);
136
    goto end;
unknown's avatar
unknown committed
137
  }
unknown's avatar
unknown committed
138 139 140 141
  TABLE *ptr[3];				// Lock tables for quick update
  ptr[0]= tables[0].table;
  ptr[1]= tables[1].table;
  ptr[2]= tables[2].table;
142
  if (!(lock=mysql_lock_tables(thd,ptr,3)))
unknown's avatar
unknown committed
143 144 145
  {
    sql_print_error("Fatal error: Can't lock privilege tables: %s",
		    thd->net.last_error);
146
    goto end;
unknown's avatar
unknown committed
147
  }
148
  init_sql_alloc(&mem,1024,0);
unknown's avatar
unknown committed
149
  init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0);
150
  VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
unknown's avatar
unknown committed
151 152 153
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_HOST host;
154 155
    update_hostname(&host.host,get_field(&mem, table->field[0]));
    host.db=	 get_field(&mem, table->field[1]);
unknown's avatar
unknown committed
156 157 158
    host.access= get_access(table,2);
    host.access= fix_rights_for_db(host.access);
    host.sort=   get_sort(2,host.host.hostname,host.db);
unknown's avatar
unknown committed
159 160 161 162
#ifndef TO_BE_REMOVED
    if (table->fields ==  8)
    {						// Without grant
      if (host.access & CREATE_ACL)
163
	host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
unknown's avatar
unknown committed
164 165 166 167 168 169 170 171 172 173
    }
#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);
174
  VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
unknown's avatar
unknown committed
175 176 177 178 179 180 181 182
  if (table->field[2]->field_length == 8 &&
      protocol_version == PROTOCOL_VERSION)
  {
    sql_print_error(
	    "Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */
    protocol_version=9; /* purecov: tested */
  }

183 184 185 186 187 188 189 190
  DBUG_PRINT("info",("user table fields: %d, password length: %d",
		     table->fields, table->field[2]->field_length));
  if (table->field[2]->field_length < 45 && !use_old_passwords)
  {
    sql_print_error("mysql.user table is not updated to new password format;  Disabling new password usage until mysql_fix_privilege_tables is run");
    use_old_passwords= 1;
  }

unknown's avatar
unknown committed
191 192 193 194 195
  allow_all_hosts=0;
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_USER user;
    uint length=0;
196 197 198
    update_hostname(&user.host,get_field(&mem, table->field[0]));
    user.user=get_field(&mem, table->field[1]);
    user.password=get_field(&mem, table->field[2]);
unknown's avatar
unknown committed
199
    if (user.password && (length=(uint) strlen(user.password)) == 8 &&
unknown's avatar
unknown committed
200 201 202
	protocol_version == PROTOCOL_VERSION)
    {
      sql_print_error(
203
		      "Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)",
unknown's avatar
unknown committed
204 205
		      user.user ? user.user : ""); /* purecov: tested */
    }
206
    else  /* non empty and not short passwords */
unknown's avatar
unknown committed
207
    {
208 209 210 211 212 213 214 215 216 217
      user.pversion=get_password_version(user.password);
      /* Only passwords of specific lengths depending on version are allowed */
      if ( (!user.pversion && length % 8) ||  (user.pversion && length!=45 ))
      {
        sql_print_error(
	                "Found invalid password for user: '%s@%s'; Ignoring user",
		        user.user ? user.user : "",
		        user.host.hostname ? user.host.hostname : ""); /* purecov: tested */
        continue;					/* purecov: tested */
      }
unknown's avatar
unknown committed
218
    }
unknown's avatar
unknown committed
219
    get_salt_from_password(user.salt,user.password);
220
    user.access=get_access(table,3) & GLOBAL_ACLS;
unknown's avatar
unknown committed
221
    user.sort=get_sort(2,user.host.hostname,user.user);
222 223
    user.hostname_length= (user.host.hostname ?
			   (uint) strlen(user.host.hostname) : 0);
unknown's avatar
unknown committed
224
    if (table->fields >= 31)     /* Starting from 4.0.2 we have more fields */
unknown's avatar
unknown committed
225
    {
226
      char *ssl_type=get_field(&mem, table->field[24]);
unknown's avatar
unknown committed
227 228 229 230 231 232 233 234 235
      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;

236 237 238
      user.ssl_cipher=   get_field(&mem, table->field[25]);
      user.x509_issuer=  get_field(&mem, table->field[26]);
      user.x509_subject= get_field(&mem, table->field[27]);
239

240
      char *ptr = get_field(&mem, table->field[28]);
241
      user.user_resource.questions=atoi(ptr);
242
      ptr = get_field(&mem, table->field[29]);
243
      user.user_resource.updates=atoi(ptr);
244
      ptr = get_field(&mem, table->field[30]);
245
      user.user_resource.connections=atoi(ptr);
246 247
      if (user.user_resource.questions || user.user_resource.updates ||
	  user.user_resource.connections)
unknown's avatar
unknown committed
248
	mqh_used=1;
unknown's avatar
unknown committed
249
    }
unknown's avatar
unknown committed
250
    else
unknown's avatar
unknown committed
251 252
    {
      user.ssl_type=SSL_TYPE_NONE;
253
      bzero(&(user.user_resource),sizeof(user.user_resource));
unknown's avatar
unknown committed
254
#ifndef TO_BE_REMOVED
unknown's avatar
unknown committed
255 256 257 258 259 260
      if (table->fields <= 13)
      {						// Without grant
	if (user.access & CREATE_ACL)
	  user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
      }
      /* Convert old privileges */
unknown's avatar
unknown committed
261
      user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
unknown's avatar
unknown committed
262 263 264 265
      if (user.access & FILE_ACL)
	user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
      if (user.access & PROCESS_ACL)
	user.access|= SUPER_ACL | EXECUTE_ACL;
unknown's avatar
unknown committed
266
#endif
unknown's avatar
unknown committed
267
    }
unknown's avatar
unknown committed
268 269 270 271 272 273 274 275 276
    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
  }
  qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
	sizeof(ACL_USER),(qsort_cmp) acl_compare);
  end_read_record(&read_record_info);
  freeze_size(&acl_users);
unknown's avatar
unknown committed
277

unknown's avatar
unknown committed
278
  init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0);
279
  VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100));
unknown's avatar
unknown committed
280 281 282
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_DB db;
283 284
    update_hostname(&db.host,get_field(&mem, table->field[0]));
    db.db=get_field(&mem, table->field[1]);
285 286 287
    if (!db.db)
    {
      sql_print_error("Found an entry in the 'db' table with empty database name; Skipped");
288
      continue;
289
    }
290
    db.user=get_field(&mem, table->field[2]);
unknown's avatar
unknown committed
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
    db.access=get_access(table,3);
    db.access=fix_rights_for_db(db.access);
    db.sort=get_sort(3,db.host.hostname,db.db,db.user);
#ifndef TO_BE_REMOVED
    if (table->fields <=  9)
    {						// 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();

  mysql_unlock_tables(thd, lock);
310
  initialized=1;
unknown's avatar
unknown committed
311
  thd->version--;				// Force close to free memory
312 313 314
  return_val=0;

end:
unknown's avatar
unknown committed
315 316
  close_thread_tables(thd);
  delete thd;
317 318
  if (org_thd)
    org_thd->store_globals();			/* purecov: inspected */
unknown's avatar
unknown committed
319 320 321 322 323
  else
  {
    /* Remember that we don't have a THD */
    my_pthread_setspecific_ptr(THR_THD,  0);
  }
324
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
325 326 327 328 329
}


void acl_free(bool end)
{
330
  free_root(&mem,MYF(0));
unknown's avatar
unknown committed
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
  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;
  }
}

	/* Reload acl list if possible */

347
void acl_reload(THD *thd)
unknown's avatar
unknown committed
348 349 350 351 352 353
{
  DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs;
  MEM_ROOT old_mem;
  bool old_initialized;
  DBUG_ENTER("acl_reload");

354
  if (thd && thd->locked_tables)
unknown's avatar
unknown committed
355
  {					// Can't have locked tables here
356 357 358
    thd->lock=thd->locked_tables;
    thd->locked_tables=0;
    close_thread_tables(thd);
unknown's avatar
unknown committed
359 360 361 362 363 364 365 366 367 368 369
  }
  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);

unknown's avatar
unknown committed
370
  if (acl_init(thd, 0))
unknown's avatar
unknown committed
371
  {					// Error. Revert to old list
372
    DBUG_PRINT("error",("Reverting to old privileges"));
373
    acl_free();				/* purecov: inspected */
unknown's avatar
unknown committed
374 375 376 377 378 379 380 381
    acl_hosts=old_acl_hosts;
    acl_users=old_acl_users;
    acl_dbs=old_acl_dbs;
    mem=old_mem;
    init_check_host();
  }
  else
  {
382
    free_root(&old_mem,MYF(0));
unknown's avatar
unknown committed
383 384 385 386 387 388 389 390 391 392
    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));
  DBUG_VOID_RETURN;
}


unknown's avatar
unknown committed
393 394 395 396 397
/*
  Get all access bits from table after fieldnr
  We know that the access privileges ends when there is no more fields
  or the field is not an enum with two elements.
*/
unknown's avatar
unknown committed
398

unknown's avatar
unknown committed
399
static ulong get_access(TABLE *form, uint fieldnr)
unknown's avatar
unknown committed
400
{
unknown's avatar
unknown committed
401
  ulong access_bits=0,bit;
unknown's avatar
unknown committed
402
  char buff[2];
unknown's avatar
unknown committed
403
  String res(buff,sizeof(buff),&my_charset_latin1);
unknown's avatar
unknown committed
404 405
  Field **pos;

unknown's avatar
unknown committed
406 407 408 409
  for (pos=form->field+fieldnr, bit=1;
       *pos && (*pos)->real_type() == FIELD_TYPE_ENUM &&
	 ((Field_enum*) (*pos))->typelib->count == 2 ;
       pos++ , bit<<=1)
unknown's avatar
unknown committed
410 411
  {
    (*pos)->val_str(&res,&res);
unknown's avatar
unknown committed
412
    if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
unknown's avatar
unknown committed
413
      access_bits|= bit;
unknown's avatar
unknown committed
414 415 416 417 418 419
  }
  return access_bits;
}


/*
unknown's avatar
unknown committed
420 421 422 423 424
  Return a number which, if sorted 'desc', puts strings in this order:
    no wildcards
    wildcards
    empty string
*/
unknown's avatar
unknown committed
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462

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

  while (count--)
  {
    char *str=va_arg(args,char*);
    uint chars=0,wild=0;

    if (str)
    {
      for (; *str ; str++)
      {
	if (*str == wild_many || *str == wild_one || *str == wild_prefix)
	  wild++;
	else
	  chars++;
      }
    }
    sort= (sort << 8) + (wild ? 1 : chars ? 2 : 0);
  }
  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;
}

463

464
/*
465
  Prepare crypted scramble to be sent to the client
466 467
*/

unknown's avatar
unknown committed
468
void prepare_scramble(THD *thd, ACL_USER *acl_user,char* prepared_scramble)
469 470
{
  /* Binary password format to be used for generation*/
unknown's avatar
unknown committed
471
  char bin_password[SCRAMBLE41_LENGTH];
472
  /* Generate new long scramble for the thread */
unknown's avatar
unknown committed
473 474
  create_random_string(SCRAMBLE41_LENGTH,&thd->rand,thd->scramble);
  thd->scramble[SCRAMBLE41_LENGTH]=0;
475
  /* Get binary form, First 4 bytes of prepared scramble is salt */
476 477
  get_hash_and_password(acl_user->salt,acl_user->pversion,prepared_scramble,
			(unsigned char*) bin_password);
478 479 480
  /* Store "*" as identifier for old passwords */
  if (!acl_user->pversion)
    prepared_scramble[0]='*';
481
  /* Finally encrypt password to get prepared scramble */
482 483
  password_crypt(thd->scramble, prepared_scramble+4, bin_password,
		 SCRAMBLE41_LENGTH);
484 485 486
}


unknown's avatar
unknown committed
487 488 489
/*
  Get master privilges for user (priviliges for all tables).
  Required before connecting to MySQL
unknown's avatar
unknown committed
490

491
  As we have 2 stage handshake now we cache user not to lookup
unknown's avatar
unknown committed
492
  it second time. At the second stage we do not lookup user in case
unknown's avatar
unknown committed
493
  we already know it;
unknown's avatar
unknown committed
494

unknown's avatar
unknown committed
495 496
*/

unknown's avatar
unknown committed
497 498
ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
		  const char *password,const char *message,char **priv_user,
499
		  bool old_ver, USER_RESOURCES  *mqh, char *prepared_scramble,
500
                  uint *cur_priv_version, ACL_USER **cached_user)
unknown's avatar
unknown committed
501
{
unknown's avatar
unknown committed
502
  ulong user_access=NO_ACCESS;
503 504
  *priv_user= (char*) user;
  bool password_correct= 0;
505
  int stage= (*cached_user != NULL); /* NULL passed as first stage */
506
  ACL_USER *acl_user= NULL;
507
  DBUG_ENTER("acl_getroot");
unknown's avatar
unknown committed
508

509
  bzero(mqh,sizeof(USER_RESOURCES));
unknown's avatar
unknown committed
510
  if (!initialized)
511 512 513 514
  {
    // If no data allow anything
    DBUG_RETURN((ulong) ~NO_ACCESS);		/* purecov: tested */
  }
unknown's avatar
unknown committed
515
  VOID(pthread_mutex_lock(&acl_cache->lock));
unknown's avatar
unknown committed
516

unknown's avatar
unknown committed
517 518 519
  /*
    Get possible access from user_list. This is or'ed to others not
    fully specified
unknown's avatar
unknown committed
520

unknown's avatar
unknown committed
521
    If we have cached user use it, in other case look it up.
unknown's avatar
unknown committed
522
  */
unknown's avatar
unknown committed
523

524 525
  if (stage && (*cur_priv_version == priv_version))
    acl_user= *cached_user;
unknown's avatar
unknown committed
526
  else
527
  {
unknown's avatar
unknown committed
528
    for (uint i=0 ; i < acl_users.elements ; i++)
unknown's avatar
unknown committed
529
    {
unknown's avatar
unknown committed
530 531
      ACL_USER *acl_user_search=dynamic_element(&acl_users,i,ACL_USER*);
      if (!acl_user_search->user || !strcmp(user,acl_user_search->user))
unknown's avatar
unknown committed
532
      {
unknown's avatar
unknown committed
533 534
        if (compare_hostname(&acl_user_search->host,host,ip))
        {
unknown's avatar
unknown committed
535
          /* Found mathing user */
536
          acl_user= acl_user_search;
unknown's avatar
unknown committed
537
          /* Store it as a cache */
538 539
          *cached_user= acl_user;
          *cur_priv_version= priv_version;
unknown's avatar
unknown committed
540
          break;
unknown's avatar
unknown committed
541
        }
unknown's avatar
unknown committed
542
      }
unknown's avatar
unknown committed
543
    }
544
  }
unknown's avatar
unknown committed
545

unknown's avatar
unknown committed
546
  /* Now we have acl_user found and may start our checks */
unknown's avatar
unknown committed
547 548 549 550

  if (acl_user)
  {
    /* Password should present for both or absend for both */
551 552 553 554 555 556 557
    if (!acl_user->password && !*password)
      password_correct=1;
    else if (!acl_user->password || !*password)
    {
      *cached_user= 0;				// Impossible to connect
    }
    else
unknown's avatar
unknown committed
558
    {
559 560
      /* New version password is checked differently */
      if (acl_user->pversion)
unknown's avatar
unknown committed
561
      {
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
	if (stage) /* We check password only on the second stage */
	{
	  if (!validate_password(password,message,acl_user->salt))
	    password_correct=1;
	}
	else  /* First stage - just prepare scramble */
	  prepare_scramble(thd,acl_user,prepared_scramble);
      }
      /* Old way to check password */
      else
      {
	/* Checking the scramble at any stage. First - old clients */
	if (!check_scramble(password,message,acl_user->salt,
			    (my_bool) old_ver))
	  password_correct=1;
	else if (!stage)		/* Here if password incorrect  */
	{
	  /* At the first stage - prepare scramble */
	  prepare_scramble(thd,acl_user,prepared_scramble);
	}
unknown's avatar
unknown committed
582 583 584
      }
    }
  }
unknown's avatar
unknown committed
585

unknown's avatar
unknown committed
586
  /* If user not found password_correct will also be zero */
unknown's avatar
unknown committed
587
  if (!password_correct)
588
    goto unlock_and_exit;
unknown's avatar
unknown committed
589

unknown's avatar
unknown committed
590
  /* OK. User found and password checked continue validation */
unknown's avatar
unknown committed
591

unknown's avatar
unknown committed
592
#ifdef HAVE_OPENSSL
593 594 595 596 597 598 599 600 601 602 603
  {
    Vio *vio=thd->net.vio;
    /*
      In this point we know that user is allowed to connect
      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
    case SSL_TYPE_NONE: /* SSL is not required to connect */
unknown's avatar
unknown committed
604
      user_access=acl_user->access;
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
      break;
    case SSL_TYPE_ANY: /* Any kind of SSL is good enough */
      if (vio_type(vio) == VIO_TYPE_SSL)
	user_access=acl_user->access;
      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.
      */
      if (SSL_get_peer_certificate(vio->ssl_))
	user_access=acl_user->access;
      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.
      */
      if (acl_user->ssl_cipher)
unknown's avatar
unknown committed
626
      {
627 628 629 630 631 632 633 634 635
	DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
			   acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)));
	if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)))
	  user_access=acl_user->access;
	else
	{
	  user_access=NO_ACCESS;
	  break;
	}
unknown's avatar
unknown committed
636
      }
637 638 639 640 641 642
      /* Prepare certificate (if exists) */
      DBUG_PRINT("info",("checkpoint 1"));
      X509* cert=SSL_get_peer_certificate(vio->ssl_);
      DBUG_PRINT("info",("checkpoint 2"));
      /* If X509 issuer is speified, we check it... */
      if (acl_user->x509_issuer)
unknown's avatar
unknown committed
643
      {
644 645 646 647 648 649 650 651 652 653 654 655
	DBUG_PRINT("info",("checkpoint 3"));
	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));
	if (strcmp(acl_user->x509_issuer, ptr))
	{
	  user_access=NO_ACCESS;
	  free(ptr);
	  break;
	}
	user_access=acl_user->access;
	free(ptr);
unknown's avatar
unknown committed
656
      }
657 658 659 660 661 662 663 664 665 666 667 668 669 670
      DBUG_PRINT("info",("checkpoint 4"));
      /* X509 subject is specified, we check it .. */
      if (acl_user->x509_subject)
      {
	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))
	  user_access=NO_ACCESS;
	else
	  user_access=acl_user->access;
	free(ptr);
      }
      break;
unknown's avatar
unknown committed
671
    }
unknown's avatar
unknown committed
672
  }
unknown's avatar
unknown committed
673 674 675 676 677 678
#else  /* HAVE_OPENSSL */
  user_access=acl_user->access;
#endif /* HAVE_OPENSSL */
  *mqh=acl_user->user_resource;
  if (!acl_user->user)
    *priv_user=(char*) "";	// Change to anonymous user /* purecov: inspected */
unknown's avatar
unknown committed
679

unknown's avatar
unknown committed
680
unlock_and_exit:
unknown's avatar
unknown committed
681
  VOID(pthread_mutex_unlock(&acl_cache->lock));
682
  DBUG_RETURN(user_access);
unknown's avatar
unknown committed
683 684 685 686 687 688 689 690 691 692 693
}


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

static void acl_update_user(const char *user, const char *host,
unknown's avatar
unknown committed
694
			    const char *password,
695 696 697 698
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
unknown's avatar
unknown committed
699
			    USER_RESOURCES  *mqh,
unknown's avatar
unknown committed
700
			    ulong privileges)
unknown's avatar
unknown committed
701 702 703 704 705 706 707 708 709
{
  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] ||
unknown's avatar
unknown committed
710
	  acl_user->host.hostname &&
unknown's avatar
unknown committed
711
	  !my_strcasecmp(&my_charset_latin1, host, acl_user->host.hostname))
unknown's avatar
unknown committed
712 713
      {
	acl_user->access=privileges;
714
	if (mqh->bits & 1)
715
	  acl_user->user_resource.questions=mqh->questions;
716
	if (mqh->bits & 2)
717
	  acl_user->user_resource.updates=mqh->updates;
718
	if (mqh->bits & 4)
719
	  acl_user->user_resource.connections=mqh->connections;
720 721 722 723 724 725 726 727 728 729
	if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
	{
	  acl_user->ssl_type= ssl_type;
	  acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&mem,ssl_cipher) :
				 0);
	  acl_user->x509_issuer= (x509_issuer ? strdup_root(&mem,x509_issuer) :
				  0);
	  acl_user->x509_subject= (x509_subject ?
				   strdup_root(&mem,x509_subject) : 0);
	}
unknown's avatar
unknown committed
730 731
	if (password)
	{
732 733
	  if (!password[0]) /* If password is empty set it to null */
          {
unknown's avatar
unknown committed
734
	    acl_user->password=0;
unknown's avatar
unknown committed
735 736
            acl_user->pversion=0; // just initialize
          }
unknown's avatar
unknown committed
737 738 739 740
	  else
	  {
	    acl_user->password=(char*) "";	// Just point at something
	    get_salt_from_password(acl_user->salt,password);
741
	    acl_user->pversion=get_password_version(acl_user->password);
unknown's avatar
unknown committed
742 743 744 745 746 747 748 749 750 751
	  }
	}
	break;
      }
    }
  }
}


static void acl_insert_user(const char *user, const char *host,
unknown's avatar
unknown committed
752
			    const char *password,
753 754 755 756
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
757
			    USER_RESOURCES *mqh,
unknown's avatar
unknown committed
758
			    ulong privileges)
unknown's avatar
unknown committed
759 760 761 762 763 764
{
  ACL_USER acl_user;
  acl_user.user=strdup_root(&mem,user);
  update_hostname(&acl_user.host,strdup_root(&mem,host));
  acl_user.password=0;
  acl_user.access=privileges;
765
  acl_user.user_resource = *mqh;
unknown's avatar
unknown committed
766
  acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
unknown's avatar
unknown committed
767
  acl_user.hostname_length=(uint) strlen(acl_user.host.hostname);
768 769 770 771 772
  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;
unknown's avatar
unknown committed
773 774 775 776
  if (password)
  {
    acl_user.password=(char*) "";		// Just point at something
    get_salt_from_password(acl_user.salt,password);
777
    acl_user.pversion=get_password_version(password);
unknown's avatar
unknown committed
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
  }

  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])
    allow_all_hosts=1;			// Anyone can connect /* purecov: tested */
  qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
	sizeof(ACL_USER),(qsort_cmp) acl_compare);

  /* We must free acl_check_hosts as its memory is mapped to acl_user */
  delete_dynamic(&acl_wild_hosts);
  hash_free(&acl_check_hosts);
  init_check_host();
}


static void acl_update_db(const char *user, const char *host, const char *db,
unknown's avatar
unknown committed
795
			  ulong privileges)
unknown's avatar
unknown committed
796 797 798 799 800 801 802 803 804
{
  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] ||
805
	  acl_db->host.hostname &&
unknown's avatar
unknown committed
806
	  !my_strcasecmp(&my_charset_latin1, host, acl_db->host.hostname))
unknown's avatar
unknown committed
807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
      {
	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);
	}
      }
    }
  }
}


822 823 824 825 826 827 828 829 830 831 832 833 834 835
/*
  Insert a user/db/host combination into the global acl_cache

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

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

unknown's avatar
unknown committed
836
static void acl_insert_db(const char *user, const char *host, const char *db,
unknown's avatar
unknown committed
837
			  ulong privileges)
unknown's avatar
unknown committed
838 839
{
  ACL_DB acl_db;
840
  safe_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
  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);
}


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

unknown's avatar
unknown committed
856
ulong acl_get(const char *host, const char *ip, const char *bin_ip,
unknown's avatar
unknown committed
857 858
	     const char *user, const char *db)
{
unknown's avatar
unknown committed
859 860
  ulong host_access,db_access;
  uint i,key_length;
unknown's avatar
unknown committed
861
  db_access=0; host_access= ~0;
unknown's avatar
unknown committed
862
  char key[ACL_KEY_LENGTH],*tmp_db,*end;
unknown's avatar
unknown committed
863 864 865 866
  acl_entry *entry;

  VOID(pthread_mutex_lock(&acl_cache->lock));
  memcpy_fixed(&key,bin_ip,sizeof(struct in_addr));
unknown's avatar
unknown committed
867 868 869
  end=strmov((tmp_db=strmov(key+sizeof(struct in_addr),user)+1),db);
  if (lower_case_table_names)
  {
unknown's avatar
unknown committed
870
    my_casedn_str(&my_charset_latin1, tmp_db);
unknown's avatar
unknown committed
871 872
    db=tmp_db;
  }
unknown's avatar
unknown committed
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
  key_length=(uint) (end-key);
  if ((entry=(acl_entry*) acl_cache->search(key,key_length)))
  {
    db_access=entry->access;
    VOID(pthread_mutex_unlock(&acl_cache->lock));
    return db_access;
  }

  /*
    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))
      {
	if (!acl_db->db || !wild_compare(db,acl_db->db))
	{
	  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))
    {
      if (!acl_host->db || !wild_compare(db,acl_host->db))
      {
	host_access=acl_host->access;		// Fully specified. Take it
	break;
      }
    }
  }
exit:
  /* Save entry in cache for quick retrieval */
  if ((entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
  {
    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));
  return (db_access & host_access);
}


934
int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
unknown's avatar
unknown committed
935 936 937
{
  reg3 int flag;
  DBUG_ENTER("wild_case_compare");
938
  DBUG_PRINT("enter",("str: '%s'  wildstr: '%s'",str,wildstr));
unknown's avatar
unknown committed
939 940 941 942 943 944
  while (*wildstr)
  {
    while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
    {
      if (*wildstr == wild_prefix && wildstr[1])
	wildstr++;
unknown's avatar
unknown committed
945
      if (my_toupper(cs, *wildstr++) !=
946
          my_toupper(cs, *str++)) DBUG_RETURN(1);
unknown's avatar
unknown committed
947 948 949 950
    }
    if (! *wildstr ) DBUG_RETURN (*str != 0);
    if (*wildstr++ == wild_one)
    {
unknown's avatar
unknown committed
951
      if (! *str++) DBUG_RETURN (1);	/* One char; skip */
unknown's avatar
unknown committed
952 953 954 955 956 957 958 959 960 961 962 963
    }
    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];
964 965
	  cmp=my_toupper(cs, cmp);
	  while (*str && my_toupper(cs, *str) != cmp)
unknown's avatar
unknown committed
966 967 968
	    str++;
	  if (!*str) DBUG_RETURN (1);
	}
969
	if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
unknown's avatar
unknown committed
970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985
      } while (*str++);
      DBUG_RETURN(1);
    }
  }
  DBUG_RETURN (*str != '\0');
}

/*****************************************************************************
** check if there are any possible matching entries for this host
** All host names without wild cards are stored in a hash table,
** entries with wildcards are stored in a dynamic array
*****************************************************************************/

static void init_check_host(void)
{
  DBUG_ENTER("init_check_host");
986
  VOID(my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
unknown's avatar
unknown committed
987
			  acl_users.elements,1));
unknown's avatar
unknown committed
988
  VOID(hash_init(&acl_check_hosts,&my_charset_latin1,acl_users.elements,0,0,
unknown's avatar
unknown committed
989
		 (hash_get_key) check_get_key,0,0));
unknown's avatar
unknown committed
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
  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 *);
unknown's avatar
unknown committed
1004
	  if (!my_strcasecmp(&my_charset_latin1,
1005
                             acl_user->host.hostname, acl->hostname))
unknown's avatar
unknown committed
1006 1007 1008 1009 1010 1011
	    break;				// already stored
	}
	if (j == acl_wild_hosts.elements)	// If new
	  (void) push_dynamic(&acl_wild_hosts,(char*) &acl_user->host);
      }
      else if (!hash_search(&acl_check_hosts,(byte*) &acl_user->host,
unknown's avatar
unknown committed
1012
			    (uint) strlen(acl_user->host.hostname)))
unknown's avatar
unknown committed
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
      {
	if (hash_insert(&acl_check_hosts,(byte*) acl_user))
	{					// 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;
}


/* Return true if there is no users that can match the given host */

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

unknown's avatar
unknown committed
1036 1037
  if (host && hash_search(&acl_check_hosts,(byte*) host,(uint) strlen(host)) ||
      ip && hash_search(&acl_check_hosts,(byte*) ip,(uint) strlen(ip)))
unknown's avatar
unknown committed
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock));
    return 0;					// Found host
  }
  for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
  {
    acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
    if (compare_hostname(acl, host, ip))
    {
      VOID(pthread_mutex_unlock(&acl_cache->lock));
      return 0;					// Host ok
    }
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  return 1;					// Host is not allowed
}

/*****************************************************************************
unknown's avatar
unknown committed
1056 1057
  Change password for the user if it's not an anonymous user
  Note: This should write the error directly to the client!
unknown's avatar
unknown committed
1058 1059
*****************************************************************************/

unknown's avatar
unknown committed
1060 1061 1062 1063 1064 1065 1066 1067
/*
  Check if the user is allowed to change password

  SYNOPSIS:
    check_change_password()
    thd		THD
    host	hostname for the user
    user	user name
unknown's avatar
merge  
unknown committed
1068

unknown's avatar
unknown committed
1069 1070 1071 1072 1073 1074 1075
    RETURN VALUE
    0	OK
    1	ERROR  ; In this case the error is sent to the client.
*/

bool check_change_password(THD *thd, const char *host, const char *user)
{
unknown's avatar
unknown committed
1076 1077
  if (!initialized)
  {
1078
    send_error(thd, ER_PASSWORD_NOT_ALLOWED); /* purecov: inspected */
unknown's avatar
unknown committed
1079
    return(1); /* purecov: inspected */
unknown's avatar
unknown committed
1080
  }
unknown's avatar
unknown committed
1081 1082
  if (!thd->slave_thread &&
      (strcmp(thd->user,user) ||
unknown's avatar
unknown committed
1083
       my_strcasecmp(&my_charset_latin1, host, thd->host_or_ip)))
unknown's avatar
unknown committed
1084 1085
  {
    if (check_access(thd, UPDATE_ACL, "mysql",0,1))
unknown's avatar
unknown committed
1086
      return(1);
unknown's avatar
unknown committed
1087
  }
unknown's avatar
unknown committed
1088 1089
  if (!thd->slave_thread && !thd->user[0])
  {
1090
    send_error(thd, ER_PASSWORD_ANONYMOUS_USER);
unknown's avatar
unknown committed
1091
    return(1);
unknown's avatar
unknown committed
1092
  }
unknown's avatar
unknown committed
1093 1094 1095 1096
  return(0);
}


1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
/*
  Change a password for a user

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

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

unknown's avatar
unknown committed
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
bool change_password(THD *thd, const char *host, const char *user,
		     char *new_password)
{
  uint length=0;
  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

  if (check_change_password(thd, host, user))
    DBUG_RETURN(1);

1124 1125 1126 1127
  /*
    password should always be 0,16 or 45 chars;
    Simple hack to avoid cracking
  */
unknown's avatar
unknown committed
1128
  length=(uint) strlen(new_password);
unknown's avatar
unknown committed
1129

1130 1131
  if (length!=45)
    new_password[length & 16]=0;
unknown's avatar
unknown committed
1132

unknown's avatar
unknown committed
1133 1134
  VOID(pthread_mutex_lock(&acl_cache->lock));
  ACL_USER *acl_user;
unknown's avatar
unknown committed
1135
  if (!(acl_user= find_acl_user(host,user)))
unknown's avatar
unknown committed
1136
  {
1137
    send_error(thd, ER_PASSWORD_NO_MATCH);
unknown's avatar
unknown committed
1138
    VOID(pthread_mutex_unlock(&acl_cache->lock));
unknown's avatar
unknown committed
1139
    DBUG_RETURN(1);
unknown's avatar
unknown committed
1140 1141 1142
  }
  if (update_user_table(thd,
			acl_user->host.hostname ? acl_user->host.hostname : "",
unknown's avatar
unknown committed
1143 1144
			acl_user->user ? acl_user->user : "",
			new_password))
unknown's avatar
unknown committed
1145 1146
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
1147
    send_error(thd,0); /* purecov: deadcode */
unknown's avatar
unknown committed
1148
    DBUG_RETURN(1); /* purecov: deadcode */
unknown's avatar
unknown committed
1149 1150
  }
  get_salt_from_password(acl_user->salt,new_password);
1151
  acl_user->pversion=get_password_version(new_password);
unknown's avatar
unknown committed
1152 1153 1154
  if (!new_password[0])
    acl_user->password=0;
  else
unknown's avatar
unknown committed
1155 1156
    acl_user->password=(char*) "";		// Point at something

unknown's avatar
unknown committed
1157 1158 1159
  acl_cache->clear(1);				// Clear locked hostname cache
  VOID(pthread_mutex_unlock(&acl_cache->lock));

1160
  char buff[512]; /* Extend with extended password length*/
1161
  ulong query_length=
unknown's avatar
unknown committed
1162 1163
    my_sprintf(buff,
	       (buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"",
unknown's avatar
unknown committed
1164
		acl_user->user ? acl_user->user : "",
unknown's avatar
unknown committed
1165 1166
		acl_user->host.hostname ? acl_user->host.hostname : "",
		new_password));
1167
  mysql_update_log.write(thd, buff, query_length);
1168
  Query_log_event qinfo(thd, buff, query_length, 0);
unknown's avatar
unknown committed
1169
  mysql_bin_log.write(&qinfo);
unknown's avatar
unknown committed
1170
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
}


/*
  Find first entry that matches the current user
*/

static ACL_USER *
find_acl_user(const char *host, const char *user)
{
unknown's avatar
unknown committed
1181
  DBUG_ENTER("find_acl_user");
1182
  DBUG_PRINT("enter",("host: '%s'  user: '%s'",host,user));
unknown's avatar
unknown committed
1183 1184 1185
  for (uint i=0 ; i < acl_users.elements ; i++)
  {
    ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
unknown's avatar
unknown committed
1186 1187
    DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
			    user,acl_user->user,(host),(acl_user->host)));
unknown's avatar
unknown committed
1188 1189 1190
    if (!acl_user->user && !user[0] ||
	acl_user->user && !strcmp(user,acl_user->user))
    {
unknown's avatar
unknown committed
1191 1192 1193 1194
      if (compare_hostname(&(acl_user->host),host,host))
      {
	DBUG_RETURN(acl_user);
      }
unknown's avatar
unknown committed
1195 1196
    }
  }
unknown's avatar
unknown committed
1197
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
}

/*****************************************************************************
	Handle comparing of hostname
	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.
*****************************************************************************/

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!
  if (hostname &&
      (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
       !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
  {
    host->ip=host->ip_mask=0;			// Not a masked ip
  }
}


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 ||
unknown's avatar
unknown committed
1250
	  (hostname && !wild_case_compare(&my_charset_latin1,
1251
                                          hostname,host->hostname)) ||
unknown's avatar
unknown committed
1252 1253 1254 1255 1256
	  (ip && !wild_compare(ip,host->hostname)));
}


/****************************************************************************
unknown's avatar
unknown committed
1257
  Code to update grants in the user and database privilege tables
unknown's avatar
unknown committed
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
****************************************************************************/

static bool update_user_table(THD *thd, const char *host, const char *user,
			      const char *new_password)
{
  TABLE_LIST tables;
  TABLE *table;
  bool error=1;
  DBUG_ENTER("update_user_table");
  DBUG_PRINT("enter",("user: %s  host: %s",user,host));

  bzero((char*) &tables,sizeof(tables));
1270
  tables.alias=tables.real_name=(char*) "user";
unknown's avatar
unknown committed
1271 1272 1273
  tables.db=(char*) "mysql";
  if (!(table=open_ltable(thd,&tables,TL_WRITE)))
    DBUG_RETURN(1); /* purecov: deadcode */
unknown's avatar
unknown committed
1274 1275
  table->field[0]->store(host,(uint) strlen(host), &my_charset_latin1);
  table->field[1]->store(user,(uint) strlen(user), &my_charset_latin1);
unknown's avatar
unknown committed
1276 1277 1278 1279 1280 1281 1282 1283 1284

  if (table->file->index_read_idx(table->record[0],0,
				  (byte*) table->field[0]->ptr,0,
				  HA_READ_KEY_EXACT))
  {
    my_error(ER_PASSWORD_NO_MATCH,MYF(0));	/* purecov: deadcode */
    DBUG_RETURN(1);				/* purecov: deadcode */
  }
  store_record(table,1);
unknown's avatar
unknown committed
1285
  table->field[2]->store(new_password,(uint) strlen(new_password), &my_charset_latin1);
unknown's avatar
unknown committed
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297
  if ((error=table->file->update_row(table->record[1],table->record[0])))
  {
    table->file->print_error(error,MYF(0));	/* purecov: deadcode */
    goto end;					/* purecov: deadcode */
  }
  error=0;					// Record updated

end:
  close_thread_tables(thd);
  DBUG_RETURN(error);
}

unknown's avatar
unknown committed
1298 1299 1300 1301 1302 1303 1304 1305 1306

/* Return 1 if we are allowed to create new users */

static bool test_if_create_new_users(THD *thd)
{
  bool create_new_users=1;    // Assume that we are allowed to create new users
  if (opt_safe_user_create && !(thd->master_access & INSERT_ACL))
  {
    TABLE_LIST tl;
unknown's avatar
unknown committed
1307
    ulong db_access;
unknown's avatar
unknown committed
1308 1309
    bzero((char*) &tl,sizeof(tl));
    tl.db=	   (char*) "mysql";
unknown's avatar
unknown committed
1310
    tl.real_name=  (char*) "user";
unknown's avatar
unknown committed
1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322
    db_access=acl_get(thd->host, thd->ip, (char*) &thd->remote.sin_addr,
		      thd->priv_user, tl.db);
    if (!(db_access & INSERT_ACL))
    {
      if (check_grant(thd,INSERT_ACL,&tl,0,1))
	create_new_users=0;
    }
  }
  return create_new_users;
}


unknown's avatar
unknown committed
1323 1324 1325 1326
/****************************************************************************
** Handle GRANT commands
****************************************************************************/

1327
static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
unknown's avatar
unknown committed
1328 1329
			      ulong rights, bool revoke_grant,
			      bool create_user)
unknown's avatar
unknown committed
1330 1331
{
  int error = -1;
unknown's avatar
unknown committed
1332
  bool old_row_exists=0;
unknown's avatar
unknown committed
1333
  char *password,empty_string[1];
unknown's avatar
unknown committed
1334
  char what= (revoke_grant) ? 'N' : 'Y';
unknown's avatar
unknown committed
1335
  DBUG_ENTER("replace_user_table");
1336
  safe_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
1337

1338 1339
  password=empty_string;
  empty_string[0]=0;
unknown's avatar
unknown committed
1340

unknown's avatar
unknown committed
1341
  if (combo.password.str && combo.password.str[0])
1342
  {
unknown's avatar
unknown committed
1343
    if ((combo.password.length != HASH_PASSWORD_LENGTH)
1344
         && combo.password.length != HASH_OLD_PASSWORD_LENGTH)
1345
    {
1346 1347 1348
      my_printf_error(ER_PASSWORD_NO_MATCH,
          "Password hash should be a %d-digit hexadecimal number",
          MYF(0),HASH_PASSWORD_LENGTH);
unknown's avatar
unknown committed
1349
      DBUG_RETURN(-1);
1350
    }
unknown's avatar
unknown committed
1351
    password=combo.password.str;
1352
  }
unknown's avatar
unknown committed
1353

unknown's avatar
unknown committed
1354 1355
  table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
  table->field[1]->store(combo.user.str,combo.user.length, &my_charset_latin1);
unknown's avatar
unknown committed
1356 1357 1358 1359 1360
  table->file->index_init(0);
  if (table->file->index_read(table->record[0],
			      (byte*) table->field[0]->ptr,0,
			      HA_READ_KEY_EXACT))
  {
unknown's avatar
unknown committed
1361
    if (!create_user)
unknown's avatar
unknown committed
1362
    {
unknown's avatar
unknown committed
1363 1364 1365 1366
      if (what == 'N')
	my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),
			MYF(0),combo.user.str,combo.host.str);
      else
1367 1368
	my_printf_error(ER_NO_PERMISSION_TO_CREATE_USER,
			ER(ER_NO_PERMISSION_TO_CREATE_USER),
unknown's avatar
unknown committed
1369
			MYF(0),thd->user,
1370
			thd->host_or_ip);
unknown's avatar
unknown committed
1371 1372 1373
      error= -1;
      goto end;
    }
unknown's avatar
unknown committed
1374
    old_row_exists = 0;
1375
    restore_record(table,2);	// cp empty row from record[2]
unknown's avatar
unknown committed
1376 1377 1378
    table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
    table->field[1]->store(combo.user.str,combo.user.length, &my_charset_latin1);
    table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1);
unknown's avatar
unknown committed
1379 1380 1381
  }
  else
  {
unknown's avatar
unknown committed
1382
    old_row_exists = 1;
unknown's avatar
unknown committed
1383 1384
    store_record(table,1);			// Save copy for update
    if (combo.password.str)			// If password given
unknown's avatar
unknown committed
1385
      table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1);
unknown's avatar
unknown committed
1386 1387
  }

unknown's avatar
unknown committed
1388 1389 1390 1391 1392 1393 1394 1395
  /* Update table columns with new privileges */

  Field **tmp_field;
  ulong priv;
  for (tmp_field= table->field+3, priv = SELECT_ACL;
       *tmp_field && (*tmp_field)->real_type() == FIELD_TYPE_ENUM &&
	 ((Field_enum*) (*tmp_field))->typelib->count == 2 ;
       tmp_field++, priv <<= 1)
unknown's avatar
unknown committed
1396
  {
unknown's avatar
unknown committed
1397
    if (priv & rights)				 // set requested privileges
unknown's avatar
unknown committed
1398
      (*tmp_field)->store(&what, 1, &my_charset_latin1);
unknown's avatar
unknown committed
1399 1400
  }
  rights=get_access(table,3);
1401
  DBUG_PRINT("info",("table->fields: %d",table->fields));
unknown's avatar
unknown committed
1402
  if (table->fields >= 31)		/* From 4.0.0 we have more fields */
1403
  {
unknown's avatar
unknown committed
1404
    /* We write down SSL related ACL stuff */
unknown's avatar
unknown committed
1405 1406
    switch (thd->lex.ssl_type) {
    case SSL_TYPE_ANY:
unknown's avatar
unknown committed
1407 1408 1409 1410
      table->field[24]->store("ANY",3, &my_charset_latin1);
      table->field[25]->store("", 0, &my_charset_latin1);
      table->field[26]->store("", 0, &my_charset_latin1);
      table->field[27]->store("", 0, &my_charset_latin1);
unknown's avatar
unknown committed
1411 1412
      break;
    case SSL_TYPE_X509:
unknown's avatar
unknown committed
1413 1414 1415 1416
      table->field[24]->store("X509",4, &my_charset_latin1);
      table->field[25]->store("", 0, &my_charset_latin1);
      table->field[26]->store("", 0, &my_charset_latin1);
      table->field[27]->store("", 0, &my_charset_latin1);
unknown's avatar
unknown committed
1417 1418
      break;
    case SSL_TYPE_SPECIFIED:
unknown's avatar
unknown committed
1419 1420 1421 1422
      table->field[24]->store("SPECIFIED",9, &my_charset_latin1);
      table->field[25]->store("", 0, &my_charset_latin1);
      table->field[26]->store("", 0, &my_charset_latin1);
      table->field[27]->store("", 0, &my_charset_latin1);
unknown's avatar
unknown committed
1423
      if (thd->lex.ssl_cipher)
unknown's avatar
unknown committed
1424
	table->field[25]->store(thd->lex.ssl_cipher,
unknown's avatar
unknown committed
1425
				strlen(thd->lex.ssl_cipher), &my_charset_latin1);
unknown's avatar
unknown committed
1426
      if (thd->lex.x509_issuer)
unknown's avatar
unknown committed
1427
	table->field[26]->store(thd->lex.x509_issuer,
unknown's avatar
unknown committed
1428
				strlen(thd->lex.x509_issuer), &my_charset_latin1);
unknown's avatar
unknown committed
1429
      if (thd->lex.x509_subject)
unknown's avatar
unknown committed
1430
	table->field[27]->store(thd->lex.x509_subject,
unknown's avatar
unknown committed
1431
				strlen(thd->lex.x509_subject), &my_charset_latin1);
unknown's avatar
unknown committed
1432
      break;
unknown's avatar
unknown committed
1433
    case SSL_TYPE_NOT_SPECIFIED:
unknown's avatar
unknown committed
1434 1435
      break;
    case SSL_TYPE_NONE:
unknown's avatar
unknown committed
1436 1437 1438 1439
      table->field[24]->store("", 0, &my_charset_latin1);
      table->field[25]->store("", 0, &my_charset_latin1);
      table->field[26]->store("", 0, &my_charset_latin1);
      table->field[27]->store("", 0, &my_charset_latin1);
unknown's avatar
unknown committed
1440
      break;
1441
    }
unknown's avatar
unknown committed
1442

1443
    USER_RESOURCES mqh = thd->lex.mqh;
1444
    if (mqh.bits & 1)
unknown's avatar
unknown committed
1445
      table->field[28]->store((longlong) mqh.questions);
1446
    if (mqh.bits & 2)
unknown's avatar
unknown committed
1447
      table->field[29]->store((longlong) mqh.updates);
1448
    if (mqh.bits & 4)
unknown's avatar
unknown committed
1449
      table->field[30]->store((longlong) mqh.connections);
1450
    mqh_used = mqh_used || mqh.questions || mqh.updates || mqh.connections;
unknown's avatar
unknown committed
1451
  }
unknown's avatar
unknown committed
1452
  if (old_row_exists)
unknown's avatar
unknown committed
1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477
  {
    /*
      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!
    */
    if (cmp_record(table,1) &&
	(error=table->file->update_row(table->record[1],table->record[0])))
    {						// This should never happen
      table->file->print_error(error,MYF(0));	/* purecov: deadcode */
      error= -1;				/* purecov: deadcode */
      goto end;					/* purecov: deadcode */
    }
  }
  else if ((error=table->file->write_row(table->record[0]))) // insert
  {						// This should never happen
    if (error && error != HA_ERR_FOUND_DUPP_KEY &&
	error != HA_ERR_FOUND_DUPP_UNIQUE)	/* purecov: inspected */
    {
      table->file->print_error(error,MYF(0));	/* purecov: deadcode */
      error= -1;				/* purecov: deadcode */
      goto end;					/* purecov: deadcode */
    }
  }
  error=0;					// Privileges granted / revoked

unknown's avatar
unknown committed
1478
end:
unknown's avatar
unknown committed
1479 1480 1481 1482 1483
  if (!error)
  {
    acl_cache->clear(1);			// Clear privilege cache
    if (!combo.password.str)
      password=0;				// No password given on command
unknown's avatar
unknown committed
1484
    if (old_row_exists)
1485
      acl_update_user(combo.user.str,combo.host.str,password,
unknown's avatar
unknown committed
1486 1487 1488 1489
		      thd->lex.ssl_type,
		      thd->lex.ssl_cipher,
		      thd->lex.x509_issuer,
		      thd->lex.x509_subject,
1490
		      &thd->lex.mqh,
unknown's avatar
unknown committed
1491
		      rights);
unknown's avatar
unknown committed
1492
    else
1493
      acl_insert_user(combo.user.str,combo.host.str,password,
unknown's avatar
unknown committed
1494 1495 1496 1497
		      thd->lex.ssl_type,
		      thd->lex.ssl_cipher,
		      thd->lex.x509_issuer,
		      thd->lex.x509_subject,
1498
		      &thd->lex.mqh,
unknown's avatar
unknown committed
1499
		      rights);
unknown's avatar
unknown committed
1500 1501 1502 1503 1504 1505 1506
  }
  table->file->index_end();
  DBUG_RETURN(error);
}


/*
unknown's avatar
unknown committed
1507
  change grants in the mysql.db table
unknown's avatar
unknown committed
1508 1509 1510 1511
*/

static int replace_db_table(TABLE *table, const char *db,
			    const LEX_USER &combo,
unknown's avatar
unknown committed
1512
			    ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
1513
{
unknown's avatar
unknown committed
1514 1515
  uint i;
  ulong priv,store_rights;
unknown's avatar
unknown committed
1516
  bool old_row_exists=0;
unknown's avatar
unknown committed
1517
  int error;
unknown's avatar
unknown committed
1518
  char what= (revoke_grant) ? 'N' : 'Y';
unknown's avatar
unknown committed
1519 1520 1521 1522 1523 1524 1525 1526 1527
  DBUG_ENTER("replace_db_table");

  // is there such a user in user table in memory ????
  if (!initialized || !find_acl_user(combo.host.str,combo.user.str))
  {
    my_error(ER_PASSWORD_NO_MATCH,MYF(0));
    DBUG_RETURN(-1);
  }

unknown's avatar
unknown committed
1528 1529 1530
  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);
unknown's avatar
unknown committed
1531 1532 1533 1534 1535 1536 1537 1538 1539 1540
  table->file->index_init(0);
  if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr,0,
	      HA_READ_KEY_EXACT))
  {
    if (what == 'N')
    { // no row, no revoke
      my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),MYF(0),
		      combo.user.str,combo.host.str);
      goto abort;
    }
unknown's avatar
unknown committed
1541
    old_row_exists = 0;
unknown's avatar
unknown committed
1542
    restore_record(table,2);			// cp empty row from record[2]
unknown's avatar
unknown committed
1543 1544 1545
    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);
unknown's avatar
unknown committed
1546 1547 1548
  }
  else
  {
unknown's avatar
unknown committed
1549
    old_row_exists = 1;
unknown's avatar
unknown committed
1550 1551 1552 1553
    store_record(table,1);
  }

  store_rights=get_rights_for_db(rights);
unknown's avatar
unknown committed
1554
  for (i= 3, priv= 1; i < table->fields; i++, priv <<= 1)
unknown's avatar
unknown committed
1555
  {
unknown's avatar
unknown committed
1556
    if (priv & store_rights)			// do it if priv is chosen
unknown's avatar
unknown committed
1557
      table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
unknown's avatar
unknown committed
1558 1559 1560 1561
  }
  rights=get_access(table,3);
  rights=fix_rights_for_db(rights);

unknown's avatar
unknown committed
1562
  if (old_row_exists)
unknown's avatar
unknown committed
1563
  {
unknown's avatar
unknown committed
1564
    // update old existing row
unknown's avatar
unknown committed
1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582
    if (rights)
    {
      if ((error=table->file->update_row(table->record[1],table->record[0])))
	goto table_error;			/* purecov: deadcode */
    }
    else	/* must have been a revoke of all privileges */
    {
      if ((error = table->file->delete_row(table->record[1])))
	goto table_error;			/* purecov: deadcode */
    }
  }
  else if ((error=table->file->write_row(table->record[0])))
  {
    if (error && error != HA_ERR_FOUND_DUPP_KEY) /* purecov: inspected */
      goto table_error; /* purecov: deadcode */
  }

  acl_cache->clear(1);				// Clear privilege cache
unknown's avatar
unknown committed
1583
  if (old_row_exists)
unknown's avatar
unknown committed
1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603
    acl_update_db(combo.user.str,combo.host.str,db,rights);
  else
    acl_insert_db(combo.user.str,combo.host.str,db,rights);
  table->file->index_end();
  DBUG_RETURN(0);

  /* This could only happen if the grant tables got corrupted */
 table_error:
  table->file->print_error(error,MYF(0));	/* purecov: deadcode */
  table->file->index_end();

 abort:
  DBUG_RETURN(-1);
}


class GRANT_COLUMN :public Sql_alloc
{
public:
  char *column;
unknown's avatar
unknown committed
1604 1605 1606
  ulong rights;
  uint key_length;
  GRANT_COLUMN(String &c,  ulong y) :rights (y)
unknown's avatar
unknown committed
1607
  {
unknown's avatar
unknown committed
1608
    column= memdup_root(&memex,c.ptr(), key_length=c.length());
unknown's avatar
unknown committed
1609 1610 1611
  }
};

unknown's avatar
unknown committed
1612

unknown's avatar
unknown committed
1613 1614 1615 1616 1617 1618 1619
static byte* get_key_column(GRANT_COLUMN *buff,uint *length,
			    my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
  return (byte*) buff->column;
}

unknown's avatar
unknown committed
1620

unknown's avatar
unknown committed
1621 1622 1623 1624
class GRANT_TABLE :public Sql_alloc
{
public:
  char *host,*db,*user,*tname, *hash_key;
unknown's avatar
unknown committed
1625 1626
  ulong privs, cols;
  uint key_length;
unknown's avatar
unknown committed
1627 1628
  HASH hash_columns;
  GRANT_TABLE (const char *h, const char *d,const char *u, const char *t,
unknown's avatar
unknown committed
1629
	       ulong p, ulong c)
unknown's avatar
unknown committed
1630 1631 1632 1633 1634 1635
    : privs(p), cols(c)
  {
    host = strdup_root(&memex,h);
    db =   strdup_root(&memex,d);
    user = strdup_root(&memex,u);
    tname= strdup_root(&memex,t);
unknown's avatar
unknown committed
1636 1637
    if (lower_case_table_names)
    {
unknown's avatar
unknown committed
1638 1639
      my_casedn_str(&my_charset_latin1, db);
      my_casedn_str(&my_charset_latin1, tname);
unknown's avatar
unknown committed
1640
    }
unknown's avatar
unknown committed
1641
    key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3;
unknown's avatar
unknown committed
1642 1643
    hash_key = (char*) alloc_root(&memex,key_length);
    strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
unknown's avatar
unknown committed
1644
    (void) hash_init(&hash_columns,&my_charset_latin1,
unknown's avatar
unknown committed
1645
		     0,0,0, (hash_get_key) get_key_column,0,0);
unknown's avatar
unknown committed
1646 1647 1648 1649 1650 1651
  }

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

1652 1653 1654
    host =  get_field(&memex,form->field[0]);
    db =    get_field(&memex,form->field[1]);
    user =  get_field(&memex,form->field[2]);
unknown's avatar
unknown committed
1655 1656
    if (!user)
      user=(char*) "";
1657
    tname = get_field(&memex,form->field[3]);
unknown's avatar
unknown committed
1658 1659 1660 1661 1662 1663
    if (!host || !db || !tname)
    {
      /* Wrong table row; Ignore it */
      privs = cols = 0;				/* purecov: inspected */
      return;					/* purecov: inspected */
    }
unknown's avatar
unknown committed
1664 1665
    if (lower_case_table_names)
    {
unknown's avatar
unknown committed
1666 1667
      my_casedn_str(&my_charset_latin1, db);
      my_casedn_str(&my_charset_latin1, tname);
unknown's avatar
unknown committed
1668 1669 1670
    }
    key_length = ((uint) strlen(db) + (uint) strlen(user) +
		  (uint) strlen(tname) + 3);
unknown's avatar
unknown committed
1671 1672
    hash_key = (char*) alloc_root(&memex,key_length);
    strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
unknown's avatar
unknown committed
1673 1674
    privs = (ulong) form->field[6]->val_int();
    cols  = (ulong) form->field[7]->val_int();
unknown's avatar
unknown committed
1675 1676 1677
    privs = fix_rights_for_table(privs);
    cols =  fix_rights_for_column(cols);

unknown's avatar
unknown committed
1678
    (void) hash_init(&hash_columns,&my_charset_latin1,
unknown's avatar
unknown committed
1679
		     0,0,0, (hash_get_key) get_key_column,0,0);
unknown's avatar
unknown committed
1680 1681 1682
    if (cols)
    {
      int key_len;
unknown's avatar
unknown committed
1683 1684 1685 1686
      col_privs->field[0]->store(host,(uint) strlen(host), &my_charset_latin1);
      col_privs->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
      col_privs->field[2]->store(user,(uint) strlen(user), &my_charset_latin1);
      col_privs->field[3]->store(tname,(uint) strlen(tname), &my_charset_latin1);
unknown's avatar
unknown committed
1687 1688 1689 1690 1691
      key_len=(col_privs->field[0]->pack_length()+
	       col_privs->field[1]->pack_length()+
	       col_privs->field[2]->pack_length()+
	       col_privs->field[3]->pack_length());
      key_copy(key,col_privs,0,key_len);
unknown's avatar
unknown committed
1692
      col_privs->field[4]->store("",0, &my_charset_latin1);
unknown's avatar
unknown committed
1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706
      col_privs->file->index_init(0);
      if (col_privs->file->index_read(col_privs->record[0],
				      (byte*) col_privs->field[0]->ptr,
				      key_len, HA_READ_KEY_EXACT))
      {
	cols = 0; /* purecov: deadcode */
	return;
      }
      do
      {
	String *res,column_name;
	GRANT_COLUMN *mem_check;
	/* As column name is a string, we don't have to supply a buffer */
	res=col_privs->field[4]->val_str(&column_name,&column_name);
unknown's avatar
unknown committed
1707
	ulong priv= (ulong) col_privs->field[6]->val_int();
unknown's avatar
unknown committed
1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722
	if (!(mem_check = new GRANT_COLUMN(*res,
					   fix_rights_for_column(priv))))
	{
	  // Don't use this entry
	  privs = cols = 0;			/* purecov: deadcode */
	  return;				/* purecov: deadcode */
	}
	hash_insert(&hash_columns, (byte *) mem_check);
      } while (!col_privs->file->index_next(col_privs->record[0]) &&
	       !key_cmp(col_privs,key,0,key_len));
    }
  }
  bool ok() { return privs != 0 || cols != 0; }
};

unknown's avatar
unknown committed
1723

unknown's avatar
unknown committed
1724 1725 1726 1727 1728 1729 1730
static byte* get_grant_table(GRANT_TABLE *buff,uint *length,
			     my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
  return (byte*) buff->hash_key;
}

unknown's avatar
unknown committed
1731

unknown's avatar
unknown committed
1732 1733 1734 1735 1736
void free_grant_table(GRANT_TABLE *grant_table)
{
  hash_free(&grant_table->hash_columns);
}

unknown's avatar
unknown committed
1737

unknown's avatar
unknown committed
1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749
/* Search after a matching grant. Prefer exact grants before not exact ones */

static GRANT_TABLE *table_hash_search(const char *host,const char* ip,
				      const char *db,
				      const char *user, const char *tname,
				      bool exact)
{
  char helping [NAME_LEN*2+USERNAME_LENGTH+3];
  uint len;
  GRANT_TABLE *grant_table,*found=0;

  len  = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1;
1750 1751
  for (grant_table=(GRANT_TABLE*) hash_search(&column_priv_hash,
					      (byte*) helping,
unknown's avatar
unknown committed
1752 1753
					      len) ;
       grant_table ;
1754 1755
       grant_table= (GRANT_TABLE*) hash_next(&column_priv_hash,(byte*) helping,
					     len))
unknown's avatar
unknown committed
1756 1757 1758
  {
    if (exact)
    {
1759
      if ((host &&
unknown's avatar
unknown committed
1760
	   !my_strcasecmp(&my_charset_latin1, host, grant_table->host)) ||
unknown's avatar
unknown committed
1761 1762 1763 1764 1765
	  (ip && !strcmp(ip,grant_table->host)))
	return grant_table;
    }
    else
    {
unknown's avatar
unknown committed
1766
      if ((host && !wild_case_compare(&my_charset_latin1,
1767
                                      host,grant_table->host)) ||
unknown's avatar
unknown committed
1768
	  (ip && !wild_case_compare(&my_charset_latin1,
1769
                                    ip,grant_table->host)))
unknown's avatar
unknown committed
1770 1771 1772 1773 1774 1775 1776 1777
	found=grant_table;					// Host ok
    }
  }
  return found;
}



unknown's avatar
unknown committed
1778
inline GRANT_COLUMN *
unknown's avatar
unknown committed
1779
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
unknown's avatar
unknown committed
1780 1781 1782 1783 1784 1785 1786 1787 1788
{
  return (GRANT_COLUMN*) hash_search(&t->hash_columns, (byte*) cname,length);
}


static int replace_column_table(GRANT_TABLE *g_t,
				TABLE *table, const LEX_USER &combo,
				List <LEX_COLUMN> &columns,
				const char *db, const char *table_name,
unknown's avatar
unknown committed
1789
				ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
1790 1791 1792 1793 1794 1795
{
  int error=0,result=0;
  uint key_length;
  byte key[MAX_KEY_LENGTH];
  DBUG_ENTER("replace_column_table");

unknown's avatar
unknown committed
1796 1797 1798 1799
  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);
  table->field[3]->store(table_name,(uint) strlen(table_name), &my_charset_latin1);
unknown's avatar
unknown committed
1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812
  key_length=(table->field[0]->pack_length()+ table->field[1]->pack_length()+
	      table->field[2]->pack_length()+ table->field[3]->pack_length());
  key_copy(key,table,0,key_length);

  rights &= COL_ACLS;				// Only ACL for columns

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

  List_iterator <LEX_COLUMN> iter(columns);
  class LEX_COLUMN *xx;
  table->file->index_init(0);
  while ((xx=iter++))
  {
unknown's avatar
unknown committed
1813
    ulong privileges = xx->rights;
unknown's avatar
unknown committed
1814
    bool old_row_exists=0;
unknown's avatar
unknown committed
1815
    key_restore(table,key,0,key_length);
unknown's avatar
unknown committed
1816
    table->field[4]->store(xx->column.ptr(),xx->column.length(),&my_charset_latin1);
unknown's avatar
unknown committed
1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828

    if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr,
				0, HA_READ_KEY_EXACT))
    {
      if (revoke_grant)
      {
	my_printf_error(ER_NONEXISTING_TABLE_GRANT,
			ER(ER_NONEXISTING_TABLE_GRANT),MYF(0),
			combo.user.str, combo.host.str,table_name); /* purecov: inspected */
	result= -1; /* purecov: inspected */
	continue; /* purecov: inspected */
      }
unknown's avatar
unknown committed
1829
      old_row_exists = 0;
1830
      restore_record(table,2);				// Get empty record
unknown's avatar
unknown committed
1831
      key_restore(table,key,0,key_length);
unknown's avatar
unknown committed
1832
      table->field[4]->store(xx->column.ptr(),xx->column.length(), &my_charset_latin1);
unknown's avatar
unknown committed
1833 1834 1835
    }
    else
    {
unknown's avatar
unknown committed
1836
      ulong tmp= (ulong) table->field[6]->val_int();
unknown's avatar
unknown committed
1837 1838 1839 1840 1841 1842
      tmp=fix_rights_for_column(tmp);

      if (revoke_grant)
	privileges = tmp & ~(privileges | rights);
      else
	privileges |= tmp;
unknown's avatar
unknown committed
1843
      old_row_exists = 1;
unknown's avatar
unknown committed
1844 1845 1846 1847 1848
      store_record(table,1);			// copy original row
    }

    table->field[6]->store((longlong) get_rights_for_column(privileges));

unknown's avatar
unknown committed
1849
    if (old_row_exists)
unknown's avatar
unknown committed
1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895
    {
      if (privileges)
	error=table->file->update_row(table->record[1],table->record[0]);
      else
	error=table->file->delete_row(table->record[1]);
      if (error)
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
      GRANT_COLUMN *grant_column = column_hash_search(g_t,
						      xx->column.ptr(),
						      xx->column.length());
      if (grant_column)				// Should always be true
	grant_column->rights = privileges;	// Update hash
    }
    else					// new grant
    {
      if ((error=table->file->write_row(table->record[0])))
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
      GRANT_COLUMN *grant_column = new GRANT_COLUMN(xx->column,privileges);
      hash_insert(&g_t->hash_columns,(byte*) grant_column);
    }
  }
  table->file->index_end();

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

  if (revoke_grant)
  {
    table->file->index_init(0);
    if (table->file->index_read(table->record[0], (byte*) table->field[0]->ptr,
				key_length, HA_READ_KEY_EXACT))
      goto end;

    // Scan trough all rows with the same host,db,user and table
    do
    {
unknown's avatar
unknown committed
1896
      ulong privileges = (ulong) table->field[6]->val_int();
unknown's avatar
unknown committed
1897 1898 1899 1900 1901 1902 1903
      privileges=fix_rights_for_column(privileges);
      store_record(table,1);

      if (privileges & rights)	// is in this record the priv to be revoked ??
      {
	GRANT_COLUMN *grant_column = NULL;
	char  colum_name_buf[HOSTNAME_LENGTH+1];
unknown's avatar
unknown committed
1904
	String column_name(colum_name_buf,sizeof(colum_name_buf),&my_charset_latin1);
unknown's avatar
unknown committed
1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951

	privileges&= ~rights;
	table->field[6]->store((longlong)
			       get_rights_for_column(privileges));
	table->field[4]->val_str(&column_name,&column_name);
	grant_column = column_hash_search(g_t,
					  column_name.ptr(),
					  column_name.length());
	if (privileges)
	{
	  int tmp_error;
	  if ((tmp_error=table->file->update_row(table->record[1],
						 table->record[0])))
	  {					/* purecov: deadcode */
	    table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
	    result= -1;				/* purecov: deadcode */
	    goto end;				/* purecov: deadcode */
	  }
	  if (grant_column)
	    grant_column->rights  = privileges; // Update hash
	}
	else
	{
	  int tmp_error;
	  if ((tmp_error = table->file->delete_row(table->record[1])))
	  {					/* purecov: deadcode */
	    table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
	    result= -1;				/* purecov: deadcode */
	    goto end;				/* purecov: deadcode */
	  }
	  if (grant_column)
	    hash_delete(&g_t->hash_columns,(byte*) grant_column);
	}
      }
    } while (!table->file->index_next(table->record[0]) &&
	     !key_cmp(table,key,0,key_length));
  }

 end:
  table->file->index_end();
  DBUG_RETURN(result);
}


static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
			       TABLE *table, const LEX_USER &combo,
			       const char *db, const char *table_name,
unknown's avatar
unknown committed
1952 1953
			       ulong rights, ulong col_rights,
			       bool revoke_grant)
unknown's avatar
unknown committed
1954
{
1955
  char grantor[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
unknown's avatar
unknown committed
1956
  int old_row_exists = 1;
unknown's avatar
unknown committed
1957
  int error=0;
unknown's avatar
unknown committed
1958
  ulong store_table_rights, store_col_rights;
unknown's avatar
unknown committed
1959 1960
  DBUG_ENTER("replace_table_table");

1961
  strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS);
unknown's avatar
unknown committed
1962

unknown's avatar
unknown committed
1963 1964 1965 1966
  /*
    The following should always succeed as new users are created before
    this function is called!
  */
unknown's avatar
unknown committed
1967 1968 1969 1970 1971 1972 1973
  if (!find_acl_user(combo.host.str,combo.user.str))
  {
    my_error(ER_PASSWORD_NO_MATCH,MYF(0));	/* purecov: deadcode */
    DBUG_RETURN(-1);				/* purecov: deadcode */
  }

  restore_record(table,2);			// Get empty record
unknown's avatar
unknown committed
1974 1975 1976 1977
  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);
  table->field[3]->store(table_name,(uint) strlen(table_name), &my_charset_latin1);
unknown's avatar
unknown committed
1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996
  store_record(table,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_printf_error(ER_NONEXISTING_TABLE_GRANT,
		      ER(ER_NONEXISTING_TABLE_GRANT),MYF(0),
		      combo.user.str,combo.host.str,
		      table_name);		/* purecov: deadcode */
      DBUG_RETURN(-1);				/* purecov: deadcode */
    }
unknown's avatar
unknown committed
1997
    old_row_exists = 0;
unknown's avatar
unknown committed
1998 1999 2000
    restore_record(table,1);			// Get saved record
  }

unknown's avatar
unknown committed
2001 2002
  store_table_rights= get_rights_for_table(rights);
  store_col_rights=   get_rights_for_column(col_rights);
unknown's avatar
unknown committed
2003
  if (old_row_exists)
unknown's avatar
unknown committed
2004
  {
unknown's avatar
unknown committed
2005
    ulong j,k;
unknown's avatar
unknown committed
2006
    store_record(table,1);
unknown's avatar
unknown committed
2007 2008
    j = (ulong) table->field[6]->val_int();
    k = (ulong) table->field[7]->val_int();
unknown's avatar
unknown committed
2009 2010 2011 2012 2013 2014 2015 2016

    if (revoke_grant)
    {
      // column rights are already fixed in mysql_table_grant !
      store_table_rights=j & ~store_table_rights;
    }
    else
    {
unknown's avatar
unknown committed
2017 2018
      store_table_rights|= j;
      store_col_rights|=   k;
unknown's avatar
unknown committed
2019 2020 2021
    }
  }

unknown's avatar
unknown committed
2022
  table->field[4]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
unknown's avatar
unknown committed
2023 2024 2025
  table->field[6]->store((longlong) store_table_rights);
  table->field[7]->store((longlong) store_col_rights);
  rights=fix_rights_for_table(store_table_rights);
unknown's avatar
unknown committed
2026
  col_rights=fix_rights_for_column(store_col_rights);
unknown's avatar
unknown committed
2027

unknown's avatar
unknown committed
2028
  if (old_row_exists)
unknown's avatar
unknown committed
2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044
  {
    if (store_table_rights || store_col_rights)
    {
      if ((error=table->file->update_row(table->record[1],table->record[0])))
	goto table_error;			/* purecov: deadcode */
    }
    else if ((error = table->file->delete_row(table->record[1])))
      goto table_error;				/* purecov: deadcode */
  }
  else
  {
    error=table->file->write_row(table->record[0]);
    if (error && error != HA_ERR_FOUND_DUPP_KEY)
      goto table_error;				/* purecov: deadcode */
  }

unknown's avatar
unknown committed
2045
  if (rights | col_rights)
unknown's avatar
unknown committed
2046
  {
unknown's avatar
unknown committed
2047 2048
    grant_table->privs= rights;
    grant_table->cols=  col_rights;
unknown's avatar
unknown committed
2049 2050 2051
  }
  else
  {
2052
    hash_delete(&column_priv_hash,(byte*) grant_table);
unknown's avatar
unknown committed
2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064
  }
  DBUG_RETURN(0);

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


int mysql_table_grant (THD *thd, TABLE_LIST *table_list,
		       List <LEX_USER> &user_list,
unknown's avatar
unknown committed
2065
		       List <LEX_COLUMN> &columns, ulong rights,
unknown's avatar
unknown committed
2066 2067
		       bool revoke_grant)
{
unknown's avatar
unknown committed
2068
  ulong column_priv = 0;
unknown's avatar
unknown committed
2069 2070 2071
  List_iterator <LEX_USER> str_list (user_list);
  LEX_USER *Str;
  TABLE_LIST tables[3];
unknown's avatar
unknown committed
2072
  bool create_new_users=0;
unknown's avatar
unknown committed
2073 2074 2075 2076
  DBUG_ENTER("mysql_table_grant");

  if (!initialized)
  {
2077
    send_error(thd, ER_UNKNOWN_COM_ERROR);	/* purecov: inspected */
unknown's avatar
unknown committed
2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099
    return 1;					/* purecov: inspected */
  }
  if (rights & ~TABLE_ACLS)
  {
    my_error(ER_ILLEGAL_GRANT_FOR_TABLE,MYF(0));
    DBUG_RETURN(-1);
  }

  if (columns.elements && !revoke_grant)
  {
    TABLE *table;
    class LEX_COLUMN *check;
    List_iterator <LEX_COLUMN> iter(columns);

    if (!(table=open_ltable(thd,table_list,TL_READ)))
      DBUG_RETURN(-1);
    while ((check = iter++))
    {
      if (!find_field_in_table(thd,table,check->column.ptr(),
			       check->column.length(),0,0))
      {
	my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0),
2100
			check->column.c_ptr(), table_list->alias);
unknown's avatar
unknown committed
2101 2102 2103 2104 2105 2106 2107 2108 2109
	DBUG_RETURN(-1);
      }
      column_priv |= check->rights | (rights & COL_ACLS);
    }
    close_thread_tables(thd);
  }
  else if (!(rights & CREATE_ACL) && !revoke_grant)
  {
    char buf[FN_REFLEN];
2110 2111
    sprintf(buf,"%s/%s/%s.frm",mysql_data_home, table_list->db,
	    table_list->real_name);
unknown's avatar
unknown committed
2112 2113 2114
    fn_format(buf,buf,"","",4+16+32);
    if (access(buf,F_OK))
    {
2115
      my_error(ER_NO_SUCH_TABLE,MYF(0),table_list->db, table_list->alias);
unknown's avatar
unknown committed
2116 2117 2118 2119 2120 2121 2122
      DBUG_RETURN(-1);
    }
  }

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

  bzero((char*) &tables,sizeof(tables));
2123 2124 2125
  tables[0].alias=tables[0].real_name= (char*) "user";
  tables[1].alias=tables[1].real_name= (char*) "tables_priv";
  tables[2].alias=tables[2].real_name= (char*) "columns_priv";
unknown's avatar
unknown committed
2126 2127 2128 2129 2130 2131 2132 2133
  tables[0].next=tables+1;
  /* Don't open column table if we don't need it ! */
  tables[1].next=((column_priv ||
		   (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
		  ? tables+2 : 0);
  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";

2134 2135 2136 2137 2138 2139 2140 2141 2142
#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
  if (thd->slave_thread && table_rules_on && !tables_ok(0, tables))
    DBUG_RETURN(0);
#endif

unknown's avatar
unknown committed
2143 2144 2145 2146 2147 2148
  if (open_and_lock_tables(thd,tables))
  {						// Should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
    DBUG_RETURN(-1);				/* purecov: deadcode */
  }

unknown's avatar
unknown committed
2149 2150
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
unknown's avatar
unknown committed
2151
  int result=0;
2152
  rw_wrlock(&LOCK_grant);
unknown's avatar
unknown committed
2153 2154 2155 2156 2157
  MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
  my_pthread_setspecific_ptr(THR_MALLOC,&memex);

  while ((Str = str_list++))
  {
unknown's avatar
unknown committed
2158
    int error;
unknown's avatar
unknown committed
2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172
    GRANT_TABLE *grant_table;
    if (!Str->host.str)
    {
      Str->host.str=(char*) "%";
      Str->host.length=1;
    }
    if (Str->host.length > HOSTNAME_LENGTH ||
	Str->user.length > USERNAME_LENGTH)
    {
      my_error(ER_GRANT_WRONG_HOST_OR_USER,MYF(0));
      result= -1;
      continue;
    }
    /* Create user if needed */
unknown's avatar
unknown committed
2173
    pthread_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
2174 2175
    error=replace_user_table(thd, tables[0].table, *Str,
			     0, revoke_grant, create_new_users);
unknown's avatar
unknown committed
2176 2177
    pthread_mutex_unlock(&acl_cache->lock);
    if (error)
unknown's avatar
unknown committed
2178 2179 2180 2181 2182 2183 2184 2185
    {
      result= -1;				// Remember error
      continue;					// Add next user
    }

    /* Find/create cached table grant */
    grant_table= table_hash_search(Str->host.str,NullS,table_list->db,
				   Str->user.str,
2186
				   table_list->real_name,1);
unknown's avatar
unknown committed
2187 2188 2189 2190 2191 2192
    if (!grant_table)
    {
      if (revoke_grant)
      {
	my_printf_error(ER_NONEXISTING_TABLE_GRANT,
			ER(ER_NONEXISTING_TABLE_GRANT),MYF(0),
unknown's avatar
unknown committed
2193
			Str->user.str, Str->host.str, table_list->real_name);
unknown's avatar
unknown committed
2194 2195 2196 2197 2198
	result= -1;
	continue;
      }
      grant_table = new GRANT_TABLE (Str->host.str,table_list->db,
				     Str->user.str,
2199
				     table_list->real_name,
unknown's avatar
unknown committed
2200 2201 2202 2203 2204 2205 2206
				     rights,
				     column_priv);
      if (!grant_table)				// end of memory
      {
	result= -1;				/* purecov: deadcode */
	continue;				/* purecov: deadcode */
      }
2207
      hash_insert(&column_priv_hash,(byte*) grant_table);
unknown's avatar
unknown committed
2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245
    }

    /* If revoke_grant, calculate the new column privilege for tables_priv */
    if (revoke_grant)
    {
      class LEX_COLUMN *check;
      List_iterator <LEX_COLUMN> iter(columns);
      GRANT_COLUMN *grant_column;

      /* Fix old grants */
      while ((check = iter++))
      {
	grant_column = column_hash_search(grant_table,
					  check->column.ptr(),
					  check->column.length());
	if (grant_column)
	  grant_column->rights&= ~(check->rights | rights);
      }
      /* scan trough all columns to get new column grant */
      column_priv=0;
      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 */

    if (replace_table_table(thd,grant_table,tables[1].table,*Str,
			    table_list->db,
2246
			    table_list->real_name,
unknown's avatar
unknown committed
2247 2248 2249 2250 2251 2252 2253 2254 2255
			    rights, column_priv, revoke_grant))
    {						// Crashend table ??
      result= -1;			       /* purecov: deadcode */
    }
    else if (tables[2].table)
    {
      if ((replace_column_table(grant_table,tables[2].table, *Str,
				columns,
				table_list->db,
2256
				table_list->real_name,
unknown's avatar
unknown committed
2257 2258 2259 2260 2261 2262 2263 2264
				rights, revoke_grant)))
      {
	result= -1;
      }
    }
  }
  grant_option=TRUE;
  my_pthread_setspecific_ptr(THR_MALLOC,old_root);
2265
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2266
  if (!result)
2267
    send_ok(thd);
2268
  /* Tables are automatically closed */
unknown's avatar
unknown committed
2269 2270 2271 2272
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
2273 2274
int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list,
		 ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
2275 2276 2277
{
  List_iterator <LEX_USER> str_list (list);
  LEX_USER *Str;
unknown's avatar
unknown committed
2278
  char tmp_db[NAME_LEN+1];
unknown's avatar
unknown committed
2279
  bool create_new_users=0;
unknown's avatar
unknown committed
2280 2281 2282 2283
  TABLE_LIST tables[2];
  DBUG_ENTER("mysql_grant");
  if (!initialized)
  {
2284 2285
    send_error(thd, ER_UNKNOWN_COM_ERROR);	/* purecov: tested */
    return 1;					/* purecov: tested */
unknown's avatar
unknown committed
2286 2287
  }

unknown's avatar
unknown committed
2288 2289 2290
  if (lower_case_table_names && db)
  {
    strmov(tmp_db,db);
unknown's avatar
unknown committed
2291
    my_casedn_str(&my_charset_latin1, tmp_db);
unknown's avatar
unknown committed
2292 2293
    db=tmp_db;
  }
unknown's avatar
unknown committed
2294 2295 2296

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

2297 2298
  tables[0].alias=tables[0].real_name=(char*) "user";
  tables[1].alias=tables[1].real_name=(char*) "db";
unknown's avatar
unknown committed
2299 2300 2301 2302 2303
  tables[0].next=tables+1;
  tables[1].next=0;
  tables[0].lock_type=tables[1].lock_type=TL_WRITE;
  tables[0].db=tables[1].db=(char*) "mysql";
  tables[0].table=tables[1].table=0;
2304 2305 2306 2307 2308 2309 2310 2311 2312 2313

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
  if (thd->slave_thread && table_rules_on && !tables_ok(0, tables))
    DBUG_RETURN(0);
#endif

unknown's avatar
unknown committed
2314 2315 2316 2317 2318 2319
  if (open_and_lock_tables(thd,tables))
  {						// This should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
    DBUG_RETURN(-1);				/* purecov: deadcode */
  }

unknown's avatar
unknown committed
2320 2321
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
unknown's avatar
unknown committed
2322

unknown's avatar
unknown committed
2323
  // go through users in user_list
2324
  rw_wrlock(&LOCK_grant);
unknown's avatar
unknown committed
2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342
  VOID(pthread_mutex_lock(&acl_cache->lock));
  grant_version++;

  int result=0;
  while ((Str = str_list++))
  {
    if (!Str->host.str)
    {
      Str->host.str=(char*) "%";
      Str->host.length=1;
    }
    if (Str->host.length > HOSTNAME_LENGTH ||
	Str->user.length > USERNAME_LENGTH)
    {
      my_error(ER_GRANT_WRONG_HOST_OR_USER,MYF(0));
      result= -1;
      continue;
    }
2343 2344
    if ((replace_user_table(thd,
		            tables[0].table,
unknown's avatar
unknown committed
2345
			    *Str,
unknown's avatar
unknown committed
2346 2347
			    (!db ? rights : 0), revoke_grant,
			    create_new_users)))
unknown's avatar
unknown committed
2348
      result= -1;
unknown's avatar
unknown committed
2349 2350 2351
    else
    {
      if (db && replace_db_table(tables[1].table, db, *Str, rights & DB_ACLS,
unknown's avatar
unknown committed
2352
				 revoke_grant))
unknown's avatar
unknown committed
2353 2354
	result= -1;
    }
unknown's avatar
unknown committed
2355 2356
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
2357
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2358 2359 2360
  close_thread_tables(thd);

  if (!result)
2361
    send_ok(thd);
unknown's avatar
unknown committed
2362 2363 2364
  DBUG_RETURN(result);
}

unknown's avatar
unknown committed
2365 2366

/* Free grant array if possible */
unknown's avatar
unknown committed
2367 2368 2369 2370 2371

void  grant_free(void)
{
  DBUG_ENTER("grant_free");
  grant_option = FALSE;
2372
  hash_free(&column_priv_hash);
2373
  free_root(&memex,MYF(0));
unknown's avatar
unknown committed
2374 2375 2376 2377 2378 2379
  DBUG_VOID_RETURN;
}


/* Init grant array if possible */

unknown's avatar
unknown committed
2380
my_bool grant_init(THD *org_thd)
unknown's avatar
unknown committed
2381
{
unknown's avatar
unknown committed
2382
  THD  *thd;
unknown's avatar
unknown committed
2383
  TABLE_LIST tables[2];
2384 2385
  MYSQL_LOCK *lock;
  my_bool return_val= 1;
unknown's avatar
unknown committed
2386 2387 2388 2389
  TABLE *t_table, *c_table;
  DBUG_ENTER("grant_init");

  grant_option = FALSE;
unknown's avatar
unknown committed
2390
  (void) hash_init(&column_priv_hash,&my_charset_latin1,
unknown's avatar
unknown committed
2391
		   0,0,0, (hash_get_key) get_grant_table,
unknown's avatar
unknown committed
2392
		   (hash_free_key) free_grant_table,0);
2393
  init_sql_alloc(&memex,1024,0);
unknown's avatar
unknown committed
2394

2395
  /* Don't do anything if running with --skip-grant */
unknown's avatar
unknown committed
2396 2397
  if (!initialized)
    DBUG_RETURN(0);				/* purecov: tested */
2398

unknown's avatar
unknown committed
2399 2400
  if (!(thd=new THD))
    DBUG_RETURN(1);				/* purecov: deadcode */
2401
  thd->store_globals();
unknown's avatar
unknown committed
2402 2403
  thd->db= my_strdup("mysql",MYF(0));
  thd->db_length=5;				// Safety
unknown's avatar
unknown committed
2404
  bzero((char*) &tables,sizeof(tables));
2405 2406
  tables[0].alias=tables[0].real_name= (char*) "tables_priv";
  tables[1].alias=tables[1].real_name= (char*) "columns_priv";
unknown's avatar
unknown committed
2407 2408 2409 2410 2411
  tables[0].next=tables+1;
  tables[0].lock_type=tables[1].lock_type=TL_READ;
  tables[0].db=tables[1].db=thd->db;

  if (open_tables(thd,tables))
2412 2413
    goto end;

unknown's avatar
unknown committed
2414 2415 2416
  TABLE *ptr[2];				// Lock tables for quick update
  ptr[0]= tables[0].table;
  ptr[1]= tables[1].table;
2417 2418
  if (!(lock=mysql_lock_tables(thd,ptr,2)))
    goto end;
unknown's avatar
unknown committed
2419 2420 2421 2422 2423 2424

  t_table = tables[0].table; c_table = tables[1].table;
  t_table->file->index_init(0);
  if (t_table->file->index_first(t_table->record[0]))
  {
    t_table->file->index_end();
2425
    return_val= 0;
2426
    goto end_unlock;
unknown's avatar
unknown committed
2427
  }
unknown's avatar
unknown committed
2428
  grant_option= TRUE;
unknown's avatar
unknown committed
2429 2430
  t_table->file->index_end();

2431
  /* Will be restored by org_thd->store_globals() */
unknown's avatar
unknown committed
2432
  my_pthread_setspecific_ptr(THR_MALLOC,&memex);
2433
  do
unknown's avatar
unknown committed
2434 2435 2436
  {
    GRANT_TABLE *mem_check;
    if (!(mem_check=new GRANT_TABLE(t_table,c_table)) ||
2437
	mem_check->ok() && hash_insert(&column_priv_hash,(byte*) mem_check))
unknown's avatar
unknown committed
2438 2439 2440
    {
      /* This could only happen if we are out memory */
      grant_option = FALSE;			/* purecov: deadcode */
2441
      goto end_unlock;
unknown's avatar
unknown committed
2442 2443
    }
  }
2444 2445 2446 2447 2448
  while (!t_table->file->index_next(t_table->record[0]));

  return_val=0;					// Return ok

end_unlock:
unknown's avatar
unknown committed
2449 2450
  mysql_unlock_tables(thd, lock);
  thd->version--;				// Force close to free memory
2451 2452

end:
unknown's avatar
unknown committed
2453 2454
  close_thread_tables(thd);
  delete thd;
2455 2456
  if (org_thd)
    org_thd->store_globals();
unknown's avatar
unknown committed
2457 2458 2459 2460 2461
  else
  {
    /* Remember that we don't have a THD */
    my_pthread_setspecific_ptr(THR_THD,  0);
  }
2462
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
2463 2464 2465
}


2466
/* Reload grant array (table and column privileges) if possible */
unknown's avatar
unknown committed
2467

unknown's avatar
unknown committed
2468
void grant_reload(THD *thd)
unknown's avatar
unknown committed
2469
{
2470 2471
  HASH old_column_priv_hash;
  bool old_grant_option;
unknown's avatar
unknown committed
2472 2473 2474 2475 2476
  MEM_ROOT old_mem;
  DBUG_ENTER("grant_reload");

  // Locked tables are checked by acl_init and doesn't have to be checked here

2477
  rw_wrlock(&LOCK_grant);
unknown's avatar
unknown committed
2478
  grant_version++;
2479
  old_column_priv_hash= column_priv_hash;
unknown's avatar
unknown committed
2480 2481 2482
  old_grant_option = grant_option;
  old_mem = memex;

unknown's avatar
unknown committed
2483
  if (grant_init(thd))
unknown's avatar
unknown committed
2484
  {						// Error. Revert to old hash
2485
    DBUG_PRINT("error",("Reverting to old privileges"));
unknown's avatar
unknown committed
2486
    grant_free();				/* purecov: deadcode */
2487
    column_priv_hash= old_column_priv_hash;	/* purecov: deadcode */
unknown's avatar
unknown committed
2488 2489 2490 2491 2492
    grant_option = old_grant_option;		/* purecov: deadcode */
    memex = old_mem;				/* purecov: deadcode */
  }
  else
  {
2493
    hash_free(&old_column_priv_hash);
2494
    free_root(&old_mem,MYF(0));
unknown's avatar
unknown committed
2495
  }
2496
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2497 2498 2499 2500 2501
  DBUG_VOID_RETURN;
}


/****************************************************************************
unknown's avatar
unknown committed
2502 2503
  Check grants
  All errors are written directly to the client if command name is given !
unknown's avatar
unknown committed
2504 2505
****************************************************************************/

unknown's avatar
unknown committed
2506
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
unknown's avatar
unknown committed
2507
		 uint show_table, bool no_errors)
unknown's avatar
unknown committed
2508 2509 2510 2511 2512 2513 2514 2515
{
  TABLE_LIST *table;
  char *user = thd->priv_user;

  want_access &= ~thd->master_access;
  if (!want_access)
    return 0;					// ok

2516
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
2517 2518 2519 2520 2521 2522 2523
  for (table=tables; table ;table=table->next)
  {
    if (!(~table->grant.privilege & want_access))
    {
      table->grant.want_privilege=0;
      continue;					// Already checked
    }
unknown's avatar
unknown committed
2524 2525
    GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip,
						 table->db,user,
unknown's avatar
unknown committed
2526 2527 2528 2529 2530 2531
						 table->real_name,0);
    if (!grant_table)
    {
      want_access &= ~table->grant.privilege;
      goto err;					// No grants
    }
unknown's avatar
unknown committed
2532 2533
    if (show_table)
      continue;					// We have some priv on this
unknown's avatar
unknown committed
2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549

    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
    }
  }
2550
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2551 2552 2553
  return 0;

 err:
2554
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2555
  if (!no_errors)				// Not a silent skip of table
unknown's avatar
unknown committed
2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575
  {
    const char *command="";
    if (want_access & SELECT_ACL)
       command ="select";
    else if (want_access & INSERT_ACL)
      command = "insert";
    else if (want_access & UPDATE_ACL)
      command = "update";
    else if (want_access & DELETE_ACL)
      command = "delete";
    else if (want_access & DROP_ACL)
      command = "drop";
    else if (want_access & CREATE_ACL)
      command = "create";
    else if (want_access & ALTER_ACL)
      command = "alter";
    else if (want_access & INDEX_ACL)
      command = "index";
    else if (want_access & GRANT_ACL)
      command = "grant";
2576
    net_printf(thd,ER_TABLEACCESS_DENIED_ERROR,
unknown's avatar
unknown committed
2577 2578
	       command,
	       thd->priv_user,
2579
	       thd->host_or_ip,
unknown's avatar
unknown committed
2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591
	       table ? table->real_name : "unknown");
  }
  return 1;
}


bool check_grant_column (THD *thd,TABLE *table, const char *name,
			 uint length, uint show_tables)
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;

unknown's avatar
unknown committed
2592
  ulong want_access=table->grant.want_privilege;
unknown's avatar
unknown committed
2593 2594 2595
  if (!want_access)
    return 0;					// Already checked

2596
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613

  // reload table if someone has modified any grants

  if (table->grant.version != grant_version)
  {
    table->grant.grant_table=
      table_hash_search(thd->host,thd->ip,thd->db,
			thd->priv_user,
			table->real_name,0);	/* purecov: inspected */
    table->grant.version=grant_version;		/* purecov: inspected */
  }
  if (!(grant_table=table->grant.grant_table))
    goto err;					/* purecov: deadcode */

  grant_column=column_hash_search(grant_table, name, length);
  if (grant_column && !(~grant_column->rights & want_access))
  {
2614
    rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2615 2616 2617 2618 2619
    return 0;
  }
#ifdef NOT_USED
  if (show_tables && (grant_column || table->grant.privilege & COL_ACLS))
  {
2620
    rw_unlock(&LOCK_grant);			/* purecov: deadcode */
unknown's avatar
unknown committed
2621 2622 2623 2624 2625 2626
    return 0;					/* purecov: deadcode */
  }
#endif

  /* We must use my_printf_error() here! */
 err:
2627
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2628 2629
  if (!show_tables)
  {
unknown's avatar
unknown committed
2630 2631
    char command[128];
    get_privilege_desc(command, sizeof(command), want_access);
unknown's avatar
unknown committed
2632 2633 2634 2635 2636
    my_printf_error(ER_COLUMNACCESS_DENIED_ERROR,
		    ER(ER_COLUMNACCESS_DENIED_ERROR),
		    MYF(0),
		    command,
		    thd->priv_user,
2637
		    thd->host_or_ip,
unknown's avatar
unknown committed
2638 2639 2640 2641 2642 2643 2644
		    name,
		    table ? table->real_name : "unknown");
  }
  return 1;
}


unknown's avatar
unknown committed
2645
bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table)
unknown's avatar
unknown committed
2646 2647 2648 2649 2650 2651 2652 2653 2654
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
  Field *field=0,**ptr;

  want_access &= ~table->grant.privilege;
  if (!want_access)
    return 0;					// Already checked

2655
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673

  // reload table if someone has modified any grants

  if (table->grant.version != grant_version)
  {
    table->grant.grant_table=
      table_hash_search(thd->host,thd->ip,thd->db,
			thd->priv_user,
			table->real_name,0);	/* purecov: inspected */
    table->grant.version=grant_version;		/* purecov: inspected */
  }
  // The following should always be true
  if (!(grant_table=table->grant.grant_table))
    goto err;					/* purecov: inspected */

  for (ptr=table->field; (field= *ptr) ; ptr++)
  {
    grant_column=column_hash_search(grant_table, field->field_name,
unknown's avatar
unknown committed
2674
				    (uint) strlen(field->field_name));
unknown's avatar
unknown committed
2675 2676 2677
    if (!grant_column || (~grant_column->rights & want_access))
      goto err;
  }
2678
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2679 2680 2681 2682
  return 0;

  /* We must use my_printf_error() here! */
 err:
2683
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694

  const char *command="";
  if (want_access & SELECT_ACL)
    command ="select";
  else if (want_access & INSERT_ACL)
    command = "insert";
  my_printf_error(ER_COLUMNACCESS_DENIED_ERROR,
		  ER(ER_COLUMNACCESS_DENIED_ERROR),
		  MYF(0),
		  command,
		  thd->priv_user,
2695
		  thd->host_or_ip,
unknown's avatar
unknown committed
2696 2697 2698 2699 2700 2701 2702
		  field ? field->field_name : "unknown",
		  table->real_name);
  return 1;
}


/****************************************************************************
unknown's avatar
unknown committed
2703 2704 2705
  Check if a user has the right to access a database
  Access is accepted if he has a grant for any table in the database
  Return 1 if access is denied
unknown's avatar
unknown committed
2706 2707 2708 2709 2710 2711 2712 2713 2714
****************************************************************************/

bool check_grant_db(THD *thd,const char *db)
{
  char helping [NAME_LEN+USERNAME_LENGTH+2];
  uint len;
  bool error=1;

  len  = (uint) (strmov(strmov(helping,thd->priv_user)+1,db)-helping)+ 1;
2715
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
2716

2717
  for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
unknown's avatar
unknown committed
2718
  {
2719 2720
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  idx);
unknown's avatar
unknown committed
2721 2722
    if (len < grant_table->key_length &&
	!memcmp(grant_table->hash_key,helping,len) &&
unknown's avatar
unknown committed
2723
	(thd->host && !wild_case_compare(&my_charset_latin1,
2724
                                         thd->host,grant_table->host) ||
unknown's avatar
unknown committed
2725
	 (thd->ip && !wild_case_compare(&my_charset_latin1,
2726
                                        thd->ip,grant_table->host))))
unknown's avatar
unknown committed
2727 2728 2729 2730 2731
    {
      error=0;					// Found match
      break;
    }
  }
2732
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2733 2734 2735 2736
  return error;
}

/*****************************************************************************
unknown's avatar
unknown committed
2737
  Functions to retrieve the grant for a table/column  (for SHOW functions)
unknown's avatar
unknown committed
2738 2739
*****************************************************************************/

unknown's avatar
unknown committed
2740
ulong get_table_grant(THD *thd, TABLE_LIST *table)
unknown's avatar
unknown committed
2741
{
unknown's avatar
unknown committed
2742
  ulong privilege;
unknown's avatar
unknown committed
2743 2744 2745 2746
  char *user = thd->priv_user;
  const char *db = table->db ? table->db : thd->db;
  GRANT_TABLE *grant_table;

2747
  rw_rdlock(&LOCK_grant);
2748 2749 2750
#ifdef EMBEDDED_LIBRARY
  grant_table= NULL;
#else
unknown's avatar
unknown committed
2751 2752
  grant_table = table_hash_search(thd->host,thd->ip,db,user,
				       table->real_name,0);
2753
#endif
unknown's avatar
unknown committed
2754 2755 2756 2757
  table->grant.grant_table=grant_table; // Remember for column test
  table->grant.version=grant_version;
  if (grant_table)
    table->grant.privilege|= grant_table->privs;
unknown's avatar
unknown committed
2758
  privilege= table->grant.privilege;
2759
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2760
  return privilege;
unknown's avatar
unknown committed
2761 2762 2763
}


unknown's avatar
unknown committed
2764
ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field)
unknown's avatar
unknown committed
2765 2766 2767
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
2768
  ulong priv;
unknown's avatar
unknown committed
2769

2770
  rw_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785
  // reload table if someone has modified any grants
  if (table->grant.version != grant_version)
  {
    table->grant.grant_table=
      table_hash_search(thd->host,thd->ip,thd->db,
			thd->priv_user,
			table->real_name,0);	/* purecov: inspected */
    table->grant.version=grant_version;		/* purecov: inspected */
  }

  if (!(grant_table=table->grant.grant_table))
    priv=table->grant.privilege;
  else
  {
    grant_column=column_hash_search(grant_table, field->field_name,
unknown's avatar
unknown committed
2786
				    (uint) strlen(field->field_name));
unknown's avatar
unknown committed
2787 2788 2789 2790 2791
    if (!grant_column)
      priv=table->grant.privilege;
    else
      priv=table->grant.privilege | grant_column->rights;
  }
2792
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
2793 2794 2795 2796 2797
  return priv;
}


/*****************************************************************************
unknown's avatar
unknown committed
2798 2799
  SHOW GRANTS : send to client grant-like strings depicting user@host
  privileges
unknown's avatar
unknown committed
2800 2801 2802
*****************************************************************************/

static const char *command_array[]=
unknown's avatar
unknown committed
2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813
{
  "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",
};
static uint command_lengths[]=
{
  6,6,6,6,6,4,6,8,7,4,5,10,5,5,14,5,23,11,7,17,18
};

unknown's avatar
unknown committed
2814

unknown's avatar
unknown committed
2815
int mysql_show_grants(THD *thd,LEX_USER *lex_user)
unknown's avatar
unknown committed
2816
{
unknown's avatar
unknown committed
2817 2818
  ulong want_access;
  uint counter,index;
unknown's avatar
unknown committed
2819 2820 2821
  int  error = 0;
  ACL_USER *acl_user; ACL_DB *acl_db;
  char buff[1024];
unknown's avatar
unknown committed
2822
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
2823
  DBUG_ENTER("mysql_show_grants");
unknown's avatar
unknown committed
2824 2825 2826 2827

  LINT_INIT(acl_user);
  if (!initialized)
  {
2828
    send_error(thd, ER_UNKNOWN_COM_ERROR);
unknown's avatar
unknown committed
2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851
    DBUG_RETURN(-1);
  }
  if (!lex_user->host.str)
  {
    lex_user->host.str=(char*) "%";
    lex_user->host.length=1;
  }
  if (lex_user->host.length > HOSTNAME_LENGTH ||
      lex_user->user.length > USERNAME_LENGTH)
  {
    my_error(ER_GRANT_WRONG_HOST_OR_USER,MYF(0));
    DBUG_RETURN(-1);
  }

  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))
      user="";
    if (!(host=acl_user->host.hostname))
      host="%";
    if (!strcmp(lex_user->user.str,user) &&
unknown's avatar
unknown committed
2852
	!my_strcasecmp(&my_charset_latin1, lex_user->host.str, host))
unknown's avatar
unknown committed
2853 2854
      break;
  }
unknown's avatar
unknown committed
2855
  if (counter == acl_users.elements)
unknown's avatar
unknown committed
2856 2857 2858 2859 2860 2861
  {
    my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),
		    MYF(0),lex_user->user.str,lex_user->host.str);
    DBUG_RETURN(-1);
  }

unknown's avatar
unknown committed
2862
  Item_string *field=new Item_string("",0,&my_charset_latin1);
unknown's avatar
unknown committed
2863 2864 2865 2866 2867 2868
  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);
unknown's avatar
unknown committed
2869
  if (protocol->send_fields(&field_list,1))
unknown's avatar
unknown committed
2870 2871
    DBUG_RETURN(-1);

unknown's avatar
unknown committed
2872
  rw_wrlock(&LOCK_grant);
unknown's avatar
unknown committed
2873 2874 2875 2876 2877
  VOID(pthread_mutex_lock(&acl_cache->lock));

  /* Add first global access grants */
  {
    want_access=acl_user->access;
unknown's avatar
unknown committed
2878
    String global(buff,sizeof(buff),&my_charset_latin1);
unknown's avatar
unknown committed
2879 2880 2881 2882 2883 2884 2885
    global.length(0);
    global.append("GRANT ",6);

    if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
      global.append("ALL PRIVILEGES",14);
    else if (!(want_access & ~GRANT_ACL))
      global.append("USAGE",5);
unknown's avatar
unknown committed
2886
    else
unknown's avatar
unknown committed
2887 2888
    {
      bool found=0;
unknown's avatar
unknown committed
2889
      ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
2890 2891
      for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
      {
unknown's avatar
unknown committed
2892
	if (test_access & j)
unknown's avatar
unknown committed
2893 2894 2895 2896 2897 2898 2899 2900 2901
	{
	  if (found)
	    global.append(", ",2);
	  found=1;
	  global.append(command_array[counter],command_lengths[counter]);
	}
      }
    }
    global.append (" ON *.* TO '",12);
unknown's avatar
unknown committed
2902
    global.append(lex_user->user.str,lex_user->user.length);
unknown's avatar
unknown committed
2903 2904 2905 2906 2907 2908
    global.append ("'@'",3);
    global.append(lex_user->host.str,lex_user->host.length);
    global.append ('\'');
    if (acl_user->password)
    {
      char passd_buff[HASH_PASSWORD_LENGTH+1];
2909
      make_password_from_salt(passd_buff,acl_user->salt,acl_user->pversion);
unknown's avatar
unknown committed
2910 2911 2912 2913
      global.append(" IDENTIFIED BY PASSWORD '",25);
      global.append(passd_buff);
      global.append('\'');
    }
unknown's avatar
unknown committed
2914 2915
    /* "show grants" SSL related stuff */
    if (acl_user->ssl_type == SSL_TYPE_ANY)
2916
      global.append(" REQUIRE SSL",12);
2917
    else if (acl_user->ssl_type == SSL_TYPE_X509)
2918
      global.append(" REQUIRE X509",13);
2919
    else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
2920
    {
2921
      int ssl_options = 0;
2922
      global.append(" REQUIRE ",9);
unknown's avatar
unknown committed
2923 2924
      if (acl_user->x509_issuer)
      {
2925
        ssl_options++;
2926
        global.append("ISSUER \'",8);
2927
        global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
2928
	global.append('\'');
2929
      }
unknown's avatar
unknown committed
2930 2931 2932
      if (acl_user->x509_subject)
      {
        if (ssl_options++)
2933 2934
          global.append(' ');
        global.append("SUBJECT \'",9);
2935
        global.append(acl_user->x509_subject,strlen(acl_user->x509_subject));
2936
	global.append('\'');
unknown's avatar
unknown committed
2937
      }
unknown's avatar
unknown committed
2938 2939 2940
      if (acl_user->ssl_cipher)
      {
        if (ssl_options++)
2941
          global.append(' ');
2942
        global.append("CIPHER '",8);
2943
        global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher));
2944
	global.append('\'');
2945 2946
      }
    }
unknown's avatar
unknown committed
2947 2948 2949
    if ((want_access & GRANT_ACL) ||
	(acl_user->user_resource.questions | acl_user->user_resource.updates |
	 acl_user->user_resource.connections))
2950
    {
unknown's avatar
unknown committed
2951
      global.append(" WITH",5);
unknown's avatar
unknown committed
2952
      if (want_access & GRANT_ACL)
unknown's avatar
unknown committed
2953
	global.append(" GRANT OPTION",13);
unknown's avatar
unknown committed
2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974
      if (acl_user->user_resource.questions)
      {
	char buff[22], *p; // just as in int2str
	global.append(" MAX_QUERIES_PER_HOUR ",22);
	p=int10_to_str(acl_user->user_resource.questions,buff,10);
	global.append(buff,p-buff);
      }
      if (acl_user->user_resource.updates)
      {
	char buff[22], *p; // just as in int2str
	global.append(" MAX_UPDATES_PER_HOUR ",22);
	p=int10_to_str(acl_user->user_resource.updates,buff,10);
	global.append(buff,p-buff);
      }
      if (acl_user->user_resource.connections)
      {
	char buff[22], *p; // just as in int2str
	global.append(" MAX_CONNECTIONS_PER_HOUR ",26);
	p=int10_to_str(acl_user->user_resource.connections,buff,10);
	global.append(buff,p-buff);
      }
unknown's avatar
unknown committed
2975
    }
unknown's avatar
unknown committed
2976
    protocol->prepare_for_resend();
2977
    protocol->store(global.ptr(),global.length(),global.charset());
unknown's avatar
unknown committed
2978
    if (protocol->write())
unknown's avatar
unknown committed
2979
    {
unknown's avatar
unknown committed
2980 2981
      error=-1;
      goto end;
unknown's avatar
unknown committed
2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996
    }
  }

  /* Add database access */
  for (counter=0 ; counter < acl_dbs.elements ; counter++)
  {
    const char *user,*host;

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

    if (!strcmp(lex_user->user.str,user) &&
unknown's avatar
unknown committed
2997
	!my_strcasecmp(&my_charset_latin1, lex_user->host.str, host))
unknown's avatar
unknown committed
2998 2999
    {
      want_access=acl_db->access;
unknown's avatar
unknown committed
3000
      if (want_access)
unknown's avatar
unknown committed
3001
      {
unknown's avatar
unknown committed
3002
	String db(buff,sizeof(buff),&my_charset_latin1);
unknown's avatar
unknown committed
3003 3004 3005 3006 3007
	db.length(0);
	db.append("GRANT ",6);

	if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
	  db.append("ALL PRIVILEGES",14);
unknown's avatar
unknown committed
3008 3009
	else if (!(want_access & ~GRANT_ACL))
	  db.append("USAGE",5);	
unknown's avatar
unknown committed
3010 3011 3012
	else
	{
	  int found=0, cnt;
unknown's avatar
unknown committed
3013
	  ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024
	  for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
	  {
	    if (test_access & j)
	    {
	      if (found)
		db.append(", ",2);
	      found = 1;
	      db.append(command_array[cnt],command_lengths[cnt]);
	    }
	  }
	}
unknown's avatar
unknown committed
3025
	db.append (" ON `",5);
unknown's avatar
unknown committed
3026
	db.append(acl_db->db);
unknown's avatar
unknown committed
3027
	db.append ("`.* TO '",8);
unknown's avatar
unknown committed
3028
	db.append(lex_user->user.str,lex_user->user.length);
unknown's avatar
unknown committed
3029 3030
	db.append ("'@'",3);
	db.append(lex_user->host.str, lex_user->host.length);
unknown's avatar
unknown committed
3031
	db.append ('\'');
unknown's avatar
unknown committed
3032
	if (want_access & GRANT_ACL)
unknown's avatar
unknown committed
3033
	  db.append(" WITH GRANT OPTION",18);
unknown's avatar
unknown committed
3034
	protocol->prepare_for_resend();
3035
	protocol->store(db.ptr(),db.length(),db.charset());
unknown's avatar
unknown committed
3036
	if (protocol->write())
unknown's avatar
unknown committed
3037 3038 3039 3040 3041 3042 3043 3044 3045
	{
	  error=-1;
	  goto end;
	}
      }
    }
  }

  /* Add column access */
3046
  for (index=0 ; index < column_priv_hash.records ; index++)
unknown's avatar
unknown committed
3047 3048
  {
    const char *user,*host;
3049 3050
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
unknown's avatar
unknown committed
3051 3052 3053 3054 3055 3056 3057

    if (!(user=grant_table->user))
      user="";
    if (!(host=grant_table->host))
      host="";

    if (!strcmp(lex_user->user.str,user) &&
unknown's avatar
unknown committed
3058
	!my_strcasecmp(&my_charset_latin1, lex_user->host.str, host))
unknown's avatar
unknown committed
3059 3060
    {
      want_access=grant_table->privs;
unknown's avatar
unknown committed
3061
      if ((want_access | grant_table->cols) != 0)
unknown's avatar
unknown committed
3062
      {
unknown's avatar
unknown committed
3063
	String global(buff,sizeof(buff),&my_charset_latin1);
unknown's avatar
unknown committed
3064 3065 3066
	global.length(0);
	global.append("GRANT ",6);

unknown's avatar
unknown committed
3067
	if (test_all_bits(grant_table->privs,(TABLE_ACLS & ~GRANT_ACL)))
unknown's avatar
unknown committed
3068
	  global.append("ALL PRIVILEGES",14);
unknown's avatar
unknown committed
3069
	else
unknown's avatar
unknown committed
3070 3071
	{
	  int found=0;
unknown's avatar
unknown committed
3072
	  ulong j,test_access= (want_access | grant_table->cols) & ~GRANT_ACL;
unknown's avatar
unknown committed
3073 3074 3075

	  for (counter=0, j = SELECT_ACL;j <= TABLE_ACLS; counter++,j <<= 1)
	  {
unknown's avatar
unknown committed
3076
	    if (test_access & j)
unknown's avatar
unknown committed
3077 3078 3079 3080 3081 3082
	    {
	      if (found)
		global.append(", ",2);
	      found = 1;
	      global.append(command_array[counter],command_lengths[counter]);

unknown's avatar
unknown committed
3083
	      if (grant_table->cols)
unknown's avatar
unknown committed
3084 3085 3086 3087 3088 3089 3090 3091
	      {
		uint found_col=0;
		for (uint col_index=0 ;
		     col_index < grant_table->hash_columns.records ;
		     col_index++)
		{
		  GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
		    hash_element(&grant_table->hash_columns,col_index);
unknown's avatar
unknown committed
3092
		  if (grant_column->rights & j)
unknown's avatar
unknown committed
3093
		  {
unknown's avatar
unknown committed
3094
		    if (!found_col)
unknown's avatar
unknown committed
3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115
		    {
		      global.append(" (",2);
		      found_col=1;
		    }
		    else
		      global.append(", ",2);
		    global.append(grant_column->column,
				  grant_column->key_length);
		  }
		}
		if (found_col)
		  global.append(')');
	      }
	    }
	  }
	}
	global.append(" ON ",4);
	global.append(grant_table->db);
	global.append(".",1);
	global.append(grant_table->tname);
	global.append(" TO '",5);
unknown's avatar
unknown committed
3116
	global.append(lex_user->user.str,lex_user->user.length);
unknown's avatar
unknown committed
3117
	global.append("'@'",3);
unknown's avatar
unknown committed
3118
	global.append(lex_user->host.str,lex_user->host.length);
unknown's avatar
unknown committed
3119 3120
	global.append('\'');
	if (want_access & GRANT_ACL)
unknown's avatar
unknown committed
3121
	  global.append(" WITH GRANT OPTION",18);
unknown's avatar
unknown committed
3122
	protocol->prepare_for_resend();
3123
	protocol->store(global.ptr(),global.length(),global.charset());
unknown's avatar
unknown committed
3124
	if (protocol->write())
unknown's avatar
unknown committed
3125
	{
unknown's avatar
unknown committed
3126
	  error= -1;
unknown's avatar
unknown committed
3127
	  break;
unknown's avatar
unknown committed
3128 3129 3130 3131 3132 3133
	}
      }
    }
  }
 end:
  VOID(pthread_mutex_unlock(&acl_cache->lock));
unknown's avatar
unknown committed
3134
  rw_unlock(&LOCK_grant);
unknown's avatar
unknown committed
3135

3136
  send_eof(thd);
unknown's avatar
unknown committed
3137 3138 3139 3140
  DBUG_RETURN(error);
}


unknown's avatar
unknown committed
3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168
/*
  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;
}


3169
void get_mqh(const char *user, const char *host, USER_CONN *uc)
unknown's avatar
unknown committed
3170 3171
{
  ACL_USER *acl_user;
3172 3173 3174 3175
  if (initialized && (acl_user= find_acl_user(host,user)))
    uc->user_resources= acl_user->user_resource;
  else
    bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
unknown's avatar
unknown committed
3176 3177 3178
}


3179

unknown's avatar
unknown committed
3180
/*****************************************************************************
unknown's avatar
unknown committed
3181
  Instantiate used templates
unknown's avatar
unknown committed
3182 3183 3184 3185 3186 3187 3188 3189
*****************************************************************************/

#ifdef __GNUC__
template class List_iterator<LEX_COLUMN>;
template class List_iterator<LEX_USER>;
template class List<LEX_COLUMN>;
template class List<LEX_USER>;
#endif