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

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

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

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


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

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

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

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

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

48

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

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

static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
63
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
64 65 66
static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
static uint grant_version=0;
peter@mysql.com's avatar
peter@mysql.com committed
67
static uint priv_version=0; /* Version of priv tables. incremented by acl_init */
68
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
69 70 71 72 73
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,
74
			      const char *new_password, uint new_password_len);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
75
static void update_hostname(acl_host_and_ip *host, const char *hostname);
76
static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
77 78
			     const char *ip);

79 80 81 82 83 84 85 86 87 88 89 90 91 92
/*
  Convert scrambled password to binary form, according to scramble type, 
  Binary form is stored in user.salt.
*/

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

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
/*
  This after_update function is used when user.password is less than
  SCRAMBLE_LENGTH bytes.
*/

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

120

121 122 123 124 125
/*
  Read grant privileges from the privilege tables in the 'mysql' database.

  SYNOPSIS
    acl_init()
126
    thd				Thread handler
127 128 129 130 131 132 133 134
    dont_read_acl_tables	Set to 1 if run with --skip-grant

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


135
my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
136
{
137
  THD  *thd;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
138 139 140
  TABLE_LIST tables[3];
  TABLE *table;
  READ_RECORD read_record_info;
141 142
  MYSQL_LOCK *lock;
  my_bool return_val=1;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
143
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
144 145 146 147 148
  DBUG_ENTER("acl_init");

  if (!acl_cache)
    acl_cache=new hash_filo(ACL_CACHE_SIZE,0,0,
			    (hash_get_key) acl_entry_get_key,
bar@bar.mysql.r18.ru's avatar
bar@bar.mysql.r18.ru committed
149
			    (hash_free_key) free, system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
150
  if (dont_read_acl_tables)
151
  {
bk@work.mysql.com's avatar
bk@work.mysql.com committed
152
    DBUG_RETURN(0); /* purecov: tested */
peter@mysql.com's avatar
peter@mysql.com committed
153 154
  }

155
  priv_version++; /* Privileges updated */
156
  mysql_proc_table_exists= 1;			// Assume mysql.proc exists
peter@mysql.com's avatar
peter@mysql.com committed
157

158 159 160
  /*
    To be able to run this from boot, we allocate a temporary THD
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
161 162
  if (!(thd=new THD))
    DBUG_RETURN(1); /* purecov: inspected */
163 164
  thd->store_globals();

bk@work.mysql.com's avatar
bk@work.mysql.com committed
165
  acl_cache->clear(1);				// Clear locked hostname cache
166 167
  thd->db= my_strdup("mysql",MYF(0));
  thd->db_length=5;				// Safety
bk@work.mysql.com's avatar
bk@work.mysql.com committed
168
  bzero((char*) &tables,sizeof(tables));
169 170 171
  tables[0].alias=tables[0].table_name=(char*) "host";
  tables[1].alias=tables[1].table_name=(char*) "user";
  tables[2].alias=tables[2].table_name=(char*) "db";
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
172 173
  tables[0].next_local= tables[0].next_global= tables+1;
  tables[1].next_local= tables[1].next_global= tables+2;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
174 175 176
  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;

177 178
  uint counter;
  if (open_tables(thd, tables, &counter))
179 180 181
  {
    sql_print_error("Fatal error: Can't open privilege tables: %s",
		    thd->net.last_error);
182
    goto end;
183
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
184 185 186 187
  TABLE *ptr[3];				// Lock tables for quick update
  ptr[0]= tables[0].table;
  ptr[1]= tables[1].table;
  ptr[2]= tables[2].table;
188
  if (!(lock=mysql_lock_tables(thd,ptr,3)))
189 190 191
  {
    sql_print_error("Fatal error: Can't lock privilege tables: %s",
		    thd->net.last_error);
192
    goto end;
193
  }
194
  init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
195
  init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0);
196
  VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
197 198 199
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_HOST host;
200 201
    update_hostname(&host.host,get_field(&mem, table->field[0]));
    host.db=	 get_field(&mem, table->field[1]);
202 203
    host.access= get_access(table,2);
    host.access= fix_rights_for_db(host.access);
204
    host.sort=	 get_sort(2,host.host.hostname,host.db);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
205 206
    if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
    {
serg@serg.mylan's avatar
serg@serg.mylan committed
207
      sql_print_warning("'host' entry '%s|%s' "
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
208
		      "ignored in --skip-name-resolve mode.",
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
209 210 211
		      host.host.hostname, host.db, host.host.hostname);
      continue;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
212
#ifndef TO_BE_REMOVED
213
    if (table->s->fields == 8)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
214 215
    {						// Without grant
      if (host.access & CREATE_ACL)
216
	host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
217 218 219 220 221 222 223 224 225 226
    }
#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);
227
  VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
228
  if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
229
  {
230 231 232
    sql_print_error("Fatal error: mysql.user table is damaged or in "
                    "unsupported 3.20 format.");
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
233 234
  }

235
  DBUG_PRINT("info",("user table fields: %d, password length: %d",
236
		     table->s->fields, table->field[2]->field_length));
237 238 239
  
  pthread_mutex_lock(&LOCK_global_system_variables);
  if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
240
  {
241 242 243 244 245 246 247 248 249 250 251 252 253 254
    if (opt_secure_auth)
    {
      pthread_mutex_unlock(&LOCK_global_system_variables);
      sql_print_error("Fatal error: mysql.user table is in old format, "
                      "but server started with --secure-auth option.");
      goto end;
    }
    sys_old_passwords.after_update= restrict_update_of_old_passwords_var;
    if (global_system_variables.old_passwords)
      pthread_mutex_unlock(&LOCK_global_system_variables);
    else
    {
      global_system_variables.old_passwords= 1;
      pthread_mutex_unlock(&LOCK_global_system_variables);
255 256 257
      sql_print_warning("mysql.user table is not updated to new password format; "
                        "Disabling new password usage until "
                        "mysql_fix_privilege_tables is run");
258 259 260 261
    }
    thd->variables.old_passwords= 1;
  }
  else
262
  {
263 264
    sys_old_passwords.after_update= 0;
    pthread_mutex_unlock(&LOCK_global_system_variables);
265 266
  }

bk@work.mysql.com's avatar
bk@work.mysql.com committed
267 268 269 270
  allow_all_hosts=0;
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_USER user;
271 272
    update_hostname(&user.host, get_field(&mem, table->field[0]));
    user.user= get_field(&mem, table->field[1]);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
273 274
    if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
    {
serg@serg.mylan's avatar
serg@serg.mylan committed
275 276
      sql_print_warning("'user' entry '%s@%s' "
                        "ignored in --skip-name-resolve mode.",
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
277 278 279 280
		      user.user, user.host.hostname, user.host.hostname);
      continue;
    }

281 282 283 284
    const char *password= get_field(&mem, table->field[2]);
    uint password_len= password ? strlen(password) : 0;
    set_user_salt(&user, password, password_len);
    if (user.salt_len == 0 && password_len != 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
285
    {
286 287
      switch (password_len) {
      case 45: /* 4.1: to be removed */
serg@serg.mylan's avatar
serg@serg.mylan committed
288 289 290 291 292
        sql_print_warning("Found 4.1 style password for user '%s@%s'. "
                          "Ignoring user. "
                          "You should change password for this user.",
                          user.user ? user.user : "",
                          user.host.hostname ? user.host.hostname : "");
293 294
        break;
      default:
serg@serg.mylan's avatar
serg@serg.mylan committed
295 296 297
        sql_print_warning("Found invalid password for user: '%s@%s'; "
                          "Ignoring user", user.user ? user.user : "",
                           user.host.hostname ? user.host.hostname : "");
298 299
        break;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
300
    }
301
    else                                        // password is correct
bk@work.mysql.com's avatar
bk@work.mysql.com committed
302
    {
303 304
      uint next_field;
      user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
305 306 307 308
      /*
        if it is pre 5.0.1 privilege table then map CREATE privilege on
        CREATE VIEW & SHOW VIEW privileges
      */
309
      if (table->s->fields <= 31 && (user.access & CREATE_ACL))
310
        user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
311 312 313 314 315

      /*
        if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
        CREATE PROCEDURE & ALTER PROCEDURE privileges
      */
316
      if (table->s->fields <= 33 && (user.access & CREATE_ACL))
317
        user.access|= CREATE_PROC_ACL;
318
      if (table->s->fields <= 33 && (user.access & ALTER_ACL))
319 320
        user.access|= ALTER_PROC_ACL;

321 322 323
      user.sort= get_sort(2,user.host.hostname,user.user);
      user.hostname_length= (user.host.hostname ?
                             (uint) strlen(user.host.hostname) : 0);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
324

325 326
      /* Starting from 4.0.2 we have more fields */
      if (table->s->fields >= 31)
327
      {
328
        char *ssl_type=get_field(&mem, table->field[next_field++]);
329 330 331 332 333 334 335 336 337
        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;

338 339 340
        user.ssl_cipher=   get_field(&mem, table->field[next_field++]);
        user.x509_issuer=  get_field(&mem, table->field[next_field++]);
        user.x509_subject= get_field(&mem, table->field[next_field++]);
341

342 343 344 345 346
        char *ptr = get_field(&mem, table->field[next_field++]);
        user.user_resource.questions=ptr ? atoi(ptr) : 0;
        ptr = get_field(&mem, table->field[next_field++]);
        user.user_resource.updates=ptr ? atoi(ptr) : 0;
        ptr = get_field(&mem, table->field[next_field++]);
347
        user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
348
        if (user.user_resource.questions || user.user_resource.updates ||
349
            user.user_resource.conn_per_hour)
350
          mqh_used=1;
351

352
        if (table->s->fields >= 36)
353 354 355 356 357 358 359
        {
          /* Starting from 5.0.3 we have max_user_connections field */
          ptr= get_field(&mem, table->field[next_field++]);
          user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
        }
        else
          user.user_resource.user_conn= 0;
360
      }
361 362 363
      else
      {
        user.ssl_type=SSL_TYPE_NONE;
364
        bzero((char *)&(user.user_resource),sizeof(user.user_resource));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
365
#ifndef TO_BE_REMOVED
366
        if (table->s->fields <= 13)
367 368 369 370 371 372 373 374 375 376
        {						// Without grant
          if (user.access & CREATE_ACL)
            user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
        }
        /* Convert old privileges */
        user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
        if (user.access & FILE_ACL)
          user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
        if (user.access & PROCESS_ACL)
          user.access|= SUPER_ACL | EXECUTE_ACL;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
377
#endif
378 379 380 381 382
      }
      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
383
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
384 385 386 387 388
  }
  qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
	sizeof(ACL_USER),(qsort_cmp) acl_compare);
  end_read_record(&read_record_info);
  freeze_size(&acl_users);
peter@mysql.com's avatar
peter@mysql.com committed
389

bk@work.mysql.com's avatar
bk@work.mysql.com committed
390
  init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0);
391
  VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
392 393 394
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_DB db;
395 396
    update_hostname(&db.host,get_field(&mem, table->field[0]));
    db.db=get_field(&mem, table->field[1]);
397 398
    if (!db.db)
    {
serg@serg.mylan's avatar
serg@serg.mylan committed
399
      sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
400
      continue;
401
    }
402
    db.user=get_field(&mem, table->field[2]);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
403 404
    if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
    {
serg@serg.mylan's avatar
serg@serg.mylan committed
405 406 407
      sql_print_warning("'db' entry '%s %s@%s' "
		        "ignored in --skip-name-resolve mode.",
		        db.db, db.user, db.host.hostname, db.host.hostname);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
408 409
      continue;
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
410 411 412 413
    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
414
    if (table->s->fields <=  9)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
415 416 417 418 419 420 421 422 423 424 425 426 427 428
    {						// 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);
429
  initialized=1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
430
  thd->version--;				// Force close to free memory
431 432 433
  return_val=0;

end:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
434 435
  close_thread_tables(thd);
  delete thd;
436 437
  if (org_thd)
    org_thd->store_globals();			/* purecov: inspected */
438 439 440 441 442
  else
  {
    /* Remember that we don't have a THD */
    my_pthread_setspecific_ptr(THR_THD,  0);
  }
443
  DBUG_RETURN(return_val);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
444 445 446 447 448
}


void acl_free(bool end)
{
449
  free_root(&mem,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
450 451 452 453 454 455 456 457 458 459 460 461 462 463
  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;
  }
}

464 465 466 467 468 469

/*
  Forget current privileges and read new privileges from the privilege tables

  SYNOPSIS
    acl_reload()
monty@mysql.com's avatar
monty@mysql.com committed
470 471
    thd			Thread handle. Note that this may be NULL if we refresh
			because we got a signal    
472
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
473

474
void acl_reload(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
475 476 477 478 479 480
{
  DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs;
  MEM_ROOT old_mem;
  bool old_initialized;
  DBUG_ENTER("acl_reload");

481
  if (thd && thd->locked_tables)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
482
  {					// Can't have locked tables here
483 484 485
    thd->lock=thd->locked_tables;
    thd->locked_tables=0;
    close_thread_tables(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
486 487 488 489 490 491 492 493 494 495 496
  }
  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);

497
  if (acl_init(thd, 0))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
498
  {					// Error. Revert to old list
499
    DBUG_PRINT("error",("Reverting to old privileges"));
500
    acl_free();				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
501 502 503 504 505 506 507 508
    acl_hosts=old_acl_hosts;
    acl_users=old_acl_users;
    acl_dbs=old_acl_dbs;
    mem=old_mem;
    init_check_host();
  }
  else
  {
509
    free_root(&old_mem,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
510 511 512 513 514 515 516 517 518 519
    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;
}


520 521
/*
  Get all access bits from table after fieldnr
522 523

  IMPLEMENTATION
524 525
  We know that the access privileges ends when there is no more fields
  or the field is not an enum with two elements.
526 527 528 529 530 531 532 533 534 535 536

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

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

539
static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
540
{
541
  ulong access_bits=0,bit;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
542
  char buff[2];
543
  String res(buff,sizeof(buff),&my_charset_latin1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
544 545
  Field **pos;

546 547 548
  for (pos=form->field+fieldnr, bit=1;
       *pos && (*pos)->real_type() == FIELD_TYPE_ENUM &&
	 ((Field_enum*) (*pos))->typelib->count == 2 ;
549
       pos++, fieldnr++, bit<<=1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
550
  {
551
    (*pos)->val_str(&res);
552
    if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
553
      access_bits|= bit;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
554
  }
555 556
  if (next_field)
    *next_field=fieldnr;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
557 558 559 560 561
  return access_bits;
}


/*
562 563 564 565 566
  Return a number which, if sorted 'desc', puts strings in this order:
    no wildcards
    wildcards
    empty string
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
567 568 569 570 571 572 573

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

574 575 576
  /* Should not use this function with more than 4 arguments for compare. */
  DBUG_ASSERT(count <= 4);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
577 578
  while (count--)
  {
579 580 581
    char *start, *str= va_arg(args,char*);
    uint chars= 0;
    uint wild_pos= 0;           /* first wildcard position */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
582

monty@mysql.com's avatar
monty@mysql.com committed
583
    if ((start= str))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
584 585 586 587
    {
      for (; *str ; str++)
      {
	if (*str == wild_many || *str == wild_one || *str == wild_prefix)
588
        {
monty@mysql.com's avatar
monty@mysql.com committed
589
          wild_pos= (uint) (str - start) + 1;
590 591
          break;
        }
monty@mysql.com's avatar
monty@mysql.com committed
592
        chars= 128;                             // Marker that chars existed
bk@work.mysql.com's avatar
bk@work.mysql.com committed
593 594
      }
    }
monty@mysql.com's avatar
monty@mysql.com committed
595
    sort= (sort << 8) + (wild_pos ? min(wild_pos, 127) : chars);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
  }
  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;
}

611

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

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

621
  SYNOPSIS
622 623 624 625 626 627
    acl_getroot()
    thd         thread handle. If all checks are OK,
                thd->priv_user, thd->master_access are updated.
                thd->host, thd->ip, thd->user are used for checks.
    mqh         user resources; on success mqh is reset, else
                unchanged
628
    passwd      scrambled & crypted password, received from client
629 630 631 632 633 634 635
                (to check): thd->scramble or thd->scramble_323 is
                used to decrypt passwd, so they must contain
                original random string,
    passwd_len  length of passwd, must be one of 0, 8,
                SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH
    'thd' and 'mqh' are updated on success; other params are IN.
  
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
636
  RETURN VALUE
637 638
    0  success: thd->priv_user, thd->priv_host, thd->master_access, mqh are
       updated
639
    1  user not found or authentication failure
640
    2  user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
641
   -1  user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
642 643
*/

644 645
int acl_getroot(THD *thd, USER_RESOURCES  *mqh,
                const char *passwd, uint passwd_len)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
646
{
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
647 648 649
  ulong user_access= NO_ACCESS;
  int res= 1;
  ACL_USER *acl_user= 0;
650
  DBUG_ENTER("acl_getroot");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
651 652

  if (!initialized)
653
  {
654 655 656 657 658 659
    /* 
      here if mysqld's been started with --skip-grant-tables option.
    */
    thd->priv_user= (char *) "";                // privileges for
    *thd->priv_host= '\0';                      // the user are unknown
    thd->master_access= ~NO_ACCESS;             // everything is allowed
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
660
    bzero((char*) mqh, sizeof(*mqh));
661
    DBUG_RETURN(0);
662
  }
663

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
666
  /*
667 668 669
    Find acl entry in user database. Note, that find_acl_user is not the same,
    because it doesn't take into account the case when user is not empty,
    but acl_user->user is empty
bk@work.mysql.com's avatar
bk@work.mysql.com committed
670
  */
peter@mysql.com's avatar
peter@mysql.com committed
671

672
  for (uint i=0 ; i < acl_users.elements ; i++)
673
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
674 675
    ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
    if (!acl_user_tmp->user || !strcmp(thd->user, acl_user_tmp->user))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
676
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
677
      if (compare_hostname(&acl_user_tmp->host, thd->host, thd->ip))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
678
      {
679
        /* check password: it should be empty or valid */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
680
        if (passwd_len == acl_user_tmp->salt_len)
peter@mysql.com's avatar
peter@mysql.com committed
681
        {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
682
          if (acl_user_tmp->salt_len == 0 ||
serg@serg.mylan's avatar
serg@serg.mylan committed
683 684
              (acl_user_tmp->salt_len == SCRAMBLE_LENGTH ?
              check_scramble(passwd, thd->scramble, acl_user_tmp->salt) :
685
              check_scramble_323(passwd, thd->scramble,
serg@serg.mylan's avatar
serg@serg.mylan committed
686
                                 (ulong *) acl_user_tmp->salt)) == 0)
687
          {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
688
            acl_user= acl_user_tmp;
689 690
            res= 0;
          }
peter@mysql.com's avatar
peter@mysql.com committed
691
        }
692
        else if (passwd_len == SCRAMBLE_LENGTH &&
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
693
                 acl_user_tmp->salt_len == SCRAMBLE_LENGTH_323)
694
          res= -1;
695
        else if (passwd_len == SCRAMBLE_LENGTH_323 &&
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
696
                 acl_user_tmp->salt_len == SCRAMBLE_LENGTH)
697
          res= 2;
698 699
        /* linear search complete: */
        break;
peter@mysql.com's avatar
peter@mysql.com committed
700
      }
peter@mysql.com's avatar
peter@mysql.com committed
701
    }
702
  }
703 704 705 706
  /*
    This was moved to separate tree because of heavy HAVE_OPENSSL case.
    If acl_user is not null, res is 0.
  */
peter@mysql.com's avatar
peter@mysql.com committed
707 708 709

  if (acl_user)
  {
710
    /* OK. User found and password checked continue validation */
711
#ifdef HAVE_OPENSSL
712
    Vio *vio=thd->net.vio;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
713
    SSL *ssl= (SSL*) vio->ssl_arg;
714
#endif
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
715

716
    /*
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
717
      At this point we know that user is allowed to connect
718 719 720 721 722 723
      from given host by given username/password pair. Now
      we check if SSL is required, if user is using SSL and
      if X509 certificate attributes are OK
    */
    switch (acl_user->ssl_type) {
    case SSL_TYPE_NOT_SPECIFIED:		// Impossible
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
724 725
    case SSL_TYPE_NONE:				// SSL is not required
      user_access= acl_user->access;
726
      break;
727
#ifdef HAVE_OPENSSL
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
728
    case SSL_TYPE_ANY:				// Any kind of SSL is ok
729
      if (vio_type(vio) == VIO_TYPE_SSL)
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
730
	user_access= acl_user->access;
731 732 733 734 735
      break;
    case SSL_TYPE_X509: /* Client should have any valid certificate. */
      /*
	Connections with non-valid certificates are dropped already
	in sslaccept() anyway, so we do not check validity here.
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
736

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
737 738
	We need to check for absence of SSL because without SSL
	we should reject connection.
739
      */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
740
      if (vio_type(vio) == VIO_TYPE_SSL &&
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
741 742
	  SSL_get_verify_result(ssl) == X509_V_OK &&
	  SSL_get_peer_certificate(ssl))
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
743
	user_access= acl_user->access;
744 745 746 747 748 749 750 751
      break;
    case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
      /*
	We do not check for absence of SSL because without SSL it does
	not pass all checks here anyway.
	If cipher name is specified, we compare it to actual cipher in
	use.
      */
monty@mysql.com's avatar
monty@mysql.com committed
752
      X509 *cert;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
753
      if (vio_type(vio) != VIO_TYPE_SSL ||
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
754
	  SSL_get_verify_result(ssl) != X509_V_OK)
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
755
	break;
756
      if (acl_user->ssl_cipher)
peter@mysql.com's avatar
peter@mysql.com committed
757
      {
758
	DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
759 760
			   acl_user->ssl_cipher,SSL_get_cipher(ssl)));
	if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl)))
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
761
	  user_access= acl_user->access;
762 763
	else
	{
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
764
	  if (global_system_variables.log_warnings)
serg@serg.mylan's avatar
serg@serg.mylan committed
765 766 767
	    sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
			      acl_user->ssl_cipher,
			      SSL_get_cipher(ssl));
768 769
	  break;
	}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
770
      }
771 772
      /* Prepare certificate (if exists) */
      DBUG_PRINT("info",("checkpoint 1"));
monty@mysql.com's avatar
monty@mysql.com committed
773 774 775 776 777
      if (!(cert= SSL_get_peer_certificate(ssl)))
      {
	user_access=NO_ACCESS;
	break;
      }
778
      DBUG_PRINT("info",("checkpoint 2"));
779
      /* If X509 issuer is specified, we check it... */
780
      if (acl_user->x509_issuer)
peter@mysql.com's avatar
peter@mysql.com committed
781
      {
kostja@oak.local's avatar
kostja@oak.local committed
782
        DBUG_PRINT("info",("checkpoint 3"));
783 784 785
	char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
	DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
			   acl_user->x509_issuer, ptr));
kostja@oak.local's avatar
kostja@oak.local committed
786
        if (strcmp(acl_user->x509_issuer, ptr))
787
        {
kostja@oak.local's avatar
kostja@oak.local committed
788
          if (global_system_variables.log_warnings)
serg@serg.mylan's avatar
serg@serg.mylan committed
789 790
            sql_print_information("X509 issuer mismatch: should be '%s' "
			      "but is '%s'", acl_user->x509_issuer, ptr);
791
          free(ptr);
kostja@oak.local's avatar
kostja@oak.local committed
792
          break;
793
        }
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
794
        user_access= acl_user->access;
kostja@oak.local's avatar
kostja@oak.local committed
795
        free(ptr);
peter@mysql.com's avatar
peter@mysql.com committed
796
      }
797 798 799 800
      DBUG_PRINT("info",("checkpoint 4"));
      /* X509 subject is specified, we check it .. */
      if (acl_user->x509_subject)
      {
kostja@oak.local's avatar
kostja@oak.local committed
801 802 803 804
        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))
805
        {
kostja@oak.local's avatar
kostja@oak.local committed
806
          if (global_system_variables.log_warnings)
serg@serg.mylan's avatar
serg@serg.mylan committed
807
            sql_print_information("X509 subject mismatch: '%s' vs '%s'",
kostja@oak.local's avatar
kostja@oak.local committed
808
                            acl_user->x509_subject, ptr);
809
        }
kostja@oak.local's avatar
kostja@oak.local committed
810
        else
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
811
          user_access= acl_user->access;
kostja@oak.local's avatar
kostja@oak.local committed
812
        free(ptr);
813 814
      }
      break;
peter@mysql.com's avatar
peter@mysql.com committed
815
#else  /* HAVE_OPENSSL */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
816
    default:
817
      /*
kostja@oak.local's avatar
kostja@oak.local committed
818 819 820
        If we don't have SSL but SSL is required for this user the 
        authentication should fail.
      */
821 822
      break;
#endif /* HAVE_OPENSSL */
peter@mysql.com's avatar
peter@mysql.com committed
823
    }
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
824
    thd->master_access= user_access;
825 826
    thd->priv_user= acl_user->user ? thd->user : (char *) "";
    *mqh= acl_user->user_resource;
827

828 829 830 831 832
    if (acl_user->host.hostname)
      strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
    else
      *thd->priv_host= 0;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
833
  VOID(pthread_mutex_unlock(&acl_cache->lock));
834
  DBUG_RETURN(res);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
835 836 837
}


838 839 840
/*
 * This is like acl_getroot() above, but it doesn't check password,
 * and we don't care about the user resources.
841
 * Used to get access rights for SQL SECURITY DEFINER invocation of
842 843 844 845 846 847
 * stored procedures.
 */
int acl_getroot_no_password(THD *thd)
{
  ulong user_access= NO_ACCESS;
  int res= 1;
848
  uint i;
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
  ACL_USER *acl_user= 0;
  DBUG_ENTER("acl_getroot_no_password");

  if (!initialized)
  {
    /* 
      here if mysqld's been started with --skip-grant-tables option.
    */
    thd->priv_user= (char *) "";                // privileges for
    *thd->priv_host= '\0';                      // the user are unknown
    thd->master_access= ~NO_ACCESS;             // everything is allowed
    DBUG_RETURN(0);
  }

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

865 866 867
  thd->master_access= 0;
  thd->db_access= 0;

868 869 870 871 872 873
  /*
     Find acl entry in user database.
     This is specially tailored to suit the check we do for CALL of
     a stored procedure; thd->user is set to what is actually a
     priv_user, which can be ''.
  */
874
  for (i=0 ; i < acl_users.elements ; i++)
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
  {
    acl_user= dynamic_element(&acl_users,i,ACL_USER*);
    if ((!acl_user->user && (!thd->user || !thd->user[0])) ||
	(acl_user->user && strcmp(thd->user, acl_user->user) == 0))
    {
      if (compare_hostname(&acl_user->host, thd->host, thd->ip))
      {
	res= 0;
	break;
      }
    }
  }

  if (acl_user)
  {
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905
    for (i=0 ; i < acl_dbs.elements ; i++)
    {
      ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
      if (!acl_db->user ||
	  (thd->user && thd->user[0] && !strcmp(thd->user, acl_db->user)))
      {
	if (compare_hostname(&acl_db->host, thd->host, thd->ip))
	{
	  if (!acl_db->db || (thd->db && !strcmp(acl_db->db, thd->db)))
	  {
	    thd->db_access= acl_db->access;
	    break;
	  }
	}
      }
    }
906 907 908 909 910 911 912 913 914 915 916 917
    thd->master_access= acl_user->access;
    thd->priv_user= acl_user->user ? thd->user : (char *) "";

    if (acl_user->host.hostname)
      strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
    else
      *thd->priv_host= 0;
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  DBUG_RETURN(res);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
918 919 920 921 922 923 924
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;
}

925

bk@work.mysql.com's avatar
bk@work.mysql.com committed
926
static void acl_update_user(const char *user, const char *host,
927
			    const char *password, uint password_len,
928 929 930 931
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
peter@mysql.com's avatar
peter@mysql.com committed
932
			    USER_RESOURCES  *mqh,
933
			    ulong privileges)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
934 935 936 937 938 939 940 941 942
{
  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] ||
943
	  acl_user->host.hostname &&
944
	  !my_strcasecmp(system_charset_info, host, acl_user->host.hostname))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
945 946
      {
	acl_user->access=privileges;
947
	if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
948
	  acl_user->user_resource.questions=mqh->questions;
949
	if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
950
	  acl_user->user_resource.updates=mqh->updates;
951 952 953 954
	if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
	  acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
	if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
	  acl_user->user_resource.user_conn= mqh->user_conn;
955 956 957 958 959 960 961 962 963 964
	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);
	}
965 966
	if (password)
	  set_user_salt(acl_user, password, password_len);
967
        /* search complete: */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
968 969 970 971 972 973 974 975
	break;
      }
    }
  }
}


static void acl_insert_user(const char *user, const char *host,
976
			    const char *password, uint password_len,
977 978 979 980
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
981
			    USER_RESOURCES *mqh,
982
			    ulong privileges)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
983 984
{
  ACL_USER acl_user;
985
  acl_user.user=*user ? strdup_root(&mem,user) : 0;
monty@mysql.com's avatar
monty@mysql.com committed
986
  update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
987
  acl_user.access=privileges;
988
  acl_user.user_resource = *mqh;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
989
  acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
990
  acl_user.hostname_length=(uint) strlen(host);
991 992 993 994 995
  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;
996 997

  set_user_salt(&acl_user, password, password_len);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
998 999 1000 1001

  VOID(push_dynamic(&acl_users,(gptr) &acl_user));
  if (!acl_user.host.hostname || acl_user.host.hostname[0] == wild_many
      && !acl_user.host.hostname[1])
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1002
    allow_all_hosts=1;		// Anyone can connect /* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
  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,
1014
			  ulong privileges)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1015 1016 1017 1018 1019 1020 1021 1022 1023
{
  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] ||
1024
	  acl_db->host.hostname &&
1025
	  !my_strcasecmp(system_charset_info, host, acl_db->host.hostname))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
      {
	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);
	}
      }
    }
  }
}


1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
/*
  Insert a user/db/host combination into the global acl_cache

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

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1055
static void acl_insert_db(const char *user, const char *host, const char *db,
1056
			  ulong privileges)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1057 1058
{
  ACL_DB acl_db;
1059
  safe_mutex_assert_owner(&acl_cache->lock);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
  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);
}


1071 1072 1073 1074

/*
  Get privilege for a host, user and db combination
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1075

1076
ulong acl_get(const char *host, const char *ip,
1077
              const char *user, const char *db, my_bool db_is_pattern)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1078
{
1079
  ulong host_access= ~0, db_access= 0;
1080
  uint i,key_length;
1081
  char key[ACL_KEY_LENGTH],*tmp_db,*end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1082
  acl_entry *entry;
monty@mysql.com's avatar
monty@mysql.com committed
1083
  DBUG_ENTER("acl_get");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1084 1085

  VOID(pthread_mutex_lock(&acl_cache->lock));
1086
  end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db);
1087 1088
  if (lower_case_table_names)
  {
1089
    my_casedn_str(files_charset_info, tmp_db);
1090 1091
    db=tmp_db;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1092 1093 1094 1095 1096
  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));
monty@mysql.com's avatar
monty@mysql.com committed
1097 1098
    DBUG_PRINT("exit", ("access: 0x%lx", db_access));
    DBUG_RETURN(db_access);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
  }

  /*
    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))
      {
1111
	if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
	{
	  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))
    {
1133
      if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
      {
	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));
monty@mysql.com's avatar
monty@mysql.com committed
1150 1151
  DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
  DBUG_RETURN(db_access & host_access);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1152 1153
}

1154 1155 1156 1157 1158 1159 1160
/*
  Check if there are any possible matching entries for this host

  NOTES
    All host names without wild cards are stored in a hash table,
    entries with wildcards are stored in a dynamic array
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1161 1162 1163 1164

static void init_check_host(void)
{
  DBUG_ENTER("init_check_host");
1165
  VOID(my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1166
			  acl_users.elements,1));
1167
  VOID(hash_init(&acl_check_hosts,system_charset_info,acl_users.elements,0,0,
1168
		 (hash_get_key) check_get_key,0,0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182
  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 *);
1183
	  if (!my_strcasecmp(system_charset_info,
1184
                             acl_user->host.hostname, acl->hostname))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1185 1186 1187 1188 1189 1190
	    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,
1191
			    (uint) strlen(acl_user->host.hostname)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1192
      {
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1193
	if (my_hash_insert(&acl_check_hosts,(byte*) acl_user))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
	{					// 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));

1215 1216
  if (host && hash_search(&acl_check_hosts,(byte*) host,(uint) strlen(host)) ||
      ip && hash_search(&acl_check_hosts,(byte*) ip,(uint) strlen(ip)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
  {
    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
}


1235 1236 1237 1238 1239 1240 1241 1242
/*
  Check if the user is allowed to change password

  SYNOPSIS:
    check_change_password()
    thd		THD
    host	hostname for the user
    user	user name
monty@hundin.mysql.fi's avatar
merge  
monty@hundin.mysql.fi committed
1243

1244
    RETURN VALUE
1245 1246
      0		OK
      1		ERROR  ; In this case the error is sent to the client.
1247 1248
*/

1249 1250
bool check_change_password(THD *thd, const char *host, const char *user,
                           char *new_password)
1251
{
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1252 1253
  if (!initialized)
  {
1254
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1255
    return(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1256
  }
1257 1258
  if (!thd->slave_thread &&
      (strcmp(thd->user,user) ||
1259
       my_strcasecmp(system_charset_info, host, thd->host_or_ip)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1260
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
1261
    if (check_access(thd, UPDATE_ACL, "mysql",0,1,0))
1262
      return(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1263
  }
1264 1265
  if (!thd->slave_thread && !thd->user[0])
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1266 1267
    my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
               MYF(0));
1268
    return(1);
1269
  }
1270
  uint len=strlen(new_password);
1271
  if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
1272 1273
      len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
  {
1274
    my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
1275 1276
    return -1;
  }
1277 1278 1279 1280
  return(0);
}


1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
/*
  Change a password for a user

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

  RETURN VALUES
    0	ok
    1	ERROR; In this case the error is sent to the client.
peter@mysql.com's avatar
peter@mysql.com committed
1294
*/
1295

1296 1297 1298 1299 1300 1301 1302 1303
bool change_password(THD *thd, const char *host, const char *user,
		     char *new_password)
{
  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

1304
  if (check_change_password(thd, host, user, new_password))
1305 1306
    DBUG_RETURN(1);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1307 1308
  VOID(pthread_mutex_lock(&acl_cache->lock));
  ACL_USER *acl_user;
1309
  if (!(acl_user= find_acl_user(host, user)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1310 1311
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1312
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
1313
    DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1314
  }
1315 1316 1317 1318
  /* update loaded acl entry: */
  uint new_password_len= new_password ? strlen(new_password) : 0;
  set_user_salt(acl_user, new_password, new_password_len);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1319 1320
  if (update_user_table(thd,
			acl_user->host.hostname ? acl_user->host.hostname : "",
1321
			acl_user->user ? acl_user->user : "",
1322
			new_password, new_password_len))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1323 1324
  {
    VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
1325
    DBUG_RETURN(1); /* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1326
  }
peter@mysql.com's avatar
peter@mysql.com committed
1327

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1328 1329 1330
  acl_cache->clear(1);				// Clear locked hostname cache
  VOID(pthread_mutex_unlock(&acl_cache->lock));

1331
  char buff[512]; /* Extend with extended password length*/
1332
  ulong query_length=
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1333 1334
    my_sprintf(buff,
	       (buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"",
1335
		acl_user->user ? acl_user->user : "",
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1336 1337
		acl_user->host.hostname ? acl_user->host.hostname : "",
		new_password));
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1338
  thd->clear_error();
1339
  Query_log_event qinfo(thd, buff, query_length, 0, FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1340
  mysql_bin_log.write(&qinfo);
1341
  DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1342 1343 1344 1345 1346 1347 1348 1349 1350 1351
}


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

static ACL_USER *
find_acl_user(const char *host, const char *user)
{
1352
  DBUG_ENTER("find_acl_user");
1353
  DBUG_PRINT("enter",("host: '%s'  user: '%s'",host,user));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1354 1355 1356
  for (uint i=0 ; i < acl_users.elements ; i++)
  {
    ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
1357
    DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
1358 1359 1360 1361 1362
		       user,
		       acl_user->user ? acl_user->user : "",
		       host,
		       acl_user->host.hostname ? acl_user->host.hostname :
		       ""));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1363 1364 1365
    if (!acl_user->user && !user[0] ||
	acl_user->user && !strcmp(user,acl_user->user))
    {
1366
      if (compare_hostname(&acl_user->host,host,host))
1367 1368 1369
      {
	DBUG_RETURN(acl_user);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1370 1371
    }
  }
1372
  DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1373 1374 1375
}


1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386
/*
  Comparing of hostnames

  NOTES
  A hostname may be of type:
  hostname   (May include wildcards);   monty.pp.sci.fi
  ip	   (May include wildcards);   192.168.0.0
  ip/netmask			      192.168.0.0/255.255.255.0

  A net mask of 0.0.0.0 is not allowed.
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409

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!
1410
  if (!hostname ||
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1411 1412 1413
      (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
       !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
  {
1414
    host->ip= host->ip_mask=0;			// Not a masked ip
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427
  }
}


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 ||
1428
	  (hostname && !wild_case_compare(system_charset_info,
1429
                                          hostname,host->hostname)) ||
1430
	  (ip && !wild_compare(ip,host->hostname,0)));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1431 1432
}

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1433 1434 1435 1436
bool hostname_requires_resolving(const char *hostname)
{
  char cur;
  if (!hostname)
monty@mysql.com's avatar
monty@mysql.com committed
1437
    return FALSE;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1438 1439 1440
  int namelen= strlen(hostname);
  int lhlen= strlen(my_localhost);
  if ((namelen == lhlen) &&
1441
      !my_strnncoll(system_charset_info, (const uchar *)hostname,  namelen,
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1442
		    (const uchar *)my_localhost, strlen(my_localhost)))
monty@mysql.com's avatar
monty@mysql.com committed
1443
    return FALSE;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1444 1445 1446 1447
  for (; (cur=*hostname); hostname++)
  {
    if ((cur != '%') && (cur != '_') && (cur != '.') &&
	((cur < '0') || (cur > '9')))
monty@mysql.com's avatar
monty@mysql.com committed
1448
      return TRUE;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1449
  }
monty@mysql.com's avatar
monty@mysql.com committed
1450
  return FALSE;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1451
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1452

1453 1454 1455
/*
  Update grants in the user and database privilege tables
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1456 1457

static bool update_user_table(THD *thd, const char *host, const char *user,
1458
			      const char *new_password, uint new_password_len)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1459 1460 1461 1462
{
  TABLE_LIST tables;
  TABLE *table;
  bool error=1;
1463
  char user_key[MAX_KEY_LENGTH];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1464 1465 1466 1467
  DBUG_ENTER("update_user_table");
  DBUG_PRINT("enter",("user: %s  host: %s",user,host));

  bzero((char*) &tables,sizeof(tables));
1468
  tables.alias=tables.table_name=(char*) "user";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1469
  tables.db=(char*) "mysql";
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1470

1471 1472 1473 1474 1475 1476 1477
#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)
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1478 1479 1480
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.  It's ok to leave 'updating' set after tables_ok.
1481
    */
1482
    tables.updating= 1;
1483 1484 1485 1486 1487 1488
    /* Thanks to bzero, tables.next==0 */
    if (!tables_ok(0, &tables))
      DBUG_RETURN(0);
  }
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1489 1490
  if (!(table=open_ltable(thd,&tables,TL_WRITE)))
    DBUG_RETURN(1); /* purecov: deadcode */
1491 1492
  table->field[0]->store(host,(uint) strlen(host), system_charset_info);
  table->field[1]->store(user,(uint) strlen(user), system_charset_info);
1493 1494
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1495

1496
  table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
1497 1498
  if (table->file->index_read_idx(table->record[0], 0,
				  user_key, table->key_info->key_length,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1499 1500
				  HA_READ_KEY_EXACT))
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1501 1502
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
               MYF(0));	/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1503 1504
    DBUG_RETURN(1);				/* purecov: deadcode */
  }
1505
  store_record(table,record[1]);
1506
  table->field[2]->store(new_password, new_password_len, system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518
  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);
}

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1519 1520 1521 1522 1523 1524 1525 1526 1527

/* 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;
1528
    ulong db_access;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1529 1530
    bzero((char*) &tl,sizeof(tl));
    tl.db=	   (char*) "mysql";
1531
    tl.table_name=  (char*) "user";
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1532

1533
    db_access=acl_get(thd->host, thd->ip,
1534
		      thd->priv_user, tl.db, 0);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1535 1536
    if (!(db_access & INSERT_ACL))
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1537
      if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1538 1539 1540 1541 1542 1543 1544
	create_new_users=0;
    }
  }
  return create_new_users;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
1545
/****************************************************************************
1546
  Handle GRANT commands
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1547 1548
****************************************************************************/

1549
static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
1550 1551
			      ulong rights, bool revoke_grant,
			      bool create_user)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1552 1553
{
  int error = -1;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1554
  bool old_row_exists=0;
1555
  const char *password= "";
1556
  uint password_len= 0;
1557
  char what= (revoke_grant) ? 'N' : 'Y';
1558
  byte user_key[MAX_KEY_LENGTH];
1559
  LEX *lex= thd->lex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1560
  DBUG_ENTER("replace_user_table");
1561

1562
  safe_mutex_assert_owner(&acl_cache->lock);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1563 1564

  if (combo.password.str && combo.password.str[0])
1565
  {
1566 1567
    if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
        combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1568
    {
1569
      my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
1570
      DBUG_RETURN(-1);
1571
    }
1572
    password_len= combo.password.length;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1573
    password=combo.password.str;
1574
  }
peter@mysql.com's avatar
peter@mysql.com committed
1575

1576 1577
  table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
  table->field[1]->store(combo.user.str,combo.user.length, system_charset_info);
1578 1579 1580
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

1581
  table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
1582
  if (table->file->index_read_idx(table->record[0], 0,
1583 1584
                                  user_key, table->key_info->key_length,
                                  HA_READ_KEY_EXACT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1585
  {
1586 1587
    /* what == 'N' means revoke */
    if (what == 'N')
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1588
    {
1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
      goto end;
    }
    /*
      There are four options which affect the process of creation of 
      a new user(mysqld option --safe-create-user, 'insert' privilege
      on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
      SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
      how it should work.
      if (safe-user-create && ! INSERT_priv) => reject
      else if (identified_by) => create
      else if (no_auto_create_user) => reject
      else create
    */
    else if (((thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER) &&
              !password_len) || !create_user)
    {
      my_error(ER_NO_PERMISSION_TO_CREATE_USER, MYF(0),
               thd->user, thd->host_or_ip);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1608 1609
      goto end;
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1610
    old_row_exists = 0;
1611
    restore_record(table,s->default_values);
1612
    table->field[0]->store(combo.host.str,combo.host.length,
1613
                           system_charset_info);
1614
    table->field[1]->store(combo.user.str,combo.user.length,
1615
                           system_charset_info);
1616
    table->field[2]->store(password, password_len,
1617
                           system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1618 1619 1620
  }
  else
  {
1621 1622 1623 1624 1625 1626 1627
    /*
      Check that the user isn't trying to change a password for another
      user if he doesn't have UPDATE privilege to the MySQL database
    */
    DBUG_ASSERT(combo.host.str);
    if (thd->user && combo.password.str &&
        (strcmp(thd->user,combo.user.str) ||
1628
         my_strcasecmp(system_charset_info,
1629 1630 1631
                       combo.host.str, thd->host_or_ip)) &&
        check_access(thd, UPDATE_ACL, "mysql",0,1,0))
      goto end;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1632
    old_row_exists = 1;
1633
    store_record(table,record[1]);			// Save copy for update
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1634
    if (combo.password.str)			// If password given
1635
      table->field[2]->store(password, password_len, system_charset_info);
1636
    else if (!rights && !revoke_grant &&
1637 1638
             lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
             !lex->mqh.specified_limits)
1639 1640 1641
    {
      DBUG_RETURN(0);
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1642 1643
  }

1644 1645 1646 1647
  /* Update table columns with new privileges */

  Field **tmp_field;
  ulong priv;
1648
  uint next_field;
1649 1650 1651 1652
  for (tmp_field= table->field+3, priv = SELECT_ACL;
       *tmp_field && (*tmp_field)->real_type() == FIELD_TYPE_ENUM &&
	 ((Field_enum*) (*tmp_field))->typelib->count == 2 ;
       tmp_field++, priv <<= 1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1653
  {
1654
    if (priv & rights)				 // set requested privileges
1655
      (*tmp_field)->store(&what, 1, &my_charset_latin1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1656
  }
1657
  rights= get_access(table, 3, &next_field);
1658 1659
  DBUG_PRINT("info",("table fields: %d",table->s->fields));
  if (table->s->fields >= 31)		/* From 4.0.0 we have more fields */
1660
  {
1661
    /* We write down SSL related ACL stuff */
1662
    switch (lex->ssl_type) {
1663
    case SSL_TYPE_ANY:
1664 1665 1666 1667
      table->field[next_field]->store("ANY", 3, &my_charset_latin1);
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
1668 1669
      break;
    case SSL_TYPE_X509:
1670 1671 1672 1673
      table->field[next_field]->store("X509", 4, &my_charset_latin1);
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
1674 1675
      break;
    case SSL_TYPE_SPECIFIED:
1676 1677 1678 1679
      table->field[next_field]->store("SPECIFIED", 9, &my_charset_latin1);
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
1680
      if (lex->ssl_cipher)
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1681 1682
        table->field[next_field+1]->store(lex->ssl_cipher,
                                strlen(lex->ssl_cipher), system_charset_info);
1683
      if (lex->x509_issuer)
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1684 1685
        table->field[next_field+2]->store(lex->x509_issuer,
                                strlen(lex->x509_issuer), system_charset_info);
1686
      if (lex->x509_subject)
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1687 1688
        table->field[next_field+3]->store(lex->x509_subject,
                                strlen(lex->x509_subject), system_charset_info);
1689
      break;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1690
    case SSL_TYPE_NOT_SPECIFIED:
gluh@gluh.(none)'s avatar
gluh@gluh.(none) committed
1691 1692
      break;
    case SSL_TYPE_NONE:
1693 1694 1695 1696
      table->field[next_field]->store("", 0, &my_charset_latin1);
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
gluh@gluh.(none)'s avatar
gluh@gluh.(none) committed
1697
      break;
1698
    }
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1699
    next_field+=4;
1700

1701
    USER_RESOURCES mqh= lex->mqh;
1702
    if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1703
      table->field[next_field]->store((longlong) mqh.questions);
1704
    if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1705
      table->field[next_field+1]->store((longlong) mqh.updates);
1706
    if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1707
      table->field[next_field+2]->store((longlong) mqh.conn_per_hour);
1708
    if (table->s->fields >= 36 &&
1709
        (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
1710
      table->field[next_field+3]->store((longlong) mqh.user_conn);
1711
    mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
1712
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1713
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1714 1715 1716 1717 1718
  {
    /*
      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!
    */
1719
    table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
1720
    if (cmp_record(table,record[1]) &&
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739
	(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

1740
end:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1741 1742 1743
  if (!error)
  {
    acl_cache->clear(1);			// Clear privilege cache
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1744
    if (old_row_exists)
1745 1746
      acl_update_user(combo.user.str, combo.host.str,
                      combo.password.str, password_len,
1747 1748 1749 1750 1751
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
1752
		      rights);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1753
    else
1754
      acl_insert_user(combo.user.str, combo.host.str, password, password_len,
1755 1756 1757 1758 1759
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
1760
		      rights);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1761 1762 1763 1764 1765 1766
  }
  DBUG_RETURN(error);
}


/*
1767
  change grants in the mysql.db table
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1768 1769 1770 1771
*/

static int replace_db_table(TABLE *table, const char *db,
			    const LEX_USER &combo,
1772
			    ulong rights, bool revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1773
{
1774 1775
  uint i;
  ulong priv,store_rights;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1776
  bool old_row_exists=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1777
  int error;
1778
  char what= (revoke_grant) ? 'N' : 'Y';
1779
  byte user_key[MAX_KEY_LENGTH];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1780 1781
  DBUG_ENTER("replace_db_table");

1782 1783
  if (!initialized)
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1784
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1785 1786 1787
    DBUG_RETURN(-1);
  }

1788
  /* Check if there is such a user in user table in memory? */
1789
  if (!find_acl_user(combo.host.str,combo.user.str))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1790
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1791
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1792 1793 1794
    DBUG_RETURN(-1);
  }

1795 1796 1797
  table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
  table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
1798 1799 1800
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

1801 1802
  table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
  if (table->file->index_read_idx(table->record[0],0,
1803 1804
                                  user_key, table->key_info->key_length,
                                  HA_READ_KEY_EXACT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1805 1806 1807
  {
    if (what == 'N')
    { // no row, no revoke
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1808
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1809 1810
      goto abort;
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1811
    old_row_exists = 0;
1812
    restore_record(table, s->default_values);
1813 1814 1815
    table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
    table->field[1]->store(db,(uint) strlen(db), system_charset_info);
    table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1816 1817 1818
  }
  else
  {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1819
    old_row_exists = 1;
1820
    store_record(table,record[1]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1821 1822 1823
  }

  store_rights=get_rights_for_db(rights);
1824
  for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1825
  {
1826
    if (priv & store_rights)			// do it if priv is chosen
1827
      table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1828 1829 1830 1831
  }
  rights=get_access(table,3);
  rights=fix_rights_for_db(rights);

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1832
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1833
  {
1834
    /* update old existing row */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1835 1836
    if (rights)
    {
1837
      table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1838 1839 1840 1841 1842 1843 1844 1845 1846
      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 */
    }
  }
1847
  else if (rights && (error=table->file->write_row(table->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1848 1849 1850 1851 1852 1853
  {
    if (error && error != HA_ERR_FOUND_DUPP_KEY) /* purecov: inspected */
      goto table_error; /* purecov: deadcode */
  }

  acl_cache->clear(1);				// Clear privilege cache
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1854
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1855 1856
    acl_update_db(combo.user.str,combo.host.str,db,rights);
  else
1857
  if (rights)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1858 1859 1860 1861
    acl_insert_db(combo.user.str,combo.host.str,db,rights);
  DBUG_RETURN(0);

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

1865
abort:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1866 1867 1868 1869 1870 1871 1872 1873
  DBUG_RETURN(-1);
}


class GRANT_COLUMN :public Sql_alloc
{
public:
  char *column;
1874 1875 1876
  ulong rights;
  uint key_length;
  GRANT_COLUMN(String &c,  ulong y) :rights (y)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1877
  {
1878
    column= memdup_root(&memex,c.ptr(), key_length=c.length());
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1879 1880 1881
  }
};

1882

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1883 1884 1885 1886 1887 1888 1889
static byte* get_key_column(GRANT_COLUMN *buff,uint *length,
			    my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
  return (byte*) buff->column;
}

1890

1891
class GRANT_NAME :public Sql_alloc
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1892 1893
{
public:
monty@mysql.com's avatar
monty@mysql.com committed
1894
  char *host,*db, *user, *tname, *hash_key, *orig_host;
1895
  ulong privs;
1896
  ulong sort;
1897
  uint key_length;
1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909
  GRANT_NAME(const char *h, const char *d,const char *u,
             const char *t, ulong p);
  GRANT_NAME (TABLE *form);
  virtual ~GRANT_NAME() {};
  virtual bool ok() { return privs != 0; }
};


class GRANT_TABLE :public GRANT_NAME
{
public:
  ulong cols;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1910
  HASH hash_columns;
monty@mysql.com's avatar
monty@mysql.com committed
1911 1912 1913 1914

  GRANT_TABLE(const char *h, const char *d,const char *u,
              const char *t, ulong p, ulong c);
  GRANT_TABLE (TABLE *form, TABLE *col_privs);
1915
  ~GRANT_TABLE();
1916 1917
  bool ok() { return privs != 0 || cols != 0; }
};
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1918

1919

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

1921 1922 1923
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
                       const char *t, ulong p)
  :privs(p)
1924 1925 1926
{
  /* Host given by user */
  orig_host=  strdup_root(&memex,h);
1927
  /* Convert empty hostname to '%' for easy comparison */
1928 1929 1930 1931 1932 1933
  host=  orig_host[0] ? orig_host : (char*) "%";
  db =   strdup_root(&memex,d);
  user = strdup_root(&memex,u);
  sort=  get_sort(3,host,db,user);
  tname= strdup_root(&memex,t);
  if (lower_case_table_names)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1934
  {
1935 1936
    my_casedn_str(files_charset_info, db);
    my_casedn_str(files_charset_info, tname);
1937 1938 1939 1940
  }
  key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3;
  hash_key = (char*) alloc_root(&memex,key_length);
  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
1941 1942 1943 1944 1945 1946 1947
}


GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
                	 const char *t, ulong p, ulong c)
  :GRANT_NAME(h,d,u,t,p), cols(c)
{
1948
  (void) hash_init(&hash_columns,system_charset_info,
monty@mysql.com's avatar
monty@mysql.com committed
1949
                   0,0,0, (hash_get_key) get_key_column,0,0);
1950
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1951

1952

1953
GRANT_NAME::GRANT_NAME(TABLE *form)
1954
{
monty@mysql.com's avatar
monty@mysql.com committed
1955 1956 1957
  orig_host= host= get_field(&memex, form->field[0]);
  db=    get_field(&memex,form->field[1]);
  user=  get_field(&memex,form->field[2]);
1958 1959 1960 1961 1962 1963 1964
  if (!user)
    user= (char*) "";
  if (!orig_host)
  {
    orig_host= (char*) "";
    host= (char*) "%";
  }
monty@mysql.com's avatar
monty@mysql.com committed
1965 1966
  sort=  get_sort(3, orig_host, db, user);
  tname= get_field(&memex,form->field[3]);
1967 1968 1969
  if (!db || !tname)
  {
    /* Wrong table row; Ignore it */
1970
    privs= 0;
1971 1972 1973 1974
    return;					/* purecov: inspected */
  }
  if (lower_case_table_names)
  {
1975 1976
    my_casedn_str(files_charset_info, db);
    my_casedn_str(files_charset_info, tname);
1977 1978 1979 1980 1981 1982 1983
  }
  key_length = ((uint) strlen(db) + (uint) strlen(user) +
                (uint) strlen(tname) + 3);
  hash_key = (char*) alloc_root(&memex,key_length);
  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
  privs = (ulong) form->field[6]->val_int();
  privs = fix_rights_for_table(privs);
1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999
}


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

  if (!db || !tname)
  {
    /* Wrong table row; Ignore it */
    hash_clear(&hash_columns);                  /* allow for destruction */
    cols= 0;
    return;
  }
  cols= (ulong) form->field[7]->val_int();
2000 2001
  cols =  fix_rights_for_column(cols);

2002
  (void) hash_init(&hash_columns,system_charset_info,
monty@mysql.com's avatar
monty@mysql.com committed
2003
                   0,0,0, (hash_get_key) get_key_column,0,0);
2004 2005
  if (cols)
  {
2006 2007
    uint key_prefix_len;
    KEY_PART_INFO *key_part= col_privs->key_info->key_part;
monty@mysql.com's avatar
monty@mysql.com committed
2008
    col_privs->field[0]->store(orig_host,(uint) strlen(orig_host),
2009 2010 2011 2012
                               system_charset_info);
    col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
    col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
    col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
2013 2014 2015 2016 2017 2018

    key_prefix_len= (key_part[0].store_length +
                     key_part[1].store_length +
                     key_part[2].store_length +
                     key_part[3].store_length);
    key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
monty@mysql.com's avatar
monty@mysql.com committed
2019
    col_privs->field[4]->store("",0, &my_charset_latin1);
2020

2021 2022
    col_privs->file->ha_index_init(0);
    if (col_privs->file->index_read(col_privs->record[0],
2023 2024
                                    (byte*) key,
                                    key_prefix_len, HA_READ_KEY_EXACT))
2025
    {
2026
      cols = 0; /* purecov: deadcode */
2027
      col_privs->file->ha_index_end();
2028
      return;
2029
    }
2030
    do
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2031
    {
2032 2033 2034
      String *res,column_name;
      GRANT_COLUMN *mem_check;
      /* As column name is a string, we don't have to supply a buffer */
monty@mysql.com's avatar
monty@mysql.com committed
2035
      res=col_privs->field[4]->val_str(&column_name);
2036 2037 2038
      ulong priv= (ulong) col_privs->field[6]->val_int();
      if (!(mem_check = new GRANT_COLUMN(*res,
                                         fix_rights_for_column(priv))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2039
      {
2040 2041 2042
        /* Don't use this entry */
        privs = cols = 0;			/* purecov: deadcode */
        return;				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2043
      }
monty@mysql.com's avatar
monty@mysql.com committed
2044
      my_hash_insert(&hash_columns, (byte *) mem_check);
2045
    } while (!col_privs->file->index_next(col_privs->record[0]) &&
2046
             !key_cmp_if_same(col_privs,key,0,key_prefix_len));
2047
    col_privs->file->ha_index_end();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2048
  }
2049
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2050

2051

2052 2053 2054 2055 2056 2057
GRANT_TABLE::~GRANT_TABLE()
{
  hash_free(&hash_columns);
}


2058
static byte* get_grant_table(GRANT_NAME *buff,uint *length,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2059 2060 2061 2062 2063 2064
			     my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
  return (byte*) buff->hash_key;
}

2065

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2066 2067 2068 2069 2070
void free_grant_table(GRANT_TABLE *grant_table)
{
  hash_free(&grant_table->hash_columns);
}

2071

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

2074 2075
static GRANT_NAME *name_hash_search(HASH *name_hash,
				      const char *host,const char* ip,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2076 2077 2078 2079 2080 2081
				      const char *db,
				      const char *user, const char *tname,
				      bool exact)
{
  char helping [NAME_LEN*2+USERNAME_LENGTH+3];
  uint len;
2082
  GRANT_NAME *grant_name,*found=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2083 2084

  len  = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1;
2085
  for (grant_name=(GRANT_NAME*) hash_search(name_hash,
2086
					      (byte*) helping,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2087
					      len) ;
2088 2089
       grant_name ;
       grant_name= (GRANT_NAME*) hash_next(name_hash,(byte*) helping,
2090
					     len))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2091 2092 2093
  {
    if (exact)
    {
2094
      if ((host &&
2095 2096 2097
	   !my_strcasecmp(system_charset_info, host, grant_name->host)) ||
	  (ip && !strcmp(ip,grant_name->host)))
	return grant_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2098 2099 2100
    }
    else
    {
2101
      if (((host && !wild_case_compare(system_charset_info,
2102
				       host,grant_name->host)) ||
2103
	   (ip && !wild_case_compare(system_charset_info,
2104 2105 2106
				     ip,grant_name->host))) &&
          (!found || found->sort < grant_name->sort))
	found=grant_name;					// Host ok
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2107 2108 2109 2110 2111 2112
    }
  }
  return found;
}


2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129
inline GRANT_NAME *
proc_hash_search(const char *host, const char *ip, const char *db,
		  const char *user, const char *tname, bool exact)
{
  return (GRANT_TABLE*) name_hash_search(&proc_priv_hash, host, ip, db,
					 user, tname, exact);
}


inline GRANT_TABLE *
table_hash_search(const char *host, const char *ip, const char *db,
		  const char *user, const char *tname, bool exact)
{
  return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
					 user, tname, exact);
}

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

2131
inline GRANT_COLUMN *
2132
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2133 2134 2135 2136 2137 2138 2139 2140 2141
{
  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,
2142
				ulong rights, bool revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2143 2144 2145
{
  int error=0,result=0;
  byte key[MAX_KEY_LENGTH];
2146 2147
  uint key_prefix_length;
  KEY_PART_INFO *key_part= table->key_info->key_part;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2148 2149
  DBUG_ENTER("replace_column_table");

2150 2151 2152 2153
  table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
  table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
  table->field[3]->store(table_name,(uint) strlen(table_name), system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2154

2155 2156 2157 2158
  /* Get length of 3 first key parts */
  key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
                      key_part[2].store_length + key_part[3].store_length);
  key_copy(key, table->record[0], table->key_info, key_prefix_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2159

2160
  rights&= COL_ACLS;				// Only ACL for columns
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2161 2162 2163 2164 2165

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

  List_iterator <LEX_COLUMN> iter(columns);
  class LEX_COLUMN *xx;
2166
  table->file->ha_index_init(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2167 2168
  while ((xx=iter++))
  {
2169
    ulong privileges = xx->rights;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2170
    bool old_row_exists=0;
2171 2172 2173 2174
    byte user_key[MAX_KEY_LENGTH];

    key_restore(table->record[0],key,table->key_info,
                key_prefix_length);
2175
    table->field[4]->store(xx->column.ptr(),xx->column.length(),
2176
                           system_charset_info);
2177 2178 2179
    /* Get key for the first 4 columns */
    key_copy(user_key, table->record[0], table->key_info,
             table->key_info->key_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2180

2181
    table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
2182 2183 2184
    if (table->file->index_read(table->record[0], user_key,
				table->key_info->key_length,
                                HA_READ_KEY_EXACT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2185 2186 2187
    {
      if (revoke_grant)
      {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2188
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
2189 2190
                 combo.user.str, combo.host.str,
                 table_name); /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2191 2192 2193
	result= -1; /* purecov: inspected */
	continue; /* purecov: inspected */
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2194
      old_row_exists = 0;
2195
      restore_record(table, s->default_values);		// Get empty record
2196 2197
      key_restore(table->record[0],key,table->key_info,
                  key_prefix_length);
2198
      table->field[4]->store(xx->column.ptr(),xx->column.length(),
2199
                             system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2200 2201 2202
    }
    else
    {
2203
      ulong tmp= (ulong) table->field[6]->val_int();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2204 2205 2206 2207 2208 2209
      tmp=fix_rights_for_column(tmp);

      if (revoke_grant)
	privileges = tmp & ~(privileges | rights);
      else
	privileges |= tmp;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2210
      old_row_exists = 1;
2211
      store_record(table,record[1]);			// copy original row
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2212 2213 2214 2215
    }

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

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2216
    if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
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
    {
      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);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2243
      my_hash_insert(&g_t->hash_columns,(byte*) grant_column);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2244 2245 2246 2247 2248 2249 2250 2251 2252 2253
    }
  }

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

  if (revoke_grant)
  {
2254 2255
    byte user_key[MAX_KEY_LENGTH];
    key_copy(user_key, table->record[0], table->key_info,
2256 2257
             key_prefix_length);

2258
    table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
2259
    if (table->file->index_read(table->record[0], user_key,
2260
				key_prefix_length,
2261
                                HA_READ_KEY_EXACT))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2262 2263
      goto end;

2264
    /* Scan through all rows with the same host,db,user and table */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2265 2266
    do
    {
2267
      ulong privileges = (ulong) table->field[6]->val_int();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2268
      privileges=fix_rights_for_column(privileges);
2269
      store_record(table,record[1]);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2270 2271 2272 2273 2274

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

	privileges&= ~rights;
	table->field[6]->store((longlong)
			       get_rights_for_column(privileges));
2281
	table->field[4]->val_str(&column_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311
	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]) &&
2312
	     !key_cmp_if_same(table, key, 0, key_prefix_length));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2313 2314
  }

2315
end:
2316
  table->file->ha_index_end();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2317 2318 2319 2320 2321 2322 2323
  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,
2324 2325
			       ulong rights, ulong col_rights,
			       bool revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2326
{
2327
  char grantor[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2328
  int old_row_exists = 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2329
  int error=0;
2330
  ulong store_table_rights, store_col_rights;
2331
  byte user_key[MAX_KEY_LENGTH];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2332 2333
  DBUG_ENTER("replace_table_table");

2334
  strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2335

2336 2337 2338 2339
  /*
    The following should always succeed as new users are created before
    this function is called!
  */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2340 2341
  if (!find_acl_user(combo.host.str,combo.user.str))
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2342 2343
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
               MYF(0));	/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2344 2345 2346
    DBUG_RETURN(-1);				/* purecov: deadcode */
  }

2347
  restore_record(table, s->default_values);     // Get empty record
2348 2349 2350 2351
  table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
  table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
  table->field[3]->store(table_name,(uint) strlen(table_name), system_charset_info);
2352
  store_record(table,record[1]);			// store at pos 1
2353 2354
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2355

2356
  table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
2357 2358
  if (table->file->index_read_idx(table->record[0], 0,
                                  user_key, table->key_info->key_length,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2359 2360 2361 2362 2363 2364 2365 2366 2367
				  HA_READ_KEY_EXACT))
  {
    /*
      The following should never happen as we first check the in memory
      grant tables for the user.  There is however always a small change that
      the user has modified the grant tables directly.
    */
    if (revoke_grant)
    { // no row, no revoke
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2368 2369
      my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
               combo.user.str, combo.host.str,
2370
               table_name);		        /* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2371 2372
      DBUG_RETURN(-1);				/* purecov: deadcode */
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2373
    old_row_exists = 0;
2374
    restore_record(table,record[1]);			// Get saved record
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2375 2376
  }

2377 2378
  store_table_rights= get_rights_for_table(rights);
  store_col_rights=   get_rights_for_column(col_rights);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2379
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2380
  {
2381
    ulong j,k;
2382
    store_record(table,record[1]);
2383 2384
    j = (ulong) table->field[6]->val_int();
    k = (ulong) table->field[7]->val_int();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2385 2386 2387

    if (revoke_grant)
    {
2388
      /* column rights are already fixed in mysql_table_grant */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2389 2390 2391 2392
      store_table_rights=j & ~store_table_rights;
    }
    else
    {
2393 2394
      store_table_rights|= j;
      store_col_rights|=   k;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2395 2396 2397
    }
  }

2398
  table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2399 2400 2401
  table->field[6]->store((longlong) store_table_rights);
  table->field[7]->store((longlong) store_col_rights);
  rights=fix_rights_for_table(store_table_rights);
2402
  col_rights=fix_rights_for_column(store_col_rights);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2403

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2404
  if (old_row_exists)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420
  {
    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 */
  }

2421
  if (rights | col_rights)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2422
  {
2423
    grant_table->privs= rights;
2424
    grant_table->cols=	col_rights;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2425 2426 2427
  }
  else
  {
2428
    hash_delete(&column_priv_hash,(byte*) grant_table);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2429 2430 2431
  }
  DBUG_RETURN(0);

2432 2433
  /* This should never happen */
table_error:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2434 2435 2436 2437 2438
  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
  DBUG_RETURN(-1); /* purecov: deadcode */
}


2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467
static int replace_proc_table(THD *thd, GRANT_NAME *grant_name,
			      TABLE *table, const LEX_USER &combo,
			      const char *db, const char *proc_name,
			      ulong rights, bool revoke_grant)
{
  char grantor[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
  int old_row_exists= 1;
  int error=0;
  ulong store_proc_rights;
  DBUG_ENTER("replace_proc_table");

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

  strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS);

  /*
    The following should always succeed as new users are created before
    this function is called!
  */
  if (!find_acl_user(combo.host.str,combo.user.str))
  {
    my_error(ER_PASSWORD_NO_MATCH,MYF(0));
    DBUG_RETURN(-1);
  }

2468
  restore_record(table, s->default_values);		// Get empty record
2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549
  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(proc_name,(uint) strlen(proc_name), &my_charset_latin1);
  store_record(table,record[1]);			// store at pos 1

  if (table->file->index_read_idx(table->record[0],0,
				  (byte*) table->field[0]->ptr,0,
				  HA_READ_KEY_EXACT))
  {
    /*
      The following should never happen as we first check the in memory
      grant tables for the user.  There is however always a small change that
      the user has modified the grant tables directly.
    */
    if (revoke_grant)
    { // no row, no revoke
      my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
               combo.user.str, combo.host.str, proc_name);
      DBUG_RETURN(-1);
    }
    old_row_exists= 0;
    restore_record(table,record[1]);			// Get saved record
  }

  store_proc_rights= get_rights_for_procedure(rights);
  if (old_row_exists)
  {
    ulong j;
    store_record(table,record[1]);
    j= (ulong) table->field[6]->val_int();

    if (revoke_grant)
    {
      /* column rights are already fixed in mysql_table_grant */
      store_proc_rights=j & ~store_proc_rights;
    }
    else
    {
      store_proc_rights|= j;
    }
  }

  table->field[4]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
  table->field[6]->store((longlong) store_proc_rights);
  rights=fix_rights_for_procedure(store_proc_rights);

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

  if (rights)
  {
    grant_name->privs= rights;
  }
  else
  {
    hash_delete(&proc_priv_hash,(byte*) grant_name);
  }
  DBUG_RETURN(0);

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


2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562
/*
  Store table level and column level grants in the privilege tables

  SYNOPSIS
    mysql_table_grant()
    thd			Thread handle
    table_list		List of tables to give grant
    user_list		List of users to give grant
    columns		List of columns to give grant
    rights		Table level grant
    revoke_grant	Set to 1 if this is a REVOKE command

  RETURN
2563 2564
    FALSE ok
    TRUE  error
2565 2566
*/

2567
bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
2568 2569 2570
		      List <LEX_USER> &user_list,
		      List <LEX_COLUMN> &columns, ulong rights,
		      bool revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2571
{
2572
  ulong column_priv= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2573 2574 2575
  List_iterator <LEX_USER> str_list (user_list);
  LEX_USER *Str;
  TABLE_LIST tables[3];
2576
  bool create_new_users=0;
2577
  char *db_name, *table_name;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2578 2579 2580 2581
  DBUG_ENTER("mysql_table_grant");

  if (!initialized)
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2582 2583
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: inspected */
2584
    DBUG_RETURN(TRUE);				/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2585 2586 2587
  }
  if (rights & ~TABLE_ACLS)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2588 2589
    my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
               MYF(0));
2590
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2591 2592
  }

2593
  if (!revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2594
  {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2595
    if (columns.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2596
    {
2597 2598
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2599 2600 2601 2602
      int res;

      if (open_and_lock_tables(thd, table_list))
        DBUG_RETURN(TRUE);
2603 2604

      while ((column = column_iter++))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2605
      {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2606
        uint unused_field_idx= NO_CACHED_FIELD_INDEX;
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2607 2608
        Field *f=find_field_in_table(thd, table_list, column->column.ptr(),
                                     column->column.ptr(),
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2609
                                     column->column.length(), 0, 1, 1, 0,
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2610 2611
                                     &unused_field_idx, FALSE);
        if (f == (Field*)0)
2612
        {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2613 2614
          my_error(ER_BAD_FIELD_ERROR, MYF(0),
                   column->column.c_ptr(), table_list->alias);
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2615
          DBUG_RETURN(TRUE);
2616
        }
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2617 2618
        if (f == (Field *)-1)
          DBUG_RETURN(TRUE);
2619
        column_priv|= column->rights;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2620
      }
2621
      close_thread_tables(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2622
    }
2623
    else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2624
    {
2625 2626 2627 2628
      if (!(rights & CREATE_ACL))
      {
        char buf[FN_REFLEN];
        sprintf(buf,"%s/%s/%s.frm",mysql_data_home, table_list->db,
2629
                table_list->table_name);
2630 2631 2632
        fn_format(buf,buf,"","",4+16+32);
        if (access(buf,F_OK))
        {
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2633
          my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2634
          DBUG_RETURN(TRUE);
2635 2636 2637 2638 2639 2640 2641 2642
        }
      }
      if (table_list->grant.want_privilege)
      {
        char command[128];
        get_privilege_desc(command, sizeof(command),
                           table_list->grant.want_privilege);
        my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2643
                 command, thd->priv_user, thd->host_or_ip, table_list->alias);
2644 2645
        DBUG_RETURN(-1);
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2646 2647 2648 2649 2650 2651
    }
  }

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

  bzero((char*) &tables,sizeof(tables));
2652 2653 2654
  tables[0].alias=tables[0].table_name= (char*) "user";
  tables[1].alias=tables[1].table_name= (char*) "tables_priv";
  tables[2].alias=tables[2].table_name= (char*) "columns_priv";
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2655
  tables[0].next_local= tables[0].next_global= tables+1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2656
  /* Don't open column table if we don't need it ! */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2657 2658 2659 2660 2661
  tables[1].next_local=
    tables[1].next_global= ((column_priv ||
			     (revoke_grant &&
			      ((rights & COL_ACLS) || columns.elements)))
			    ? tables+2 : 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2662 2663 2664
  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";

2665 2666 2667 2668 2669
#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
2670 2671
  if (thd->slave_thread && table_rules_on)
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2672 2673 2674
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
2675
    */
2676
    tables[0].updating= tables[1].updating= tables[2].updating= 1;
2677
    if (!tables_ok(0, tables))
2678
      DBUG_RETURN(FALSE);
2679
  }
2680 2681
#endif

2682
  if (simple_open_n_lock_tables(thd,tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2683 2684
  {						// Should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
2685
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2686 2687
  }

2688 2689
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
2690
  bool result= FALSE;
2691
  rw_wrlock(&LOCK_grant);
2692 2693
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2694 2695 2696

  while ((Str = str_list++))
  {
2697
    int error;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2698 2699 2700 2701
    GRANT_TABLE *grant_table;
    if (Str->host.length > HOSTNAME_LENGTH ||
	Str->user.length > USERNAME_LENGTH)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
2702 2703
      my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
                 MYF(0));
2704
      result= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2705 2706 2707
      continue;
    }
    /* Create user if needed */
2708
    pthread_mutex_lock(&acl_cache->lock);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2709 2710
    error=replace_user_table(thd, tables[0].table, *Str,
			     0, revoke_grant, create_new_users);
2711 2712
    pthread_mutex_unlock(&acl_cache->lock);
    if (error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2713
    {
2714
      result= TRUE;				// Remember error
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2715 2716 2717
      continue;					// Add next user
    }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2718 2719 2720
    db_name= (table_list->view_db.length ?
	      table_list->view_db.str :
	      table_list->db);
2721
    table_name= (table_list->view_name.length ?
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2722
		table_list->view_name.str :
2723
		table_list->table_name);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2724

bk@work.mysql.com's avatar
bk@work.mysql.com committed
2725
    /* Find/create cached table grant */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2726
    grant_table= table_hash_search(Str->host.str, NullS, db_name,
2727
				   Str->user.str, table_name, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2728 2729 2730 2731
    if (!grant_table)
    {
      if (revoke_grant)
      {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2732
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
2733
                 Str->user.str, Str->host.str, table_list->table_name);
2734
	result= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2735 2736
	continue;
      }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2737
      grant_table = new GRANT_TABLE (Str->host.str, db_name,
2738
				     Str->user.str, table_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2739 2740 2741 2742
				     rights,
				     column_priv);
      if (!grant_table)				// end of memory
      {
2743
	result= TRUE;				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2744 2745
	continue;				/* purecov: deadcode */
      }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2746
      my_hash_insert(&column_priv_hash,(byte*) grant_table);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2747 2748 2749 2750 2751
    }

    /* If revoke_grant, calculate the new column privilege for tables_priv */
    if (revoke_grant)
    {
2752 2753
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2754 2755 2756
      GRANT_COLUMN *grant_column;

      /* Fix old grants */
2757
      while ((column = column_iter++))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2758 2759
      {
	grant_column = column_hash_search(grant_table,
2760 2761
					  column->column.ptr(),
					  column->column.length());
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2762
	if (grant_column)
2763
	  grant_column->rights&= ~(column->rights | rights);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2764 2765
      }
      /* scan trough all columns to get new column grant */
2766
      column_priv= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782
      for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
      {
	grant_column= (GRANT_COLUMN*) hash_element(&grant_table->hash_columns,
						   idx);
	grant_column->rights&= ~rights;		// Fix other columns
	column_priv|= grant_column->rights;
      }
    }
    else
    {
      column_priv|= grant_table->cols;
    }


    /* update table and columns */

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2783
    if (replace_table_table(thd, grant_table, tables[1].table, *Str,
2784
			    db_name, table_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2785
			    rights, column_priv, revoke_grant))
2786 2787
    {
      /* Should only happen if table is crashed */
2788
      result= TRUE;			       /* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2789 2790 2791
    }
    else if (tables[2].table)
    {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2792
      if ((replace_column_table(grant_table, tables[2].table, *Str,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2793
				columns,
2794
				db_name, table_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2795 2796
				rights, revoke_grant)))
      {
2797
	result= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2798 2799 2800 2801
      }
    }
  }
  grant_option=TRUE;
2802
  thd->mem_root= old_root;
2803
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2804
  if (!result)
2805
    send_ok(thd);
2806
  /* Tables are automatically closed */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2807 2808 2809 2810
  DBUG_RETURN(result);
}


2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834
/*
  Store procedure level grants in the privilege tables

  SYNOPSIS
    mysql_procedure_grant()
    thd			Thread handle
    table_list		List of procedures to give grant
    user_list		List of users to give grant
    rights		Table level grant
    revoke_grant	Set to 1 if this is a REVOKE command

  RETURN
    0	ok
    1	error
*/

bool mysql_procedure_grant(THD *thd, TABLE_LIST *table_list,
			   List <LEX_USER> &user_list, ulong rights,
			   bool revoke_grant, bool no_error)
{
  List_iterator <LEX_USER> str_list (user_list);
  LEX_USER *Str;
  TABLE_LIST tables[2];
  bool create_new_users=0, result=0;
2835
  char *db_name, *table_name;
2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861
  DBUG_ENTER("mysql_procedure_grant");

  if (!initialized)
  {
    if (!no_error)
      my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
               "--skip-grant-tables");
    DBUG_RETURN(TRUE);
  }
  if (rights & ~PROC_ACLS)
  {
    if (!no_error)
      my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
        	 MYF(0));
    DBUG_RETURN(TRUE);
  }

  if (!revoke_grant)
  {
    if (sp_exists_routine(thd, table_list, 0, no_error)<0)
      DBUG_RETURN(TRUE);
  }

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

  bzero((char*) &tables,sizeof(tables));
2862 2863
  tables[0].alias=tables[0].table_name= (char*) "user";
  tables[1].alias=tables[1].table_name= (char*) "procs_priv";
2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923
  tables[0].next_local= tables[0].next_global= tables+1;
  tables[0].lock_type=tables[1].lock_type=TL_WRITE;
  tables[0].db=tables[1].db=(char*) "mysql";

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
  if (thd->slave_thread && table_rules_on)
  {
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
    */
    tables[0].updating= tables[1].updating= 1;
    if (!tables_ok(0, tables))
      DBUG_RETURN(FALSE);
  }
#endif

  if (simple_open_n_lock_tables(thd,tables))
  {						// Should never happen
    close_thread_tables(thd);
    DBUG_RETURN(TRUE);
  }

  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
  rw_wrlock(&LOCK_grant);
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;

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

  while ((Str= str_list++))
  {
    int error;
    GRANT_NAME *grant_name;
    if (Str->host.length > HOSTNAME_LENGTH ||
	Str->user.length > USERNAME_LENGTH)
    {
      if (!no_error)
	my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
                   MYF(0));
      result= TRUE;
      continue;
    }
    /* Create user if needed */
    pthread_mutex_lock(&acl_cache->lock);
    error=replace_user_table(thd, tables[0].table, *Str,
			     0, revoke_grant, create_new_users);
    pthread_mutex_unlock(&acl_cache->lock);
    if (error)
    {
      result= TRUE;				// Remember error
      continue;					// Add next user
    }

    db_name= table_list->db;
2924
    table_name= table_list->table_name;
2925 2926

    grant_name= proc_hash_search(Str->host.str, NullS, db_name,
2927
    				 Str->user.str, table_name, 1);
2928 2929 2930 2931 2932 2933
    if (!grant_name)
    {
      if (revoke_grant)
      {
        if (!no_error)
          my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
2934
		   Str->user.str, Str->host.str, table_name);
2935 2936 2937 2938
	result= TRUE;
	continue;
      }
      grant_name= new GRANT_NAME(Str->host.str, db_name,
2939
				 Str->user.str, table_name,
2940 2941 2942 2943 2944 2945 2946 2947 2948 2949
				 rights);
      if (!grant_name)
      {
        result= TRUE;
	continue;
      }
      my_hash_insert(&proc_priv_hash,(byte*) grant_name);
    }
    
    if (replace_proc_table(thd, grant_name, tables[1].table, *Str,
2950
			   db_name, table_name, rights, revoke_grant))
2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965
    {
      result= TRUE;
      continue;
    }
  }
  grant_option=TRUE;
  thd->mem_root= old_root;
  rw_unlock(&LOCK_grant);
  if (!result && !no_error)
    send_ok(thd);
  /* Tables are automatically closed */
  DBUG_RETURN(result);
}


2966 2967
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
                 ulong rights, bool revoke_grant)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2968 2969 2970
{
  List_iterator <LEX_USER> str_list (list);
  LEX_USER *Str;
2971
  char tmp_db[NAME_LEN+1];
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2972
  bool create_new_users=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2973 2974 2975 2976
  TABLE_LIST tables[2];
  DBUG_ENTER("mysql_grant");
  if (!initialized)
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
2977 2978
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: tested */
2979
    DBUG_RETURN(TRUE);				/* purecov: tested */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2980 2981
  }

2982 2983 2984
  if (lower_case_table_names && db)
  {
    strmov(tmp_db,db);
2985
    my_casedn_str(files_charset_info, tmp_db);
2986 2987
    db=tmp_db;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2988 2989

  /* open the mysql.user and mysql.db tables */
2990
  bzero((char*) &tables,sizeof(tables));
2991 2992
  tables[0].alias=tables[0].table_name=(char*) "user";
  tables[1].alias=tables[1].table_name=(char*) "db";
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2993
  tables[0].next_local= tables[0].next_global= tables+1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
2994 2995
  tables[0].lock_type=tables[1].lock_type=TL_WRITE;
  tables[0].db=tables[1].db=(char*) "mysql";
2996 2997 2998 2999 3000 3001

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
3002 3003
  if (thd->slave_thread && table_rules_on)
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3004 3005 3006
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
3007
    */
3008
    tables[0].updating= tables[1].updating= 1;
3009
    if (!tables_ok(0, tables))
3010
      DBUG_RETURN(FALSE);
3011
  }
3012 3013
#endif

3014
  if (simple_open_n_lock_tables(thd,tables))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3015 3016
  {						// This should never happen
    close_thread_tables(thd);			/* purecov: deadcode */
3017
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3018 3019
  }

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3020 3021
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3022

3023
  /* go through users in user_list */
3024
  rw_wrlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3025 3026 3027 3028 3029 3030 3031 3032 3033
  VOID(pthread_mutex_lock(&acl_cache->lock));
  grant_version++;

  int result=0;
  while ((Str = str_list++))
  {
    if (Str->host.length > HOSTNAME_LENGTH ||
	Str->user.length > USERNAME_LENGTH)
    {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3034 3035
      my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
                 MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3036 3037 3038
      result= -1;
      continue;
    }
3039
    if ((replace_user_table(thd,
3040
			    tables[0].table,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3041
			    *Str,
3042 3043
			    (!db ? rights : 0), revoke_grant,
			    create_new_users)))
3044
      result= -1;
3045
    else if (db)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3046
    {
3047 3048 3049 3050 3051 3052 3053 3054 3055
      ulong db_rights= rights & DB_ACLS;
      if (db_rights  == rights)
      {
	if (replace_db_table(tables[1].table, db, *Str, db_rights,
			     revoke_grant))
	  result= -1;
      }
      else
      {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3056
	my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3057
	result= -1;
3058
      }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3059
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3060 3061
  }
  VOID(pthread_mutex_unlock(&acl_cache->lock));
3062
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3063 3064 3065
  close_thread_tables(thd);

  if (!result)
3066
    send_ok(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3067 3068 3069
  DBUG_RETURN(result);
}

3070 3071

/* Free grant array if possible */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3072 3073 3074 3075 3076

void  grant_free(void)
{
  DBUG_ENTER("grant_free");
  grant_option = FALSE;
3077
  hash_free(&column_priv_hash);
3078
  hash_free(&proc_priv_hash);
3079
  free_root(&memex,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3080 3081 3082 3083 3084 3085
  DBUG_VOID_RETURN;
}


/* Init grant array if possible */

3086
my_bool grant_init(THD *org_thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3087
{
3088
  THD  *thd;
3089
  TABLE_LIST tables[3];
3090
  MYSQL_LOCK *lock;
3091
  MEM_ROOT *memex_ptr;
3092
  my_bool return_val= 1;
3093
  TABLE *t_table, *c_table, *p_table;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3094
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3095 3096 3097
  DBUG_ENTER("grant_init");

  grant_option = FALSE;
3098
  (void) hash_init(&column_priv_hash,system_charset_info,
3099
		   0,0,0, (hash_get_key) get_grant_table,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3100
		   (hash_free_key) free_grant_table,0);
3101 3102 3103
  (void) hash_init(&proc_priv_hash,system_charset_info,
		   0,0,0, (hash_get_key) get_grant_table,
		   0,0);
3104
  init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3105

3106
  /* Don't do anything if running with --skip-grant */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3107 3108
  if (!initialized)
    DBUG_RETURN(0);				/* purecov: tested */
3109

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3110 3111
  if (!(thd=new THD))
    DBUG_RETURN(1);				/* purecov: deadcode */
3112
  thd->store_globals();
3113 3114
  thd->db= my_strdup("mysql",MYF(0));
  thd->db_length=5;				// Safety
3115
  bzero((char*) &tables, sizeof(tables));
3116 3117 3118
  tables[0].alias=tables[0].table_name= (char*) "tables_priv";
  tables[1].alias=tables[1].table_name= (char*) "columns_priv";
  tables[2].alias=tables[2].table_name= (char*) "procs_priv";
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3119
  tables[0].next_local= tables[0].next_global= tables+1;
3120 3121 3122
  tables[1].next_local= tables[1].next_global= 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;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3123

3124 3125
  uint counter;
  if (open_tables(thd, tables, &counter))
3126 3127
    goto end;

3128
  TABLE *ptr[3];				// Lock tables for quick update
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3129 3130
  ptr[0]= tables[0].table;
  ptr[1]= tables[1].table;
3131 3132
  ptr[2]= tables[2].table;
  if (!(lock=mysql_lock_tables(thd,ptr,3)))
3133
    goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3134 3135

  t_table = tables[0].table; c_table = tables[1].table;
3136
  p_table= tables[2].table;
3137
  t_table->file->ha_index_init(0);
3138 3139
  p_table->file->ha_index_init(0);
  if (!t_table->file->index_first(t_table->record[0]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3140
  {
3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152
    /* Will be restored by org_thd->store_globals() */
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
    {
      GRANT_TABLE *mem_check;
      if (!(mem_check=new GRANT_TABLE(t_table,c_table)))
      {
	/* This could only happen if we are out memory */
	grant_option= FALSE;
	goto end_unlock;
      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3153

3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177
      if (check_no_resolve)
      {
	if (hostname_requires_resolving(mem_check->host))
	{
          sql_print_warning("'tables_priv' entry '%s %s@%s' "
                            "ignored in --skip-name-resolve mode.",
                            mem_check->tname, mem_check->user,
                            mem_check->host, mem_check->host);
	  continue;
	}
      }

      if (! mem_check->ok())
	delete mem_check;
      else if (my_hash_insert(&column_priv_hash,(byte*) mem_check))
      {
	delete mem_check;
	grant_option= FALSE;
	goto end_unlock;
      }
    }
    while (!t_table->file->index_next(t_table->record[0]));
  }
  if (!p_table->file->index_first(p_table->record[0]))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3178
  {
3179 3180 3181 3182
    /* Will be restored by org_thd->store_globals() */
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3183
    {
3184 3185 3186 3187 3188 3189 3190
      GRANT_NAME *mem_check;
      if (!(mem_check=new GRANT_NAME(p_table)))
      {
	/* This could only happen if we are out memory */
	grant_option= FALSE;
	goto end_unlock;
      }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3191

3192
      if (check_no_resolve)
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3193
      {
3194 3195 3196 3197 3198 3199 3200 3201
	if (hostname_requires_resolving(mem_check->host))
	{
          sql_print_warning("'procs_priv' entry '%s %s@%s' "
                            "ignored in --skip-name-resolve mode.",
                            mem_check->tname, mem_check->user,
                            mem_check->host, mem_check->host);
	  continue;
	}
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3202 3203
      }

3204 3205 3206 3207 3208 3209 3210 3211 3212
      mem_check->privs= fix_rights_for_procedure(mem_check->privs);
      if (! mem_check->ok())
	delete mem_check;
      else if (my_hash_insert(&proc_priv_hash,(byte*) mem_check))
      {
	delete mem_check;
	grant_option= FALSE;
	goto end_unlock;
      }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3213
    }
3214
    while (!p_table->file->index_next(p_table->record[0]));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3215
  }
3216
  grant_option= TRUE;
3217 3218 3219
  return_val=0;					// Return ok

end_unlock:
3220
  t_table->file->ha_index_end();
3221
  p_table->file->ha_index_end();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3222 3223
  mysql_unlock_tables(thd, lock);
  thd->version--;				// Force close to free memory
3224 3225

end:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3226 3227
  close_thread_tables(thd);
  delete thd;
3228 3229
  if (org_thd)
    org_thd->store_globals();
3230 3231 3232 3233 3234
  else
  {
    /* Remember that we don't have a THD */
    my_pthread_setspecific_ptr(THR_THD,  0);
  }
3235
  DBUG_RETURN(return_val);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3236 3237 3238
}


3239
/*
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3240
 Reload grant array (table and column privileges) if possible
3241 3242 3243 3244 3245 3246 3247 3248

  SYNOPSIS
    grant_reload()
    thd			Thread handler

  NOTES
    Locked tables are checked by acl_init and doesn't have to be checked here
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3249

3250
void grant_reload(THD *thd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3251
{
3252
  HASH old_column_priv_hash, old_proc_priv_hash;
3253
  bool old_grant_option;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3254 3255 3256
  MEM_ROOT old_mem;
  DBUG_ENTER("grant_reload");

3257
  rw_wrlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3258
  grant_version++;
3259
  old_column_priv_hash= column_priv_hash;
3260
  old_proc_priv_hash= proc_priv_hash;
3261
  old_grant_option= grant_option;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3262
  old_mem= memex;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3263

3264
  if (grant_init(thd))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3265
  {						// Error. Revert to old hash
3266
    DBUG_PRINT("error",("Reverting to old privileges"));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3267
    grant_free();				/* purecov: deadcode */
3268
    column_priv_hash= old_column_priv_hash;	/* purecov: deadcode */
3269
    proc_priv_hash= old_proc_priv_hash;
3270
    grant_option= old_grant_option;		/* purecov: deadcode */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3271
    memex= old_mem;				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3272 3273 3274
  }
  else
  {
3275
    hash_free(&old_column_priv_hash);
3276
    hash_free(&old_proc_priv_hash);
3277
    free_root(&old_mem,MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3278
  }
3279
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3280 3281 3282 3283 3284
  DBUG_VOID_RETURN;
}


/****************************************************************************
3285
  Check table level grants
3286

3287
  SYNOPSIS
3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300
   bool check_grant()
   thd		Thread handler
   want_access  Bits of privileges user needs to have
   tables	List of tables to check. The user should have 'want_access'
		to all tables in list.
   show_table	<> 0 if we are in show table. In this case it's enough to have
	        any privilege for the table
   number	Check at most this number of tables.
   no_errors	If 0 then we write an error. The error is sent directly to
		the client

   RETURN
     0  ok
3301
     1  Error: User did not have the requested privileges
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3302 3303
****************************************************************************/

3304
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3305
		 uint show_table, uint number, bool no_errors)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3306 3307 3308
{
  TABLE_LIST *table;
  char *user = thd->priv_user;
3309 3310
  DBUG_ENTER("check_grant");
  DBUG_ASSERT(number > 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3311

3312
  want_access&= ~thd->master_access;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3313
  if (!want_access)
3314
    DBUG_RETURN(0);                             // ok
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3315

3316
  rw_rdlock(&LOCK_grant);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3317
  for (table= tables; table && number--; table= table->next_global)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3318
  {
3319
    GRANT_TABLE *grant_table;
3320
    if (!(~table->grant.privilege & want_access) || 
3321
        table->derived || table->schema_table || table->belong_to_view)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3322
    {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3323 3324 3325 3326 3327
      /*
        It is subquery in the FROM clause. VIEW set table->derived after
        table opening, but this function always called before table opening.
      */
      table->grant.want_privilege= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3328 3329
      continue;					// Already checked
    }
3330
    if (!(grant_table= table_hash_search(thd->host,thd->ip,
3331
                                         table->db,user, table->table_name,0)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3332 3333 3334 3335
    {
      want_access &= ~table->grant.privilege;
      goto err;					// No grants
    }
monty@tramp.mysql.fi's avatar
monty@tramp.mysql.fi committed
3336 3337
    if (show_table)
      continue;					// We have some priv on this
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353

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

3357
err:
3358
  rw_unlock(&LOCK_grant);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
3359
  if (!no_errors)				// Not a silent skip of table
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3360
  {
3361 3362
    char command[128];
    get_privilege_desc(command, sizeof(command), want_access);
3363 3364 3365 3366
    my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
             command,
             thd->priv_user,
             thd->host_or_ip,
3367
             table ? table->table_name : "unknown");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3368
  }
3369
  DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3370 3371 3372
}


bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3373
bool check_grant_column(THD *thd, GRANT_INFO *grant,
3374
			const char *db_name, const char *table_name,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3375
			const char *name, uint length, uint show_tables)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3376 3377 3378
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3379
  ulong want_access= grant->want_privilege & ~grant->privilege;
monty@mysql.com's avatar
monty@mysql.com committed
3380 3381 3382
  DBUG_ENTER("check_grant_column");
  DBUG_PRINT("enter", ("table: %s  want_access: %u", table_name, want_access));

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3383
  if (!want_access)
monty@mysql.com's avatar
monty@mysql.com committed
3384
    DBUG_RETURN(0);				// Already checked
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3385

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

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3390
  if (grant->version != grant_version)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3391
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3392 3393
    grant->grant_table=
      table_hash_search(thd->host, thd->ip, db_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3394
			thd->priv_user,
monty@mysql.com's avatar
monty@mysql.com committed
3395
			table_name, 0);         /* purecov: inspected */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3396
    grant->version= grant_version;		/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3397
  }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3398
  if (!(grant_table= grant->grant_table))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3399 3400 3401 3402 3403
    goto err;					/* purecov: deadcode */

  grant_column=column_hash_search(grant_table, name, length);
  if (grant_column && !(~grant_column->rights & want_access))
  {
3404
    rw_unlock(&LOCK_grant);
monty@mysql.com's avatar
monty@mysql.com committed
3405
    DBUG_RETURN(0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3406 3407
  }
#ifdef NOT_USED
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3408
  if (show_tables && (grant_column || grant->privilege & COL_ACLS))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3409
  {
3410
    rw_unlock(&LOCK_grant);			/* purecov: deadcode */
monty@mysql.com's avatar
monty@mysql.com committed
3411
    DBUG_RETURN(0);				/* purecov: deadcode */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3412 3413 3414
  }
#endif

3415
err:
3416
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3417 3418
  if (!show_tables)
  {
3419 3420
    char command[128];
    get_privilege_desc(command, sizeof(command), want_access);
3421 3422 3423 3424 3425 3426
    my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
             command,
             thd->priv_user,
             thd->host_or_ip,
             name,
             table_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3427
  }
monty@mysql.com's avatar
monty@mysql.com committed
3428
  DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3429 3430 3431
}


bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3432
bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant,
3433
                             const char* db_name, const char *table_name,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3434
                             Field_iterator *fields)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3435 3436 3437
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
3438
  Field *field=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3439

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3440
  want_access &= ~grant->privilege;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3441
  if (!want_access)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3442
    return 0;				// Already checked
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3443 3444
  if (!grant_option)
    goto err2;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3445

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

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3450
  if (grant->version != grant_version)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3451
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3452 3453
    grant->grant_table=
      table_hash_search(thd->host, thd->ip, db_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3454
			thd->priv_user,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3455 3456
			table_name, 0);	/* purecov: inspected */
    grant->version= grant_version;		/* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3457
  }
3458
  /* The following should always be true */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3459
  if (!(grant_table= grant->grant_table))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3460 3461
    goto err;					/* purecov: inspected */

3462
  for (; !fields->end_of_fields(); fields->next())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3463
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3464 3465 3466
    const char *field_name= fields->name();
    grant_column= column_hash_search(grant_table, field_name,
				    (uint) strlen(field_name));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3467 3468 3469
    if (!grant_column || (~grant_column->rights & want_access))
      goto err;
  }
3470
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3471 3472
  return 0;

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3473
err:
3474
  rw_unlock(&LOCK_grant);
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
3475
err2:
3476 3477
  char command[128];
  get_privilege_desc(command, sizeof(command), want_access);
3478 3479 3480 3481 3482 3483
  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
           command,
           thd->priv_user,
           thd->host_or_ip,
           fields->name(),
           table_name);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3484 3485 3486 3487
  return 1;
}


3488
/*
3489
  Check if a user has the right to access a database
3490
  Access is accepted if he has a grant for any table/routine in the database
3491
  Return 1 if access is denied
3492
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3493 3494 3495 3496 3497 3498 3499 3500

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;
3501
  rw_rdlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3502

3503
  for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3504
  {
3505 3506
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  idx);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3507 3508
    if (len < grant_table->key_length &&
	!memcmp(grant_table->hash_key,helping,len) &&
3509
	(thd->host && !wild_case_compare(system_charset_info,
3510
                                         thd->host,grant_table->host) ||
3511
	 (thd->ip && !wild_case_compare(system_charset_info,
3512
                                        thd->ip,grant_table->host))))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3513 3514 3515 3516 3517
    {
      error=0;					// Found match
      break;
    }
  }
3518
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3519 3520 3521
  return error;
}

3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555

/****************************************************************************
  Check procedure level grants

  SYNPOSIS
   bool check_grant_procedure()
   thd		Thread handler
   want_access  Bits of privileges user needs to have
   procs	List of procedures to check. The user should have 'want_access'
   no_errors	If 0 then we write an error. The error is sent directly to
		the client

   RETURN
     0  ok
     1  Error: User did not have the requested privielges
****************************************************************************/

bool check_grant_procedure(THD *thd, ulong want_access, 
			   TABLE_LIST *procs, bool no_errors)
{
  TABLE_LIST *table;
  char *user= thd->priv_user;
  char *host= thd->priv_host;
  DBUG_ENTER("check_grant_procedure");

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

  rw_rdlock(&LOCK_grant);
  for (table= procs; table; table= table->next_global)
  {
    GRANT_NAME *grant_proc;
    if ((grant_proc= proc_hash_search(host,thd->ip, 
3556
				      table->db, user, table->table_name, 0)))
3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573
      table->grant.privilege|= grant_proc->privs;

    if (want_access & ~table->grant.privilege)
    {
      want_access &= ~table->grant.privilege;
      goto err;
    }
  }
  rw_unlock(&LOCK_grant);
  DBUG_RETURN(0);
err:
  rw_unlock(&LOCK_grant);
  if (!no_errors)
  {
    char buff[1024];
    const char *command="";
    if (table)
3574
      strxmov(buff, table->db, ".", table->table_name, NullS);
3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587
    if (want_access & EXECUTE_ACL)
      command= "execute";
    else if (want_access & ALTER_PROC_ACL)
      command= "alter procedure";
    else if (want_access & GRANT_ACL)
      command= "grant";
    my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
             command, user, host, table ? buff : "unknown");
  }
  DBUG_RETURN(1);
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
3588
/*****************************************************************************
3589
  Functions to retrieve the grant for a table/column  (for SHOW functions)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3590 3591
*****************************************************************************/

3592
ulong get_table_grant(THD *thd, TABLE_LIST *table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3593
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3594
  ulong privilege;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3595 3596 3597 3598
  char *user = thd->priv_user;
  const char *db = table->db ? table->db : thd->db;
  GRANT_TABLE *grant_table;

3599
  rw_rdlock(&LOCK_grant);
3600 3601 3602
#ifdef EMBEDDED_LIBRARY
  grant_table= NULL;
#else
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3603
  grant_table= table_hash_search(thd->host, thd->ip, db, user,
3604
				 table->table_name, 0);
3605
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3606 3607 3608 3609
  table->grant.grant_table=grant_table; // Remember for column test
  table->grant.version=grant_version;
  if (grant_table)
    table->grant.privilege|= grant_table->privs;
3610
  privilege= table->grant.privilege;
3611
  rw_unlock(&LOCK_grant);
3612
  return privilege;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3613 3614 3615
}


bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3616 3617 3618
ulong get_column_grant(THD *thd, GRANT_INFO *grant,
                       const char *db_name, const char *table_name,
                       const char *field_name)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3619 3620 3621
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
3622
  ulong priv;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3623

3624
  rw_rdlock(&LOCK_grant);
3625
  /* reload table if someone has modified any grants */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3626
  if (grant->version != grant_version)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3627
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3628 3629
    grant->grant_table=
      table_hash_search(thd->host, thd->ip, db_name,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3630
			thd->priv_user,
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3631 3632
			table_name, 0);	        /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3633 3634
  }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3635 3636
  if (!(grant_table= grant->grant_table))
    priv= grant->privilege;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3637 3638
  else
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3639 3640
    grant_column= column_hash_search(grant_table, field_name,
                                     (uint) strlen(field_name));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3641
    if (!grant_column)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3642
      priv= grant->privilege;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3643
    else
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3644
      priv= grant->privilege | grant_column->rights;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3645
  }
3646
  rw_unlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3647 3648 3649
  return priv;
}

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

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

3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664
static void add_user_option(String *grant, ulong value, const char *name)
{
  if (value)
  {
    char buff[22], *p; // just as in int2str
    grant->append(' ');
    grant->append(name, strlen(name));
    grant->append(' ');
    p=int10_to_str(value, buff, 10);
    grant->append(buff,p-buff);
  }
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3665 3666

static const char *command_array[]=
3667
{
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
3668 3669 3670 3671
  "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",
3672
  "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
3673
};
3674

3675 3676
static uint command_lengths[]=
{
3677
  6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9, 14, 13
3678 3679
};

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

3681 3682 3683 3684 3685 3686 3687
/*
  SHOW GRANTS;  Send grants for a user to the client

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

3688
bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3689
{
3690 3691
  ulong want_access;
  uint counter,index;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3692
  int  error = 0;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3693 3694
  ACL_USER *acl_user;
  ACL_DB *acl_db;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3695
  char buff[1024];
3696
  Protocol *protocol= thd->protocol;
tonu@x153.internalnet's avatar
tonu@x153.internalnet committed
3697
  DBUG_ENTER("mysql_show_grants");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3698 3699 3700 3701

  LINT_INIT(acl_user);
  if (!initialized)
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3702
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3703
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3704
  }
monty@mysql.com's avatar
monty@mysql.com committed
3705 3706 3707 3708 3709 3710

  if (!lex_user->host.str)
  {
    lex_user->host.str= (char*) "%";
    lex_user->host.length=1;
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3711 3712 3713
  if (lex_user->host.length > HOSTNAME_LENGTH ||
      lex_user->user.length > USERNAME_LENGTH)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
3714 3715
    my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER),
               MYF(0));
3716
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3717 3718 3719 3720 3721 3722 3723
  }

  for (counter=0 ; counter < acl_users.elements ; counter++)
  {
    const char *user,*host;
    acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
    if (!(user=acl_user->user))
monty@mysql.com's avatar
monty@mysql.com committed
3724
      user= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3725
    if (!(host=acl_user->host.hostname))
monty@mysql.com's avatar
monty@mysql.com committed
3726
      host= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3727
    if (!strcmp(lex_user->user.str,user) &&
3728
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3729 3730
      break;
  }
peter@mysql.com's avatar
peter@mysql.com committed
3731
  if (counter == acl_users.elements)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3732
  {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
3733 3734
    my_error(ER_NONEXISTING_GRANT, MYF(0),
             lex_user->user.str, lex_user->host.str);
3735
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3736 3737
  }

3738
  Item_string *field=new Item_string("",0,&my_charset_latin1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3739 3740 3741 3742 3743 3744
  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);
3745 3746
  if (protocol->send_fields(&field_list,
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
3747
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3748

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3749
  rw_wrlock(&LOCK_grant);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3750 3751 3752 3753
  VOID(pthread_mutex_lock(&acl_cache->lock));

  /* Add first global access grants */
  {
3754
    String global(buff,sizeof(buff),system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3755 3756 3757
    global.length(0);
    global.append("GRANT ",6);

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3758
    want_access= acl_user->access;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3759 3760 3761 3762
    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);
peter@mysql.com's avatar
peter@mysql.com committed
3763
    else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3764 3765
    {
      bool found=0;
3766
      ulong j,test_access= want_access & ~GRANT_ACL;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3767 3768
      for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
      {
peter@mysql.com's avatar
peter@mysql.com committed
3769
	if (test_access & j)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3770 3771 3772 3773 3774 3775 3776 3777 3778
	{
	  if (found)
	    global.append(", ",2);
	  found=1;
	  global.append(command_array[counter],command_lengths[counter]);
	}
      }
    }
    global.append (" ON *.* TO '",12);
3779 3780
    global.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3781
    global.append ("'@'",3);
3782 3783
    global.append(lex_user->host.str,lex_user->host.length,
		  system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3784
    global.append ('\'');
3785
    if (acl_user->salt_len)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3786
    {
3787 3788 3789 3790 3791
      char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
      if (acl_user->salt_len == SCRAMBLE_LENGTH)
        make_password_from_salt(passwd_buff, acl_user->salt);
      else
        make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3792
      global.append(" IDENTIFIED BY PASSWORD '",25);
3793
      global.append(passwd_buff);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3794 3795
      global.append('\'');
    }
3796 3797
    /* "show grants" SSL related stuff */
    if (acl_user->ssl_type == SSL_TYPE_ANY)
3798
      global.append(" REQUIRE SSL",12);
3799
    else if (acl_user->ssl_type == SSL_TYPE_X509)
3800
      global.append(" REQUIRE X509",13);
3801
    else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
3802
    {
3803
      int ssl_options = 0;
3804
      global.append(" REQUIRE ",9);
3805 3806
      if (acl_user->x509_issuer)
      {
3807 3808 3809
	ssl_options++;
	global.append("ISSUER \'",8);
	global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
3810
	global.append('\'');
3811
      }
3812 3813
      if (acl_user->x509_subject)
      {
3814 3815 3816
	if (ssl_options++)
	  global.append(' ');
	global.append("SUBJECT \'",9);
3817 3818
	global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
                      system_charset_info);
3819
	global.append('\'');
tonu@x153.internalnet's avatar
tonu@x153.internalnet committed
3820
      }
3821 3822
      if (acl_user->ssl_cipher)
      {
3823 3824 3825
	if (ssl_options++)
	  global.append(' ');
	global.append("CIPHER '",8);
3826 3827
	global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
                      system_charset_info);
3828
	global.append('\'');
3829 3830
      }
    }
3831
    if ((want_access & GRANT_ACL) ||
3832 3833 3834 3835
	(acl_user->user_resource.questions ||
         acl_user->user_resource.updates ||
         acl_user->user_resource.conn_per_hour ||
         acl_user->user_resource.user_conn))
3836
    {
peter@mysql.com's avatar
peter@mysql.com committed
3837
      global.append(" WITH",5);
3838
      if (want_access & GRANT_ACL)
peter@mysql.com's avatar
peter@mysql.com committed
3839
	global.append(" GRANT OPTION",13);
3840 3841 3842 3843
      add_user_option(&global, acl_user->user_resource.questions,
		      "MAX_QUERIES_PER_HOUR");
      add_user_option(&global, acl_user->user_resource.updates,
		      "MAX_UPDATES_PER_HOUR");
3844
      add_user_option(&global, acl_user->user_resource.conn_per_hour,
3845
		      "MAX_CONNECTIONS_PER_HOUR");
3846 3847
      add_user_option(&global, acl_user->user_resource.user_conn,
		      "MAX_USER_CONNECTIONS");
3848
    }
3849
    protocol->prepare_for_resend();
3850
    protocol->store(global.ptr(),global.length(),global.charset());
3851
    if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3852
    {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3853
      error= -1;
3854
      goto end;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3855 3856 3857 3858 3859 3860
    }
  }

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

    acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
    if (!(user=acl_db->user))
monty@mysql.com's avatar
monty@mysql.com committed
3865
      user= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3866
    if (!(host=acl_db->host.hostname))
monty@mysql.com's avatar
monty@mysql.com committed
3867
      host= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3868 3869

    if (!strcmp(lex_user->user.str,user) &&
3870
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3871 3872
    {
      want_access=acl_db->access;
peter@mysql.com's avatar
peter@mysql.com committed
3873
      if (want_access)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3874
      {
3875
	String db(buff,sizeof(buff),system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3876 3877 3878 3879 3880
	db.length(0);
	db.append("GRANT ",6);

	if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
	  db.append("ALL PRIVILEGES",14);
3881
	else if (!(want_access & ~GRANT_ACL))
3882
	  db.append("USAGE",5);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3883 3884 3885
	else
	{
	  int found=0, cnt;
3886
	  ulong j,test_access= want_access & ~GRANT_ACL;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897
	  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]);
	    }
	  }
	}
3898 3899 3900
	db.append (" ON ",4);
	append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
	db.append (".* TO '",7);
3901 3902
	db.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3903
	db.append ("'@'",3);
3904 3905
	db.append(lex_user->host.str, lex_user->host.length,
                  system_charset_info);
peter@mysql.com's avatar
peter@mysql.com committed
3906
	db.append ('\'');
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3907
	if (want_access & GRANT_ACL)
3908
	  db.append(" WITH GRANT OPTION",18);
3909
	protocol->prepare_for_resend();
3910
	protocol->store(db.ptr(),db.length(),db.charset());
3911
	if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3912
	{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3913
	  error= -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3914 3915 3916 3917 3918 3919
	  goto end;
	}
      }
    }
  }

3920
  /* Add table & column access */
3921
  for (index=0 ; index < column_priv_hash.records ; index++)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3922
  {
monty@mysql.com's avatar
monty@mysql.com committed
3923
    const char *user;
3924 3925
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3926 3927

    if (!(user=grant_table->user))
3928
      user= "";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3929 3930

    if (!strcmp(lex_user->user.str,user) &&
3931
	!my_strcasecmp(system_charset_info, lex_user->host.str,
monty@mysql.com's avatar
monty@mysql.com committed
3932
                       grant_table->orig_host))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3933
    {
3934 3935
      ulong table_access= grant_table->privs;
      if ((table_access | grant_table->cols) != 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3936
      {
3937
	String global(buff, sizeof(buff), system_charset_info);
3938 3939
	ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3940 3941 3942
	global.length(0);
	global.append("GRANT ",6);

3943
	if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3944
	  global.append("ALL PRIVILEGES",14);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3945
	else if (!test_access)
3946
 	  global.append("USAGE",5);
peter@mysql.com's avatar
peter@mysql.com committed
3947
	else
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3948
	{
3949
          /* Add specific column access */
3950
	  int found= 0;
3951
	  ulong j;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3952

3953
	  for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3954
	  {
peter@mysql.com's avatar
peter@mysql.com committed
3955
	    if (test_access & j)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3956 3957 3958
	    {
	      if (found)
		global.append(", ",2);
3959
	      found= 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3960 3961
	      global.append(command_array[counter],command_lengths[counter]);

peter@mysql.com's avatar
peter@mysql.com committed
3962
	      if (grant_table->cols)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3963
	      {
3964
		uint found_col= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3965 3966 3967 3968 3969 3970
		for (uint col_index=0 ;
		     col_index < grant_table->hash_columns.records ;
		     col_index++)
		{
		  GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
		    hash_element(&grant_table->hash_columns,col_index);
peter@mysql.com's avatar
peter@mysql.com committed
3971
		  if (grant_column->rights & j)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3972
		  {
peter@mysql.com's avatar
peter@mysql.com committed
3973
		    if (!found_col)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3974
		    {
3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985
		      found_col= 1;
		      /*
			If we have a duplicated table level privilege, we
			must write the access privilege name again.
		      */
		      if (table_access & j)
		      {
			global.append(", ", 2);
			global.append(command_array[counter],
				      command_lengths[counter]);
		      }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3986 3987 3988 3989 3990
		      global.append(" (",2);
		    }
		    else
		      global.append(", ",2);
		    global.append(grant_column->column,
3991 3992
				  grant_column->key_length,
				  system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
3993 3994 3995 3996 3997 3998 3999 4000
		  }
		}
		if (found_col)
		  global.append(')');
	      }
	    }
	  }
	}
4001 4002 4003 4004 4005 4006 4007
	global.append(" ON ",4);
	append_identifier(thd, &global, grant_table->db,
			  strlen(grant_table->db));
	global.append('.');
	append_identifier(thd, &global, grant_table->tname,
			  strlen(grant_table->tname));
	global.append(" TO '",5);
4008 4009
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4010
	global.append("'@'",3);
4011 4012
	global.append(lex_user->host.str,lex_user->host.length,
		      system_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4013
	global.append('\'');
4014
	if (table_access & GRANT_ACL)
peter@mysql.com's avatar
peter@mysql.com committed
4015
	  global.append(" WITH GRANT OPTION",18);
4016
	protocol->prepare_for_resend();
4017
	protocol->store(global.ptr(),global.length(),global.charset());
4018
	if (protocol->write())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4019
	{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4020
	  error= -1;
4021
	  break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4022 4023 4024 4025
	}
      }
    }
  }
4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093

  /* Add procedure access */
  for (index=0 ; index < proc_priv_hash.records ; index++)
  {
    const char *user;
    GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash,
						       index);

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

    if (!strcmp(lex_user->user.str,user) &&
	!my_strcasecmp(system_charset_info, lex_user->host.str,
                       grant_proc->orig_host))
    {
      ulong proc_access= grant_proc->privs;
      if (proc_access != 0)
      {
	String global(buff, sizeof(buff), system_charset_info);
	ulong test_access= proc_access & ~GRANT_ACL;

	global.length(0);
	global.append("GRANT ",6);

	if (!test_access)
 	  global.append("USAGE",5);
	else
	{
          /* Add specific procedure access */
	  int found= 0;
	  ulong j;

	  for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
	  {
	    if (test_access & j)
	    {
	      if (found)
		global.append(", ",2);
	      found= 1;
	      global.append(command_array[counter],command_lengths[counter]);
	    }
	  }
	}
	global.append(" ON ",4);
	append_identifier(thd, &global, grant_proc->db,
			  strlen(grant_proc->db));
	global.append('.');
	append_identifier(thd, &global, grant_proc->tname,
			  strlen(grant_proc->tname));
	global.append(" TO '",5);
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
	global.append("'@'",3);
	global.append(lex_user->host.str,lex_user->host.length,
		      system_charset_info);
	global.append('\'');
	if (proc_access & GRANT_ACL)
	  global.append(" WITH GRANT OPTION",18);
	protocol->prepare_for_resend();
	protocol->store(global.ptr(),global.length(),global.charset());
	if (protocol->write())
	{
	  error= -1;
	  break;
	}
      }
    }
  }
4094
end:
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4095
  VOID(pthread_mutex_unlock(&acl_cache->lock));
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4096
  rw_unlock(&LOCK_grant);
4097

4098
  send_eof(thd);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
4099 4100 4101 4102
  DBUG_RETURN(error);
}


4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130
/*
  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;
}


4131
void get_mqh(const char *user, const char *host, USER_CONN *uc)
4132 4133
{
  ACL_USER *acl_user;
4134 4135 4136 4137
  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));
4138 4139
}

4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160
/*
  Open the grant tables.

  SYNOPSIS
    open_grant_tables()
    thd                         The current thread.
    tables (out)                The 4 elements array for the opened tables.

  DESCRIPTION
    Tables are numbered as follows:
    0 user
    1 db
    2 tables_priv
    3 columns_priv

  RETURN
    1           Skip GRANT handling during replication.
    0           OK.
    < 0         Error.
*/

4161
#define GRANT_TABLES 5
4162 4163 4164 4165 4166 4167
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("open_grant_tables");

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

4172
  bzero((char*) tables, GRANT_TABLES*sizeof(*tables));
4173 4174 4175 4176 4177
  tables->alias= tables->table_name= (char*) "user";
  (tables+1)->alias= (tables+1)->table_name= (char*) "db";
  (tables+2)->alias= (tables+2)->table_name= (char*) "tables_priv";
  (tables+3)->alias= (tables+3)->table_name= (char*) "columns_priv";
  (tables+4)->alias= (tables+4)->table_name= (char*) "procs_priv";
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
4178 4179 4180
  tables->next_local= tables->next_global= tables+1;
  (tables+1)->next_local= (tables+1)->next_global= tables+2;
  (tables+2)->next_local= (tables+2)->next_global= tables+3;
4181
  (tables+3)->next_local= (tables+3)->next_global= tables+4;
4182
  tables->lock_type= (tables+1)->lock_type=
4183 4184 4185 4186
    (tables+2)->lock_type= (tables+3)->lock_type= 
    (tables+4)->lock_type= TL_WRITE;
  tables->db= (tables+1)->db= (tables+2)->db= 
    (tables+3)->db= (tables+4)->db= (char*) "mysql";
4187 4188 4189 4190 4191 4192

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
4193 4194
  if (thd->slave_thread && table_rules_on)
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4195 4196 4197
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
4198
    */
4199 4200
    tables[0].updating=tables[1].updating=tables[2].updating=
      tables[3].updating=tables[4].updating=1;
4201 4202
    if (!tables_ok(0, tables))
      DBUG_RETURN(1);
4203 4204
    tables[0].updating=tables[1].updating=tables[2].updating=
      tables[3].updating=tables[4].updating=0;;
4205
  }
4206 4207
#endif

4208
  if (simple_open_n_lock_tables(thd, tables))
4209 4210 4211 4212 4213 4214 4215 4216 4217
  {						// This should never happen
    close_thread_tables(thd);
    DBUG_RETURN(-1);
  }

  DBUG_RETURN(0);
}

ACL_USER *check_acl_user(LEX_USER *user_name,
monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
4218
			 uint *acl_acl_userdx)
4219 4220 4221 4222 4223 4224 4225 4226 4227
{
  ACL_USER *acl_user= 0;
  uint counter;

  for (counter= 0 ; counter < acl_users.elements ; counter++)
  {
    const char *user,*host;
    acl_user= dynamic_element(&acl_users, counter, ACL_USER*);
    if (!(user=acl_user->user))
monty@mysql.com's avatar
monty@mysql.com committed
4228
      user= "";
4229
    if (!(host=acl_user->host.hostname))
monty@mysql.com's avatar
monty@mysql.com committed
4230
      host= "%";
4231 4232 4233 4234 4235 4236 4237
    if (!strcmp(user_name->user.str,user) &&
	!my_strcasecmp(system_charset_info, user_name->host.str, host))
      break;
  }
  if (counter == acl_users.elements)
    return 0;

monty@narttu.mysql.fi's avatar
merge  
monty@narttu.mysql.fi committed
4238
  *acl_acl_userdx= counter;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4239
  return acl_user;
4240 4241
}

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

4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264
/*
  Modify a privilege table.

  SYNOPSIS
    modify_grant_table()
    table                       The table to modify.
    host_field                  The host name field.
    user_field                  The user name field.
    user_to                     The new name for the user if to be renamed,
                                NULL otherwise.

  DESCRIPTION
  Update user/host in the current record if user_to is not NULL.
  Delete the current record if user_to is NULL.

  RETURN
    0           OK.
    != 0        Error.
*/

static int modify_grant_table(TABLE *table, Field *host_field,
                              Field *user_field, LEX_USER *user_to)
4265
{
4266 4267
  int error;
  DBUG_ENTER("modify_grant_table");
4268

4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285
  if (user_to)
  {
    /* rename */
    store_record(table, record[1]);
    host_field->store(user_to->host.str, user_to->host.length,
                      system_charset_info);
    user_field->store(user_to->user.str, user_to->user.length,
                      system_charset_info);
    if ((error= table->file->update_row(table->record[1], table->record[0])))
      table->file->print_error(error, MYF(0));
  }
  else
  {
    /* delete */
    if ((error=table->file->delete_row(table->record[0])))
      table->file->print_error(error, MYF(0));
  }
4286

4287 4288
  DBUG_RETURN(error);
}
4289 4290


4291 4292 4293 4294 4295 4296
/*
  Handle a privilege table.

  SYNOPSIS
    handle_grant_table()
    tables                      The array with the four open tables.
4297
    table_no                    The number of the table to handle (0..4).
4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314
    drop                        If user_from is to be dropped.
    user_from                   The the user to be searched/dropped/renamed.
    user_to                     The new name for the user if to be renamed,
                                NULL otherwise.

  DESCRIPTION
    Scan through all records in a grant table and apply the requested
    operation. For the "user" table, a single index access is sufficient,
    since there is an unique index on (host, user).
    Delete from grant table if drop is true.
    Update in grant table if drop is false and user_to is not NULL.
    Search in grant table if drop is false and user_to is NULL.
    Tables are numbered as follows:
    0 user
    1 db
    2 tables_priv
    3 columns_priv
4315
    4 procs_priv
4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334

  RETURN
    > 0         At least one record matched.
    0           OK, but no record matched.
    < 0         Error.
*/

static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
                              LEX_USER *user_from, LEX_USER *user_to)
{
  int result= 0;
  int error;
  TABLE *table= tables[table_no].table;
  Field *host_field= table->field[0];
  Field *user_field= table->field[table_no ? 2 : 1];
  char *host_str= user_from->host.str;
  char *user_str= user_from->user.str;
  const char *host;
  const char *user;
4335
  byte user_key[MAX_KEY_LENGTH];
4336
  uint key_prefix_length;
4337 4338
  DBUG_ENTER("handle_grant_table");

4339
  if (! table_no) // mysql.user table
4340
  {
4341 4342 4343 4344 4345 4346 4347 4348 4349 4350
    /*
      The 'user' table has an unique index on (host, user).
      Thus, we can handle everything with a single index access.
      The host- and user fields are consecutive in the user table records.
      So we set host- and user fields of table->record[0] and use the
      pointer to the host field as key.
      index_read_idx() will replace table->record[0] (its first argument)
      by the searched record, if it exists.
    */
    DBUG_PRINT("info",("read table: '%s'  search: '%s'@'%s'",
4351
                       table->s->table_name, user_str, host_str));
4352 4353
    host_field->store(host_str, user_from->host.length, system_charset_info);
    user_field->store(user_str, user_from->user.length, system_charset_info);
4354 4355 4356 4357 4358

    key_prefix_length= (table->key_info->key_part[0].store_length +
                        table->key_info->key_part[1].store_length);
    key_copy(user_key, table->record[0], table->key_info, key_prefix_length);

4359
    if ((error= table->file->index_read_idx(table->record[0], 0,
4360
                                            user_key, key_prefix_length,
4361
                                            HA_READ_KEY_EXACT)))
4362
    {
4363 4364 4365 4366 4367
      if (error != HA_ERR_KEY_NOT_FOUND)
      {
        table->file->print_error(error, MYF(0));
        result= -1;
      }
4368
    }
4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385
    else
    {
      /* If requested, delete or update the record. */
      result= ((drop || user_to) &&
               modify_grant_table(table, host_field, user_field, user_to)) ?
        -1 : 1; /* Error or found. */
    }
    DBUG_PRINT("info",("read result: %d", result));
  }
  else
  {
    /*
      The non-'user' table do not have indexes on (host, user).
      And their host- and user fields are not consecutive.
      Thus, we need to do a table scan to find all matching records.
    */
    if ((error= table->file->ha_rnd_init(1)))
4386
    {
4387
      table->file->print_error(error, MYF(0));
4388
      result= -1;
4389 4390 4391 4392 4393
    }
    else
    {
#ifdef EXTRA_DEBUG
      DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
4394
                         table->s->table_name, user_str, host_str));
4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442
#endif
      while ((error= table->file->rnd_next(table->record[0])) != 
             HA_ERR_END_OF_FILE)
      {
        if (error)
        {
          /* Most probable 'deleted record'. */
          DBUG_PRINT("info",("scan error: %d", error));
          continue;
        }
        if (! (host= get_field(&mem, host_field)))
          host= "";
        if (! (user= get_field(&mem, user_field)))
          user= "";

#ifdef EXTRA_DEBUG
        DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
                           user, host,
                           get_field(&mem, table->field[1]) /*db*/,
                           get_field(&mem, table->field[3]) /*table*/,
                           get_field(&mem, table->field[4]) /*column*/));
#endif
        if (strcmp(user_str, user) ||
            my_strcasecmp(system_charset_info, host_str, host))
          continue;

        /* If requested, delete or update the record. */
        result= ((drop || user_to) &&
                 modify_grant_table(table, host_field, user_field, user_to)) ?
          -1 : result ? result : 1; /* Error or keep result or found. */
        /* If search is requested, we do not need to search further. */
        if (! drop && ! user_to)
          break ;
      }
      (void) table->file->ha_rnd_end();
      DBUG_PRINT("info",("scan result: %d", result));
    }
  }

  DBUG_RETURN(result);
}


/*
  Handle an in-memory privilege structure.

  SYNOPSIS
    handle_grant_struct()
4443
    struct_no                   The number of the structure to handle (0..3).
4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458
    drop                        If user_from is to be dropped.
    user_from                   The the user to be searched/dropped/renamed.
    user_to                     The new name for the user if to be renamed,
                                NULL otherwise.

  DESCRIPTION
    Scan through all elements in an in-memory grant structure and apply
    the requested operation.
    Delete from grant structure if drop is true.
    Update in grant structure if drop is false and user_to is not NULL.
    Search in grant structure if drop is false and user_to is NULL.
    Structures are numbered as follows:
    0 acl_users
    1 acl_dbs
    2 column_priv_hash
4459
    3 procs_priv_hash
4460 4461 4462 4463

  RETURN
    > 0         At least one element matched.
    0           OK, but no element matched.
4464
    -1		Wrong arguments to function
4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476
*/

static int handle_grant_struct(uint struct_no, bool drop,
                               LEX_USER *user_from, LEX_USER *user_to)
{
  int result= 0;
  uint idx;
  uint elements;
  const char *user;
  const char *host;
  ACL_USER *acl_user;
  ACL_DB *acl_db;
4477
  GRANT_NAME *grant_name;
4478 4479 4480
  DBUG_ENTER("handle_grant_struct");
  LINT_INIT(acl_user);
  LINT_INIT(acl_db);
4481
  LINT_INIT(grant_name);
4482 4483 4484 4485
  DBUG_PRINT("info",("scan struct: %u  search: '%s'@'%s'",
                     struct_no, user_from->user.str, user_from->host.str));

  /* Get the number of elements in the in-memory structure. */
4486
  switch (struct_no) {
4487 4488 4489 4490 4491 4492
  case 0:
    elements= acl_users.elements;
    break;
  case 1:
    elements= acl_dbs.elements;
    break;
4493
  case 2:
4494
    elements= column_priv_hash.records;
4495 4496 4497 4498 4499 4500
    break;
  case 3:
    elements= proc_priv_hash.records;
    break;
  default:
    return -1;
4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513
  }

#ifdef EXTRA_DEBUG
    DBUG_PRINT("loop",("scan struct: %u  search    user: '%s'  host: '%s'",
                       struct_no, user_from->user.str, user_from->host.str));
#endif
  /* Loop over all elements. */
  for (idx= 0; idx < elements; idx++)
  {
    /*
      Get a pointer to the element.
      Unfortunaltely, the host default differs for the structures.
    */
4514
    switch (struct_no) {
4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527
    case 0:
      acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
      user= acl_user->user;
      if (!(host= acl_user->host.hostname))
        host= "%";
      break;

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

4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538
    case 2:
      grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx);
      user= grant_name->user;
      host= grant_name->host;
      break;

    case 3:
      grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx);
      user= grant_name->user;
      host= grant_name->host;
      break;
4539 4540
    }
    if (! user)
4541
      user= "";
4542
    if (! host)
4543
      host= "";
4544 4545 4546 4547 4548 4549
#ifdef EXTRA_DEBUG
    DBUG_PRINT("loop",("scan struct: %u  index: %u  user: '%s'  host: '%s'",
                       struct_no, idx, user, host));
#endif
    if (strcmp(user_from->user.str, user) ||
        my_strcasecmp(system_charset_info, user_from->host.str, host))
4550
      continue;
4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564

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

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

4565 4566 4567 4568 4569 4570 4571
      case 2:
        hash_delete(&column_priv_hash, (byte*) grant_name);
	break;

      case 3:
        hash_delete(&proc_priv_hash, (byte*) grant_name);
	break;
4572 4573 4574
      }
      elements--;
      idx--;
4575
    }
4576 4577
    else if ( user_to )
    {
4578
      switch ( struct_no ) {
4579 4580 4581 4582
      case 0:
        acl_user->user= strdup_root(&mem, user_to->user.str);
        acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
        break;
4583

4584 4585 4586 4587 4588
      case 1:
        acl_db->user= strdup_root(&mem, user_to->user.str);
        acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
        break;

4589 4590 4591 4592 4593
      case 2:
      case 3:
        grant_name->user= strdup_root(&mem, user_to->user.str);
        grant_name->host= strdup_root(&mem, user_to->host.str);
	break;
4594 4595 4596
      }
    }
    else
4597
    {
4598 4599 4600 4601 4602 4603 4604
      /* If search is requested, we do not need to search further. */
      break;
    }
  }
#ifdef EXTRA_DEBUG
  DBUG_PRINT("loop",("scan struct: %u  result %d", struct_no, result));
#endif
4605

4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649
  DBUG_RETURN(result);
}


/*
  Handle all privilege tables and in-memory privilege structures.

  SYNOPSIS
    handle_grant_data()
    tables                      The array with the four open tables.
    drop                        If user_from is to be dropped.
    user_from                   The the user to be searched/dropped/renamed.
    user_to                     The new name for the user if to be renamed,
                                NULL otherwise.

  DESCRIPTION
    Go through all grant tables and in-memory grant structures and apply
    the requested operation.
    Delete from grant data if drop is true.
    Update in grant data if drop is false and user_to is not NULL.
    Search in grant data if drop is false and user_to is NULL.

  RETURN
    > 0         At least one element matched.
    0           OK, but no element matched.
    < 0         Error.
*/

static int handle_grant_data(TABLE_LIST *tables, bool drop,
                             LEX_USER *user_from, LEX_USER *user_to)
{
  int result= 0;
  int found;
  DBUG_ENTER("handle_grant_data");

  /* Handle user table. */
  if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
  {
    /* Handle of table failed, don't touch the in-memory array. */
    result= -1;
  }
  else
  {
    /* Handle user array. */
4650 4651
    if ((handle_grant_struct(0, drop, user_from, user_to) && ! result) ||
        found)
4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678
    {
      result= 1; /* At least one record/element found. */
      /* If search is requested, we do not need to search further. */
      if (! drop && ! user_to)
        goto end;
    }
  }

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

4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697
  /* Handle procedures table. */
  if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0)
  {
    /* Handle of table failed, don't touch in-memory array. */
    result= -1;
  }
  else
  {
    /* Handle procs array. */
    if (((handle_grant_struct(3, drop, user_from, user_to) && ! result) ||
         found) && ! result)
    {
      result= 1; /* At least one record/element found. */
      /* If search is requested, we do not need to search further. */
      if (! drop && ! user_to)
        goto end;
    }
  }

4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711
  /* Handle tables table. */
  if ((found= handle_grant_table(tables, 2, drop, user_from, user_to)) < 0)
  {
    /* Handle of table failed, don't touch columns and in-memory array. */
    result= -1;
  }
  else
  {
    if (found && ! result)
    {
      result= 1; /* At least one record found. */
      /* If search is requested, we do not need to search further. */
      if (! drop && ! user_to)
        goto end;
4712
    }
4713 4714 4715

    /* Handle columns table. */
    if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
4716
    {
4717
      /* Handle of table failed, don't touch the in-memory array. */
4718 4719
      result= -1;
    }
4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731
    else
    {
      /* Handle columns hash. */
      if (((handle_grant_struct(2, drop, user_from, user_to) && ! result) ||
           found) && ! result)
        result= 1; /* At least one record/element found. */
    }
  }
 end:
  DBUG_RETURN(result);
}

4732

4733 4734 4735 4736 4737 4738 4739 4740 4741 4742
static void append_user(String *str, LEX_USER *user)
{
  if (str->length())
    str->append(',');
  str->append('\'');
  str->append(user->user.str);
  str->append("'@'");
  str->append(user->host.str);
  str->append('\'');
}
4743

4744

4745 4746 4747 4748 4749 4750 4751
/*
  Create a list of users.

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

4753 4754 4755 4756 4757 4758 4759 4760 4761
  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_create_user(THD *thd, List <LEX_USER> &list)
{
  int result;
  int found;
4762
  String wrong_users;
4763 4764 4765
  ulong sql_mode;
  LEX_USER *user_name;
  List_iterator <LEX_USER> user_list(list);
4766
  TABLE_LIST tables[GRANT_TABLES];
4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782
  DBUG_ENTER("mysql_create_user");

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

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

  while ((user_name= user_list++))
  {
    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
    if ((found= handle_grant_data(tables, 0, user_name, NULL)))
4783
    {
4784
      append_user(&wrong_users, user_name);
4785
      result= TRUE;
4786
      continue;
4787
    }
4788

4789 4790 4791 4792
    sql_mode= thd->variables.sql_mode;
    thd->variables.sql_mode&= ~MODE_NO_AUTO_CREATE_USER;
    if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1))
    {
4793
      append_user(&wrong_users, user_name);
4794 4795 4796 4797 4798 4799 4800 4801 4802
      result= TRUE;
    }
    thd->variables.sql_mode= sql_mode;
  }

  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
4803
    my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr());
4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824
  DBUG_RETURN(result);
}


/*
  Drop a list of users and all their privileges.

  SYNOPSIS
    mysql_drop_user()
    thd                         The current thread.
    list                        The users to drop.

  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
{
  int result;
  int found;
4825
  String wrong_users;
4826 4827
  LEX_USER *user_name;
  List_iterator <LEX_USER> user_list(list);
4828
  TABLE_LIST tables[GRANT_TABLES];
4829 4830
  DBUG_ENTER("mysql_drop_user");

4831
  /* DROP USER may be skipped on replication client. */
4832 4833 4834 4835 4836 4837 4838 4839
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

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

  while ((user_name= user_list++))
  {
4840
    if ((found= handle_grant_data(tables, 1, user_name, NULL)) <= 0)
4841
    {
4842
      append_user(&wrong_users, user_name);
4843
      result= TRUE;
4844
    }
4845
  }
4846

4847 4848 4849 4850
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
monty@mysql.com's avatar
monty@mysql.com committed
4851
    my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872
  DBUG_RETURN(result);
}


/*
  Rename a user.

  SYNOPSIS
    mysql_rename_user()
    thd                         The current thread.
    list                        The user name pairs: (from, to).

  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
{
  int result= 0;
  int found;
4873
  String wrong_users;
4874 4875 4876
  LEX_USER *user_from;
  LEX_USER *user_to;
  List_iterator <LEX_USER> user_list(list);
4877
  TABLE_LIST tables[GRANT_TABLES];
4878 4879
  DBUG_ENTER("mysql_rename_user");

4880
  /* RENAME USER may be skipped on replication client. */
4881 4882 4883 4884 4885 4886 4887 4888 4889
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

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

  while ((user_from= user_list++))
  {
    user_to= user_list++;
4890
    DBUG_ASSERT(user_to); /* Syntax enforces pairs of users. */
4891 4892 4893 4894 4895

    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
4896 4897
    if (handle_grant_data(tables, 0, user_to, NULL) ||
        handle_grant_data(tables, 0, user_from, user_to) <= 0)
4898
    {
4899
      append_user(&wrong_users, user_from);
4900 4901
      result= TRUE;
    }
4902
  }
4903

4904 4905 4906 4907
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
  if (result)
4908
    my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr());
4909 4910 4911
  DBUG_RETURN(result);
}

4912

4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926
/*
  Revoke all privileges from a list of users.

  SYNOPSIS
    mysql_revoke_all()
    thd                         The current thread.
    list                        The users to revoke all privileges from.

  RETURN
    > 0         Error. Error message already sent.
    0           OK.
    < 0         Error. Error message not yet sent.
*/

4927
bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
4928
{
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4929
  uint counter, revoked;
4930
  int result;
4931
  ACL_DB *acl_db;
4932
  TABLE_LIST tables[GRANT_TABLES];
4933 4934 4935
  DBUG_ENTER("mysql_revoke_all");

  if ((result= open_grant_tables(thd, tables)))
4936
    DBUG_RETURN(result != 1);
4937 4938 4939 4940 4941 4942 4943 4944

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

  LEX_USER *lex_user;
  List_iterator <LEX_USER> user_list(list);
  while ((lex_user=user_list++))
  {
4945
    if (!check_acl_user(lex_user, &counter))
4946
    {
4947 4948
      sql_print_error("REVOKE ALL PRIVILEGES, GRANT: User '%s'@'%s' does not "
                      "exists", lex_user->user.str, lex_user->host.str);
4949 4950 4951
      result= -1;
      continue;
    }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
4952

4953 4954 4955 4956 4957 4958 4959 4960
    if (replace_user_table(thd, tables[0].table,
			   *lex_user, ~0, 1, 0))
    {
      result= -1;
      continue;
    }

    /* Remove db access privileges */
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4961 4962 4963 4964 4965
    /*
      Because acl_dbs and column_priv_hash shrink and may re-order
      as privileges are removed, removal occurs in a repeated loop
      until no more privileges are revoked.
     */
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4966
    do
4967
    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4968
      for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
4969
      {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4970
	const char *user,*host;
4971

dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4972 4973 4974 4975 4976
	acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
	if (!(user=acl_db->user))
	  user= "";
	if (!(host=acl_db->host.hostname))
	  host= "";
4977

dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4978 4979 4980
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4981
	  if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, ~0, 1))
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4982
	  {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4983 4984 4985 4986 4987
	    /*
	      Don't increment counter as replace_db_table deleted the
	      current element in acl_dbs.
	     */
	    revoked= 1;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4988 4989
	    continue;
	  }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4990
	  result= -1; // Something went wrong
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4991
	}
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4992
	counter++;
4993
      }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4994
    } while (revoked);
4995 4996

    /* Remove column access */
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4997
    do
4998
    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
4999
      for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
5000
      {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5001 5002 5003 5004 5005 5006 5007
	const char *user,*host;
	GRANT_TABLE *grant_table= (GRANT_TABLE*)hash_element(&column_priv_hash,
							     counter);
	if (!(user=grant_table->user))
	  user= "";
	if (!(host=grant_table->host))
	  host= "";
5008

dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5009 5010
	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
5011
	{
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5012 5013 5014 5015
	  if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
				  grant_table->db,
				  grant_table->tname,
				  ~0, 0, 1))
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5016
	  {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5017
	    result= -1;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5018
	  }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5019
	  else
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5020
	  {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5021
	    if (!grant_table->cols)
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5022
	    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5023 5024
	      revoked= 1;
	      continue;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5025
	    }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5026 5027
	    List<LEX_COLUMN> columns;
	    if (!replace_column_table(grant_table,tables[3].table, *lex_user,
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5028 5029 5030 5031
				      columns,
				      grant_table->db,
				      grant_table->tname,
				      ~0, 1))
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5032
	    {
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5033
	      revoked= 1;
5034
	      continue;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5035
	    }
5036
	    result= -1;
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5037
	  }
5038
	}
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5039
	counter++;
5040
      }
dellis@goetia.(none)'s avatar
dellis@goetia.(none) committed
5041
    } while (revoked);
5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070

    /* Remove procedure access */
    do {
      for (counter= 0, revoked= 0 ; counter < proc_priv_hash.records ; )
      {
	const char *user,*host;
	GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash,
							   counter);
	if (!(user=grant_proc->user))
	  user= "";
	if (!(host=grant_proc->host))
	  host= "";

	if (!strcmp(lex_user->user.str,user) &&
	    !my_strcasecmp(system_charset_info, lex_user->host.str, host))
	{
	  if (!replace_proc_table(thd,grant_proc,tables[4].table,*lex_user,
				  grant_proc->db,
				  grant_proc->tname,
				  ~0, 1))
	  {
	    revoked= 1;
	    continue;
	  }
	  result= -1;	// Something went wrong
	}
	counter++;
      }
    } while (revoked);
5071
  }
5072

5073 5074 5075
  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);
5076

5077
  if (result)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
5078
    my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
5079

5080 5081
  DBUG_RETURN(result);
}
5082

5083

5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186
/*
  Revoke privileges for all users on a stored procedure

  SYNOPSIS
    sp_revoke_privileges()
    thd                         The current thread.
    db				DB of the stored procedure
    name			Name of the stored procedure

  RETURN
    0           OK.
    < 0         Error. Error message not yet sent.
*/

bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name)
{
  uint counter, revoked;
  int result;
  ACL_DB *acl_db;
  TABLE_LIST tables[GRANT_TABLES];
  DBUG_ENTER("sp_revoke_privileges");

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

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

  /* Remove procedure access */
  do {
    for (counter= 0, revoked= 0 ; counter < proc_priv_hash.records ; )
    {
      const char *db,*name;
      GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash,
							 counter);
      if (!my_strcasecmp(system_charset_info, grant_proc->db, sp_db) &&
	  !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
      {
        LEX_USER lex_user;
	lex_user.user.str= grant_proc->user;
	lex_user.user.length= strlen(grant_proc->user);
	lex_user.host.str= grant_proc->host;
	lex_user.host.length= strlen(grant_proc->host);
	if (!replace_proc_table(thd,grant_proc,tables[4].table,lex_user,
				grant_proc->db, grant_proc->tname, ~0, 1))
	{
	  revoked= 1;
	  continue;
	}
	result= -1;	// Something went wrong
      }
      counter++;
    }
  } while (revoked);

  VOID(pthread_mutex_unlock(&acl_cache->lock));
  rw_unlock(&LOCK_grant);
  close_thread_tables(thd);

  if (result)
    my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));

  DBUG_RETURN(result);
}


/*
  Grant EXECUTE,ALTER privilege for a stored procedure

  SYNOPSIS
    sp_grant_privileges()
    thd                         The current thread.
    db				DB of the stored procedure
    name			Name of the stored procedure

  RETURN
    0           OK.
    < 0         Error. Error message not yet sent.
*/

bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name)
{
  LEX_USER *combo;
  TABLE_LIST tables[1];
  List<LEX_USER> user_list;
  bool result;
  DBUG_ENTER("sp_grant_privileges");  

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

  combo->user.str= thd->user;
  
  if (!find_acl_user(combo->host.str=(char*)thd->host_or_ip, combo->user.str) &&
      !find_acl_user(combo->host.str=(char*)thd->host, combo->user.str) &&
      !find_acl_user(combo->host.str=(char*)thd->ip, combo->user.str) &&
      !find_acl_user(combo->host.str=(char*)"%", combo->user.str))
    DBUG_RETURN(TRUE);

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

  tables->db= (char*)sp_db;
5187
  tables->table_name= tables->alias= (char*)sp_name;
5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199
  
  combo->host.length= strlen(combo->host.str);
  combo->user.length= strlen(combo->user.str);
  combo->host.str= thd->strmake(combo->host.str,combo->host.length);
  combo->user.str= thd->strmake(combo->user.str,combo->user.length);
  combo->password.str= (char*)"";
  combo->password.length= 0;

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

  thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
5200
  bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
5201 5202 5203 5204 5205 5206 5207

  result= mysql_procedure_grant(thd, tables, user_list,
  				DEFAULT_CREATE_PROC_ACLS, 0, 1);
  DBUG_RETURN(result);
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
5208
/*****************************************************************************
5209
  Instantiate used templates
bk@work.mysql.com's avatar
bk@work.mysql.com committed
5210 5211 5212 5213 5214 5215 5216 5217
*****************************************************************************/

#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
hf@deer.(none)'s avatar
hf@deer.(none) committed
5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264

#endif /*NO_EMBEDDED_ACCESS_CHECKS */


int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
{
  reg3 int flag;
  DBUG_ENTER("wild_case_compare");
  DBUG_PRINT("enter",("str: '%s'  wildstr: '%s'",str,wildstr));
  while (*wildstr)
  {
    while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
    {
      if (*wildstr == wild_prefix && wildstr[1])
	wildstr++;
      if (my_toupper(cs, *wildstr++) !=
          my_toupper(cs, *str++)) DBUG_RETURN(1);
    }
    if (! *wildstr ) DBUG_RETURN (*str != 0);
    if (*wildstr++ == wild_one)
    {
      if (! *str++) DBUG_RETURN (1);	/* One char; skip */
    }
    else
    {						/* Found '*' */
      if (!*wildstr) DBUG_RETURN(0);		/* '*' as last char: OK */
      flag=(*wildstr != wild_many && *wildstr != wild_one);
      do
      {
	if (flag)
	{
	  char cmp;
	  if ((cmp= *wildstr) == wild_prefix && wildstr[1])
	    cmp=wildstr[1];
	  cmp=my_toupper(cs, cmp);
	  while (*str && my_toupper(cs, *str) != cmp)
	    str++;
	  if (!*str) DBUG_RETURN (1);
	}
	if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
      } while (*str++);
      DBUG_RETURN(1);
    }
  }
  DBUG_RETURN (*str != '\0');
}

5265 5266 5267 5268 5269 5270 5271 5272

void update_schema_privilege(TABLE *table, char *buff, const char* db,
                             const char* t_name, const char* column,
                             uint col_length, const char *priv, 
                             uint priv_length, const char* is_grantable)
{
  int i= 2;
  CHARSET_INFO *cs= system_charset_info;
5273
  restore_record(table, s->default_values);
5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325
  table->field[0]->store(buff, strlen(buff), cs);
  if (db)
    table->field[i++]->store(db, strlen(db), cs);
  if (t_name)
    table->field[i++]->store(t_name, strlen(t_name), cs);
  if (column)
    table->field[i++]->store(column, col_length, cs);
  table->field[i++]->store(priv, priv_length, cs);
  table->field[i]->store(is_grantable, strlen(is_grantable), cs);
  table->file->write_row(table->record[0]);
}


int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  uint counter;
  ACL_USER *acl_user;
  ulong want_access;

  char buff[100];
  TABLE *table= tables->table;
  DBUG_ENTER("fill_schema_user_privileges");
  for (counter=0 ; counter < acl_users.elements ; counter++)
  {
    const char *user,*host, *is_grantable="YES";
    acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
    if (!(user=acl_user->user))
      user= "";
    if (!(host=acl_user->host.hostname))
      host= "";
    want_access= acl_user->access;
    if (!(want_access & GRANT_ACL))
      is_grantable= "NO";

    strxmov(buff,"'",user,"'@'",host,"'",NullS);
    if (!(want_access & ~GRANT_ACL))
      update_schema_privilege(table, buff, 0, 0, 0, 0, "USAGE", 5, is_grantable);
    else
    {
      uint priv_id;
      ulong j,test_access= want_access & ~GRANT_ACL;
      for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
      {
	if (test_access & j)
          update_schema_privilege(table, buff, 0, 0, 0, 0, 
                                  command_array[priv_id],
                                  command_lengths[priv_id], is_grantable);
      }
    }
  }
  DBUG_RETURN(0);
5326 5327 5328
#else
  return(0);
#endif
5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375
}


int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  uint counter;
  ACL_DB *acl_db;
  ulong want_access;
  char buff[100];
  TABLE *table= tables->table;
  DBUG_ENTER("fill_schema_schema_privileges");

  for (counter=0 ; counter < acl_dbs.elements ; counter++)
  {
    const char *user, *host, *is_grantable="YES";

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

    want_access=acl_db->access;
    if (want_access)
    {
      if (!(want_access & GRANT_ACL))
      {
        is_grantable= "NO";
      }
      strxmov(buff,"'",user,"'@'",host,"'",NullS);
      if (!(want_access & ~GRANT_ACL))
        update_schema_privilege(table, buff, acl_db->db, 0, 0,
                                0, "USAGE", 5, is_grantable);
      else
      {
        int cnt;
        ulong j,test_access= want_access & ~GRANT_ACL;
        for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
          if (test_access & j)
            update_schema_privilege(table, buff, acl_db->db, 0, 0, 0,
                                    command_array[cnt], command_lengths[cnt],
                                    is_grantable);
      }
    }
  }
  DBUG_RETURN(0);
5376 5377 5378
#else
  return (0);
#endif
5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397
}


int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  uint index;
  char buff[100];
  TABLE *table= tables->table;
  DBUG_ENTER("fill_schema_table_privileges");

  for (index=0 ; index < column_priv_hash.records ; index++)
  {
    const char *user, *is_grantable= "YES";
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
    ulong table_access= grant_table->privs;
5398
    if (table_access)
5399 5400
    {
      ulong test_access= table_access & ~GRANT_ACL;
5401 5402 5403 5404
      /*
        We should skip 'usage' privilege on table if
        we have any privileges on column(s) of this table
      */
5405 5406
      if (!test_access && grant_table->cols)
        continue;
5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428
      if (!(table_access & GRANT_ACL))
        is_grantable= "NO";

      strxmov(buff,"'",user,"'@'",grant_table->orig_host,"'",NullS);
      if (!test_access)
        update_schema_privilege(table, buff, grant_table->db, grant_table->tname,
                                0, 0, "USAGE", 5, is_grantable);
      else
      {
        ulong j;
        int cnt;
        for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
        {
          if (test_access & j)
            update_schema_privilege(table, buff, grant_table->db, 
                                    grant_table->tname, 0, 0, command_array[cnt],
                                    command_lengths[cnt], is_grantable);
        }
      }
    }
  }
  DBUG_RETURN(0);
5429 5430 5431
#else
  return (0);
#endif
5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452
}


int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  uint index;
  char buff[100];
  TABLE *table= tables->table;
  DBUG_ENTER("fill_schema_table_privileges");

  for (index=0 ; index < column_priv_hash.records ; index++)
  {
    const char *user, *is_grantable= "YES";
    GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
							  index);
    if (!(user=grant_table->user))
      user= "";
    ulong table_access= grant_table->cols;
    if (table_access != 0)
    {
5453
      if (!(grant_table->privs & GRANT_ACL))
5454 5455
        is_grantable= "NO";

5456
      ulong test_access= table_access & ~GRANT_ACL;
5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487
      strxmov(buff,"'",user,"'@'",grant_table->orig_host,"'",NullS);
      if (!test_access)
        continue;
      else
      {
        ulong j;
        int cnt;
        for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
        {
          if (test_access & j)
          {
            for (uint col_index=0 ;
                 col_index < grant_table->hash_columns.records ;
                 col_index++)
            {
              GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
                hash_element(&grant_table->hash_columns,col_index);
              if ((grant_column->rights & j) && (table_access & j))
                  update_schema_privilege(table, buff, grant_table->db,
                                          grant_table->tname,
                                          grant_column->column,
                                          grant_column->key_length,
                                          command_array[cnt],
                                          command_lengths[cnt], is_grantable);
            }
          }
        }
      }
    }
  }
  DBUG_RETURN(0);
5488 5489 5490
#else
  return (0);
#endif
5491 5492 5493
}


bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5494 5495 5496 5497 5498
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
  fill effective privileges for table

  SYNOPSIS
5499 5500
    fill_effective_table_privileges()
    thd     thread handler
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5501 5502 5503 5504 5505 5506 5507 5508
    grant   grants table descriptor
    db      db name
    table   table name
*/

void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
                                     const char *db, const char *table)
{
5509 5510 5511 5512 5513 5514 5515
  /* --skip-grants */
  if (!initialized)
  {
    grant->privilege= ~NO_ACCESS;             // everything is allowed
    return;
  }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5516 5517
  /* global privileges */
  grant->privilege= thd->master_access;
5518

5519 5520 5521
  /* db privileges */
  grant->privilege|= acl_get(thd->host, thd->ip, thd->priv_user, db, 0);

5522 5523 5524
  if (!grant_option)
    return;

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5525 5526 5527
  /* table privileges */
  if (grant->version != grant_version)
  {
5528
    rw_rdlock(&LOCK_grant);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5529 5530 5531 5532 5533
    grant->grant_table=
      table_hash_search(thd->host, thd->ip, db,
			thd->priv_user,
			table, 0);              /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
5534
    rw_unlock(&LOCK_grant);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
5535 5536 5537 5538 5539 5540 5541
  }
  if (grant->grant_table != 0)
  {
    grant->privilege|= grant->grant_table->privs;
  }
}
#endif