sql_acl.cc 315 KB
Newer Older
1
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates.
Sergei Golubchik's avatar
Sergei Golubchik committed
2
   Copyright (c) 2009, 2013, Monty Program Ab
unknown's avatar
unknown committed
3

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

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

unknown's avatar
unknown committed
13 14
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
15
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA */
unknown's avatar
unknown committed
16 17 18 19


/*
  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.
unknown's avatar
unknown committed
22 23 24 25 26 27
  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.
*/

28
#include "my_global.h"                          /* NO_EMBEDDED_ACCESS_CHECKS */
29 30
#include "sql_priv.h"
#include "sql_acl.h"         // MYSQL_DB_FIELD_COUNT, ACL_ACCESS
31
#include "sql_base.h"                           // close_mysql_tables
32 33 34
#include "key.h"             // key_copy, key_cmp_if_same, key_restore
#include "sql_show.h"        // append_identifier
#include "sql_table.h"                         // build_table_filename
unknown's avatar
unknown committed
35
#include "hash_filo.h"
36 37 38 39 40
#include "sql_parse.h"                          // check_access
#include "sql_view.h"                           // VIEW_ANY_ACL
#include "records.h"              // READ_RECORD, read_record_info,
                                  // init_read_record, end_read_record
#include "rpl_filter.h"           // rpl_filter
unknown's avatar
unknown committed
41 42
#include <m_ctype.h>
#include <stdarg.h>
43 44
#include "sp_head.h"
#include "sp.h"
45
#include "transaction.h"
46 47
#include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
#include "records.h"             // init_read_record, end_read_record
48 49
#include <sql_common.h>
#include <mysql/plugin_auth.h>
50 51 52
#include "sql_connect.h"
#include "hostname.h"
#include "sql_db.h"
53
#include "sql_array.h"
unknown's avatar
unknown committed
54

55 56
#include "sql_plugin_compat.h"

57 58
bool mysql_user_table_is_in_short_password_format= false;

59 60
static const
TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
61
  {
62
    { C_STRING_WITH_LEN("Host") },
unknown's avatar
unknown committed
63
    { C_STRING_WITH_LEN("char(60)") },
64
    {NULL, 0}
65
  },
66
  {
67
    { C_STRING_WITH_LEN("Db") },
unknown's avatar
unknown committed
68
    { C_STRING_WITH_LEN("char(64)") },
69
    {NULL, 0}
70
  },
71
  {
unknown's avatar
unknown committed
72
    { C_STRING_WITH_LEN("User") },
73
    { C_STRING_WITH_LEN("char(") },
74 75 76
    {NULL, 0}
  },
  {
unknown's avatar
unknown committed
77 78 79
    { C_STRING_WITH_LEN("Select_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
80 81
  },
  {
unknown's avatar
unknown committed
82 83 84
    { C_STRING_WITH_LEN("Insert_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
85 86
  },
  {
unknown's avatar
unknown committed
87 88 89
    { C_STRING_WITH_LEN("Update_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
90 91
  },
  {
unknown's avatar
unknown committed
92 93 94
    { C_STRING_WITH_LEN("Delete_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
95 96
  },
  {
unknown's avatar
unknown committed
97 98 99
    { C_STRING_WITH_LEN("Create_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
100 101
  },
  {
unknown's avatar
unknown committed
102 103 104
    { C_STRING_WITH_LEN("Drop_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
105 106
  },
  {
unknown's avatar
unknown committed
107 108 109
    { C_STRING_WITH_LEN("Grant_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
110 111
  },
  {
unknown's avatar
unknown committed
112 113 114
    { C_STRING_WITH_LEN("References_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
115 116
  },
  {
unknown's avatar
unknown committed
117 118 119
    { C_STRING_WITH_LEN("Index_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
120 121
  },
  {
unknown's avatar
unknown committed
122 123 124
    { C_STRING_WITH_LEN("Alter_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
125 126
  },
  {
unknown's avatar
unknown committed
127 128 129
    { C_STRING_WITH_LEN("Create_tmp_table_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
130 131
  },
  {
unknown's avatar
unknown committed
132 133 134
    { C_STRING_WITH_LEN("Lock_tables_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
135 136
  },
  {
unknown's avatar
unknown committed
137 138 139
    { C_STRING_WITH_LEN("Create_view_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
140 141
  },
  {
unknown's avatar
unknown committed
142 143 144
    { C_STRING_WITH_LEN("Show_view_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
145 146
  },
  {
unknown's avatar
unknown committed
147 148 149
    { C_STRING_WITH_LEN("Create_routine_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
150 151
  },
  {
unknown's avatar
unknown committed
152 153 154
    { C_STRING_WITH_LEN("Alter_routine_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
155 156
  },
  {
unknown's avatar
unknown committed
157 158 159
    { C_STRING_WITH_LEN("Execute_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
160 161
  },
  {
unknown's avatar
unknown committed
162 163 164
    { C_STRING_WITH_LEN("Event_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
165 166
  },
  {
unknown's avatar
unknown committed
167 168 169
    { C_STRING_WITH_LEN("Trigger_priv") },
    { C_STRING_WITH_LEN("enum('N','Y')") },
    { C_STRING_WITH_LEN("utf8") }
170 171 172
  }
};

173
const TABLE_FIELD_DEF
174
mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields, 0, (uint*) 0 };
175

176 177 178
static LEX_STRING native_password_plugin_name= {
  C_STRING_WITH_LEN("mysql_native_password")
};
179

180 181 182
static LEX_STRING old_password_plugin_name= {
  C_STRING_WITH_LEN("mysql_old_password")
};
183

184 185 186
/// @todo make it configurable
LEX_STRING *default_auth_plugin_name= &native_password_plugin_name;

187
#ifndef NO_EMBEDDED_ACCESS_CHECKS
188
static plugin_ref old_password_plugin;
189
#endif
190
static plugin_ref native_password_plugin;
191 192 193 194 195 196

/* Classes */

struct acl_host_and_ip
{
  char *hostname;
197
  long ip, ip_mask;                      // Used with masked ip:s
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
};

class ACL_ACCESS {
public:
  ulong sort;
  ulong access;
};

/* ACL_HOST is used if no host is specified */

class ACL_HOST :public ACL_ACCESS
{
public:
  acl_host_and_ip host;
  char *db;
};

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
class ACL_USER_BASE :public ACL_ACCESS
{

public:
  static void *operator new(size_t size, MEM_ROOT *mem_root)
  { return (void*) alloc_root(mem_root, size); }

  uchar flags;           // field used to store various state information
  LEX_STRING user;
  /*
    list to hold references to granted roles (ACL_USER instances)
  */
  DYNAMIC_ARRAY role_grants;
};

class ACL_USER :public ACL_USER_BASE
231 232 233 234 235
{
public:
  acl_host_and_ip host;
  uint hostname_length;
  USER_RESOURCES user_resource;
236
  uint8 salt[SCRAMBLE_LENGTH + 1];       // scrambled password in binary form
237
  uint8 salt_len;        // 0 - no password, 4 - 3.20, 8 - 4.0,  20 - 4.1.1
238 239 240 241 242 243 244
  enum SSL_type ssl_type;
  const char *ssl_cipher, *x509_issuer, *x509_subject;
  LEX_STRING plugin;
  LEX_STRING auth_string;

  ACL_USER *copy(MEM_ROOT *root)
  {
245
    ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
246 247 248
    if (!dst)
      return 0;
    *dst= *this;
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
249 250
    dst->user.str= safe_strdup_root(root, user.str);
    dst->user.length= user.length;
251 252 253 254 255 256 257 258
    dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
    dst->x509_issuer= safe_strdup_root(root, x509_issuer);
    dst->x509_subject= safe_strdup_root(root, x509_subject);
    if (plugin.str == native_password_plugin_name.str ||
        plugin.str == old_password_plugin_name.str)
      dst->plugin= plugin;
    else
      dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
259
    dst->auth_string.str= safe_strdup_root(root, auth_string.str);
260
    dst->host.hostname= safe_strdup_root(root, host.hostname);
261
    bzero(&dst->role_grants, sizeof(role_grants));
262 263
    return dst;
  }
264 265 266

};

267
class ACL_ROLE :public ACL_USER_BASE
268 269 270 271 272 273 274 275 276 277 278 279 280
{
public:
  /*
    In case of granting a role to a role, the access bits are merged together
    via a bit OR operation and placed in the ACL_USER::access field.

    When rebuilding role_grants via the rebuild_role_grant function,
    the ACL_USER::access field needs to be reset aswell. The field
    initial_role_access holds the initial grants present in the table row.
  */
  ulong initial_role_access;
  DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted

281 282
  ACL_ROLE(ACL_USER * user, MEM_ROOT *mem);

283 284 285 286 287 288 289 290 291
};

class ACL_DB :public ACL_ACCESS
{
public:
  acl_host_and_ip host;
  char *user,*db;
};

292
#ifndef NO_EMBEDDED_ACCESS_CHECKS
293 294
static void update_hostname(acl_host_and_ip *host, const char *hostname);
static ulong get_sort(uint count,...);
295
static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
296
			     const char *ip);
297
static bool show_proxy_grants (THD *thd, LEX_USER *user,
298 299 300 301 302 303 304 305 306 307
                               char *buff, size_t buffsize);

class ACL_PROXY_USER :public ACL_ACCESS
{
  acl_host_and_ip host;
  const char *user;
  acl_host_and_ip proxied_host;
  const char *proxied_user;
  bool with_grant;

308 309 310
  typedef enum {
    MYSQL_PROXIES_PRIV_HOST,
    MYSQL_PROXIES_PRIV_USER,
311
    MYSQL_PROXIES_PRIV_PROXIED_HOST,
312
    MYSQL_PROXIES_PRIV_PROXIED_USER,
313 314 315
    MYSQL_PROXIES_PRIV_WITH_GRANT,
    MYSQL_PROXIES_PRIV_GRANTOR,
    MYSQL_PROXIES_PRIV_TIMESTAMP } old_acl_proxy_users;
316 317 318 319 320 321 322 323
public:
  ACL_PROXY_USER () {};

  void init(const char *host_arg, const char *user_arg,
       const char *proxied_host_arg, const char *proxied_user_arg,
       bool with_grant_arg)
  {
    user= (user_arg && *user_arg) ? user_arg : NULL;
324
    update_hostname (&host,
325
                     (host_arg && *host_arg) ? host_arg : NULL);
326
    proxied_user= (proxied_user_arg && *proxied_user_arg) ?
327
      proxied_user_arg : NULL;
328
    update_hostname (&proxied_host,
329 330 331
                     (proxied_host_arg && *proxied_host_arg) ?
                     proxied_host_arg : NULL);
    with_grant= with_grant_arg;
332 333
    sort= get_sort(4, host.hostname, user,
                   proxied_host.hostname, proxied_user);
334 335 336 337 338 339 340 341
  }

  void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
       const char *proxied_host_arg, const char *proxied_user_arg,
       bool with_grant_arg)
  {
    init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
          (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
342
          (proxied_host_arg && *proxied_host_arg) ?
343
            strdup_root (mem, proxied_host_arg) : NULL,
344
          (proxied_user_arg && *proxied_user_arg) ?
345 346 347 348 349 350
            strdup_root (mem, proxied_user_arg) : NULL,
          with_grant_arg);
  }

  void init(TABLE *table, MEM_ROOT *mem)
  {
351 352 353 354 355
    init (get_field(mem, table->field[MYSQL_PROXIES_PRIV_HOST]),
          get_field(mem, table->field[MYSQL_PROXIES_PRIV_USER]),
          get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]),
          get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]),
          table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->val_int() != 0);
356 357 358 359 360
  }

  bool get_with_grant() { return with_grant; }
  const char *get_user() { return user; }
  const char *get_host() { return host.hostname; }
361 362
  const char *get_proxied_user() { return proxied_user; }
  const char *get_proxied_host() { return proxied_host.hostname; }
363 364
  void set_user(MEM_ROOT *mem, const char *user_arg)
  {
365
    user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
366
  }
367 368 369 370
  void set_host(MEM_ROOT *mem, const char *host_arg)
  {
    update_hostname(&host,
                    (host_arg && *host_arg) ?
371
                    strdup_root(mem, host_arg) : NULL);
372 373
  }

374
  bool check_validity(bool check_no_resolve)
375
  {
376
    if (check_no_resolve &&
377 378 379
        (hostname_requires_resolving(host.hostname) ||
         hostname_requires_resolving(proxied_host.hostname)))
    {
380
      sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
381 382 383 384 385
                        "ignored in --skip-name-resolve mode.",
                        proxied_user ? proxied_user : "",
                        proxied_host.hostname ? proxied_host.hostname : "",
                        user ? user : "",
                        host.hostname ? host.hostname : "");
386 387 388 389 390
      return TRUE;
    }
    return FALSE;
  }

391
  bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
392 393
                const char *proxied_user_arg)
  {
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
    DBUG_ENTER("ACL_PROXY_USER::matches");
    DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
                        "compare_hostname(%s,%s,%s) &&"
                        "wild_compare (%s,%s) &&"
                        "wild_compare (%s,%s)",
                        host.hostname ? host.hostname : "<NULL>",
                        host_arg ? host_arg : "<NULL>",
                        ip_arg ? ip_arg : "<NULL>",
                        proxied_host.hostname ? proxied_host.hostname : "<NULL>",
                        host_arg ? host_arg : "<NULL>",
                        ip_arg ? ip_arg : "<NULL>",
                        user_arg ? user_arg : "<NULL>",
                        user ? user : "<NULL>",
                        proxied_user_arg ? proxied_user_arg : "<NULL>",
                        proxied_user ? proxied_user : "<NULL>"));
    DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
                compare_hostname(&proxied_host, host_arg, ip_arg) &&
                (!user ||
                 (user_arg && !wild_compare(user_arg, user, TRUE))) &&
413 414
                (!proxied_user ||
                 (proxied_user && !wild_compare(proxied_user_arg,
415
                                                proxied_user, TRUE))));
416 417
  }

418 419 420 421 422 423 424

  inline static bool auth_element_equals(const char *a, const char *b)
  {
    return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
  }


425
  bool pk_equals(ACL_PROXY_USER *grant)
426
  {
427 428 429 430 431 432 433 434 435 436 437 438
    DBUG_ENTER("pk_equals");
    DBUG_PRINT("info", ("strcmp(%s,%s) &&"
                        "strcmp(%s,%s) &&"
                        "wild_compare (%s,%s) &&"
                        "wild_compare (%s,%s)",
                        user ? user : "<NULL>",
                        grant->user ? grant->user : "<NULL>",
                        proxied_user ? proxied_user : "<NULL>",
                        grant->proxied_user ? grant->proxied_user : "<NULL>",
                        host.hostname ? host.hostname : "<NULL>",
                        grant->host.hostname ? grant->host.hostname : "<NULL>",
                        proxied_host.hostname ? proxied_host.hostname : "<NULL>",
439
                        grant->proxied_host.hostname ?
440
                        grant->proxied_host.hostname : "<NULL>"));
441 442 443 444

    DBUG_RETURN(auth_element_equals(user, grant->user) &&
                auth_element_equals(proxied_user, grant->proxied_user) &&
                auth_element_equals(host.hostname, grant->host.hostname) &&
445
                auth_element_equals(proxied_host.hostname,
446
                                    grant->proxied_host.hostname));
447 448
  }

449

450
  bool granted_on(const char *host_arg, const char *user_arg)
451 452
  {
    return (((!user && (!user_arg || !user_arg[0])) ||
453
             (user && user_arg && !strcmp(user, user_arg))) &&
454
            ((!host.hostname && (!host_arg || !host_arg[0])) ||
455
             (host.hostname && host_arg && !strcmp(host.hostname, host_arg))));
456 457
  }

458

459
  void print_grant(String *str)
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
  {
    str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
    if (proxied_user)
      str->append(proxied_user, strlen(proxied_user));
    str->append(STRING_WITH_LEN("'@'"));
    if (proxied_host.hostname)
      str->append(proxied_host.hostname, strlen(proxied_host.hostname));
    str->append(STRING_WITH_LEN("' TO '"));
    if (user)
      str->append(user, strlen(user));
    str->append(STRING_WITH_LEN("'@'"));
    if (host.hostname)
      str->append(host.hostname, strlen(host.hostname));
    str->append(STRING_WITH_LEN("'"));
    if (with_grant)
      str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
  }

478
  void set_data(ACL_PROXY_USER *grant)
479 480 481 482
  {
    with_grant= grant->with_grant;
  }

483 484
  static int store_pk(TABLE *table,
                      const LEX_STRING *host,
485
                      const LEX_STRING *user,
486
                      const LEX_STRING *proxied_host,
487
                      const LEX_STRING *proxied_user)
488
  {
489 490 491 492 493 494
    DBUG_ENTER("ACL_PROXY_USER::store_pk");
    DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
                        host->str ? host->str : "<NULL>",
                        user->str ? user->str : "<NULL>",
                        proxied_host->str ? proxied_host->str : "<NULL>",
                        proxied_user->str ? proxied_user->str : "<NULL>"));
495
    if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
496 497 498
                                                   host->length,
                                                   system_charset_info))
      DBUG_RETURN(TRUE);
499
    if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
500 501 502
                                                   user->length,
                                                   system_charset_info))
      DBUG_RETURN(TRUE);
503
    if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str,
504 505 506
                                                           proxied_host->length,
                                                           system_charset_info))
      DBUG_RETURN(TRUE);
507
    if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str,
508 509 510 511 512 513 514
                                                           proxied_user->length,
                                                           system_charset_info))
      DBUG_RETURN(TRUE);

    DBUG_RETURN(FALSE);
  }

515 516
  static int store_data_record(TABLE *table,
                               const LEX_STRING *host,
517
                               const LEX_STRING *user,
518
                               const LEX_STRING *proxied_host,
519
                               const LEX_STRING *proxied_user,
520 521
                               bool with_grant,
                               const char *grantor)
522
  {
523 524
    DBUG_ENTER("ACL_PROXY_USER::store_pk");
    if (store_pk(table,  host, user, proxied_host, proxied_user))
525
      DBUG_RETURN(TRUE);
526
    DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
527 528
    if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
                                                           TRUE))
529
      DBUG_RETURN(TRUE);
530
    if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
531 532 533
                                                        strlen(grantor),
                                                        system_charset_info))
      DBUG_RETURN(TRUE);
534 535 536 537

    DBUG_RETURN(FALSE);
  }
};
538 539 540

#define FIRST_NON_YN_FIELD 26

unknown's avatar
unknown committed
541 542 543
class acl_entry :public hash_filo_element
{
public:
unknown's avatar
unknown committed
544
  ulong access;
unknown's avatar
unknown committed
545 546 547 548
  uint16 length;
  char key[1];					// Key will be stored here
};

unknown's avatar
unknown committed
549

550 551
static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
                                my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
552 553
{
  *length=(uint) entry->length;
554
  return (uchar*) entry->key;
unknown's avatar
unknown committed
555 556
}

557
static uchar* acl_role_get_key(ACL_ROLE *entry, size_t *length,
558
                               my_bool not_used __attribute__((unused)))
559
{
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
560 561
  *length=(uint) entry->user.length;
  return (uchar*) entry->user.str;
562 563
}

564 565 566 567 568 569 570
typedef struct st_role_grant
{
  char *u_uname;
  char *u_hname;
  char *r_uname;
  LEX_STRING hashkey;
} ROLE_GRANT_PAIR;
571 572 573
/*
  Struct to hold the state of a node during a Depth First Search exploration
*/
574
class NODE_STATE
575 576
{
public:
577 578
  ACL_ROLE *node_data; /* pointer to the node data */
  uint neigh_idx;      /* the neighbour that needs to be evaluated next */
579
};
580 581 582 583 584 585 586 587

static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length,
                                  my_bool not_used __attribute__((unused)))
{
  *length=(uint) entry->hashkey.length;
  return (uchar*) entry->hashkey.str;
}

588 589 590
static void init_role_grant_pair(MEM_ROOT *mem, ROLE_GRANT_PAIR *entry,
                                 char *username, char *hostname, char *rolename)
{
591 592 593
      size_t uname_l = username ? strlen(username) : 0;
      size_t hname_l = hostname ? strlen(hostname) : 0;
      size_t rname_l = rolename ? strlen(rolename) : 0;
594 595 596 597
      /*
        Create a buffer that holds all 3 NULL terminated strings in succession
        To save memory space, the same buffer is used as the hashkey
      */
598
      size_t bufflen = uname_l + hname_l + rname_l + 3; //add the '\0' aswell
599 600 601 602 603
      char *buff= (char *)alloc_root(mem, bufflen);
      /*
        Offsets in the buffer for all 3 strings
      */
      char *username_pos= buff;
604 605
      char *hostname_pos= buff + uname_l + 1;
      char *rolename_pos= buff + uname_l + hname_l + 2;
606

607 608 609
      if (username) //prevent undefined behaviour
        memcpy(username_pos, username, uname_l);
      username_pos[uname_l]= '\0';         //#1 string terminator
610 611
      entry->u_uname= username_pos;

612 613 614
      if (hostname) //prevent undefined behaviour
        memcpy(hostname_pos, hostname, hname_l);
      hostname_pos[hname_l]= '\0';         //#2 string terminator
615 616
      entry->u_hname= hostname_pos;

617 618 619
      if (rolename) //prevent undefined behaviour
        memcpy(rolename_pos, rolename, rname_l);
      rolename_pos[rname_l]= '\0';         //#3 string terminator
620 621 622 623 624 625
      entry->r_uname= rolename_pos;

      entry->hashkey.str = buff;
      entry->hashkey.length = bufflen;
}

626 627 628
#define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
#define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
                        1 + USERNAME_LENGTH + 1)
unknown's avatar
unknown committed
629

630 631 632
#if defined(HAVE_OPENSSL)
/*
  Without SSL the handshake consists of one packet. This packet
633
  has both client capabilities and scrambled password.
634 635 636
  With SSL the handshake might consist of two packets. If the first
  packet (client capabilities) has CLIENT_SSL flag set, we have to
  switch to SSL and read the second packet. The scrambled password
637
  is in the second packet and client_capabilities field will be ignored.
638 639 640 641 642 643 644 645
  Maybe it is better to accept flags other than CLIENT_SSL from the
  second packet?
*/
#define SSL_HANDSHAKE_SIZE      2
#define MIN_HANDSHAKE_SIZE      2
#else
#define MIN_HANDSHAKE_SIZE      6
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
646
#define NORMAL_HANDSHAKE_SIZE   6
647

648
#define ROLE_ASSIGN_COLUMN_IDX  42
649 650
/* various flags valid for ACL_USER */
#define IS_ROLE                 (1L << 0)
651
/* Flag to mark that a ROLE has been visited in a DEPTH_FIRST_SEARCH */
652
#define ROLE_VISITED            (1L << 1)
653 654 655 656
/*
   Flag to mark that the ROLE's access bits are final, having been inherited
   from other granted roles
 */
657 658
#define ROLE_GRANTS_FINAL       (1L << 2)

659 660 661

static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users;
static HASH acl_roles;
662 663 664 665 666 667 668
/*
  An hash containing mappings user <--> role

  A hash is used so as to make updates quickly
  The hashkey used represents all the entries combined
*/
static HASH acl_roles_mappings;
unknown's avatar
unknown committed
669 670 671
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
672
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
unknown's avatar
unknown committed
673 674
static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
675
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
unknown's avatar
unknown committed
676
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
677
static bool check_is_role(TABLE *form);
unknown's avatar
unknown committed
678 679 680
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
681
static void rebuild_check_host(void);
682
static void rebuild_role_grants(void);
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
683
static void free_acl_user(ACL_USER *acl_user);
684
static void free_acl_role(ACL_ROLE *acl_role);
685
static ACL_USER *find_user_no_anon(const char *host, const char *user,
686 687
                                   my_bool exact);
static ACL_USER *find_user(const char *host, const char *user, const char *ip);
688
static ACL_ROLE *find_acl_role(const char *user);
Sergei Golubchik's avatar
Sergei Golubchik committed
689 690 691
static bool update_user_table(THD *thd, TABLE *table, const char *host,
                              const char *user, const char *new_password,
                              uint new_password_len);
692
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
693
static my_bool grant_load(THD *thd, TABLE_LIST *tables);
694
static inline void get_grantor(THD *thd, char* grantor);
695 696
static my_bool acl_user_reset_grant(ACL_USER *user,
                                    void * not_used __attribute__((unused)));
697
static my_bool acl_role_reset_grant(ACL_ROLE *role,
698
                                    void * not_used __attribute__((unused)));
699
static my_bool acl_role_propagate_grants(ACL_ROLE *role,
700
                                         void * not_used __attribute__((unused)));
701
static int add_role_user_mapping(ROLE_GRANT_PAIR *mapping);
702
static my_bool get_role_access(ACL_ROLE *role, ulong *access);
703

704 705 706 707 708 709
/*
 Enumeration of various ACL's and Hashes used in handle_grant_struct()
*/
enum enum_acl_lists
{
  USER_ACL= 0,
710
  ROLE_ACL,
711 712 713
  DB_ACL,
  COLUMN_PRIVILEGES_HASH,
  PROC_PRIVILEGES_HASH,
Sergei Golubchik's avatar
Sergei Golubchik committed
714
  FUNC_PRIVILEGES_HASH,
715 716
  PROXY_USERS_ACL,
  ROLES_MAPPINGS_HASH
717
};
unknown's avatar
unknown committed
718

719
ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root)
720
{
721 722 723 724 725 726 727 728

  access= user->access;
  sort= user->sort;
  this->user.str= safe_strdup_root(root, user->user.str);
  this->user.length= user->user.length;
  bzero(&role_grants, sizeof(role_grants));
  bzero(&parent_grantee, sizeof(parent_grantee));
  flags= IS_ROLE;
729
}
730

Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
731 732 733 734 735 736
static
void
free_acl_user(ACL_USER *user)
{
  delete_dynamic(&(user->role_grants));
}
737

738 739 740 741 742 743
static
void
free_acl_role(ACL_ROLE *role)
{
  delete_dynamic(&(role->role_grants));
  delete_dynamic(&(role->parent_grantee));
744 745 746
}

/*
747
  Convert scrambled password to binary form, according to scramble type,
748 749 750 751 752 753 754 755 756 757 758 759
  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;
  }
760
  else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
761 762
  {
    get_salt_from_password_323((ulong *) acl_user->salt, password);
763
    acl_user->salt_len= SCRAMBLE_LENGTH_323;
764 765 766 767 768
  }
  else
    acl_user->salt_len= 0;
}

769 770 771 772 773 774 775 776 777 778 779 780 781
static char *fix_plugin_ptr(char *name)
{
  if (my_strcasecmp(system_charset_info, name,
                    native_password_plugin_name.str) == 0)
    return native_password_plugin_name.str;
  else
  if (my_strcasecmp(system_charset_info, name,
                    old_password_plugin_name.str) == 0)
    return old_password_plugin_name.str;
  else
    return name;
}

782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
/**
  Fix ACL::plugin pointer to point to a hard-coded string, if appropriate

  Make sure that if ACL_USER's plugin is a built-in, then it points
  to a hard coded string, not to an allocated copy. Run-time, for
  authentication, we want to be able to detect built-ins by comparing
  pointers, not strings.

  Additionally - update the salt if the plugin is built-in.

  @retval 0 the pointers were fixed
  @retval 1 this ACL_USER uses a not built-in plugin
*/
static bool fix_user_plugin_ptr(ACL_USER *user)
{
Sergei Golubchik's avatar
Sergei Golubchik committed
797
  user->salt_len= 0;
798 799 800 801 802 803 804 805 806
  if (my_strcasecmp(system_charset_info, user->plugin.str,
                    native_password_plugin_name.str) == 0)
    user->plugin= native_password_plugin_name;
  else
  if (my_strcasecmp(system_charset_info, user->plugin.str,
                    old_password_plugin_name.str) == 0)
    user->plugin= old_password_plugin_name;
  else
    return true;
807

808 809 810 811
  set_user_salt(user, user->auth_string.str, user->auth_string.length);
  return false;
}

812
/*
813 814
  Initialize structures responsible for user/db-level privilege checking and
  load privilege information for them from tables in the 'mysql' database.
815 816 817

  SYNOPSIS
    acl_init()
818 819 820 821 822 823
      dont_read_acl_tables  TRUE if we want to skip loading data from
                            privilege tables and disable privilege checking.

  NOTES
    This function is mostly responsible for preparatory steps, main work
    on initialization and grants loading is done in acl_reload().
824 825 826 827 828 829

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

830
my_bool acl_init(bool dont_read_acl_tables)
unknown's avatar
unknown committed
831
{
unknown's avatar
unknown committed
832
  THD  *thd;
833
  my_bool return_val;
unknown's avatar
unknown committed
834 835
  DBUG_ENTER("acl_init");

836
  acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
Konstantin Osipov's avatar
Konstantin Osipov committed
837 838
                           (my_hash_get_key) acl_entry_get_key,
                           (my_hash_free_key) free,
839
                           &my_charset_utf8_bin);
840 841 842 843 844 845 846 847 848 849 850 851 852

  /*
    cache built-in native authentication plugins,
    to avoid hash searches and a global mutex lock on every connect
  */
  native_password_plugin= my_plugin_lock_by_name(0,
           &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
  old_password_plugin= my_plugin_lock_by_name(0,
           &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);

  if (!native_password_plugin || !old_password_plugin)
    DBUG_RETURN(1);

unknown's avatar
unknown committed
853
  if (dont_read_acl_tables)
854
  {
unknown's avatar
unknown committed
855
    DBUG_RETURN(0); /* purecov: tested */
unknown's avatar
unknown committed
856 857
  }

858 859 860
  /*
    To be able to run this from boot, we allocate a temporary THD
  */
unknown's avatar
unknown committed
861 862
  if (!(thd=new THD))
    DBUG_RETURN(1); /* purecov: inspected */
863
  thd->thread_stack= (char*) &thd;
864
  thd->store_globals();
865 866 867 868 869 870 871 872
  /*
    It is safe to call acl_reload() since acl_* arrays and hashes which
    will be freed there are global static objects and thus are initialized
    by zeros at startup.
  */
  return_val= acl_reload(thd);
  delete thd;
  /* Remember that we don't have a THD */
873
  set_current_thd(0);
874 875 876
  DBUG_RETURN(return_val);
}

877 878 879 880 881 882 883
/**
  Choose from either native or old password plugins when assigning a password
*/

static bool
set_user_plugin (ACL_USER *user, int password_len)
{
884
  switch (password_len)
885 886 887 888 889 890 891 892 893 894
  {
  case 0: /* no password */
  case SCRAMBLED_PASSWORD_CHAR_LENGTH:
    user->plugin= native_password_plugin_name;
    return FALSE;
  case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
    user->plugin= old_password_plugin_name;
    return FALSE;
  default:
    sql_print_warning("Found invalid password for user: '%s@%s'; "
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
895
                      "Ignoring user", user->user.str ? user->user.str : "",
896 897 898 899 900
                      user->host.hostname ? user->host.hostname : "");
    return TRUE;
  }
}

901 902 903 904 905 906 907 908

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

  SYNOPSIS
    acl_load()
      thd     Current thread
909 910 911
      tables  List containing open "mysql.host", "mysql.user",
              "mysql.db", "mysql.proxies_priv" and "mysql.roles_mapping"
              tables.
912 913 914 915 916 917 918 919 920 921

  RETURN VALUES
    FALSE  Success
    TRUE   Error
*/

static my_bool acl_load(THD *thd, TABLE_LIST *tables)
{
  TABLE *table;
  READ_RECORD read_record_info;
922
  my_bool return_val= TRUE;
923
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
924
  char tmp_name[SAFE_NAME_LEN+1];
925
  int password_length;
926
  ulonglong old_sql_mode= thd->variables.sql_mode;
927 928
  DBUG_ENTER("acl_load");

929 930
  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;

931
  grant_version++; /* Privileges updated */
932

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

935
  init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
936 937 938 939
  if (init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0, 
                       FALSE))
    goto end;

940
  table->use_all_columns();
941
  (void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST), 20, 50, MYF(0));
unknown's avatar
unknown committed
942 943 944
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_HOST host;
945 946
    update_hostname(&host.host,get_field(&mem, table->field[0]));
    host.db=	 get_field(&mem, table->field[1]);
947
    if (lower_case_table_names && host.db)
948 949
    {
      /*
950 951
        convert db to lower case and give a warning if the db wasn't
        already in lower case
952
      */
953 954 955 956 957 958
      char *end = strnmov(tmp_name, host.db, sizeof(tmp_name));
      if (end >= tmp_name + sizeof(tmp_name))
      {
        sql_print_warning(ER(ER_WRONG_DB_NAME), host.db);
        continue;
      }
959
      my_casedn_str(files_charset_info, host.db);
960 961 962
      if (strcmp(host.db, tmp_name) != 0)
        sql_print_warning("'host' entry '%s|%s' had database in mixed "
                          "case that has been forced to lowercase because "
963 964
                          "lower_case_table_names is set. It will not be "
                          "possible to remove this privilege using REVOKE.",
965 966
                          host.host.hostname ? host.host.hostname : "",
                          host.db ? host.db : "");
967
    }
unknown's avatar
unknown committed
968 969
    host.access= get_access(table,2);
    host.access= fix_rights_for_db(host.access);
970
    host.sort=	 get_sort(2,host.host.hostname,host.db);
unknown's avatar
SCRUM  
unknown committed
971 972
    if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
    {
unknown's avatar
unknown committed
973
      sql_print_warning("'host' entry '%s|%s' "
unknown's avatar
SCRUM  
unknown committed
974
		      "ignored in --skip-name-resolve mode.",
975 976
			host.host.hostname ? host.host.hostname : "",
			host.db ? host.db : "");
unknown's avatar
SCRUM  
unknown committed
977 978
      continue;
    }
unknown's avatar
unknown committed
979
#ifndef TO_BE_REMOVED
980
    if (table->s->fields == 8)
unknown's avatar
unknown committed
981 982
    {						// Without grant
      if (host.access & CREATE_ACL)
983
	host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
unknown's avatar
unknown committed
984 985
    }
#endif
Konstantin Osipov's avatar
Konstantin Osipov committed
986
    (void) push_dynamic(&acl_hosts,(uchar*) &host);
unknown's avatar
unknown committed
987
  }
988 989
  my_qsort((uchar*) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
	   sizeof(ACL_HOST),(qsort_cmp) acl_compare);
unknown's avatar
unknown committed
990 991 992
  end_read_record(&read_record_info);
  freeze_size(&acl_hosts);

993 994 995 996
  if (init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0,
                       FALSE))
    goto end;

997
  table->use_all_columns();
998
  (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0));
999
  (void) my_hash_init2(&acl_roles,50,system_charset_info,
1000
                       0,0,0, (my_hash_get_key) acl_role_get_key,
1001
                       (void (*)(void *))free_acl_role, 0);
1002

1003
  username_char_length= min(table->field[1]->char_length(), USERNAME_CHAR_LENGTH);
1004 1005 1006
  password_length= table->field[2]->field_length /
    table->field[2]->charset()->mbmaxlen;
  if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
unknown's avatar
unknown committed
1007
  {
1008 1009 1010
    sql_print_error("Fatal error: mysql.user table is damaged or in "
                    "unsupported 3.20 format.");
    goto end;
unknown's avatar
unknown committed
1011 1012
  }

1013
  DBUG_PRINT("info",("user table fields: %d, password length: %d",
1014
		     table->s->fields, password_length));
1015

Marc Alff's avatar
Marc Alff committed
1016
  mysql_mutex_lock(&LOCK_global_system_variables);
1017
  if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
1018
  {
1019 1020
    if (opt_secure_auth)
    {
Marc Alff's avatar
Marc Alff committed
1021
      mysql_mutex_unlock(&LOCK_global_system_variables);
1022 1023 1024 1025
      sql_print_error("Fatal error: mysql.user table is in old format, "
                      "but server started with --secure-auth option.");
      goto end;
    }
1026
    mysql_user_table_is_in_short_password_format= true;
1027
    if (global_system_variables.old_passwords)
Marc Alff's avatar
Marc Alff committed
1028
      mysql_mutex_unlock(&LOCK_global_system_variables);
1029 1030 1031
    else
    {
      global_system_variables.old_passwords= 1;
Marc Alff's avatar
Marc Alff committed
1032
      mysql_mutex_unlock(&LOCK_global_system_variables);
1033 1034 1035
      sql_print_warning("mysql.user table is not updated to new password format; "
                        "Disabling new password usage until "
                        "mysql_fix_privilege_tables is run");
1036 1037 1038 1039
    }
    thd->variables.old_passwords= 1;
  }
  else
1040
  {
1041
    mysql_user_table_is_in_short_password_format= false;
Marc Alff's avatar
Marc Alff committed
1042
    mysql_mutex_unlock(&LOCK_global_system_variables);
1043 1044
  }

unknown's avatar
unknown committed
1045 1046 1047 1048
  allow_all_hosts=0;
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_USER user;
1049
    bool is_role= FALSE;
1050
    bzero(&user, sizeof(user));
1051
    update_hostname(&user.host, get_field(&mem, table->field[0]));
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1052 1053 1054
    char *username= get_field(&mem, table->field[1]);
    user.user.str= username;
    user.user.length= username? strlen(username) : 0;
1055

1056 1057
    /*
       If the user entry is a role, skip password and hostname checks
1058
       A user can not log in with a role so some checks are not necessary
1059
    */
1060
    is_role= check_is_role(table);
1061 1062 1063

    if (!is_role && check_no_resolve &&
        hostname_requires_resolving(user.host.hostname))
unknown's avatar
SCRUM  
unknown committed
1064
    {
unknown's avatar
unknown committed
1065 1066
      sql_print_warning("'user' entry '%s@%s' "
                        "ignored in --skip-name-resolve mode.",
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1067
                        user.user.str ? user.user.str : "",
1068
			user.host.hostname ? user.host.hostname : "");
unknown's avatar
SCRUM  
unknown committed
1069 1070 1071
      continue;
    }

1072
    char *password= get_field(&mem, table->field[2]);
1073
    uint password_len= password ? strlen(password) : 0;
1074 1075
    user.auth_string.str= password ? password : const_cast<char*>("");
    user.auth_string.length= password_len;
1076
    set_user_salt(&user, password, password_len);
1077

1078
    if (!is_role && set_user_plugin(&user, password_len))
1079 1080
      continue;
    
unknown's avatar
unknown committed
1081
    {
unknown's avatar
unknown committed
1082 1083
      uint next_field;
      user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
1084 1085 1086 1087
      /*
        if it is pre 5.0.1 privilege table then map CREATE privilege on
        CREATE VIEW & SHOW VIEW privileges
      */
1088
      if (table->s->fields <= 31 && (user.access & CREATE_ACL))
1089
        user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
1090 1091 1092 1093 1094

      /*
        if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
        CREATE PROCEDURE & ALTER PROCEDURE privileges
      */
1095
      if (table->s->fields <= 33 && (user.access & CREATE_ACL))
1096
        user.access|= CREATE_PROC_ACL;
1097
      if (table->s->fields <= 33 && (user.access & ALTER_ACL))
1098 1099
        user.access|= ALTER_PROC_ACL;

1100 1101 1102 1103 1104 1105
      /*
        pre 5.0.3 did not have CREATE_USER_ACL
      */
      if (table->s->fields <= 36 && (user.access & GRANT_ACL))
        user.access|= CREATE_USER_ACL;

unknown's avatar
unknown committed
1106 1107

      /*
unknown's avatar
unknown committed
1108
        if it is pre 5.1.6 privilege table then map CREATE privilege on
unknown's avatar
unknown committed
1109 1110
        CREATE|ALTER|DROP|EXECUTE EVENT
      */
unknown's avatar
unknown committed
1111
      if (table->s->fields <= 37 && (user.access & SUPER_ACL))
unknown's avatar
unknown committed
1112 1113
        user.access|= EVENT_ACL;

1114 1115 1116 1117 1118 1119
      /*
        if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
      */
      if (table->s->fields <= 38 && (user.access & SUPER_ACL))
        user.access|= TRIGGER_ACL;

1120 1121 1122
      user.sort= get_sort(2,user.host.hostname,user.user);
      user.hostname_length= (user.host.hostname ?
                             (uint) strlen(user.host.hostname) : 0);
unknown's avatar
VIEW  
unknown committed
1123

1124 1125
      /* Starting from 4.0.2 we have more fields */
      if (table->s->fields >= 31)
1126
      {
1127
        char *ssl_type=get_field(thd->mem_root, table->field[next_field++]);
1128 1129 1130 1131 1132 1133 1134 1135 1136
        if (!ssl_type)
          user.ssl_type=SSL_TYPE_NONE;
        else if (!strcmp(ssl_type, "ANY"))
          user.ssl_type=SSL_TYPE_ANY;
        else if (!strcmp(ssl_type, "X509"))
          user.ssl_type=SSL_TYPE_X509;
        else  /* !strcmp(ssl_type, "SPECIFIED") */
          user.ssl_type=SSL_TYPE_SPECIFIED;

unknown's avatar
unknown committed
1137 1138 1139
        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++]);
1140

1141
        char *ptr = get_field(thd->mem_root, table->field[next_field++]);
unknown's avatar
unknown committed
1142
        user.user_resource.questions=ptr ? atoi(ptr) : 0;
1143
        ptr = get_field(thd->mem_root, table->field[next_field++]);
unknown's avatar
unknown committed
1144
        user.user_resource.updates=ptr ? atoi(ptr) : 0;
1145
        ptr = get_field(thd->mem_root, table->field[next_field++]);
1146
        user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
1147
        if (user.user_resource.questions || user.user_resource.updates ||
1148
            user.user_resource.conn_per_hour)
1149
          mqh_used=1;
1150

1151
        if (table->s->fields >= 36)
1152 1153
        {
          /* Starting from 5.0.3 we have max_user_connections field */
1154
          ptr= get_field(thd->mem_root, table->field[next_field++]);
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
1155
          user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
1156
        }
1157

1158
        if (!is_role && table->s->fields >= 41)
1159 1160 1161 1162 1163
        {
          /* We may have plugin & auth_String fields */
          char *tmpstr= get_field(&mem, table->field[next_field++]);
          if (tmpstr)
          {
1164 1165
            user.plugin.str= tmpstr;
            user.plugin.length= strlen(user.plugin.str);
1166 1167 1168 1169 1170
            if (user.auth_string.length)
            {
              sql_print_warning("'user' entry '%s@%s' has both a password "
                                "and an authentication plugin specified. The "
                                "password will be ignored.",
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1171
                                user.user.str ? user.user.str : "",
1172 1173 1174 1175 1176 1177
                                user.host.hostname ? user.host.hostname : "");
            }
            user.auth_string.str= get_field(&mem, table->field[next_field++]);
            if (!user.auth_string.str)
              user.auth_string.str= const_cast<char*>("");
            user.auth_string.length= strlen(user.auth_string.str);
1178 1179

            fix_user_plugin_ptr(&user);
1180 1181
          }
        }
1182
      }
1183 1184 1185
      else
      {
        user.ssl_type=SSL_TYPE_NONE;
unknown's avatar
unknown committed
1186
#ifndef TO_BE_REMOVED
1187
        if (table->s->fields <= 13)
1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
        {						// Without grant
          if (user.access & CREATE_ACL)
            user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
        }
        /* Convert old privileges */
        user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
        if (user.access & FILE_ACL)
          user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
        if (user.access & PROCESS_ACL)
          user.access|= SUPER_ACL | EXECUTE_ACL;
unknown's avatar
unknown committed
1198
#endif
1199
      }
1200

1201
      (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *),
1202 1203
                                   50, 100, MYF(0));

1204
      if (is_role) {
1205
        DBUG_PRINT("info", ("Found role %s", user.user.str));
1206
        ACL_ROLE *entry= new (&mem) ACL_ROLE(&user, &mem);
1207
        entry->role_grants = user.role_grants;
1208 1209
        (void) my_init_dynamic_array(&entry->parent_grantee,
                                     sizeof(ACL_USER_BASE *), 50, 100, MYF(0));
1210
        /* set initial role access the same as the table row privileges */
1211
        entry->initial_role_access= entry->access;
1212 1213
        my_hash_insert(&acl_roles, (uchar *)entry);

Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1214
        continue;
1215 1216 1217
      }
      else
      {
1218
        DBUG_PRINT("info", ("Found user %s", user.user.str));
1219 1220
        (void) push_dynamic(&acl_users,(uchar*) &user);
      }
1221 1222
      if (!user.host.hostname ||
	  (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
1223
        allow_all_hosts=1;			// Anyone can connect
unknown's avatar
unknown committed
1224
    }
unknown's avatar
unknown committed
1225
  }
1226 1227
  my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
	   sizeof(ACL_USER),(qsort_cmp) acl_compare);
unknown's avatar
unknown committed
1228 1229
  end_read_record(&read_record_info);
  freeze_size(&acl_users);
unknown's avatar
unknown committed
1230

1231 1232 1233 1234
  if (init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0,
                       FALSE))
    goto end;

1235
  table->use_all_columns();
1236
  (void) my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB), 50, 100, MYF(0));
unknown's avatar
unknown committed
1237 1238 1239
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_DB db;
1240 1241
    update_hostname(&db.host,get_field(&mem, table->field[MYSQL_DB_FIELD_HOST]));
    db.db=get_field(&mem, table->field[MYSQL_DB_FIELD_DB]);
1242 1243
    if (!db.db)
    {
unknown's avatar
unknown committed
1244
      sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
1245
      continue;
1246
    }
1247
    db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]);
unknown's avatar
SCRUM  
unknown committed
1248 1249
    if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
    {
unknown's avatar
unknown committed
1250 1251
      sql_print_warning("'db' entry '%s %s@%s' "
		        "ignored in --skip-name-resolve mode.",
1252 1253 1254
		        db.db,
			db.user ? db.user : "",
			db.host.hostname ? db.host.hostname : "");
unknown's avatar
SCRUM  
unknown committed
1255 1256
      continue;
    }
unknown's avatar
unknown committed
1257 1258
    db.access=get_access(table,3);
    db.access=fix_rights_for_db(db.access);
1259 1260 1261
    if (lower_case_table_names)
    {
      /*
1262 1263
        convert db to lower case and give a warning if the db wasn't
        already in lower case
1264
      */
1265 1266 1267 1268 1269 1270
      char *end = strnmov(tmp_name, db.db, sizeof(tmp_name));
      if (end >= tmp_name + sizeof(tmp_name))
      {
        sql_print_warning(ER(ER_WRONG_DB_NAME), db.db);
        continue;
      }
1271
      my_casedn_str(files_charset_info, db.db);
1272 1273 1274 1275
      if (strcmp(db.db, tmp_name) != 0)
      {
        sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
                          "case that has been forced to lowercase because "
1276 1277
                          "lower_case_table_names is set. It will not be "
                          "possible to remove this privilege using REVOKE.",
1278 1279 1280
		          db.db,
			  db.user ? db.user : "",
			  db.host.hostname ? db.host.hostname : "");
1281 1282
      }
    }
unknown's avatar
unknown committed
1283 1284
    db.sort=get_sort(3,db.host.hostname,db.db,db.user);
#ifndef TO_BE_REMOVED
1285
    if (table->s->fields <=  9)
unknown's avatar
unknown committed
1286 1287 1288 1289 1290
    {						// Without grant
      if (db.access & CREATE_ACL)
	db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
    }
#endif
Konstantin Osipov's avatar
Konstantin Osipov committed
1291
    (void) push_dynamic(&acl_dbs,(uchar*) &db);
unknown's avatar
unknown committed
1292
  }
1293 1294
  my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
	   sizeof(ACL_DB),(qsort_cmp) acl_compare);
unknown's avatar
unknown committed
1295 1296
  end_read_record(&read_record_info);
  freeze_size(&acl_dbs);
1297

1298
  (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER),
1299
                               50, 100, MYF(0));
1300
  if (tables[3].table)
1301
  {
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
    init_read_record(&read_record_info, thd, table= tables[3].table, NULL, 1, 
                     0, FALSE);
    table->use_all_columns();
    while (!(read_record_info.read_record(&read_record_info)))
    {
      ACL_PROXY_USER proxy;
      proxy.init(table, &mem);
      if (proxy.check_validity(check_no_resolve))
        continue;
      if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
      {
        end_read_record(&read_record_info);
        goto end;
      }
    }
    my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
             acl_proxy_users.elements,
             sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
    end_read_record(&read_record_info);
  }
  else
  {
    sql_print_error("Missing system table mysql.proxies_priv; "
                    "please run mysql_upgrade to create it");
1326 1327 1328
  }
  freeze_size(&acl_proxy_users);

1329 1330 1331 1332 1333 1334 1335
  if (tables[4].table)
  {
    if (init_read_record(&read_record_info, thd, table= tables[4].table,
                         NULL, 1, 1, FALSE))
      goto end;
    table->use_all_columns();
    /* account for every role mapping */
1336

1337
    /* acquire lock for the find_user_no_anon functions */
1338 1339
    if (!initialized)
      mysql_mutex_lock(&acl_cache->lock);
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1340

1341 1342
    (void) my_hash_init2(&acl_roles_mappings,50,system_charset_info,
                         0,0,0, (my_hash_get_key) acl_role_map_get_key, 0,0);
1343 1344
    MEM_ROOT temp_root;
    init_alloc_root(&temp_root, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
1345 1346
    while (!(read_record_info.read_record(&read_record_info)))
    {
1347
      ROLE_GRANT_PAIR *mapping= (ROLE_GRANT_PAIR *)alloc_root(
1348 1349
                                                      &mem,
                                                      sizeof(ROLE_GRANT_PAIR));
1350 1351 1352 1353
      char *hostname= get_field(&temp_root, table->field[0]);
      char *username= get_field(&temp_root, table->field[1]);
      char *rolename= get_field(&temp_root, table->field[2]);
      init_role_grant_pair(&mem, mapping, username, hostname, rolename);
1354
      if (add_role_user_mapping(mapping) == -1) {
1355
        sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'",
1356 1357
                        mapping->u_uname ? mapping->u_uname : "",
                        mapping->u_hname ? mapping->u_hname : "",
1358
                        mapping->r_uname ? mapping->r_uname : "");
1359 1360
        continue;
      }
1361

1362
      my_hash_insert(&acl_roles_mappings, (uchar*) mapping);
1363
    }
1364

1365
    free_root(&temp_root, MYF(0));
1366
    end_read_record(&read_record_info);
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1367

1368 1369 1370
    my_hash_iterate(&acl_roles,
                    (my_hash_walk_action) acl_role_propagate_grants, NULL);

1371 1372
    if (!initialized)
      mysql_mutex_unlock(&acl_cache->lock);
1373 1374 1375

  }

unknown's avatar
unknown committed
1376 1377
  init_check_host();

1378
  initialized=1;
1379
  return_val= FALSE;
1380 1381

end:
1382
  thd->variables.sql_mode= old_sql_mode;
1383
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
1384 1385 1386 1387 1388
}


void acl_free(bool end)
{
1389
  my_hash_free(&acl_roles);
1390
  free_root(&mem,MYF(0));
unknown's avatar
unknown committed
1391
  delete_dynamic(&acl_hosts);
1392
  delete_dynamic_recursive(&acl_users, (FREE_FUNC) free_acl_user);
unknown's avatar
unknown committed
1393 1394
  delete_dynamic(&acl_dbs);
  delete_dynamic(&acl_wild_hosts);
1395
  delete_dynamic(&acl_proxy_users);
Konstantin Osipov's avatar
Konstantin Osipov committed
1396
  my_hash_free(&acl_check_hosts);
1397
  my_hash_free(&acl_roles_mappings);
1398 1399
  plugin_unlock(0, native_password_plugin);
  plugin_unlock(0, old_password_plugin);
unknown's avatar
unknown committed
1400 1401 1402 1403 1404 1405 1406 1407 1408
  if (!end)
    acl_cache->clear(1); /* purecov: inspected */
  else
  {
    delete acl_cache;
    acl_cache=0;
  }
}

1409 1410

/*
1411 1412
  Forget current user/db-level privileges and read new privileges
  from the privilege tables.
1413 1414 1415

  SYNOPSIS
    acl_reload()
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426
      thd  Current thread

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

  RETURN VALUE
    FALSE  Success
    TRUE   Failure
1427
*/
unknown's avatar
unknown committed
1428

1429
my_bool acl_reload(THD *thd)
unknown's avatar
unknown committed
1430
{
1431
  TABLE_LIST tables[5];
1432
  DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users;
1433
  HASH old_acl_roles, old_acl_roles_mappings;
unknown's avatar
unknown committed
1434 1435
  MEM_ROOT old_mem;
  bool old_initialized;
1436
  my_bool return_val= TRUE;
unknown's avatar
unknown committed
1437 1438
  DBUG_ENTER("acl_reload");

1439 1440 1441 1442
  /*
    To avoid deadlocks we should obtain table locks before
    obtaining acl_cache->lock mutex.
  */
1443 1444 1445 1446 1447 1448
  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("host"), "host", TL_READ);
  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("user"), "user", TL_READ);
  tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("db"), "db", TL_READ);
1449
  tables[3].init_one_table(C_STRING_WITH_LEN("mysql"),
1450 1451
                           C_STRING_WITH_LEN("proxies_priv"), 
                           "proxies_priv", TL_READ);
1452 1453 1454
  tables[4].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("roles_mapping"),
                           "roles_mapping", TL_READ);
1455 1456 1457
  tables[0].next_local= tables[0].next_global= tables + 1;
  tables[1].next_local= tables[1].next_global= tables + 2;
  tables[2].next_local= tables[2].next_global= tables + 3;
1458
  tables[3].next_local= tables[3].next_global= tables + 4;
1459
  tables[0].open_type= tables[1].open_type= tables[2].open_type= 
1460 1461 1462 1463
  tables[3].open_type= tables[4].open_type= OT_BASE_ONLY;
  tables[0].open_strategy= tables[3].open_strategy=
  tables[4].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
 
1464

1465
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
1466
  {
1467 1468 1469 1470
    /*
      Execution might have been interrupted; only print the error message
      if an error condition has been raised.
    */
1471
    if (thd->stmt_da->is_error())
1472
      sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
1473
                      thd->stmt_da->message());
1474 1475 1476
    goto end;
  }

unknown's avatar
unknown committed
1477
  if ((old_initialized=initialized))
Marc Alff's avatar
Marc Alff committed
1478
    mysql_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
1479

1480 1481
  old_acl_hosts= acl_hosts;
  old_acl_users= acl_users;
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1482
  old_acl_roles= acl_roles;
1483
  old_acl_roles_mappings= acl_roles_mappings;
1484 1485 1486
  old_acl_proxy_users= acl_proxy_users;
  old_acl_dbs= acl_dbs;
  old_mem= mem;
unknown's avatar
unknown committed
1487
  delete_dynamic(&acl_wild_hosts);
Konstantin Osipov's avatar
Konstantin Osipov committed
1488
  my_hash_free(&acl_check_hosts);
unknown's avatar
unknown committed
1489

1490
  if ((return_val= acl_load(thd, tables)))
unknown's avatar
unknown committed
1491
  {					// Error. Revert to old list
1492
    DBUG_PRINT("error",("Reverting to old privileges"));
1493
    acl_free();				/* purecov: inspected */
1494 1495
    acl_hosts= old_acl_hosts;
    acl_users= old_acl_users;
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1496
    acl_roles= old_acl_roles;
1497
    acl_roles_mappings= old_acl_roles_mappings;
1498 1499 1500
    acl_proxy_users= old_acl_proxy_users;
    acl_dbs= old_acl_dbs;
    mem= old_mem;
unknown's avatar
unknown committed
1501 1502 1503 1504
    init_check_host();
  }
  else
  {
1505
    my_hash_free(&old_acl_roles);
1506
    free_root(&old_mem,MYF(0));
unknown's avatar
unknown committed
1507
    delete_dynamic(&old_acl_hosts);
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1508
    delete_dynamic_recursive(&old_acl_users, (FREE_FUNC) free_acl_user);
1509
    delete_dynamic(&old_acl_proxy_users);
unknown's avatar
unknown committed
1510
    delete_dynamic(&old_acl_dbs);
1511 1512
    my_hash_free(&old_acl_roles_mappings);
    my_hash_free(&old_acl_roles_mappings);
unknown's avatar
unknown committed
1513 1514
  }
  if (old_initialized)
Marc Alff's avatar
Marc Alff committed
1515
    mysql_mutex_unlock(&acl_cache->lock);
1516
end:
1517
  close_mysql_tables(thd);
1518
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
1519 1520 1521
}


unknown's avatar
unknown committed
1522 1523
/*
  Get all access bits from table after fieldnr
unknown's avatar
unknown committed
1524 1525

  IMPLEMENTATION
unknown's avatar
unknown committed
1526 1527
  We know that the access privileges ends when there is no more fields
  or the field is not an enum with two elements.
unknown's avatar
unknown committed
1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538

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

  RETURN VALUE
    privilege mask
unknown's avatar
unknown committed
1539
*/
unknown's avatar
unknown committed
1540

unknown's avatar
unknown committed
1541
static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
unknown's avatar
unknown committed
1542
{
unknown's avatar
unknown committed
1543
  ulong access_bits=0,bit;
unknown's avatar
unknown committed
1544
  char buff[2];
unknown's avatar
unknown committed
1545
  String res(buff,sizeof(buff),&my_charset_latin1);
unknown's avatar
unknown committed
1546 1547
  Field **pos;

unknown's avatar
unknown committed
1548
  for (pos=form->field+fieldnr, bit=1;
1549
       *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
unknown's avatar
unknown committed
1550
	 ((Field_enum*) (*pos))->typelib->count == 2 ;
unknown's avatar
unknown committed
1551
       pos++, fieldnr++, bit<<=1)
unknown's avatar
unknown committed
1552
  {
1553
    (*pos)->val_str(&res);
unknown's avatar
unknown committed
1554
    if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
unknown's avatar
unknown committed
1555
      access_bits|= bit;
unknown's avatar
unknown committed
1556
  }
unknown's avatar
unknown committed
1557 1558
  if (next_field)
    *next_field=fieldnr;
unknown's avatar
unknown committed
1559 1560 1561
  return access_bits;
}

1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593
/*
  Check if a user entry in the user table is marked as being a role entry

  IMPLEMENTATION
  Access the coresponding column and check the coresponding ENUM of the form
  ENUM('N', 'Y')

  SYNOPSIS
    check_is_role()
    form      an open table to read the entry from.
              The record should be already read in table->record[0]

  RETURN VALUE
    TRUE      if the user is marked as a role
    FALSE     otherwise
*/

static inline bool check_is_role(TABLE *form)
{
  char buff[2];
  String res(buff, sizeof(buff), &my_charset_latin1);
  /* Table version does not support roles */
  if (form->s->fields <= 42)
    return FALSE;

  form->field[ROLE_ASSIGN_COLUMN_IDX]->val_str(&res);
  if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
    return TRUE;

  return FALSE;
}

unknown's avatar
unknown committed
1594 1595

/*
unknown's avatar
unknown committed
1596 1597 1598 1599 1600
  Return a number which, if sorted 'desc', puts strings in this order:
    no wildcards
    wildcards
    empty string
*/
unknown's avatar
unknown committed
1601 1602 1603 1604 1605 1606 1607

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

1608 1609 1610
  /* Should not use this function with more than 4 arguments for compare. */
  DBUG_ASSERT(count <= 4);

unknown's avatar
unknown committed
1611 1612
  while (count--)
  {
1613 1614 1615
    char *start, *str= va_arg(args,char*);
    uint chars= 0;
    uint wild_pos= 0;           /* first wildcard position */
unknown's avatar
unknown committed
1616

unknown's avatar
unknown committed
1617
    if ((start= str))
unknown's avatar
unknown committed
1618 1619 1620
    {
      for (; *str ; str++)
      {
1621 1622 1623
        if (*str == wild_prefix && str[1])
          str++;
        else if (*str == wild_many || *str == wild_one)
1624
        {
unknown's avatar
unknown committed
1625
          wild_pos= (uint) (str - start) + 1;
1626 1627
          break;
        }
unknown's avatar
unknown committed
1628
        chars= 128;                             // Marker that chars existed
unknown's avatar
unknown committed
1629 1630
      }
    }
unknown's avatar
unknown committed
1631
    sort= (sort << 8) + (wild_pos ? min(wild_pos, 127) : chars);
unknown's avatar
unknown committed
1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646
  }
  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;
}

1647

1648
/*
1649
  Gets user credentials without authentication and resource limit checks.
unknown's avatar
unknown committed
1650

1651
  SYNOPSIS
1652
    acl_getroot()
1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663
      sctx               Context which should be initialized
      user               user name
      host               host name
      ip                 IP
      db                 current data base name

  RETURN
    FALSE  OK
    TRUE   Error
*/

1664 1665
bool acl_getroot(Security_context *sctx, char *user, char *host,
                 char *ip, char *db)
1666 1667
{
  int res= 1;
1668
  uint i;
1669
  ACL_USER *acl_user= 0;
1670
  DBUG_ENTER("acl_getroot");
1671

1672 1673
  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
                       (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
1674
                       user, (db ? db : "(NULL)")));
1675 1676 1677 1678 1679
  sctx->user= user;
  sctx->host= host;
  sctx->ip= ip;
  sctx->host_or_ip= host ? host : (ip ? ip : "");

1680 1681
  if (!initialized)
  {
1682
    /*
1683 1684
      here if mysqld's been started with --skip-grant-tables option.
    */
1685
    sctx->skip_grants();
1686
    DBUG_RETURN(FALSE);
1687 1688
  }

Marc Alff's avatar
Marc Alff committed
1689
  mysql_mutex_lock(&acl_cache->lock);
1690

1691 1692
  sctx->master_access= 0;
  sctx->db_access= 0;
1693
  *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0;
1694

1695 1696 1697
  /*
     Find acl entry in user database.
     This is specially tailored to suit the check we do for CALL of
1698
     a stored procedure; user is set to what is actually a
1699 1700
     priv_user, which can be ''.
  */
1701
  for (i=0 ; i < acl_users.elements ; i++)
1702
  {
1703
    ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1704 1705
    if ((!acl_user_tmp->user.str && !user[0]) ||
        (acl_user_tmp->user.str && strcmp(user, acl_user_tmp->user.str) == 0))
1706
    {
1707
      if (compare_hostname(&acl_user_tmp->host, host, ip))
1708
      {
1709 1710 1711
        acl_user= acl_user_tmp;
        res= 0;
        break;
1712 1713 1714 1715 1716 1717
      }
    }
  }

  if (acl_user)
  {
1718 1719 1720 1721
    for (i=0 ; i < acl_dbs.elements ; i++)
    {
      ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
      if (!acl_db->user ||
1722
	  (user && user[0] && !strcmp(user, acl_db->user)))
1723
      {
1724
	if (compare_hostname(&acl_db->host, host, ip))
1725
	{
1726
	  if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
1727
	  {
1728
	    sctx->db_access= acl_db->access;
1729 1730 1731 1732 1733
	    break;
	  }
	}
      }
    }
1734
    sctx->master_access= acl_user->access;
1735

Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1736
    if (acl_user->user.str)
1737
      strmake_buf(sctx->priv_user, user);
1738 1739
    else
      *sctx->priv_user= 0;
1740 1741

    if (acl_user->host.hostname)
1742
      strmake_buf(sctx->priv_host, acl_user->host.hostname);
1743
    else
1744
      *sctx->priv_host= 0;
1745
  }
Marc Alff's avatar
Marc Alff committed
1746
  mysql_mutex_unlock(&acl_cache->lock);
1747 1748 1749
  DBUG_RETURN(res);
}

1750
int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
1751 1752 1753 1754 1755 1756 1757
{
  bool is_granted;
  int result= 0;

  /* clear role privileges */
  mysql_mutex_lock(&acl_cache->lock);

1758
  ACL_ROLE *role= find_acl_role(rolename);
1759
  ACL_USER_BASE *acl_user_base;
1760 1761 1762 1763 1764
  ACL_USER *acl_user;

  if (!strcasecmp(rolename, "NONE")) {
    /* have to clear the privileges */
    /* get the current user */
1765 1766
    acl_user= find_user(thd->security_ctx->host, thd->security_ctx->user,
                        thd->security_ctx->ip);
1767
    if (acl_user == NULL)
1768 1769
    {
      my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename);
1770
      result= -1;
1771
    }
1772 1773
    else if (access)
      *access= acl_user->access;
1774 1775 1776 1777

    goto end;
  }

1778
  /* According to SQL standard, the same error message must be presented */
1779
  if (role == NULL) {
1780
    my_error(ER_INVALID_ROLE, MYF(0), rolename);
1781 1782 1783 1784
    result= -1;
    goto end;
  }

1785
  for (uint i=0 ; i < role->parent_grantee.elements ; i++)
1786
  {
1787 1788 1789 1790 1791
    acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**));
    if (acl_user_base->flags & IS_ROLE)
      continue;

    acl_user= (ACL_USER *)acl_user_base;
1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804
    if ((!acl_user->user.str && !thd->security_ctx->user[0]) ||
        (acl_user->user.str && !strcmp(thd->security_ctx->user,
                                       acl_user->user.str)))
    {
      if (compare_hostname(&acl_user->host, thd->security_ctx->host,
                                            thd->security_ctx->host))
      {
        is_granted= TRUE;
        break;
      }
    }
  }

1805
  /* According to SQL standard, the same error message must be presented */
1806 1807
  if (!is_granted)
  {
1808
    my_error(ER_INVALID_ROLE, MYF(0), rolename);
1809 1810 1811 1812
    result= 1;
    goto end;
  }

1813 1814 1815 1816
  if (access)
  {
    *access = acl_user->access | role->access;
  }
1817 1818 1819 1820 1821
end:
  mysql_mutex_unlock(&acl_cache->lock);
  return result;
}

1822 1823
int acl_setrole(THD *thd, char *rolename, ulonglong access) {
  /* merge the privileges */
1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841
  Security_context *sctx= thd->security_ctx;
  sctx->master_access= access;
  if (thd->db)
  {
    sctx->db_access= acl_get(sctx->host,
                             sctx->ip, sctx->user, thd->db, FALSE);
    sctx->db_access= acl_get("", "", rolename, thd->db, FALSE);
  }
  if (!strcasecmp(rolename, "NONE"))
  {
    thd->security_ctx->priv_role[0]= 0;
  }
  else
  {
    /* mark the current role */
    strmake(thd->security_ctx->priv_role, rolename,
            sizeof(thd->security_ctx->priv_role)-1);
  }
1842 1843 1844 1845 1846
  return 0;
}



1847 1848
static uchar* check_get_key(ACL_USER *buff, size_t *length,
                            my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
1849 1850
{
  *length=buff->hostname_length;
1851
  return (uchar*) buff->host.hostname;
unknown's avatar
unknown committed
1852 1853
}

1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912
static void acl_update_role(const char *rolename,
                            ulong privileges)
{
  ACL_ROLE *role;
  ulong unused;
  mysql_mutex_assert_owner(&acl_cache->lock);

  role= find_acl_role(rolename);
  if (!role)
  {
    return;
  }

  /*
     Changing privileges of a role causes all other roles that had
     this role granted to them to have their rights invalidated.

     We need to rebuild all roles' related access bits.
  */

  role->initial_role_access= privileges;
  role->flags&= ~ROLE_GRANTS_FINAL;
  role->access= role->initial_role_access;
  get_role_access(role, &unused);

  for (uint i= 0; i < role->parent_grantee.elements; i++)
  {
    ACL_USER_BASE *acl_user_base;
    ACL_ROLE *grantee;
    acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**));
    if (acl_user_base->flags & IS_ROLE)
    {
      grantee= (ACL_ROLE *)acl_user_base;
      grantee->flags&= ~ROLE_GRANTS_FINAL;
      grantee->access= grantee->initial_role_access;
    }
  }
  /*
     This needs to be run again after resetting the ROLE_GRANTS_FINAL flag,
     because otherwise diamond shaped grants will interfere with the reset
     process.

     Example: RoleA -> RoleB; RoleA -> RoleC; RoleB -> RoleC;
     We are updating RoleC, and we reset RoleA first. If we were to run
     get_role_access without resetting RoleB on RoleA, we would get the old
     privileges from RoleC via RoleB into RoleA.
  */
  for (uint i= 0; i < role->parent_grantee.elements; i++)
  {
    ACL_USER_BASE *acl_user_base;
    ACL_ROLE *grantee;
    acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**));
    if (acl_user_base->flags & IS_ROLE)
    {
      grantee= (ACL_ROLE *)acl_user_base;
      get_role_access(grantee, &unused);
    }
  }
}
1913

unknown's avatar
unknown committed
1914
static void acl_update_user(const char *user, const char *host,
1915
			    const char *password, uint password_len,
1916 1917 1918 1919
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
unknown's avatar
unknown committed
1920
			    USER_RESOURCES  *mqh,
1921 1922 1923
			    ulong privileges,
			    const LEX_STRING *plugin,
			    const LEX_STRING *auth)
unknown's avatar
unknown committed
1924
{
Marc Alff's avatar
Marc Alff committed
1925
  mysql_mutex_assert_owner(&acl_cache->lock);
1926

1927 1928 1929 1930 1931 1932
  if (host[0] == '\0' && find_acl_role(user))
  {
    acl_update_role(user, privileges);
    return;
  }

unknown's avatar
unknown committed
1933 1934 1935
  for (uint i=0 ; i < acl_users.elements ; i++)
  {
    ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
1936 1937
    if ((!acl_user->user.str && !user[0]) ||
        (acl_user->user.str && !strcmp(user,acl_user->user.str)))
unknown's avatar
unknown committed
1938
    {
1939 1940
      if ((!acl_user->host.hostname && !host[0]) ||
	  (acl_user->host.hostname &&
1941
           !my_strcasecmp(system_charset_info, host, acl_user->host.hostname)))
unknown's avatar
unknown committed
1942
      {
1943 1944
        if (plugin->str[0])
        {
1945
          acl_user->plugin= *plugin;
1946 1947 1948
          acl_user->auth_string.str= auth->str ?
            strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
          acl_user->auth_string.length= auth->length;
1949 1950
          if (fix_user_plugin_ptr(acl_user))
            acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
1951
        }
1952
        else
Sergei Golubchik's avatar
Sergei Golubchik committed
1953
          if (password[0])
1954 1955 1956 1957
          {
            acl_user->auth_string.str= strmake_root(&mem, password, password_len);
            acl_user->auth_string.length= password_len;
            set_user_salt(acl_user, password, password_len);
Sergei Golubchik's avatar
Sergei Golubchik committed
1958
            set_user_plugin(acl_user, password_len);
1959
          }
unknown's avatar
unknown committed
1960
	acl_user->access=privileges;
1961
	if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1962
	  acl_user->user_resource.questions=mqh->questions;
1963
	if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1964
	  acl_user->user_resource.updates=mqh->updates;
1965 1966 1967 1968
	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;
1969 1970 1971 1972 1973 1974 1975 1976 1977 1978
	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);
	}
1979
        /* search complete: */
unknown's avatar
unknown committed
1980 1981 1982 1983 1984 1985 1986 1987
	break;
      }
    }
  }
}


static void acl_insert_user(const char *user, const char *host,
1988
			    const char *password, uint password_len,
1989 1990 1991 1992
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
1993
			    USER_RESOURCES *mqh,
1994 1995 1996
			    ulong privileges,
			    const LEX_STRING *plugin,
			    const LEX_STRING *auth)
unknown's avatar
unknown committed
1997 1998
{
  ACL_USER acl_user;
1999

Marc Alff's avatar
Marc Alff committed
2000
  mysql_mutex_assert_owner(&acl_cache->lock);
2001

Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
2002 2003
  acl_user.user.str=*user ? strdup_root(&mem,user) : 0;
  acl_user.user.length= strlen(user);
unknown's avatar
unknown committed
2004
  update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
2005 2006
  if (plugin->str[0])
  {
2007
    acl_user.plugin= *plugin;
2008 2009 2010
    acl_user.auth_string.str= auth->str ?
      strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
    acl_user.auth_string.length= auth->length;
2011 2012
    if (fix_user_plugin_ptr(&acl_user))
      acl_user.plugin.str= strmake_root(&mem, plugin->str, plugin->length);
2013 2014 2015 2016 2017
  }
  else
  {
    acl_user.auth_string.str= strmake_root(&mem, password, password_len);
    acl_user.auth_string.length= password_len;
Sergei Golubchik's avatar
Sergei Golubchik committed
2018 2019
    set_user_salt(&acl_user, password, password_len);
    set_user_plugin(&acl_user, password_len);
2020 2021
  }

unknown's avatar
unknown committed
2022
  acl_user.access=privileges;
2023
  acl_user.user_resource = *mqh;
unknown's avatar
unknown committed
2024
  acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
2025
  acl_user.hostname_length=(uint) strlen(host);
2026 2027 2028 2029 2030
  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;
2031 2032
  (void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *),
                               50, 100, MYF(0));
2033

Konstantin Osipov's avatar
Konstantin Osipov committed
2034
  (void) push_dynamic(&acl_users,(uchar*) &acl_user);
2035 2036
  if (!acl_user.host.hostname ||
      (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
unknown's avatar
unknown committed
2037
    allow_all_hosts=1;		// Anyone can connect /* purecov: tested */
2038 2039
  my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
	   sizeof(ACL_USER),(qsort_cmp) acl_compare);
unknown's avatar
unknown committed
2040

2041 2042
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();
2043 2044 2045
  /* Rebuild every user's role_grants because the acl_user has been modified
     and some grants might now be invalid */
  rebuild_role_grants();
unknown's avatar
unknown committed
2046 2047 2048 2049
}


static void acl_update_db(const char *user, const char *host, const char *db,
unknown's avatar
unknown committed
2050
			  ulong privileges)
unknown's avatar
unknown committed
2051
{
Marc Alff's avatar
Marc Alff committed
2052
  mysql_mutex_assert_owner(&acl_cache->lock);
2053

unknown's avatar
unknown committed
2054 2055 2056
  for (uint i=0 ; i < acl_dbs.elements ; i++)
  {
    ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
2057 2058 2059
    if ((!acl_db->user && !user[0]) ||
	(acl_db->user &&
	!strcmp(user,acl_db->user)))
unknown's avatar
unknown committed
2060
    {
2061 2062
      if ((!acl_db->host.hostname && !host[0]) ||
	  (acl_db->host.hostname &&
Sergey Petrunya's avatar
Sergey Petrunya committed
2063
	   !strcmp(host, acl_db->host.hostname)))
unknown's avatar
unknown committed
2064
      {
2065 2066
	if ((!acl_db->db && !db[0]) ||
	    (acl_db->db && !strcmp(db,acl_db->db)))
Sergey Petrunya's avatar
Sergey Petrunya committed
2067

unknown's avatar
unknown committed
2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079
	{
	  if (privileges)
	    acl_db->access=privileges;
	  else
	    delete_dynamic_element(&acl_dbs,i);
	}
      }
    }
  }
}


2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093
/*
  Insert a user/db/host combination into the global acl_cache

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

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

unknown's avatar
unknown committed
2094
static void acl_insert_db(const char *user, const char *host, const char *db,
unknown's avatar
unknown committed
2095
			  ulong privileges)
unknown's avatar
unknown committed
2096 2097
{
  ACL_DB acl_db;
Marc Alff's avatar
Marc Alff committed
2098
  mysql_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
2099
  acl_db.user=strdup_root(&mem,user);
2100
  update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
unknown's avatar
unknown committed
2101 2102 2103
  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);
Konstantin Osipov's avatar
Konstantin Osipov committed
2104
  (void) push_dynamic(&acl_dbs,(uchar*) &acl_db);
2105 2106
  my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
	   sizeof(ACL_DB),(qsort_cmp) acl_compare);
unknown's avatar
unknown committed
2107 2108 2109
}


2110 2111 2112

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

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

2118
ulong acl_get(const char *host, const char *ip,
2119
              const char *user, const char *db, my_bool db_is_pattern)
unknown's avatar
unknown committed
2120
{
2121
  ulong host_access= ~(ulong)0, db_access= 0;
2122 2123
  uint i;
  size_t key_length;
unknown's avatar
unknown committed
2124
  char key[ACL_KEY_LENGTH],*tmp_db,*end;
unknown's avatar
unknown committed
2125
  acl_entry *entry;
unknown's avatar
unknown committed
2126
  DBUG_ENTER("acl_get");
unknown's avatar
unknown committed
2127

2128 2129 2130 2131 2132 2133
  tmp_db= strmov(strmov(key, ip ? ip : "") + 1, user) + 1;
  end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db);

  if (end >= key + sizeof(key)) // db name was truncated
    DBUG_RETURN(0);             // no privileges for an invalid db name

unknown's avatar
unknown committed
2134 2135
  if (lower_case_table_names)
  {
2136
    my_casedn_str(files_charset_info, tmp_db);
unknown's avatar
unknown committed
2137 2138
    db=tmp_db;
  }
2139
  key_length= (size_t) (end-key);
2140

Sergei Golubchik's avatar
Sergei Golubchik committed
2141
  mysql_mutex_lock(&acl_cache->lock);
2142 2143
  if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key,
                                                              key_length)))
unknown's avatar
unknown committed
2144 2145
  {
    db_access=entry->access;
Marc Alff's avatar
Marc Alff committed
2146
    mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
2147 2148
    DBUG_PRINT("exit", ("access: 0x%lx", db_access));
    DBUG_RETURN(db_access);
unknown's avatar
unknown committed
2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160
  }

  /*
    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))
      {
2161 2162 2163 2164 2165 2166 2167 2168 2169
        if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
        {
          db_access=acl_db->access;
          if (acl_db->host.hostname)
            goto exit;                          // Fully specified. Take it
          /* XXX is this an alright way to bypass the host table for roles? */
          if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user))
            goto exit;
          break; /* purecov: tested */
unknown's avatar
unknown committed
2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185
	}
      }
    }
  }
  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))
    {
2186
      if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
unknown's avatar
unknown committed
2187 2188 2189 2190 2191 2192 2193 2194
      {
	host_access=acl_host->access;		// Fully specified. Take it
	break;
      }
    }
  }
exit:
  /* Save entry in cache for quick retrieval */
2195 2196
  if (!db_is_pattern &&
      (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
unknown's avatar
unknown committed
2197 2198 2199
  {
    entry->access=(db_access & host_access);
    entry->length=key_length;
2200
    memcpy((uchar*) entry->key,key,key_length);
unknown's avatar
unknown committed
2201 2202
    acl_cache->add(entry);
  }
Marc Alff's avatar
Marc Alff committed
2203
  mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
2204 2205
  DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
  DBUG_RETURN(db_access & host_access);
unknown's avatar
unknown committed
2206 2207
}

2208 2209 2210 2211 2212 2213 2214
/*
  Check if there are any possible matching entries for this host

  NOTES
    All host names without wild cards are stored in a hash table,
    entries with wildcards are stored in a dynamic array
*/
unknown's avatar
unknown committed
2215 2216 2217 2218

static void init_check_host(void)
{
  DBUG_ENTER("init_check_host");
Konstantin Osipov's avatar
Konstantin Osipov committed
2219
  (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
2220
                               acl_users.elements, 1, MYF(0));
Konstantin Osipov's avatar
Konstantin Osipov committed
2221 2222
  (void) my_hash_init(&acl_check_hosts,system_charset_info,
                      acl_users.elements, 0, 0,
2223
                      (my_hash_get_key) check_get_key, 0, 0);
unknown's avatar
unknown committed
2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237
  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 *);
2238
	  if (!my_strcasecmp(system_charset_info,
2239
                             acl_user->host.hostname, acl->hostname))
unknown's avatar
unknown committed
2240 2241 2242
	    break;				// already stored
	}
	if (j == acl_wild_hosts.elements)	// If new
2243
	  (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
unknown's avatar
unknown committed
2244
      }
Konstantin Osipov's avatar
Konstantin Osipov committed
2245 2246 2247
      else if (!my_hash_search(&acl_check_hosts,(uchar*)
                               acl_user->host.hostname,
                               strlen(acl_user->host.hostname)))
unknown's avatar
unknown committed
2248
      {
2249
	if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
unknown's avatar
unknown committed
2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262
	{					// 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;
}


2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273
/*
  Rebuild lists used for checking of allowed hosts

  We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
  dropping or renaming user, since they contain pointers to elements of
  'acl_user' array, which are invalidated by drop operation, and use
  ACL_USER::host::hostname as a key, which is changed by rename.
*/
void rebuild_check_host(void)
{
  delete_dynamic(&acl_wild_hosts);
Konstantin Osipov's avatar
Konstantin Osipov committed
2274
  my_hash_free(&acl_check_hosts);
2275 2276 2277
  init_check_host();
}

2278
static my_bool acl_role_propagate_grants(ACL_ROLE *role,
2279 2280 2281
                                         void * not_used __attribute__((unused)))
{
  ulong access;
2282
  get_role_access(role, &access);
2283 2284 2285
  return 0;
}

2286
/*
2287 2288 2289 2290 2291
  Reset a role role_grants dynamic array.
  Also, the role's access bits are reset to the ones present in the table.

  The function can be used as a walk action for hash elements aswell.
*/
2292
my_bool acl_role_reset_grant(ACL_ROLE *role,
2293 2294 2295
                             void * not_used __attribute__((unused)))
{
  reset_dynamic(&role->role_grants);
2296
  reset_dynamic(&role->parent_grantee);
2297 2298
  /* Also reset the role access bits */
  role->access= role->initial_role_access;
2299
  role->flags&= ~ROLE_GRANTS_FINAL;
2300 2301
  return 0;
}
2302

2303 2304
/*
  Reset a users role_grants dynamic array.
2305

2306
  The function can be used as a walk action for hash elements aswell.
2307
*/
2308
my_bool acl_user_reset_grant(ACL_USER *user,
2309
                             void * not_used __attribute__((unused)))
2310 2311 2312 2313 2314
{
  reset_dynamic(&user->role_grants);
  return 0;
}

2315 2316 2317 2318 2319 2320 2321 2322
/*
  The function scans through all roles granted to the role passed as argument
  and places the permissions in the access variable.

  Return values:
    TRUE: Error or invalid parameteres
    FALSE: All ok;
*/
2323
my_bool get_role_access(ACL_ROLE *role, ulong *access)
2324 2325
{
  DBUG_ENTER("get_role_access");
2326
  DBUG_PRINT("enter",("role: '%s'", role->user.str));
2327 2328
  DBUG_ASSERT(role);
  DBUG_ASSERT(access);
2329 2330 2331 2332
  /*
     The search operation should always leave the ROLE_VISITED flag clean
     for all nodes involved in the search
  */
2333
  DBUG_ASSERT(!(role->flags & ROLE_VISITED));
2334

2335 2336 2337 2338 2339 2340 2341 2342
  /*
    There exists the possibility that the role's access bits are final
    and we can just get the access bits without doing the more expensive
    search operation
  */
  if (role->flags & ROLE_GRANTS_FINAL)
  {
    *access= role->access;
2343
    DBUG_PRINT("exit", ("Role access: %lu", *access));
2344 2345
    DBUG_RETURN(FALSE);
  }
2346

2347 2348 2349 2350 2351 2352 2353
  /*
     Stack used to simulate the recursive calls of DFS.
     It uses a DYNAMIC_ARRAY to reduce the number of
     malloc calls to a minimum
  */
  DYNAMIC_ARRAY stack;  
  NODE_STATE state;     /* variable used to insert elements in the stack */
2354 2355 2356 2357 2358

  state.neigh_idx= 0;
  state.node_data= role;
  role->flags|= ROLE_VISITED;

2359
  (void) my_init_dynamic_array(&stack, sizeof(NODE_STATE), 20, 50, MYF(0));
2360
  push_dynamic(&stack, (uchar*)&state);
2361 2362 2363

  while (stack.elements)
  {
2364 2365
    NODE_STATE *curr_state= dynamic_element(&stack, stack.elements - 1,
                                            NODE_STATE *);
2366 2367 2368

    DBUG_ASSERT(curr_state->node_data->flags & ROLE_VISITED);

2369
    ACL_ROLE *current= curr_state->node_data;
2370
    ACL_ROLE *neighbour= NULL;
2371
    DBUG_PRINT("info", ("Examining role %s", current->user.str));
2372 2373 2374 2375
    /*
      Iterate through the neighbours until a first valid jump-to
      neighbour is found
    */
2376 2377 2378 2379
    my_bool found= FALSE;
    uint i;
    for (i= curr_state->neigh_idx;
         i < current->role_grants.elements && found == FALSE; i++)
2380
    {
2381
      neighbour= *(dynamic_element(&current->role_grants, i, ACL_ROLE**));
2382
      DBUG_PRINT("info", ("Examining neighbour role %s", neighbour->user.str));
2383

2384 2385 2386 2387
      /* check if it forms a cycle */
      if (neighbour->flags & ROLE_VISITED)
      {
        /* TODO the edge needs to be ignored */
2388
        DBUG_PRINT("info", ("Found cycle"));
2389
        continue;
2390 2391
      }

2392 2393 2394 2395
      /*
         Check if it was already explored, in that case, just set the rights
         and move on
      */
2396 2397
      if (neighbour->flags & ROLE_GRANTS_FINAL)
      {
2398
        DBUG_PRINT("info", ("Neighbour access is final, merging"));
2399 2400 2401
        current->access|= neighbour->access;
        continue;
      }
2402

2403 2404
      /*
         Set the current state search index to the next index
2405 2406
         this needs to be done before inserting, so as to make sure that the
         pointer is valid
2407
      */
2408
      found= TRUE;
2409
      break;
2410 2411 2412 2413
    }

    if (found)
    {
2414 2415
      /*
         we're going to have to take a look at the same neighbour again
2416
         once it is done being explored, thus, set the neigh_idx to "i"
2417 2418
         which is the current neighbour that will be added on the stack
      */
2419 2420 2421 2422 2423 2424 2425 2426 2427
      curr_state->neigh_idx= i;

      /* some sanity checks */
      DBUG_ASSERT(!(neighbour->flags & ROLE_VISITED));
      DBUG_ASSERT(!(neighbour->flags & ROLE_GRANTS_FINAL));
      /* add the neighbour on the stack */
      neighbour->flags|= ROLE_VISITED;
      state.neigh_idx= 0;
      state.node_data= neighbour;
2428
      push_dynamic(&stack, (uchar*)&state);
2429 2430 2431
    }
    else
    {
2432
      /* Make sure we got a correct node */
2433 2434
      DBUG_ASSERT(!(curr_state->node_data->flags & ROLE_GRANTS_FINAL));
      DBUG_ASSERT(curr_state->node_data->flags & ROLE_VISITED);
2435 2436
      /* Finished with exploring the current node, pop it off the stack */
      curr_state= (NODE_STATE *)pop_dynamic(&stack);
2437 2438
      curr_state->node_data->flags&= ~ROLE_VISITED; /* clear the visited bit */
      curr_state->node_data->flags|= ROLE_GRANTS_FINAL;
2439
      /* Add the own role's rights once it's finished exploring */
2440
      curr_state->node_data->access|= curr_state->node_data->initial_role_access;
2441 2442 2443
      DBUG_PRINT("info",
                 ("Setting final access for node: %s %lu",
                  curr_state->node_data->user.str, curr_state->node_data->access));
2444 2445 2446 2447
    }
  }


2448
  /* Cleanup */
2449
  delete_dynamic(&stack);
2450
  /* Finally set the access */
2451
  *access= role->access;
2452 2453
  DBUG_PRINT("exit", ("Role access: %lu", *access));
  DBUG_RETURN(FALSE);
2454 2455
}

2456 2457 2458 2459
/*
   Add a the coresponding pointers present in the mapping to the entries in
   acl_users and acl_roles

2460 2461 2462 2463 2464 2465
   Return values:
     0: The entry is valid and was added.
    -1: The entry is invalid and was not added.
     1: The entry represents a mapping between two roles.
*/
int add_role_user_mapping(ROLE_GRANT_PAIR *mapping)
2466
{
2467 2468

  ACL_USER_BASE *user= find_user_no_anon((mapping->u_hname) ? mapping->u_hname: "",
2469 2470
                                    (mapping->u_uname) ? mapping->u_uname: "",
                                    TRUE);
2471
  ACL_ROLE *role= find_acl_role(mapping->r_uname ? mapping->r_uname: "");
2472

2473

2474 2475
  int result= 0;

2476
  if (user == NULL || role == NULL)
2477
  {
2478 2479 2480 2481 2482 2483 2484 2485 2486
    /* There still exists the possibility that the user is actually a role */
    if (user == NULL && role && (!mapping->u_hname || !mapping->u_hname[0])
        && /* in this case the grantee is a role */
        ((user= find_acl_role(mapping->u_uname ? mapping->u_uname: ""))))
    {
      result= 1;
    }
    else
    {
2487
      DBUG_PRINT("warning", ("Invalid add_role_user_mapping '%s'@'%s' %s %p %p",
2488
                             mapping->u_uname, mapping->u_hname,
2489
                             mapping->r_uname, user, role));
2490 2491 2492

      return -1;
    }
2493
  }
2494

2495
  push_dynamic(&user->role_grants, (uchar*) &role);
2496
  push_dynamic(&role->parent_grantee, (uchar*) &user);
2497

2498
  DBUG_PRINT("info", ("Found %s %s@%s having role granted %s\n",
2499
                        (result) ? "role" : "user",
2500 2501 2502
                        user->user.str,
                        (result) ? "" : ((ACL_USER *)user)->host.hostname,
                        role->user.str));
2503
  return result;
2504 2505 2506
}


2507 2508 2509 2510 2511 2512 2513
/*
  Rebuild the role grants every time the acl_users is modified

  The role grants in the ACL_USER class need to be rebuilt, as they contain
  pointers to elements of the acl_users array.
*/

2514 2515
void rebuild_role_grants(void)
{
2516
  DBUG_ENTER("rebuild_role_grants");
2517 2518 2519 2520
  /*
    Reset every user's and role's role_grants array
  */
  for (uint i=0; i < acl_users.elements; i++) {
2521
    ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *);
2522
    acl_user_reset_grant(user, NULL);
2523 2524
  }
  my_hash_iterate(&acl_roles,
2525
                  (my_hash_walk_action) acl_role_reset_grant, NULL);
2526

2527 2528 2529
  /*
    Rebuild the direct links between users and roles in ACL_USER::role_grants
  */
2530
  for (uint i=0; i < acl_roles_mappings.records; i++) {
2531 2532
    ROLE_GRANT_PAIR *mapping= (ROLE_GRANT_PAIR*)
                                my_hash_element(&acl_roles_mappings, i);
2533
    my_bool status = add_role_user_mapping(mapping);
2534 2535 2536 2537 2538 2539
    /*
       The invariant chosen is that acl_roles_mappings should _always_
       only contain valid entries, referencing correct user and role grants.
       If add_role_user_mapping detects an invalid entry, it will not add
       the mapping into the ACL_USER::role_grants array.
    */
2540
     DBUG_ASSERT(status >= 0);
2541
  }
2542

2543 2544 2545
  my_hash_iterate(&acl_roles,
                  (my_hash_walk_action) acl_role_propagate_grants, NULL);

2546
  DBUG_VOID_RETURN;
2547
}
unknown's avatar
unknown committed
2548 2549 2550 2551 2552 2553
/* 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;
Marc Alff's avatar
Marc Alff committed
2554
  mysql_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
2555

Konstantin Osipov's avatar
Konstantin Osipov committed
2556 2557
  if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
      (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
unknown's avatar
unknown committed
2558
  {
Marc Alff's avatar
Marc Alff committed
2559
    mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
2560 2561 2562 2563 2564 2565 2566
    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))
    {
Marc Alff's avatar
Marc Alff committed
2567
      mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
2568 2569 2570
      return 0;					// Host ok
    }
  }
Marc Alff's avatar
Marc Alff committed
2571
  mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
2572 2573 2574 2575
  return 1;					// Host is not allowed
}


unknown's avatar
unknown committed
2576 2577 2578 2579 2580 2581 2582 2583
/*
  Check if the user is allowed to change password

  SYNOPSIS:
    check_change_password()
    thd		THD
    host	hostname for the user
    user	user name
2584 2585 2586 2587
    new_password new password

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

unknown's avatar
unknown committed
2589
    RETURN VALUE
2590 2591
      0		OK
      1		ERROR  ; In this case the error is sent to the client.
unknown's avatar
unknown committed
2592 2593
*/

2594
int check_change_password(THD *thd, const char *host, const char *user,
2595
                           char *new_password, uint new_password_len)
unknown's avatar
unknown committed
2596
{
unknown's avatar
unknown committed
2597 2598
  if (!initialized)
  {
unknown's avatar
unknown committed
2599
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2600
    return(1);
unknown's avatar
unknown committed
2601
  }
unknown's avatar
unknown committed
2602
  if (!thd->slave_thread &&
2603 2604 2605
      (strcmp(thd->security_ctx->user, user) ||
       my_strcasecmp(system_charset_info, host,
                     thd->security_ctx->priv_host)))
unknown's avatar
unknown committed
2606
  {
Marc Alff's avatar
Marc Alff committed
2607
    if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
unknown's avatar
unknown committed
2608
      return(1);
unknown's avatar
unknown committed
2609
  }
2610
  if (!thd->slave_thread && !thd->security_ctx->user[0])
unknown's avatar
unknown committed
2611
  {
unknown's avatar
unknown committed
2612 2613
    my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
               MYF(0));
unknown's avatar
unknown committed
2614
    return(1);
unknown's avatar
unknown committed
2615
  }
2616
  size_t len= strlen(new_password);
unknown's avatar
unknown committed
2617
  if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
2618 2619
      len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
  {
unknown's avatar
unknown committed
2620
    my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
2621 2622
    return -1;
  }
unknown's avatar
unknown committed
2623 2624 2625 2626
  return(0);
}


2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639
/*
  Change a password for a user

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

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

unknown's avatar
unknown committed
2642 2643 2644
bool change_password(THD *thd, const char *host, const char *user,
		     char *new_password)
{
2645 2646
  TABLE_LIST tables;
  TABLE *table;
2647
  Rpl_filter *rpl_filter= thd->rpl_filter;
2648 2649 2650
  /* Buffer should be extended when password length is extended. */
  char buff[512];
  ulong query_length;
2651
  enum_binlog_format save_binlog_format;
2652
  uint new_password_len= (uint) strlen(new_password);
2653
  bool result= 1;
unknown's avatar
unknown committed
2654 2655 2656 2657 2658
  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

2659
  if (check_change_password(thd, host, user, new_password, new_password_len))
unknown's avatar
unknown committed
2660 2661
    DBUG_RETURN(1);

Konstantin Osipov's avatar
Konstantin Osipov committed
2662
  tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
2663 2664 2665 2666 2667 2668

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
2669
  if (thd->slave_thread && rpl_filter->is_on())
2670 2671 2672 2673 2674 2675 2676
  {
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.  It's ok to leave 'updating' set after tables_ok.
    */
    tables.updating= 1;
    /* Thanks to bzero, tables.next==0 */
2677
    if (!(thd->spcont || rpl_filter->tables_ok(0, &tables)))
2678 2679 2680
      DBUG_RETURN(0);
  }
#endif
2681
  if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
2682 2683
    DBUG_RETURN(1);

2684 2685 2686 2687
  /*
    This statement will be replicated as a statement, even when using
    row-based replication.  The flag will be reset at the end of the
    statement.
2688 2689
    This has to be handled here as it's called by set_var.cc, which is
    not automaticly handled by sql_parse.cc
2690
  */
2691
  save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
2692

Marc Alff's avatar
Marc Alff committed
2693
  mysql_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
2694
  ACL_USER *acl_user;
2695
  if (!(acl_user= find_user_no_anon(host, user, TRUE)))
unknown's avatar
unknown committed
2696
  {
Marc Alff's avatar
Marc Alff committed
2697
    mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
2698
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
2699
    goto end;
unknown's avatar
unknown committed
2700
  }
2701

2702
  /* update loaded acl entry: */
2703
  if (acl_user->plugin.str == native_password_plugin_name.str ||
2704 2705 2706 2707 2708
      acl_user->plugin.str == old_password_plugin_name.str)
  {
    acl_user->auth_string.str= strmake_root(&mem, new_password, new_password_len);
    acl_user->auth_string.length= new_password_len;
    set_user_salt(acl_user, new_password, new_password_len);
Sergei Golubchik's avatar
Sergei Golubchik committed
2709
    set_user_plugin(acl_user, new_password_len);
2710
  }
Sergei Golubchik's avatar
Sergei Golubchik committed
2711
  else
2712 2713
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                 ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
2714

2715
  if (update_user_table(thd, table,
unknown's avatar
unknown committed
2716
			acl_user->host.hostname ? acl_user->host.hostname : "",
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
2717
                        acl_user->user.str ? acl_user->user.str : "",
2718
			new_password, new_password_len))
unknown's avatar
unknown committed
2719
  {
Marc Alff's avatar
Marc Alff committed
2720
    mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
2721
    goto end;
unknown's avatar
unknown committed
2722
  }
unknown's avatar
unknown committed
2723

unknown's avatar
unknown committed
2724
  acl_cache->clear(1);				// Clear locked hostname cache
Marc Alff's avatar
Marc Alff committed
2725
  mysql_mutex_unlock(&acl_cache->lock);
2726 2727 2728
  result= 0;
  if (mysql_bin_log.is_open())
  {
2729
    query_length=
Sergei Golubchik's avatar
Sergei Golubchik committed
2730
      sprintf(buff,"SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
2731
              acl_user->user.str ? acl_user->user.str : "",
Sergei Golubchik's avatar
Sergei Golubchik committed
2732 2733
              acl_user->host.hostname ? acl_user->host.hostname : "",
              new_password);
2734
    thd->clear_error();
2735 2736
    result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
                              FALSE, FALSE, FALSE, 0);
2737 2738
  }
end:
2739
  close_mysql_tables(thd);
2740
  thd->restore_stmt_binlog_format(save_binlog_format);
2741

2742
  DBUG_RETURN(result);
unknown's avatar
unknown committed
2743 2744 2745
}


2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761
/*
  Find user in ACL

  SYNOPSIS
    is_acl_user()
    host                 host name
    user                 user name

  RETURN
   FALSE  user not fond
   TRUE   there are such user
*/

bool is_acl_user(const char *host, const char *user)
{
  bool res;
2762 2763 2764 2765 2766

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

Marc Alff's avatar
Marc Alff committed
2767
  mysql_mutex_lock(&acl_cache->lock);
2768
  res= find_user_no_anon(host, user, TRUE) != NULL;
Marc Alff's avatar
Marc Alff committed
2769
  mysql_mutex_unlock(&acl_cache->lock);
2770 2771 2772 2773
  return res;
}


2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793
static ACL_USER *
find_user(const char *host, const char *user, const char *ip)
{
  ACL_USER *result= NULL;
  mysql_mutex_assert_owner(&acl_cache->lock);
  for (uint i=0; i < acl_users.elements; i++)
  {
    ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
    if ((!acl_user_tmp->user.str ||
         !strcmp(user, acl_user_tmp->user.str)) &&
         compare_hostname(&acl_user_tmp->host, host, ip))
    {
      result= acl_user_tmp;
      break;
    }
  }
  return result;
}


unknown's avatar
unknown committed
2794
/*
2795
  Find first entry that matches the current user
unknown's avatar
unknown committed
2796 2797
*/
static ACL_USER *
2798
find_user_no_anon(const char *host, const char *user, my_bool exact)
unknown's avatar
unknown committed
2799
{
2800
  DBUG_ENTER("find_user_no_anon");
2801
  DBUG_PRINT("enter",("host: '%s'  user: '%s'",host,user));
2802

Marc Alff's avatar
Marc Alff committed
2803
  mysql_mutex_assert_owner(&acl_cache->lock);
2804

2805
  for (uint i=0 ; i < acl_users.elements ; i++)
unknown's avatar
unknown committed
2806
  {
2807
    ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
unknown's avatar
unknown committed
2808
    DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
2809
                       user, acl_user->user.str ? acl_user->user.str : "",
2810 2811 2812
                       host,
                       acl_user->host.hostname ? acl_user->host.hostname :
                       ""));
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
2813 2814
    if ((!acl_user->user.str && !user[0]) ||
        (acl_user->user.str && !strcmp(user,acl_user->user.str)))
unknown's avatar
unknown committed
2815
    {
2816
      if (exact ? !my_strcasecmp(system_charset_info, host,
2817 2818
                                 acl_user->host.hostname ?
				 acl_user->host.hostname : "") :
2819
          compare_hostname(&acl_user->host,host,host))
unknown's avatar
unknown committed
2820 2821 2822
      {
	DBUG_RETURN(acl_user);
      }
unknown's avatar
unknown committed
2823 2824
    }
  }
unknown's avatar
unknown committed
2825
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2826 2827
}

2828 2829 2830
/*
  Find first entry that matches the current user
*/
2831
static ACL_ROLE *
2832
find_acl_role(const char *user)
2833
{
2834 2835
  DBUG_ENTER("find_acl_role");
  DBUG_PRINT("enter",("user: '%s'", user));
2836
  DBUG_PRINT("info", ("Hash elements: %ld", acl_roles.records));
2837 2838 2839

  mysql_mutex_assert_owner(&acl_cache->lock);

2840
  DBUG_RETURN((ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)user,
2841
                                         user ? strlen(user) : 0));
2842 2843
}

unknown's avatar
unknown committed
2844

2845 2846


2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857
/*
  Comparing of hostnames

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

  A net mask of 0.0.0.0 is not allowed.
*/
unknown's avatar
unknown committed
2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879

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)
{
2880
  host->hostname=(char*) hostname;             // This will not be modified!
2881
  if (!hostname ||
unknown's avatar
unknown committed
2882 2883 2884
      (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
       !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
  {
2885
    host->ip= host->ip_mask=0;			// Not a masked ip
unknown's avatar
unknown committed
2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898
  }
}


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 ||
2899
	  (hostname && !wild_case_compare(system_charset_info,
2900 2901
                                          hostname, host->hostname)) ||
	  (ip && !wild_compare(ip, host->hostname, 0)));
unknown's avatar
unknown committed
2902 2903
}

2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929
/**
  Check if the given host name needs to be resolved or not.
  Host name has to be resolved if it actually contains *name*.

  For example:
    192.168.1.1               --> FALSE
    192.168.1.0/255.255.255.0 --> FALSE
    %                         --> FALSE
    192.168.1.%               --> FALSE
    AB%                       --> FALSE

    AAAAFFFF                  --> TRUE (Hostname)
    AAAA:FFFF:1234:5678       --> FALSE
    ::1                       --> FALSE

  This function does not check if the given string is a valid host name or
  not. It assumes that the argument is a valid host name.

  @param hostname   the string to check.

  @return a flag telling if the argument needs to be resolved or not.
  @retval TRUE the argument is a host name and needs to be resolved.
  @retval FALSE the argument is either an IP address, or a patter and
          should not be resolved.
*/

unknown's avatar
SCRUM  
unknown committed
2930 2931 2932
bool hostname_requires_resolving(const char *hostname)
{
  if (!hostname)
unknown's avatar
unknown committed
2933
    return FALSE;
2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945

  /* Check if hostname is the localhost. */

  size_t hostname_len= strlen(hostname);
  size_t localhost_len= strlen(my_localhost);

  if (hostname == my_localhost ||
      (hostname_len == localhost_len &&
       !my_strnncoll(system_charset_info,
                     (const uchar *) hostname,  hostname_len,
                     (const uchar *) my_localhost, strlen(my_localhost))))
  {
unknown's avatar
unknown committed
2946
    return FALSE;
2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957
  }

  /*
    If the string contains any of {':', '%', '_', '/'}, it is definitely
    not a host name:
      - ':' means that the string is an IPv6 address;
      - '%' or '_' means that the string is a pattern;
      - '/' means that the string is an IPv4 network address;
  */

  for (const char *p= hostname; *p; ++p)
unknown's avatar
SCRUM  
unknown committed
2958
  {
2959 2960 2961 2962 2963 2964 2965
    switch (*p) {
      case ':':
      case '%':
      case '_':
      case '/':
        return FALSE;
    }
unknown's avatar
SCRUM  
unknown committed
2966
  }
2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980

  /*
    Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
    (12.34.56.78). The assumption is that if the string contains only
    digits and dots, it is an IPv4 address. Otherwise -- a host name.
  */

  for (const char *p= hostname; *p; ++p)
  {
    if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
      return TRUE; /* a "letter" has been found. */
  }

  return FALSE; /* all characters are either dots or digits. */
unknown's avatar
SCRUM  
unknown committed
2981
}
unknown's avatar
unknown committed
2982

2983

2984
/*
2985 2986 2987 2988 2989 2990 2991 2992 2993 2994
  Update record for user in mysql.user privilege table with new password.

  SYNOPSIS
    update_user_table()
      thd               Thread handle
      table             Pointer to TABLE object for open mysql.user table
      host/user         Hostname/username pair identifying user for which
                        new password should be set
      new_password      New password
      new_password_len  Length of new password
2995
*/
unknown's avatar
unknown committed
2996

2997 2998
static bool update_user_table(THD *thd, TABLE *table,
                              const char *host, const char *user,
2999
			      const char *new_password, uint new_password_len)
unknown's avatar
unknown committed
3000
{
3001
  char user_key[MAX_KEY_LENGTH];
3002
  int error;
unknown's avatar
unknown committed
3003 3004 3005
  DBUG_ENTER("update_user_table");
  DBUG_PRINT("enter",("user: %s  host: %s",user,host));

3006
  table->use_all_columns();
3007 3008
  table->field[0]->store(host,(uint) strlen(host), system_charset_info);
  table->field[1]->store(user,(uint) strlen(user), system_charset_info);
3009
  key_copy((uchar *) user_key, table->record[0], table->key_info,
3010
           table->key_info->key_length);
unknown's avatar
unknown committed
3011

3012 3013 3014
  if (table->file->ha_index_read_idx_map(table->record[0], 0,
                                         (uchar *) user_key, HA_WHOLE_KEY,
                                         HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
3015
  {
unknown's avatar
unknown committed
3016 3017
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
               MYF(0));	/* purecov: deadcode */
unknown's avatar
unknown committed
3018 3019
    DBUG_RETURN(1);				/* purecov: deadcode */
  }
unknown's avatar
unknown committed
3020
  store_record(table,record[1]);
3021
  table->field[2]->store(new_password, new_password_len, system_charset_info);
3022 3023
  if ((error=table->file->ha_update_row(table->record[1],table->record[0])) &&
      error != HA_ERR_RECORD_IS_THE_SAME)
unknown's avatar
unknown committed
3024 3025
  {
    table->file->print_error(error,MYF(0));	/* purecov: deadcode */
3026
    DBUG_RETURN(1);
unknown's avatar
unknown committed
3027
  }
3028
  DBUG_RETURN(0);
unknown's avatar
unknown committed
3029 3030
}

unknown's avatar
unknown committed
3031

3032 3033 3034 3035 3036 3037
/*
  Return 1 if we are allowed to create new users
  the logic here is: INSERT_ACL is sufficient.
  It's also a requirement in opt_safe_user_create,
  otherwise CREATE_USER_ACL is enough.
*/
unknown's avatar
unknown committed
3038 3039 3040

static bool test_if_create_new_users(THD *thd)
{
3041
  Security_context *sctx= thd->security_ctx;
3042
  bool create_new_users= test(sctx->master_access & INSERT_ACL) ||
3043
                         (!opt_safe_user_create &&
3044
                          test(sctx->master_access & CREATE_USER_ACL));
3045
  if (!create_new_users)
unknown's avatar
unknown committed
3046 3047
  {
    TABLE_LIST tl;
unknown's avatar
unknown committed
3048
    ulong db_access;
3049 3050
    tl.init_one_table(C_STRING_WITH_LEN("mysql"),
                      C_STRING_WITH_LEN("user"), "user", TL_WRITE);
3051
    create_new_users= 1;
unknown's avatar
unknown committed
3052

3053 3054
    db_access=acl_get(sctx->host, sctx->ip,
		      sctx->priv_user, tl.db, 0);
3055 3056
    if (sctx->priv_role[0])
      db_access|= acl_get("", "", sctx->priv_role, tl.db, 0);
unknown's avatar
unknown committed
3057 3058
    if (!(db_access & INSERT_ACL))
    {
3059
      if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
unknown's avatar
unknown committed
3060 3061 3062 3063 3064 3065 3066
	create_new_users=0;
    }
  }
  return create_new_users;
}


unknown's avatar
unknown committed
3067
/****************************************************************************
3068
  Handle GRANT commands
unknown's avatar
unknown committed
3069 3070
****************************************************************************/

Sergei Golubchik's avatar
Sergei Golubchik committed
3071
static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
unknown's avatar
unknown committed
3072
			      ulong rights, bool revoke_grant,
unknown's avatar
unknown committed
3073
			      bool can_create_user, bool no_auto_create)
unknown's avatar
unknown committed
3074 3075
{
  int error = -1;
unknown's avatar
unknown committed
3076
  bool old_row_exists=0;
unknown's avatar
unknown committed
3077
  char what= (revoke_grant) ? 'N' : 'Y';
3078
  uchar user_key[MAX_KEY_LENGTH];
3079
  LEX *lex= thd->lex;
unknown's avatar
unknown committed
3080
  DBUG_ENTER("replace_user_table");
unknown's avatar
unknown committed
3081

Marc Alff's avatar
Marc Alff committed
3082
  mysql_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
3083 3084

  if (combo.password.str && combo.password.str[0])
3085
  {
3086 3087
    if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
        combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
3088
    {
3089
      my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
unknown's avatar
unknown committed
3090
      DBUG_RETURN(-1);
3091 3092
    }
  }
Sergei Golubchik's avatar
Sergei Golubchik committed
3093 3094
  else
    combo.password= empty_lex_str;
unknown's avatar
unknown committed
3095

3096 3097 3098 3099 3100
  table->use_all_columns();
  table->field[0]->store(combo.host.str,combo.host.length,
                         system_charset_info);
  table->field[1]->store(combo.user.str,combo.user.length,
                         system_charset_info);
3101 3102 3103
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

3104 3105 3106
  if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
                                         HA_WHOLE_KEY,
                                         HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
3107
  {
3108 3109
    /* what == 'N' means revoke */
    if (what == 'N')
unknown's avatar
unknown committed
3110
    {
3111 3112 3113 3114
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
      goto end;
    }
    /*
3115 3116
      There are four options which affect the process of creation of
      a new user (mysqld option --safe-create-user, 'insert' privilege
3117 3118 3119 3120 3121 3122 3123
      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
3124 3125

      see also test_if_create_new_users()
3126
    */
Sergei Golubchik's avatar
Sergei Golubchik committed
3127
    else if (!combo.password.length && !combo.plugin.length && no_auto_create)
unknown's avatar
unknown committed
3128
    {
3129
      my_error(ER_PASSWORD_NO_MATCH, MYF(0));
unknown's avatar
unknown committed
3130 3131 3132
      goto end;
    }
    else if (!can_create_user)
3133
    {
3134
      my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
unknown's avatar
unknown committed
3135 3136
      goto end;
    }
3137 3138 3139 3140 3141 3142 3143 3144 3145
    else if (combo.plugin.str[0])
    {
      if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN))
      {
        my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str);
        goto end;
      }
    }

unknown's avatar
unknown committed
3146
    old_row_exists = 0;
3147
    restore_record(table,s->default_values);
3148
    table->field[0]->store(combo.host.str,combo.host.length,
3149
                           system_charset_info);
3150
    table->field[1]->store(combo.user.str,combo.user.length,
3151
                           system_charset_info);
unknown's avatar
unknown committed
3152 3153 3154
  }
  else
  {
unknown's avatar
unknown committed
3155
    old_row_exists = 1;
unknown's avatar
unknown committed
3156
    store_record(table,record[1]);			// Save copy for update
unknown's avatar
unknown committed
3157 3158
  }

unknown's avatar
unknown committed
3159 3160 3161 3162
  /* Update table columns with new privileges */

  Field **tmp_field;
  ulong priv;
3163
  uint next_field;
unknown's avatar
unknown committed
3164
  for (tmp_field= table->field+3, priv = SELECT_ACL;
3165
       *tmp_field && (*tmp_field)->real_type() == MYSQL_TYPE_ENUM &&
unknown's avatar
unknown committed
3166 3167
	 ((Field_enum*) (*tmp_field))->typelib->count == 2 ;
       tmp_field++, priv <<= 1)
unknown's avatar
unknown committed
3168
  {
unknown's avatar
unknown committed
3169
    if (priv & rights)				 // set requested privileges
unknown's avatar
unknown committed
3170
      (*tmp_field)->store(&what, 1, &my_charset_latin1);
unknown's avatar
unknown committed
3171
  }
3172
  rights= get_access(table, 3, &next_field);
3173
  DBUG_PRINT("info",("table fields: %d",table->s->fields));
Sergei Golubchik's avatar
Sergei Golubchik committed
3174 3175
  if (combo.password.str[0])
    table->field[2]->store(combo.password.str, combo.password.length, system_charset_info);
3176
  if (table->s->fields >= 31)		/* From 4.0.0 we have more fields */
3177
  {
unknown's avatar
unknown committed
3178
    /* We write down SSL related ACL stuff */
3179
    switch (lex->ssl_type) {
3180
    case SSL_TYPE_ANY:
3181 3182
      table->field[next_field]->store(STRING_WITH_LEN("ANY"),
                                      &my_charset_latin1);
3183 3184 3185
      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);
3186 3187
      break;
    case SSL_TYPE_X509:
3188 3189
      table->field[next_field]->store(STRING_WITH_LEN("X509"),
                                      &my_charset_latin1);
3190 3191 3192
      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);
3193 3194
      break;
    case SSL_TYPE_SPECIFIED:
3195 3196
      table->field[next_field]->store(STRING_WITH_LEN("SPECIFIED"),
                                      &my_charset_latin1);
3197 3198 3199
      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);
3200
      if (lex->ssl_cipher)
unknown's avatar
unknown committed
3201 3202
        table->field[next_field+1]->store(lex->ssl_cipher,
                                strlen(lex->ssl_cipher), system_charset_info);
3203
      if (lex->x509_issuer)
unknown's avatar
unknown committed
3204 3205
        table->field[next_field+2]->store(lex->x509_issuer,
                                strlen(lex->x509_issuer), system_charset_info);
3206
      if (lex->x509_subject)
unknown's avatar
unknown committed
3207 3208
        table->field[next_field+3]->store(lex->x509_subject,
                                strlen(lex->x509_subject), system_charset_info);
3209
      break;
unknown's avatar
unknown committed
3210
    case SSL_TYPE_NOT_SPECIFIED:
unknown's avatar
unknown committed
3211 3212
      break;
    case SSL_TYPE_NONE:
3213 3214 3215 3216
      table->field[next_field]->store("", 0, &my_charset_latin1);
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
unknown's avatar
unknown committed
3217
      break;
3218
    }
unknown's avatar
unknown committed
3219
    next_field+=4;
unknown's avatar
unknown committed
3220

3221
    USER_RESOURCES mqh= lex->mqh;
3222
    if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
3223
      table->field[next_field]->store((longlong) mqh.questions, TRUE);
3224
    if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
3225
      table->field[next_field+1]->store((longlong) mqh.updates, TRUE);
3226
    if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
3227
      table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE);
3228
    if (table->s->fields >= 36 &&
3229
        (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
3230
      table->field[next_field+3]->store((longlong) mqh.user_conn, FALSE);
3231
    mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
3232

3233
    next_field+= 4;
Sergei Golubchik's avatar
Sergei Golubchik committed
3234
    if (table->s->fields >= 41)
3235
    {
Sergei Golubchik's avatar
Sergei Golubchik committed
3236 3237
      table->field[next_field]->set_notnull();
      table->field[next_field + 1]->set_notnull();
Sergei Golubchik's avatar
Sergei Golubchik committed
3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252
      if (combo.plugin.str[0])
      {
        DBUG_ASSERT(combo.password.str[0] == 0);
        table->field[2]->reset();
        table->field[next_field]->store(combo.plugin.str, combo.plugin.length,
                                        system_charset_info);
        table->field[next_field + 1]->store(combo.auth.str, combo.auth.length,
                                            system_charset_info);
      }
      if (combo.password.str[0])
      {
        DBUG_ASSERT(combo.plugin.str[0] == 0);
        table->field[next_field]->reset();
        table->field[next_field + 1]->reset();
      }
3253
    }
3254
  }
3255

unknown's avatar
unknown committed
3256
  if (old_row_exists)
unknown's avatar
unknown committed
3257 3258 3259 3260 3261
  {
    /*
      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!
    */
3262 3263 3264 3265 3266
    if (cmp_record(table,record[1]))
    {
      if ((error=
           table->file->ha_update_row(table->record[1],table->record[0])) &&
          error != HA_ERR_RECORD_IS_THE_SAME)
3267 3268 3269 3270
      {                                         // This should never happen
        table->file->print_error(error,MYF(0)); /* purecov: deadcode */
        error= -1;                              /* purecov: deadcode */
        goto end;                               /* purecov: deadcode */
3271 3272 3273
      }
      else
        error= 0;
unknown's avatar
unknown committed
3274 3275
    }
  }
3276
  else if ((error=table->file->ha_write_row(table->record[0]))) // insert
unknown's avatar
unknown committed
3277
  {						// This should never happen
3278
    if (table->file->is_fatal_error(error, HA_CHECK_DUP))
unknown's avatar
unknown committed
3279 3280 3281 3282 3283 3284 3285 3286
    {
      table->file->print_error(error,MYF(0));	/* purecov: deadcode */
      error= -1;				/* purecov: deadcode */
      goto end;					/* purecov: deadcode */
    }
  }
  error=0;					// Privileges granted / revoked

3287
end:
unknown's avatar
unknown committed
3288 3289 3290
  if (!error)
  {
    acl_cache->clear(1);			// Clear privilege cache
unknown's avatar
unknown committed
3291
    if (old_row_exists)
3292
      acl_update_user(combo.user.str, combo.host.str,
Sergei Golubchik's avatar
Sergei Golubchik committed
3293
                      combo.password.str, combo.password.length,
3294 3295 3296 3297 3298
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
3299 3300 3301
		      rights,
		      &combo.plugin,
		      &combo.auth);
unknown's avatar
unknown committed
3302
    else
Sergei Golubchik's avatar
Sergei Golubchik committed
3303 3304
      acl_insert_user(combo.user.str, combo.host.str,
                      combo.password.str, combo.password.length,
3305 3306 3307 3308 3309
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
3310 3311 3312
		      rights,
		      &combo.plugin,
		      &combo.auth);
unknown's avatar
unknown committed
3313 3314 3315 3316 3317 3318
  }
  DBUG_RETURN(error);
}


/*
unknown's avatar
unknown committed
3319
  change grants in the mysql.db table
unknown's avatar
unknown committed
3320 3321 3322 3323
*/

static int replace_db_table(TABLE *table, const char *db,
			    const LEX_USER &combo,
unknown's avatar
unknown committed
3324
			    ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
3325
{
unknown's avatar
unknown committed
3326 3327
  uint i;
  ulong priv,store_rights;
unknown's avatar
unknown committed
3328
  bool old_row_exists=0;
unknown's avatar
unknown committed
3329
  int error;
unknown's avatar
unknown committed
3330
  char what= (revoke_grant) ? 'N' : 'Y';
3331
  uchar user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
3332 3333
  DBUG_ENTER("replace_db_table");

3334 3335
  if (!initialized)
  {
unknown's avatar
unknown committed
3336
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3337 3338 3339
    DBUG_RETURN(-1);
  }

3340
  /* Check if there is such a user in user table in memory? */
3341
  if (!find_user_no_anon(combo.host.str,combo.user.str, FALSE))
unknown's avatar
unknown committed
3342
  {
3343 3344 3345 3346 3347 3348
    /* The user could be a role, check if the user is registered as a role */
    if (!combo.host.length && !find_acl_role(combo.user.str))
    {
      my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
      DBUG_RETURN(-1);
    }
unknown's avatar
unknown committed
3349 3350
  }

3351 3352 3353
  table->use_all_columns();
  table->field[0]->store(combo.host.str,combo.host.length,
                         system_charset_info);
3354
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
3355 3356
  table->field[2]->store(combo.user.str,combo.user.length,
                         system_charset_info);
3357 3358 3359
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

3360 3361 3362
  if (table->file->ha_index_read_idx_map(table->record[0],0, user_key,
                                         HA_WHOLE_KEY,
                                         HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
3363 3364 3365
  {
    if (what == 'N')
    { // no row, no revoke
unknown's avatar
unknown committed
3366
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
unknown's avatar
unknown committed
3367 3368
      goto abort;
    }
unknown's avatar
unknown committed
3369
    old_row_exists = 0;
3370
    restore_record(table, s->default_values);
3371 3372
    table->field[0]->store(combo.host.str,combo.host.length,
                           system_charset_info);
3373
    table->field[1]->store(db,(uint) strlen(db), system_charset_info);
3374 3375
    table->field[2]->store(combo.user.str,combo.user.length,
                           system_charset_info);
unknown's avatar
unknown committed
3376 3377 3378
  }
  else
  {
unknown's avatar
unknown committed
3379
    old_row_exists = 1;
unknown's avatar
unknown committed
3380
    store_record(table,record[1]);
unknown's avatar
unknown committed
3381 3382 3383
  }

  store_rights=get_rights_for_db(rights);
3384
  for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
unknown's avatar
unknown committed
3385
  {
unknown's avatar
unknown committed
3386
    if (priv & store_rights)			// do it if priv is chosen
unknown's avatar
unknown committed
3387
      table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
unknown's avatar
unknown committed
3388 3389 3390 3391
  }
  rights=get_access(table,3);
  rights=fix_rights_for_db(rights);

unknown's avatar
unknown committed
3392
  if (old_row_exists)
unknown's avatar
unknown committed
3393
  {
3394
    /* update old existing row */
unknown's avatar
unknown committed
3395 3396
    if (rights)
    {
3397
      if ((error= table->file->ha_update_row(table->record[1],
3398 3399
                                             table->record[0])) &&
          error != HA_ERR_RECORD_IS_THE_SAME)
unknown's avatar
unknown committed
3400 3401 3402 3403
	goto table_error;			/* purecov: deadcode */
    }
    else	/* must have been a revoke of all privileges */
    {
3404
      if ((error= table->file->ha_delete_row(table->record[1])))
unknown's avatar
unknown committed
3405 3406 3407
	goto table_error;			/* purecov: deadcode */
    }
  }
3408
  else if (rights && (error= table->file->ha_write_row(table->record[0])))
unknown's avatar
unknown committed
3409
  {
3410
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
unknown's avatar
unknown committed
3411 3412 3413 3414
      goto table_error; /* purecov: deadcode */
  }

  acl_cache->clear(1);				// Clear privilege cache
unknown's avatar
unknown committed
3415
  if (old_row_exists)
unknown's avatar
unknown committed
3416 3417
    acl_update_db(combo.user.str,combo.host.str,db,rights);
  else
3418
  if (rights)
unknown's avatar
unknown committed
3419 3420 3421 3422
    acl_insert_db(combo.user.str,combo.host.str,db,rights);
  DBUG_RETURN(0);

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

3426
abort:
unknown's avatar
unknown committed
3427 3428 3429 3430
  DBUG_RETURN(-1);
}


3431
static void
3432 3433 3434 3435
acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
{
  mysql_mutex_assert_owner(&acl_cache->lock);

3436 3437
  DBUG_ENTER("acl_update_proxy_user");
  for (uint i= 0; i < acl_proxy_users.elements; i++)
3438
  {
3439
    ACL_PROXY_USER *acl_user=
3440 3441 3442 3443 3444 3445
      dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);

    if (acl_user->pk_equals(new_value))
    {
      if (is_revoke)
      {
3446
        DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
3447 3448 3449 3450
        delete_dynamic_element(&acl_proxy_users, i);
      }
      else
      {
3451
        DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
3452 3453 3454 3455 3456 3457 3458 3459 3460
        acl_user->set_data(new_value);
      }
      break;
    }
  }
  DBUG_VOID_RETURN;
}


3461
static void
3462 3463
acl_insert_proxy_user(ACL_PROXY_USER *new_value)
{
3464
  DBUG_ENTER("acl_insert_proxy_user");
3465 3466
  mysql_mutex_assert_owner(&acl_cache->lock);
  (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
3467
  my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
3468
           acl_proxy_users.elements,
3469
           sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
3470 3471 3472 3473
  DBUG_VOID_RETURN;
}


3474
static int
3475
replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
3476
                         const LEX_USER *proxied_user, bool with_grant_arg,
3477 3478
                         bool revoke_grant)
{
3479
  bool old_row_exists= 0;
3480 3481 3482
  int error;
  uchar user_key[MAX_KEY_LENGTH];
  ACL_PROXY_USER new_grant;
3483
  char grantor[USER_HOST_BUFF_SIZE];
3484

3485
  DBUG_ENTER("replace_proxies_priv_table");
3486 3487 3488 3489 3490 3491 3492 3493

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

  /* Check if there is such a user in user table in memory? */
3494
  if (!find_user_no_anon(user->host.str,user->user.str, FALSE))
3495 3496 3497 3498 3499 3500
  {
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
    DBUG_RETURN(-1);
  }

  table->use_all_columns();
3501
  ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
3502 3503 3504 3505 3506
                            &proxied_user->host, &proxied_user->user);

  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

3507 3508
  get_grantor(thd, grantor);

3509 3510 3511 3512 3513 3514 3515
  if ((error= table->file->ha_index_init(0, 1)))
  {
    table->file->print_error(error, MYF(0));
    DBUG_PRINT("info", ("ha_index_init error"));
    DBUG_RETURN(-1);
  }

Sergei Golubchik's avatar
Sergei Golubchik committed
3516 3517 3518
  if (table->file->ha_index_read_map(table->record[0], user_key,
                                     HA_WHOLE_KEY,
                                     HA_READ_KEY_EXACT))
3519 3520 3521 3522 3523 3524 3525
  {
    DBUG_PRINT ("info", ("Row not found"));
    if (revoke_grant)
    { // no row, no revoke
      my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
      goto abort;
    }
3526 3527 3528 3529 3530
    old_row_exists= 0;
    restore_record(table, s->default_values);
    ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
                                      &proxied_user->host,
                                      &proxied_user->user,
3531 3532
                                      with_grant_arg,
                                      grantor);
3533 3534 3535
  }
  else
  {
3536 3537 3538
    DBUG_PRINT("info", ("Row found"));
    old_row_exists= 1;
    store_record(table, record[1]);
3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558
  }

  if (old_row_exists)
  {
    /* update old existing row */
    if (!revoke_grant)
    {
      if ((error= table->file->ha_update_row(table->record[1],
                                             table->record[0])) &&
          error != HA_ERR_RECORD_IS_THE_SAME)
	goto table_error;			/* purecov: inspected */
    }
    else
    {
      if ((error= table->file->ha_delete_row(table->record[1])))
	goto table_error;			/* purecov: inspected */
    }
  }
  else if ((error= table->file->ha_write_row(table->record[0])))
  {
3559
    DBUG_PRINT("info", ("error inserting the row"));
3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
      goto table_error; /* purecov: inspected */
  }

  acl_cache->clear(1);				// Clear privilege cache
  if (old_row_exists)
  {
    new_grant.init(user->host.str, user->user.str,
                   proxied_user->host.str, proxied_user->user.str,
                   with_grant_arg);
    acl_update_proxy_user(&new_grant, revoke_grant);
  }
  else
  {
3574 3575 3576
    new_grant.init(&mem, user->host.str, user->user.str,
                   proxied_user->host.str, proxied_user->user.str,
                   with_grant_arg);
3577 3578 3579 3580 3581 3582 3583 3584
    acl_insert_proxy_user(&new_grant);
  }

  table->file->ha_index_end();
  DBUG_RETURN(0);

  /* This could only happen if the grant tables got corrupted */
table_error:
3585 3586
  DBUG_PRINT("info", ("table error"));
  table->file->print_error(error, MYF(0));	/* purecov: inspected */
3587 3588

abort:
3589
  DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
3590 3591 3592 3593 3594
  table->file->ha_index_end();
  DBUG_RETURN(-1);
}


unknown's avatar
unknown committed
3595 3596 3597 3598
class GRANT_COLUMN :public Sql_alloc
{
public:
  char *column;
unknown's avatar
unknown committed
3599 3600 3601
  ulong rights;
  uint key_length;
  GRANT_COLUMN(String &c,  ulong y) :rights (y)
unknown's avatar
unknown committed
3602
  {
3603
    column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length());
unknown's avatar
unknown committed
3604 3605 3606
  }
};

unknown's avatar
unknown committed
3607

3608
static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
unknown's avatar
unknown committed
3609 3610 3611
			    my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
3612
  return (uchar*) buff->column;
unknown's avatar
unknown committed
3613 3614
}

unknown's avatar
unknown committed
3615

3616
class GRANT_NAME :public Sql_alloc
unknown's avatar
unknown committed
3617 3618
{
public:
3619 3620
  acl_host_and_ip host;
  char *db, *user, *tname, *hash_key;
3621
  ulong privs;
3622
  ulong sort;
3623
  size_t key_length;
3624
  GRANT_NAME(const char *h, const char *d,const char *u,
3625 3626
             const char *t, ulong p, bool is_routine);
  GRANT_NAME (TABLE *form, bool is_routine);
3627 3628
  virtual ~GRANT_NAME() {};
  virtual bool ok() { return privs != 0; }
3629
  void set_user_details(const char *h, const char *d,
3630 3631
                        const char *u, const char *t,
                        bool is_routine);
3632 3633 3634 3635 3636 3637 3638
};


class GRANT_TABLE :public GRANT_NAME
{
public:
  ulong cols;
unknown's avatar
unknown committed
3639
  HASH hash_columns;
unknown's avatar
unknown committed
3640 3641 3642 3643

  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);
3644
  ~GRANT_TABLE();
3645 3646
  bool ok() { return privs != 0 || cols != 0; }
};
unknown's avatar
unknown committed
3647

3648

3649
void GRANT_NAME::set_user_details(const char *h, const char *d,
3650 3651
                                  const char *u, const char *t,
                                  bool is_routine)
3652 3653
{
  /* Host given by user */
3654
  update_hostname(&host, strdup_root(&memex, h));
3655 3656 3657 3658 3659 3660
  if (db != d)
  {
    db= strdup_root(&memex, d);
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, db);
  }
3661
  user = strdup_root(&memex,u);
3662
  sort=  get_sort(3,host.hostname,db,user);
3663
  if (tname != t)
unknown's avatar
unknown committed
3664
  {
3665
    tname= strdup_root(&memex, t);
3666
    if (lower_case_table_names || is_routine)
3667
      my_casedn_str(files_charset_info, tname);
3668
  }
3669 3670
  key_length= strlen(d) + strlen(u)+ strlen(t)+3;
  hash_key=   (char*) alloc_root(&memex,key_length);
3671
  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
3672 3673
}

3674
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
3675
                       const char *t, ulong p, bool is_routine)
3676 3677
  :db(0), tname(0), privs(p)
{
3678
  set_user_details(h, d, u, t, is_routine);
3679
}
3680 3681 3682

GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
                	 const char *t, ulong p, ulong c)
3683
  :GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
3684
{
Konstantin Osipov's avatar
Konstantin Osipov committed
3685 3686
  (void) my_hash_init2(&hash_columns,4,system_charset_info,
                   0,0,0, (my_hash_get_key) get_key_column,0,0);
3687
}
unknown's avatar
unknown committed
3688

3689

3690
GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
3691
{
3692
  update_hostname(&host, get_field(&memex, form->field[0]));
unknown's avatar
unknown committed
3693 3694
  db=    get_field(&memex,form->field[1]);
  user=  get_field(&memex,form->field[2]);
3695 3696
  if (!user)
    user= (char*) "";
3697
  sort=  get_sort(3, host.hostname, db, user);
unknown's avatar
unknown committed
3698
  tname= get_field(&memex,form->field[3]);
3699 3700 3701
  if (!db || !tname)
  {
    /* Wrong table row; Ignore it */
3702
    privs= 0;
3703 3704 3705 3706
    return;					/* purecov: inspected */
  }
  if (lower_case_table_names)
  {
3707
    my_casedn_str(files_charset_info, db);
3708 3709 3710
  }
  if (lower_case_table_names || is_routine)
  {
3711
    my_casedn_str(files_charset_info, tname);
3712
  }
3713 3714
  key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
  hash_key=   (char*) alloc_root(&memex, key_length);
3715 3716 3717
  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
  privs = (ulong) form->field[6]->val_int();
  privs = fix_rights_for_table(privs);
3718 3719 3720 3721
}


GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
3722
  :GRANT_NAME(form, FALSE)
3723
{
3724
  uchar key[MAX_KEY_LENGTH];
3725 3726 3727 3728

  if (!db || !tname)
  {
    /* Wrong table row; Ignore it */
Konstantin Osipov's avatar
Konstantin Osipov committed
3729
    my_hash_clear(&hash_columns);               /* allow for destruction */
3730 3731 3732 3733
    cols= 0;
    return;
  }
  cols= (ulong) form->field[7]->val_int();
3734 3735
  cols =  fix_rights_for_column(cols);

Konstantin Osipov's avatar
Konstantin Osipov committed
3736 3737
  (void) my_hash_init2(&hash_columns,4,system_charset_info,
                   0,0,0, (my_hash_get_key) get_key_column,0,0);
3738 3739
  if (cols)
  {
3740 3741
    uint key_prefix_len;
    KEY_PART_INFO *key_part= col_privs->key_info->key_part;
3742
    col_privs->field[0]->store(host.hostname,
3743 3744
                               host.hostname ? (uint) strlen(host.hostname) :
                               0,
3745 3746 3747 3748
                               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);
3749 3750 3751 3752 3753 3754

    key_prefix_len= (key_part[0].store_length +
                     key_part[1].store_length +
                     key_part[2].store_length +
                     key_part[3].store_length);
    key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
unknown's avatar
unknown committed
3755
    col_privs->field[4]->store("",0, &my_charset_latin1);
3756

3757 3758 3759 3760 3761 3762
    if (col_privs->file->ha_index_init(0, 1))
    {
      cols= 0;
      return;
    }

3763 3764 3765
    if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key,
                                           (key_part_map)15,
                                           HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
3766
    {
3767
      cols = 0; /* purecov: deadcode */
3768
      col_privs->file->ha_index_end();
3769
      return;
unknown's avatar
unknown committed
3770
    }
3771
    do
unknown's avatar
unknown committed
3772
    {
3773 3774 3775
      String *res,column_name;
      GRANT_COLUMN *mem_check;
      /* As column name is a string, we don't have to supply a buffer */
unknown's avatar
unknown committed
3776
      res=col_privs->field[4]->val_str(&column_name);
3777 3778 3779
      ulong priv= (ulong) col_privs->field[6]->val_int();
      if (!(mem_check = new GRANT_COLUMN(*res,
                                         fix_rights_for_column(priv))))
unknown's avatar
unknown committed
3780
      {
3781 3782 3783
        /* Don't use this entry */
        privs = cols = 0;			/* purecov: deadcode */
        return;				/* purecov: deadcode */
unknown's avatar
unknown committed
3784
      }
3785 3786 3787 3788 3789 3790
      if (my_hash_insert(&hash_columns, (uchar *) mem_check))
      {
        /* Invalidate this entry */
        privs= cols= 0;
        return;
      }
3791
    } while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
3792
             !key_cmp_if_same(col_privs,key,0,key_prefix_len));
3793
    col_privs->file->ha_index_end();
unknown's avatar
unknown committed
3794
  }
3795
}
unknown's avatar
unknown committed
3796

unknown's avatar
unknown committed
3797

3798 3799
GRANT_TABLE::~GRANT_TABLE()
{
Konstantin Osipov's avatar
Konstantin Osipov committed
3800
  my_hash_free(&hash_columns);
3801 3802 3803
}


3804
static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
unknown's avatar
unknown committed
3805 3806 3807
			     my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
3808
  return (uchar*) buff->hash_key;
unknown's avatar
unknown committed
3809 3810
}

unknown's avatar
unknown committed
3811

unknown's avatar
unknown committed
3812 3813
void free_grant_table(GRANT_TABLE *grant_table)
{
Konstantin Osipov's avatar
Konstantin Osipov committed
3814
  my_hash_free(&grant_table->hash_columns);
unknown's avatar
unknown committed
3815 3816
}

unknown's avatar
unknown committed
3817

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

3820
static GRANT_NAME *name_hash_search(HASH *name_hash,
unknown's avatar
unknown committed
3821 3822 3823
                                    const char *host,const char* ip,
                                    const char *db,
                                    const char *user, const char *tname,
Georgi Kodinov's avatar
merge  
Georgi Kodinov committed
3824
                                    bool exact, bool name_tolower)
unknown's avatar
unknown committed
3825
{
3826 3827
  char helping[SAFE_NAME_LEN*2+USERNAME_LENGTH+3];
  char *hend = helping + sizeof(helping);
unknown's avatar
unknown committed
3828
  uint len;
3829
  GRANT_NAME *grant_name,*found=0;
3830
  HASH_SEARCH_STATE state;
unknown's avatar
unknown committed
3831

3832 3833 3834 3835 3836 3837 3838 3839 3840
  char *db_ptr= strmov(helping, user) + 1;
  char *tname_ptr= strnmov(db_ptr, db, hend - db_ptr) + 1;
  if (tname_ptr > hend)
    return 0; // invalid name = not found
  char *end= strnmov(tname_ptr, tname, hend - tname_ptr) + 1;
  if (end > hend)
    return 0; // invalid name = not found

  len  = (uint) (end - helping);
3841
  if (name_tolower)
3842
    my_casedn_str(files_charset_info, tname_ptr);
Konstantin Osipov's avatar
Konstantin Osipov committed
3843 3844
  for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
                                               len, &state);
3845
       grant_name ;
Konstantin Osipov's avatar
Konstantin Osipov committed
3846 3847
       grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
                                              len, &state))
unknown's avatar
unknown committed
3848 3849 3850
  {
    if (exact)
    {
3851 3852
      if (!grant_name->host.hostname ||
          (host &&
3853
	   !my_strcasecmp(system_charset_info, host,
unknown's avatar
unknown committed
3854 3855
                          grant_name->host.hostname)) ||
	  (ip && !strcmp(ip, grant_name->host.hostname)))
3856
	return grant_name;
unknown's avatar
unknown committed
3857 3858 3859
    }
    else
    {
3860
      if (compare_hostname(&grant_name->host, host, ip) &&
3861 3862
          (!found || found->sort < grant_name->sort))
	found=grant_name;					// Host ok
unknown's avatar
unknown committed
3863 3864 3865 3866 3867 3868
    }
  }
  return found;
}


3869
inline GRANT_NAME *
3870 3871
routine_hash_search(const char *host, const char *ip, const char *db,
                 const char *user, const char *tname, bool proc, bool exact)
3872
{
3873 3874
  return (GRANT_TABLE*)
    name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
3875
		     host, ip, db, user, tname, exact, TRUE);
3876 3877 3878 3879 3880 3881 3882 3883
}


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,
3884
					 user, tname, exact, FALSE);
3885 3886
}

unknown's avatar
unknown committed
3887

unknown's avatar
unknown committed
3888
inline GRANT_COLUMN *
unknown's avatar
unknown committed
3889
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
unknown's avatar
unknown committed
3890
{
Konstantin Osipov's avatar
Konstantin Osipov committed
3891 3892
  return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
                                        (uchar*) cname, length);
unknown's avatar
unknown committed
3893 3894 3895 3896 3897 3898 3899
}


static int replace_column_table(GRANT_TABLE *g_t,
				TABLE *table, const LEX_USER &combo,
				List <LEX_COLUMN> &columns,
				const char *db, const char *table_name,
unknown's avatar
unknown committed
3900
				ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
3901
{
3902
  int result=0;
3903
  uchar key[MAX_KEY_LENGTH];
3904 3905
  uint key_prefix_length;
  KEY_PART_INFO *key_part= table->key_info->key_part;
unknown's avatar
unknown committed
3906 3907
  DBUG_ENTER("replace_column_table");

3908
  table->use_all_columns();
unknown's avatar
unknown committed
3909 3910 3911 3912 3913 3914 3915 3916
  table->field[0]->store(combo.host.str,combo.host.length,
                         system_charset_info);
  table->field[1]->store(db,(uint) strlen(db),
                         system_charset_info);
  table->field[2]->store(combo.user.str,combo.user.length,
                         system_charset_info);
  table->field[3]->store(table_name,(uint) strlen(table_name),
                         system_charset_info);
unknown's avatar
unknown committed
3917

3918
  /* Get length of 4 first key parts */
3919 3920 3921
  key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
                      key_part[2].store_length + key_part[3].store_length);
  key_copy(key, table->record[0], table->key_info, key_prefix_length);
unknown's avatar
unknown committed
3922

3923
  rights&= COL_ACLS;				// Only ACL for columns
unknown's avatar
unknown committed
3924 3925 3926 3927

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

  List_iterator <LEX_COLUMN> iter(columns);
unknown's avatar
unknown committed
3928
  class LEX_COLUMN *column;
3929 3930 3931 3932 3933 3934 3935
  int error= table->file->ha_index_init(0, 1);
  if (error)
  {
    table->file->print_error(error, MYF(0));
    DBUG_RETURN(-1);
  }

unknown's avatar
unknown committed
3936
  while ((column= iter++))
unknown's avatar
unknown committed
3937
  {
unknown's avatar
unknown committed
3938
    ulong privileges= column->rights;
unknown's avatar
unknown committed
3939
    bool old_row_exists=0;
3940
    uchar user_key[MAX_KEY_LENGTH];
3941 3942 3943

    key_restore(table->record[0],key,table->key_info,
                key_prefix_length);
unknown's avatar
unknown committed
3944
    table->field[4]->store(column->column.ptr(), column->column.length(),
3945
                           system_charset_info);
3946 3947 3948
    /* Get key for the first 4 columns */
    key_copy(user_key, table->record[0], table->key_info,
             table->key_info->key_length);
unknown's avatar
unknown committed
3949

3950 3951
    if (table->file->ha_index_read_map(table->record[0], user_key,
                                       HA_WHOLE_KEY, HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
3952 3953 3954
    {
      if (revoke_grant)
      {
unknown's avatar
unknown committed
3955
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
3956
                 combo.user.str, combo.host.str,
unknown's avatar
unknown committed
3957 3958 3959
                 table_name);                   /* purecov: inspected */
	result= -1;                             /* purecov: inspected */
	continue;                               /* purecov: inspected */
unknown's avatar
unknown committed
3960
      }
unknown's avatar
unknown committed
3961
      old_row_exists = 0;
3962
      restore_record(table, s->default_values);		// Get empty record
3963 3964
      key_restore(table->record[0],key,table->key_info,
                  key_prefix_length);
unknown's avatar
unknown committed
3965
      table->field[4]->store(column->column.ptr(),column->column.length(),
3966
                             system_charset_info);
unknown's avatar
unknown committed
3967 3968 3969
    }
    else
    {
unknown's avatar
unknown committed
3970
      ulong tmp= (ulong) table->field[6]->val_int();
unknown's avatar
unknown committed
3971 3972 3973 3974 3975 3976
      tmp=fix_rights_for_column(tmp);

      if (revoke_grant)
	privileges = tmp & ~(privileges | rights);
      else
	privileges |= tmp;
unknown's avatar
unknown committed
3977
      old_row_exists = 1;
unknown's avatar
unknown committed
3978
      store_record(table,record[1]);			// copy original row
unknown's avatar
unknown committed
3979 3980
    }

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

unknown's avatar
unknown committed
3983
    if (old_row_exists)
unknown's avatar
unknown committed
3984
    {
unknown's avatar
unknown committed
3985
      GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
3986
      if (privileges)
3987
	error=table->file->ha_update_row(table->record[1],table->record[0]);
unknown's avatar
unknown committed
3988
      else
3989
	error=table->file->ha_delete_row(table->record[1]);
3990
      if (error && error != HA_ERR_RECORD_IS_THE_SAME)
unknown's avatar
unknown committed
3991 3992 3993 3994 3995
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
3996 3997
      else
        error= 0;
unknown's avatar
unknown committed
3998 3999
      grant_column= column_hash_search(g_t, column->column.ptr(),
                                       column->column.length());
unknown's avatar
unknown committed
4000
      if (grant_column)				// Should always be true
unknown's avatar
unknown committed
4001
	grant_column->rights= privileges;	// Update hash
unknown's avatar
unknown committed
4002 4003 4004
    }
    else					// new grant
    {
unknown's avatar
unknown committed
4005
      GRANT_COLUMN *grant_column;
4006
      if ((error=table->file->ha_write_row(table->record[0])))
unknown's avatar
unknown committed
4007 4008 4009 4010 4011
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
unknown's avatar
unknown committed
4012
      grant_column= new GRANT_COLUMN(column->column,privileges);
4013 4014 4015 4016 4017
      if (my_hash_insert(&g_t->hash_columns,(uchar*) grant_column))
      {
        result= -1;
        goto end;
      }
unknown's avatar
unknown committed
4018 4019 4020 4021 4022 4023 4024 4025 4026 4027
    }
  }

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

  if (revoke_grant)
  {
4028
    uchar user_key[MAX_KEY_LENGTH];
4029
    key_copy(user_key, table->record[0], table->key_info,
unknown's avatar
unknown committed
4030 4031
             key_prefix_length);

4032 4033 4034
    if (table->file->ha_index_read_map(table->record[0], user_key,
                                       (key_part_map)15,
                                       HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
4035 4036
      goto end;

4037
    /* Scan through all rows with the same host,db,user and table */
unknown's avatar
unknown committed
4038 4039
    do
    {
unknown's avatar
unknown committed
4040
      ulong privileges = (ulong) table->field[6]->val_int();
unknown's avatar
unknown committed
4041
      privileges=fix_rights_for_column(privileges);
unknown's avatar
unknown committed
4042
      store_record(table,record[1]);
unknown's avatar
unknown committed
4043 4044 4045 4046 4047

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

	privileges&= ~rights;
	table->field[6]->store((longlong)
4053
			       get_rights_for_column(privileges), TRUE);
4054
	table->field[4]->val_str(&column_name);
unknown's avatar
unknown committed
4055 4056 4057 4058 4059 4060
	grant_column = column_hash_search(g_t,
					  column_name.ptr(),
					  column_name.length());
	if (privileges)
	{
	  int tmp_error;
4061
	  if ((tmp_error=table->file->ha_update_row(table->record[1],
4062 4063
						    table->record[0])) &&
              tmp_error != HA_ERR_RECORD_IS_THE_SAME)
unknown's avatar
unknown committed
4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074
	  {					/* 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;
4075
	  if ((tmp_error = table->file->ha_delete_row(table->record[1])))
unknown's avatar
unknown committed
4076 4077 4078 4079 4080 4081
	  {					/* purecov: deadcode */
	    table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
	    result= -1;				/* purecov: deadcode */
	    goto end;				/* purecov: deadcode */
	  }
	  if (grant_column)
Konstantin Osipov's avatar
Konstantin Osipov committed
4082
	    my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
unknown's avatar
unknown committed
4083 4084
	}
      }
4085
    } while (!table->file->ha_index_next(table->record[0]) &&
4086
	     !key_cmp_if_same(table, key, 0, key_prefix_length));
unknown's avatar
unknown committed
4087 4088
  }

4089
end:
unknown's avatar
unknown committed
4090
  table->file->ha_index_end();
unknown's avatar
unknown committed
4091 4092 4093
  DBUG_RETURN(result);
}

4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107
static inline void get_grantor(THD *thd, char *grantor)
{
  const char *user= thd->security_ctx->user;
  const char *host= thd->security_ctx->host_or_ip;

#if defined(HAVE_REPLICATION)
  if (thd->slave_thread && thd->has_invoker())
  {
    user= thd->get_invoker_user().str;
    host= thd->get_invoker_host().str;
  }
#endif
  strxmov(grantor, user, "@", host, NullS);
}
unknown's avatar
unknown committed
4108 4109 4110 4111

static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
			       TABLE *table, const LEX_USER &combo,
			       const char *db, const char *table_name,
unknown's avatar
unknown committed
4112 4113
			       ulong rights, ulong col_rights,
			       bool revoke_grant)
unknown's avatar
unknown committed
4114
{
4115
  char grantor[USER_HOST_BUFF_SIZE];
unknown's avatar
unknown committed
4116
  int old_row_exists = 1;
unknown's avatar
unknown committed
4117
  int error=0;
unknown's avatar
unknown committed
4118
  ulong store_table_rights, store_col_rights;
4119
  uchar user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
4120 4121
  DBUG_ENTER("replace_table_table");

4122
  get_grantor(thd, grantor);
unknown's avatar
unknown committed
4123 4124 4125 4126
  /*
    The following should always succeed as new users are created before
    this function is called!
  */
4127
  if (!find_user_no_anon(combo.host.str,combo.user.str, FALSE))
unknown's avatar
unknown committed
4128
  {
4129 4130 4131 4132 4133 4134
    if (!combo.host.length && !find_acl_role(combo.user.str))
    {
      my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
                 MYF(0)); /* purecov: deadcode */
      DBUG_RETURN(-1);                            /* purecov: deadcode */
    }
unknown's avatar
unknown committed
4135 4136
  }

4137
  table->use_all_columns();
4138
  restore_record(table, s->default_values);     // Get empty record
4139 4140
  table->field[0]->store(combo.host.str,combo.host.length,
                         system_charset_info);
4141
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
4142 4143 4144 4145
  table->field[2]->store(combo.user.str,combo.user.length,
                         system_charset_info);
  table->field[3]->store(table_name,(uint) strlen(table_name),
                         system_charset_info);
unknown's avatar
unknown committed
4146
  store_record(table,record[1]);			// store at pos 1
4147 4148
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);
unknown's avatar
unknown committed
4149

4150 4151 4152
  if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
                                         HA_WHOLE_KEY,
                                         HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
4153 4154 4155 4156 4157 4158 4159 4160
  {
    /*
      The following should never happen as we first check the in memory
      grant tables for the user.  There is however always a small change that
      the user has modified the grant tables directly.
    */
    if (revoke_grant)
    { // no row, no revoke
unknown's avatar
unknown committed
4161 4162
      my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
               combo.user.str, combo.host.str,
4163
               table_name);		        /* purecov: deadcode */
unknown's avatar
unknown committed
4164 4165
      DBUG_RETURN(-1);				/* purecov: deadcode */
    }
unknown's avatar
unknown committed
4166
    old_row_exists = 0;
unknown's avatar
unknown committed
4167
    restore_record(table,record[1]);			// Get saved record
unknown's avatar
unknown committed
4168 4169
  }

unknown's avatar
unknown committed
4170 4171
  store_table_rights= get_rights_for_table(rights);
  store_col_rights=   get_rights_for_column(col_rights);
unknown's avatar
unknown committed
4172
  if (old_row_exists)
unknown's avatar
unknown committed
4173
  {
unknown's avatar
unknown committed
4174
    ulong j,k;
unknown's avatar
unknown committed
4175
    store_record(table,record[1]);
unknown's avatar
unknown committed
4176 4177
    j = (ulong) table->field[6]->val_int();
    k = (ulong) table->field[7]->val_int();
unknown's avatar
unknown committed
4178 4179 4180

    if (revoke_grant)
    {
4181
      /* column rights are already fixed in mysql_table_grant */
unknown's avatar
unknown committed
4182 4183 4184 4185
      store_table_rights=j & ~store_table_rights;
    }
    else
    {
unknown's avatar
unknown committed
4186 4187
      store_table_rights|= j;
      store_col_rights|=   k;
unknown's avatar
unknown committed
4188 4189 4190
    }
  }

4191
  table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
4192 4193
  table->field[6]->store((longlong) store_table_rights, TRUE);
  table->field[7]->store((longlong) store_col_rights, TRUE);
unknown's avatar
unknown committed
4194
  rights=fix_rights_for_table(store_table_rights);
unknown's avatar
unknown committed
4195
  col_rights=fix_rights_for_column(store_col_rights);
unknown's avatar
unknown committed
4196

unknown's avatar
unknown committed
4197
  if (old_row_exists)
unknown's avatar
unknown committed
4198 4199 4200
  {
    if (store_table_rights || store_col_rights)
    {
4201 4202 4203
      if ((error=table->file->ha_update_row(table->record[1],
                                            table->record[0])) &&
          error != HA_ERR_RECORD_IS_THE_SAME)
unknown's avatar
unknown committed
4204 4205
	goto table_error;			/* purecov: deadcode */
    }
4206
    else if ((error = table->file->ha_delete_row(table->record[1])))
unknown's avatar
unknown committed
4207 4208 4209 4210
      goto table_error;				/* purecov: deadcode */
  }
  else
  {
4211
    error=table->file->ha_write_row(table->record[0]);
4212
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
unknown's avatar
unknown committed
4213 4214 4215
      goto table_error;				/* purecov: deadcode */
  }

unknown's avatar
unknown committed
4216
  if (rights | col_rights)
unknown's avatar
unknown committed
4217
  {
unknown's avatar
unknown committed
4218
    grant_table->privs= rights;
4219
    grant_table->cols=	col_rights;
unknown's avatar
unknown committed
4220 4221 4222
  }
  else
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
4223
    my_hash_delete(&column_priv_hash,(uchar*) grant_table);
unknown's avatar
unknown committed
4224 4225 4226
  }
  DBUG_RETURN(0);

4227 4228
  /* This should never happen */
table_error:
unknown's avatar
unknown committed
4229 4230 4231 4232 4233
  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
  DBUG_RETURN(-1); /* purecov: deadcode */
}


4234 4235 4236 4237
/**
  @retval       0  success
  @retval      -1  error
*/
4238
static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
4239
			      TABLE *table, const LEX_USER &combo,
4240 4241
			      const char *db, const char *routine_name,
			      bool is_proc, ulong rights, bool revoke_grant)
4242
{
4243
  char grantor[USER_HOST_BUFF_SIZE];
4244 4245 4246
  int old_row_exists= 1;
  int error=0;
  ulong store_proc_rights;
4247
  DBUG_ENTER("replace_routine_table");
4248 4249 4250 4251 4252 4253 4254

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

4255
  get_grantor(thd, grantor);
4256
  /*
4257 4258 4259 4260
    New users are created before this function is called.

    There may be some cases where a routine's definer is removed but the
    routine remains.
4261 4262
  */

4263
  table->use_all_columns();
4264
  restore_record(table, s->default_values);		// Get empty record
4265 4266 4267
  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);
4268 4269
  table->field[3]->store(routine_name,(uint) strlen(routine_name),
                         &my_charset_latin1);
unknown's avatar
unknown committed
4270
  table->field[4]->store((longlong)(is_proc ?
4271 4272
                                    TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION),
                         TRUE);
4273 4274
  store_record(table,record[1]);			// store at pos 1

4275 4276 4277 4278
  if (table->file->ha_index_read_idx_map(table->record[0], 0,
                                         (uchar*) table->field[0]->ptr,
                                         HA_WHOLE_KEY,
                                         HA_READ_KEY_EXACT))
4279 4280 4281 4282 4283 4284 4285 4286 4287
  {
    /*
      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),
4288
               combo.user.str, combo.host.str, routine_name);
4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312
      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;
    }
  }

4313
  table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
4314
  table->field[6]->store((longlong) store_proc_rights, TRUE);
4315 4316 4317 4318 4319 4320
  rights=fix_rights_for_procedure(store_proc_rights);

  if (old_row_exists)
  {
    if (store_proc_rights)
    {
4321 4322 4323
      if ((error=table->file->ha_update_row(table->record[1],
                                            table->record[0])) &&
          error != HA_ERR_RECORD_IS_THE_SAME)
4324 4325
	goto table_error;
    }
4326
    else if ((error= table->file->ha_delete_row(table->record[1])))
4327 4328 4329 4330
      goto table_error;
  }
  else
  {
4331
    error=table->file->ha_write_row(table->record[0]);
4332
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
4333 4334 4335 4336 4337 4338 4339 4340 4341
      goto table_error;
  }

  if (rights)
  {
    grant_name->privs= rights;
  }
  else
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
4342 4343
    my_hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*)
                   grant_name);
4344 4345 4346 4347 4348 4349 4350 4351 4352 4353
  }
  DBUG_RETURN(0);

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


4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366
/*
  Store table level and column level grants in the privilege tables

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

  RETURN
unknown's avatar
unknown committed
4367 4368
    FALSE ok
    TRUE  error
4369 4370
*/

4371
int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
unknown's avatar
unknown committed
4372 4373 4374
		      List <LEX_USER> &user_list,
		      List <LEX_COLUMN> &columns, ulong rights,
		      bool revoke_grant)
unknown's avatar
unknown committed
4375
{
4376
  ulong column_priv= 0;
unknown's avatar
unknown committed
4377
  List_iterator <LEX_USER> str_list (user_list);
4378
  LEX_USER *Str, *tmp_Str;
unknown's avatar
unknown committed
4379
  TABLE_LIST tables[3];
unknown's avatar
unknown committed
4380
  bool create_new_users=0;
4381
  char *db_name, *table_name;
4382
  Rpl_filter *rpl_filter= thd->rpl_filter;
unknown's avatar
unknown committed
4383 4384 4385 4386
  DBUG_ENTER("mysql_table_grant");

  if (!initialized)
  {
unknown's avatar
unknown committed
4387 4388
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: inspected */
unknown's avatar
unknown committed
4389
    DBUG_RETURN(TRUE);				/* purecov: inspected */
unknown's avatar
unknown committed
4390 4391 4392
  }
  if (rights & ~TABLE_ACLS)
  {
unknown's avatar
unknown committed
4393 4394
    my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
               MYF(0));
unknown's avatar
unknown committed
4395
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
4396 4397
  }

4398
  if (!revoke_grant)
unknown's avatar
unknown committed
4399
  {
unknown's avatar
unknown committed
4400
    if (columns.elements)
unknown's avatar
unknown committed
4401
    {
4402 4403
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
unknown's avatar
unknown committed
4404

Sergei Golubchik's avatar
Sergei Golubchik committed
4405
      if (open_normal_and_derived_tables(thd, table_list, 0, DT_PREPARE))
unknown's avatar
unknown committed
4406
        DBUG_RETURN(TRUE);
4407 4408

      while ((column = column_iter++))
unknown's avatar
unknown committed
4409
      {
unknown's avatar
unknown committed
4410
        uint unused_field_idx= NO_CACHED_FIELD_INDEX;
unknown's avatar
unknown committed
4411 4412
        TABLE_LIST *dummy;
        Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
4413
                                         column->column.length(),
unknown's avatar
unknown committed
4414
                                         column->column.ptr(), NULL, NULL,
4415
                                         NULL, TRUE, FALSE,
unknown's avatar
unknown committed
4416
                                         &unused_field_idx, FALSE, &dummy);
unknown's avatar
unknown committed
4417
        if (f == (Field*)0)
4418
        {
unknown's avatar
unknown committed
4419 4420
          my_error(ER_BAD_FIELD_ERROR, MYF(0),
                   column->column.c_ptr(), table_list->alias);
unknown's avatar
unknown committed
4421
          DBUG_RETURN(TRUE);
4422
        }
unknown's avatar
unknown committed
4423 4424
        if (f == (Field *)-1)
          DBUG_RETURN(TRUE);
4425
        column_priv|= column->rights;
unknown's avatar
unknown committed
4426
      }
4427
      close_mysql_tables(thd);
unknown's avatar
unknown committed
4428
    }
4429
    else
unknown's avatar
unknown committed
4430
    {
4431 4432
      if (!(rights & CREATE_ACL))
      {
4433 4434
        char buf[FN_REFLEN + 1];
        build_table_filename(buf, sizeof(buf) - 1, table_list->db,
4435
                             table_list->table_name, reg_ext, 0);
4436 4437
        fn_format(buf, buf, "", "", MY_UNPACK_FILENAME  | MY_RESOLVE_SYMLINKS |
                                    MY_RETURN_REAL_PATH | MY_APPEND_EXT);
4438 4439
        if (access(buf,F_OK))
        {
unknown's avatar
unknown committed
4440
          my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
unknown's avatar
unknown committed
4441
          DBUG_RETURN(TRUE);
4442 4443 4444 4445 4446 4447 4448 4449
        }
      }
      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),
4450 4451
                 command, thd->security_ctx->priv_user,
                 thd->security_ctx->host_or_ip, table_list->alias);
4452 4453
        DBUG_RETURN(-1);
      }
unknown's avatar
unknown committed
4454 4455 4456 4457 4458
    }
  }

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

4459 4460 4461 4462 4463 4464 4465 4466
  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("user"), "user", TL_WRITE);
  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("tables_priv"),
                           "tables_priv", TL_WRITE);
  tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("columns_priv"),
                           "columns_priv", TL_WRITE);
unknown's avatar
VIEW  
unknown committed
4467
  tables[0].next_local= tables[0].next_global= tables+1;
unknown's avatar
unknown committed
4468
  /* Don't open column table if we don't need it ! */
4469 4470
  if (column_priv || (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
    tables[1].next_local= tables[1].next_global= tables+2;
unknown's avatar
unknown committed
4471

4472 4473 4474 4475 4476
#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
4477
  if (thd->slave_thread && rpl_filter->is_on())
4478
  {
unknown's avatar
unknown committed
4479 4480 4481
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
4482
    */
4483
    tables[0].updating= tables[1].updating= tables[2].updating= 1;
unknown's avatar
unknown committed
4484
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
unknown's avatar
unknown committed
4485
      DBUG_RETURN(FALSE);
4486
  }
4487 4488
#endif

4489
  /*
4490 4491 4492 4493 4494
    The lock api is depending on the thd->lex variable which needs to be
    re-initialized.
  */
  Query_tables_list backup;
  thd->lex->reset_n_backup_query_tables_list(&backup);
4495 4496 4497 4498 4499 4500
  /*
    Restore Query_tables_list::sql_command value, which was reset
    above, as the code writing query to the binary log assumes that
    this value corresponds to the statement being executed.
  */
  thd->lex->sql_command= backup.sql_command;
4501
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
unknown's avatar
unknown committed
4502
  {						// Should never happen
4503
    thd->lex->restore_backup_query_tables_list(&backup);
unknown's avatar
unknown committed
4504
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
unknown's avatar
unknown committed
4505 4506
  }

unknown's avatar
unknown committed
4507 4508
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
unknown's avatar
unknown committed
4509
  bool result= FALSE;
Marc Alff's avatar
Marc Alff committed
4510 4511
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
4512 4513
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;
4514
  grant_version++;
unknown's avatar
unknown committed
4515

4516
  while ((tmp_Str = str_list++))
unknown's avatar
unknown committed
4517
  {
4518
    int error;
unknown's avatar
unknown committed
4519
    GRANT_TABLE *grant_table;
4520 4521 4522 4523
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
4524
    }
unknown's avatar
unknown committed
4525
    /* Create user if needed */
unknown's avatar
unknown committed
4526
    error=replace_user_table(thd, tables[0].table, *Str,
unknown's avatar
unknown committed
4527
			     0, revoke_grant, create_new_users,
unknown's avatar
unknown committed
4528 4529
                             test(thd->variables.sql_mode &
                                  MODE_NO_AUTO_CREATE_USER));
4530
    if (error)
unknown's avatar
unknown committed
4531
    {
unknown's avatar
unknown committed
4532
      result= TRUE;				// Remember error
unknown's avatar
unknown committed
4533 4534 4535
      continue;					// Add next user
    }

4536 4537
    db_name= table_list->get_db_name();
    table_name= table_list->get_table_name();
unknown's avatar
VIEW  
unknown committed
4538

unknown's avatar
unknown committed
4539
    /* Find/create cached table grant */
unknown's avatar
VIEW  
unknown committed
4540
    grant_table= table_hash_search(Str->host.str, NullS, db_name,
4541
				   Str->user.str, table_name, 1);
unknown's avatar
unknown committed
4542 4543 4544 4545
    if (!grant_table)
    {
      if (revoke_grant)
      {
unknown's avatar
unknown committed
4546
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
4547
                 Str->user.str, Str->host.str, table_list->table_name);
unknown's avatar
unknown committed
4548
	result= TRUE;
unknown's avatar
unknown committed
4549 4550
	continue;
      }
unknown's avatar
VIEW  
unknown committed
4551
      grant_table = new GRANT_TABLE (Str->host.str, db_name,
4552
				     Str->user.str, table_name,
unknown's avatar
unknown committed
4553 4554
				     rights,
				     column_priv);
4555
      if (!grant_table ||
Kristofer Pettersson's avatar
merge  
Kristofer Pettersson committed
4556
        my_hash_insert(&column_priv_hash,(uchar*) grant_table))
unknown's avatar
unknown committed
4557
      {
unknown's avatar
unknown committed
4558
	result= TRUE;				/* purecov: deadcode */
unknown's avatar
unknown committed
4559 4560 4561 4562 4563 4564 4565
	continue;				/* purecov: deadcode */
      }
    }

    /* If revoke_grant, calculate the new column privilege for tables_priv */
    if (revoke_grant)
    {
4566 4567
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
unknown's avatar
unknown committed
4568 4569 4570
      GRANT_COLUMN *grant_column;

      /* Fix old grants */
4571
      while ((column = column_iter++))
unknown's avatar
unknown committed
4572 4573
      {
	grant_column = column_hash_search(grant_table,
4574 4575
					  column->column.ptr(),
					  column->column.length());
unknown's avatar
unknown committed
4576
	if (grant_column)
4577
	  grant_column->rights&= ~(column->rights | rights);
unknown's avatar
unknown committed
4578 4579
      }
      /* scan trough all columns to get new column grant */
4580
      column_priv= 0;
unknown's avatar
unknown committed
4581 4582
      for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
4583 4584
        grant_column= (GRANT_COLUMN*)
          my_hash_element(&grant_table->hash_columns, idx);
unknown's avatar
unknown committed
4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596
	grant_column->rights&= ~rights;		// Fix other columns
	column_priv|= grant_column->rights;
      }
    }
    else
    {
      column_priv|= grant_table->cols;
    }


    /* update table and columns */

unknown's avatar
VIEW  
unknown committed
4597
    if (replace_table_table(thd, grant_table, tables[1].table, *Str,
4598
			    db_name, table_name,
unknown's avatar
unknown committed
4599
			    rights, column_priv, revoke_grant))
4600 4601
    {
      /* Should only happen if table is crashed */
unknown's avatar
unknown committed
4602
      result= TRUE;			       /* purecov: deadcode */
unknown's avatar
unknown committed
4603 4604 4605
    }
    else if (tables[2].table)
    {
unknown's avatar
VIEW  
unknown committed
4606
      if ((replace_column_table(grant_table, tables[2].table, *Str,
unknown's avatar
unknown committed
4607
				columns,
4608
				db_name, table_name,
unknown's avatar
unknown committed
4609 4610
				rights, revoke_grant)))
      {
unknown's avatar
unknown committed
4611
	result= TRUE;
unknown's avatar
unknown committed
4612 4613 4614
      }
    }
  }
unknown's avatar
unknown committed
4615
  thd->mem_root= old_root;
Marc Alff's avatar
Marc Alff committed
4616
  mysql_mutex_unlock(&acl_cache->lock);
4617 4618 4619

  if (!result) /* success */
  {
4620
    result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
4621 4622
  }

Marc Alff's avatar
Marc Alff committed
4623
  mysql_rwlock_unlock(&LOCK_grant);
4624 4625

  if (!result) /* success */
4626
    my_ok(thd);
4627

4628
  /* Tables are automatically closed */
4629
  thd->lex->restore_backup_query_tables_list(&backup);
4630
  /* Restore the state of binlog format */
unknown's avatar
unknown committed
4631 4632 4633 4634
  DBUG_RETURN(result);
}


4635
/**
4636
  Store routine level grants in the privilege tables
4637

4638 4639 4640 4641 4642 4643
  @param thd Thread handle
  @param table_list List of routines to give grant
  @param is_proc Is this a list of procedures?
  @param user_list List of users to give grant
  @param rights Table level grant
  @param revoke_grant Is this is a REVOKE command?
4644

4645 4646 4647
  @return
    @retval FALSE Success.
    @retval TRUE An error occurred.
4648 4649
*/

4650 4651
bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
			 List <LEX_USER> &user_list, ulong rights,
4652
			 bool revoke_grant, bool write_to_binlog)
4653 4654
{
  List_iterator <LEX_USER> str_list (user_list);
4655
  LEX_USER *Str, *tmp_Str;
4656 4657
  TABLE_LIST tables[2];
  bool create_new_users=0, result=0;
4658
  char *db_name, *table_name;
4659
  Rpl_filter *rpl_filter= thd->rpl_filter;
4660
  DBUG_ENTER("mysql_routine_grant");
4661 4662 4663

  if (!initialized)
  {
4664 4665
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");
4666 4667 4668 4669
    DBUG_RETURN(TRUE);
  }
  if (rights & ~PROC_ACLS)
  {
4670 4671
    my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
               MYF(0));
4672 4673 4674 4675 4676
    DBUG_RETURN(TRUE);
  }

  if (!revoke_grant)
  {
4677
    if (sp_exist_routines(thd, table_list, is_proc))
4678 4679 4680 4681 4682
      DBUG_RETURN(TRUE);
  }

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

4683 4684 4685 4686
  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("user"), "user", TL_WRITE);
  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("procs_priv"), "procs_priv", TL_WRITE);
4687 4688 4689 4690 4691 4692 4693
  tables[0].next_local= tables[0].next_global= tables+1;

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
4694
  if (thd->slave_thread && rpl_filter->is_on())
4695 4696 4697 4698 4699 4700
  {
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
    */
    tables[0].updating= tables[1].updating= 1;
unknown's avatar
unknown committed
4701
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
4702
    {
4703
      DBUG_RETURN(FALSE);
4704
    }
4705 4706 4707
  }
#endif

4708
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
4709
    DBUG_RETURN(TRUE);
4710 4711

  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4712 4713 4714

  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
Marc Alff's avatar
Marc Alff committed
4715 4716
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
4717 4718 4719 4720 4721
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;

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

4722
  while ((tmp_Str= str_list++))
4723 4724 4725
  {
    int error;
    GRANT_NAME *grant_name;
4726 4727 4728 4729
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
4730
    }
4731 4732
    /* Create user if needed */
    error=replace_user_table(thd, tables[0].table, *Str,
unknown's avatar
unknown committed
4733
			     0, revoke_grant, create_new_users,
unknown's avatar
unknown committed
4734 4735
                             test(thd->variables.sql_mode &
                                  MODE_NO_AUTO_CREATE_USER));
4736 4737 4738 4739 4740 4741 4742
    if (error)
    {
      result= TRUE;				// Remember error
      continue;					// Add next user
    }

    db_name= table_list->db;
4743
    table_name= table_list->table_name;
4744

4745 4746
    grant_name= routine_hash_search(Str->host.str, NullS, db_name,
                                    Str->user.str, table_name, is_proc, 1);
4747 4748 4749 4750
    if (!grant_name)
    {
      if (revoke_grant)
      {
4751 4752
        my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
	         Str->user.str, Str->host.str, table_name);
4753 4754 4755 4756
	result= TRUE;
	continue;
      }
      grant_name= new GRANT_NAME(Str->host.str, db_name,
4757
				 Str->user.str, table_name,
4758
				 rights, TRUE);
4759
      if (!grant_name ||
Kristofer Pettersson's avatar
merge  
Kristofer Pettersson committed
4760 4761
        my_hash_insert(is_proc ?
                       &proc_priv_hash : &func_priv_hash,(uchar*) grant_name))
4762 4763 4764 4765 4766
      {
        result= TRUE;
	continue;
      }
    }
4767

4768
    if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
4769
                              db_name, table_name, is_proc, rights,
4770
                              revoke_grant) != 0)
4771 4772 4773 4774 4775 4776
    {
      result= TRUE;
      continue;
    }
  }
  thd->mem_root= old_root;
Marc Alff's avatar
Marc Alff committed
4777
  mysql_mutex_unlock(&acl_cache->lock);
4778 4779

  if (write_to_binlog)
4780
  {
4781
    if (write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
4782
      result= TRUE;
4783 4784
  }

Marc Alff's avatar
Marc Alff committed
4785
  mysql_rwlock_unlock(&LOCK_grant);
4786

4787 4788 4789 4790 4791
  /* Tables are automatically closed */
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
4792
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
4793
                 ulong rights, bool revoke_grant, bool is_proxy)
unknown's avatar
unknown committed
4794 4795
{
  List_iterator <LEX_USER> str_list (list);
4796
  LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
4797
  char tmp_db[SAFE_NAME_LEN+1];
unknown's avatar
unknown committed
4798
  bool create_new_users=0;
unknown's avatar
unknown committed
4799
  TABLE_LIST tables[2];
4800
  Rpl_filter *rpl_filter= thd->rpl_filter;
unknown's avatar
unknown committed
4801
  DBUG_ENTER("mysql_grant");
4802

unknown's avatar
unknown committed
4803 4804
  if (!initialized)
  {
unknown's avatar
unknown committed
4805 4806
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: tested */
unknown's avatar
unknown committed
4807
    DBUG_RETURN(TRUE);				/* purecov: tested */
unknown's avatar
unknown committed
4808 4809
  }

unknown's avatar
unknown committed
4810 4811
  if (lower_case_table_names && db)
  {
4812 4813 4814 4815 4816 4817
    char *end= strnmov(tmp_db,db, sizeof(tmp_db));
    if (end >= tmp_db + sizeof(tmp_db))
    {
      my_error(ER_WRONG_DB_NAME ,MYF(0), db);
      DBUG_RETURN(TRUE);
    }
4818
    my_casedn_str(files_charset_info, tmp_db);
unknown's avatar
unknown committed
4819 4820
    db=tmp_db;
  }
unknown's avatar
unknown committed
4821

4822 4823
  if (is_proxy)
  {
4824
    DBUG_ASSERT(!db);
4825 4826 4827
    proxied_user= str_list++;
  }

4828
  /* open the mysql.user and mysql.db or mysql.proxies_priv tables */
4829 4830
  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("user"), "user", TL_WRITE);
4831 4832 4833
  if (is_proxy)

    tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
4834
                             C_STRING_WITH_LEN("proxies_priv"),
4835
                             "proxies_priv",
4836 4837 4838
                             TL_WRITE);
  else
    tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
4839 4840
                             C_STRING_WITH_LEN("db"),
                             "db",
4841
                             TL_WRITE);
unknown's avatar
VIEW  
unknown committed
4842
  tables[0].next_local= tables[0].next_global= tables+1;
4843 4844 4845 4846 4847 4848

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

4861
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
unknown's avatar
unknown committed
4862
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
4863 4864

  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
unknown's avatar
unknown committed
4865

unknown's avatar
unknown committed
4866 4867
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
unknown's avatar
unknown committed
4868

4869
  /* go through users in user_list */
Marc Alff's avatar
Marc Alff committed
4870 4871
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
4872 4873 4874
  grant_version++;

  int result=0;
4875
  while ((tmp_Str = str_list++))
unknown's avatar
unknown committed
4876
  {
4877 4878 4879 4880
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
unknown's avatar
unknown committed
4881
    }
4882 4883 4884 4885 4886 4887 4888
    /*
      No User, but a password?
      They did GRANT ... TO CURRENT_USER() IDENTIFIED BY ... !
      Get the current user, and shallow-copy the new password to them!
    */
    if (!tmp_Str->user.str && tmp_Str->password.str)
      Str->password= tmp_Str->password;
unknown's avatar
unknown committed
4889 4890
    if (replace_user_table(thd, tables[0].table, *Str,
                           (!db ? rights : 0), revoke_grant, create_new_users,
unknown's avatar
unknown committed
4891 4892
                           test(thd->variables.sql_mode &
                                MODE_NO_AUTO_CREATE_USER)))
4893
      result= -1;
unknown's avatar
unknown committed
4894
    else if (db)
unknown's avatar
unknown committed
4895
    {
unknown's avatar
unknown committed
4896 4897 4898 4899 4900 4901 4902 4903 4904
      ulong db_rights= rights & DB_ACLS;
      if (db_rights  == rights)
      {
	if (replace_db_table(tables[1].table, db, *Str, db_rights,
			     revoke_grant))
	  result= -1;
      }
      else
      {
unknown's avatar
unknown committed
4905
	my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
unknown's avatar
unknown committed
4906
	result= -1;
unknown's avatar
unknown committed
4907
      }
unknown's avatar
unknown committed
4908
    }
4909 4910
    else if (is_proxy)
    {
4911
      if (replace_proxies_priv_table (thd, tables[1].table, Str, proxied_user,
4912
                                    rights & GRANT_ACL ? TRUE : FALSE,
4913 4914 4915
                                    revoke_grant))
        result= -1;
    }
unknown's avatar
unknown committed
4916
  }
Marc Alff's avatar
Marc Alff committed
4917
  mysql_mutex_unlock(&acl_cache->lock);
4918 4919 4920

  if (!result)
  {
4921
    result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
4922 4923
  }

Marc Alff's avatar
Marc Alff committed
4924
  mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4925 4926

  if (!result)
4927
    my_ok(thd);
4928

unknown's avatar
unknown committed
4929 4930 4931
  DBUG_RETURN(result);
}

unknown's avatar
unknown committed
4932 4933

/* Free grant array if possible */
unknown's avatar
unknown committed
4934 4935 4936 4937

void  grant_free(void)
{
  DBUG_ENTER("grant_free");
Konstantin Osipov's avatar
Konstantin Osipov committed
4938 4939 4940
  my_hash_free(&column_priv_hash);
  my_hash_free(&proc_priv_hash);
  my_hash_free(&func_priv_hash);
4941
  free_root(&memex,MYF(0));
unknown's avatar
unknown committed
4942 4943 4944 4945
  DBUG_VOID_RETURN;
}


4946 4947 4948
/**
  @brief Initialize structures responsible for table/column-level privilege
   checking and load information for them from tables in the 'mysql' database.
unknown's avatar
unknown committed
4949

4950 4951 4952
  @return Error status
    @retval 0 OK
    @retval 1 Could not initialize grant subsystem.
4953
*/
unknown's avatar
unknown committed
4954

4955
my_bool grant_init()
unknown's avatar
unknown committed
4956
{
unknown's avatar
unknown committed
4957
  THD  *thd;
4958 4959 4960 4961 4962
  my_bool return_val;
  DBUG_ENTER("grant_init");

  if (!(thd= new THD))
    DBUG_RETURN(1);				/* purecov: deadcode */
4963
  thd->thread_stack= (char*) &thd;
4964 4965 4966 4967
  thd->store_globals();
  return_val=  grant_reload(thd);
  delete thd;
  /* Remember that we don't have a THD */
4968
  set_current_thd(0);
4969 4970 4971 4972
  DBUG_RETURN(return_val);
}


4973 4974
/**
  @brief Helper function to grant_reload_procs_priv
4975

4976
  Reads the procs_priv table into memory hash.
4977

4978 4979 4980 4981 4982 4983 4984 4985
  @param table A pointer to the procs_priv table structure.

  @see grant_reload
  @see grant_reload_procs_priv

  @return Error state
    @retval TRUE An error occurred
    @retval FALSE Success
4986 4987
*/

4988
static my_bool grant_load_procs_priv(TABLE *p_table)
4989
{
unknown's avatar
unknown committed
4990
  MEM_ROOT *memex_ptr;
4991
  my_bool return_val= 1;
unknown's avatar
SCRUM  
unknown committed
4992
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
4993 4994
  MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
                                                           THR_MALLOC);
4995
  DBUG_ENTER("grant_load_procs_priv");
4996
  (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
Konstantin Osipov's avatar
Konstantin Osipov committed
4997 4998
                      0,0,0, (my_hash_get_key) get_grant_table,
                      0,0);
4999
  (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
Konstantin Osipov's avatar
Konstantin Osipov committed
5000 5001
                      0,0,0, (my_hash_get_key) get_grant_table,
                      0,0);
5002 5003 5004 5005

  if (p_table->file->ha_index_init(0, 1))
    DBUG_RETURN(TRUE);

5006
  p_table->use_all_columns();
5007

5008
  if (!p_table->file->ha_index_first(p_table->record[0]))
unknown's avatar
unknown committed
5009
  {
5010 5011 5012 5013
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
    {
5014 5015
      GRANT_NAME *mem_check;
      HASH *hash;
5016
      if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE)))
5017
      {
5018 5019
        /* This could only happen if we are out memory */
        goto end_unlock;
5020
      }
unknown's avatar
unknown committed
5021

5022 5023
      if (check_no_resolve)
      {
5024
	if (hostname_requires_resolving(mem_check->host.hostname))
5025
	{
5026
          sql_print_warning("'procs_priv' entry '%s %s@%s' "
5027
                            "ignored in --skip-name-resolve mode.",
5028
                            mem_check->tname, mem_check->user,
5029 5030
                            mem_check->host.hostname ?
                            mem_check->host.hostname : "");
5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048
          continue;
        }
      }
      if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
      {
        hash= &proc_priv_hash;
      }
      else
      if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
      {
        hash= &func_priv_hash;
      }
      else
      {
        sql_print_warning("'procs_priv' entry '%s' "
                          "ignored, bad routine type",
                          mem_check->tname);
        continue;
5049 5050
      }

5051
      mem_check->privs= fix_rights_for_procedure(mem_check->privs);
5052
      if (! mem_check->ok())
5053 5054
        delete mem_check;
      else if (my_hash_insert(hash, (uchar*) mem_check))
5055
      {
5056 5057
        delete mem_check;
        goto end_unlock;
5058 5059
      }
    }
5060
    while (!p_table->file->ha_index_next(p_table->record[0]));
5061
  }
5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086
  /* Return ok */
  return_val= 0;

end_unlock:
  p_table->file->ha_index_end();
  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
  DBUG_RETURN(return_val);
}


/**
  @brief Initialize structures responsible for table/column-level privilege
    checking and load information about grants from open privilege tables.

  @param thd Current thread
  @param tables List containing open "mysql.tables_priv" and
    "mysql.columns_priv" tables.

  @see grant_reload

  @return Error state
    @retval FALSE Success
    @retval TRUE Error
*/

5087
static my_bool grant_load(THD *thd, TABLE_LIST *tables)
5088 5089 5090 5091 5092 5093 5094
{
  MEM_ROOT *memex_ptr;
  my_bool return_val= 1;
  TABLE *t_table= 0, *c_table= 0;
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
  MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
                                                           THR_MALLOC);
5095
  ulonglong old_sql_mode= thd->variables.sql_mode;
5096
  DBUG_ENTER("grant_load");
5097 5098 5099

  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;

5100
  (void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
Konstantin Osipov's avatar
Konstantin Osipov committed
5101 5102
                      0,0,0, (my_hash_get_key) get_grant_table,
                      (my_hash_free_key) free_grant_table,0);
5103 5104 5105

  t_table = tables[0].table;
  c_table = tables[1].table;
5106 5107 5108 5109

  if (t_table->file->ha_index_init(0, 1))
    goto end_index_init;

5110 5111 5112
  t_table->use_all_columns();
  c_table->use_all_columns();

5113
  if (!t_table->file->ha_index_first(t_table->record[0]))
unknown's avatar
unknown committed
5114
  {
5115 5116 5117
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
unknown's avatar
unknown committed
5118
    {
5119 5120
      GRANT_TABLE *mem_check;
      if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table)))
5121 5122 5123 5124
      {
	/* This could only happen if we are out memory */
	goto end_unlock;
      }
unknown's avatar
SCRUM  
unknown committed
5125

5126
      if (check_no_resolve)
unknown's avatar
SCRUM  
unknown committed
5127
      {
unknown's avatar
unknown committed
5128
	if (hostname_requires_resolving(mem_check->host.hostname))
5129
	{
5130
          sql_print_warning("'tables_priv' entry '%s %s@%s' "
5131
                            "ignored in --skip-name-resolve mode.",
5132 5133
                            mem_check->tname,
                            mem_check->user ? mem_check->user : "",
5134 5135
                            mem_check->host.hostname ?
                            mem_check->host.hostname : "");
5136 5137
	  continue;
	}
unknown's avatar
SCRUM  
unknown committed
5138 5139
      }

5140 5141
      if (! mem_check->ok())
	delete mem_check;
5142
      else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
5143 5144 5145 5146
      {
	delete mem_check;
	goto end_unlock;
      }
unknown's avatar
SCRUM  
unknown committed
5147
    }
5148
    while (!t_table->file->ha_index_next(t_table->record[0]));
unknown's avatar
unknown committed
5149
  }
5150

5151 5152 5153
  return_val=0;					// Return ok

end_unlock:
unknown's avatar
unknown committed
5154
  t_table->file->ha_index_end();
5155
  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
5156 5157
end_index_init:
  thd->variables.sql_mode= old_sql_mode;
5158
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
5159 5160 5161
}


5162 5163 5164
/**
  @brief Helper function to grant_reload. Reloads procs_priv table is it
    exists.
5165

5166
  @param thd A pointer to the thread handler object.
5167

5168
  @see grant_reload
5169

5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181
  @return Error state
    @retval FALSE Success
    @retval TRUE An error has occurred.
*/

static my_bool grant_reload_procs_priv(THD *thd)
{
  HASH old_proc_priv_hash, old_func_priv_hash;
  TABLE_LIST table;
  my_bool return_val= FALSE;
  DBUG_ENTER("grant_reload_procs_priv");

Konstantin Osipov's avatar
Konstantin Osipov committed
5182 5183 5184
  table.init_one_table("mysql", 5, "procs_priv",
                       strlen("procs_priv"), "procs_priv",
                       TL_READ);
5185
  table.open_type= OT_BASE_ONLY;
5186

5187
  if (open_and_lock_tables(thd, &table, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5188 5189
    DBUG_RETURN(TRUE);

Marc Alff's avatar
Marc Alff committed
5190
  mysql_rwlock_wrlock(&LOCK_grant);
5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204
  /* Save a copy of the current hash if we need to undo the grant load */
  old_proc_priv_hash= proc_priv_hash;
  old_func_priv_hash= func_priv_hash;

  if ((return_val= grant_load_procs_priv(table.table)))
  {
    /* Error; Reverting to old hash */
    DBUG_PRINT("error",("Reverting to old privileges"));
    grant_free();
    proc_priv_hash= old_proc_priv_hash;
    func_priv_hash= old_func_priv_hash;
  }
  else
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
5205 5206
    my_hash_free(&old_proc_priv_hash);
    my_hash_free(&old_func_priv_hash);
5207
  }
Marc Alff's avatar
Marc Alff committed
5208
  mysql_rwlock_unlock(&LOCK_grant);
5209

5210
  close_mysql_tables(thd);
5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227
  DBUG_RETURN(return_val);
}


/**
  @brief Reload information about table and column level privileges if possible

  @param thd Current thread

  Locked tables are checked by acl_reload() and doesn't have to be checked
  in this call.
  This function is also used for initialization of structures responsible
  for table/column-level privilege checking.

  @return Error state
    @retval FALSE Success
    @retval TRUE  Error
5228
*/
unknown's avatar
unknown committed
5229

5230
my_bool grant_reload(THD *thd)
unknown's avatar
unknown committed
5231
{
5232 5233
  TABLE_LIST tables[2];
  HASH old_column_priv_hash;
unknown's avatar
unknown committed
5234
  MEM_ROOT old_mem;
5235
  my_bool return_val= 1;
unknown's avatar
unknown committed
5236 5237
  DBUG_ENTER("grant_reload");

5238 5239 5240 5241
  /* Don't do anything if running with --skip-grant-tables */
  if (!initialized)
    DBUG_RETURN(0);

5242 5243 5244 5245 5246 5247
  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("tables_priv"),
                           "tables_priv", TL_READ);
  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("columns_priv"),
                           "columns_priv", TL_READ);
5248
  tables[0].next_local= tables[0].next_global= tables+1;
5249
  tables[0].open_type= tables[1].open_type= OT_BASE_ONLY;
5250

5251 5252 5253 5254
  /*
    To avoid deadlocks we should obtain table locks before
    obtaining LOCK_grant rwlock.
  */
5255
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5256 5257
    goto end;

Marc Alff's avatar
Marc Alff committed
5258
  mysql_rwlock_wrlock(&LOCK_grant);
5259
  old_column_priv_hash= column_priv_hash;
5260 5261 5262 5263 5264

  /*
    Create a new memory pool but save the current memory pool to make an undo
    opertion possible in case of failure.
  */
unknown's avatar
unknown committed
5265
  old_mem= memex;
5266
  init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
unknown's avatar
unknown committed
5267

5268
  if ((return_val= grant_load(thd, tables)))
unknown's avatar
unknown committed
5269
  {						// Error. Revert to old hash
5270
    DBUG_PRINT("error",("Reverting to old privileges"));
unknown's avatar
unknown committed
5271
    grant_free();				/* purecov: deadcode */
5272
    column_priv_hash= old_column_priv_hash;	/* purecov: deadcode */
unknown's avatar
unknown committed
5273
    memex= old_mem;				/* purecov: deadcode */
unknown's avatar
unknown committed
5274 5275 5276
  }
  else
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
5277
    my_hash_free(&old_column_priv_hash);
5278
    free_root(&old_mem,MYF(0));
unknown's avatar
unknown committed
5279
  }
Marc Alff's avatar
Marc Alff committed
5280
  mysql_rwlock_unlock(&LOCK_grant);
5281
  close_mysql_tables(thd);
5282 5283

  /*
5284
    It is OK failing to load procs_priv table because we may be
5285 5286 5287
    working with 4.1 privilege tables.
  */
  if (grant_reload_procs_priv(thd))
5288
    return_val= 1;
5289

Marc Alff's avatar
Marc Alff committed
5290
  mysql_rwlock_wrlock(&LOCK_grant);
5291
  grant_version++;
Marc Alff's avatar
Marc Alff committed
5292
  mysql_rwlock_unlock(&LOCK_grant);
5293 5294

end:
5295
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
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
/**
  @brief Check table level grants

  @param thd          Thread handler
  @param want_access  Bits of privileges user needs to have.
  @param tables       List of tables to check. The user should have
                      'want_access' to all tables in list.
  @param any_combination_will_do TRUE if it's enough to have any privilege for
    any combination of the table columns.
  @param number       Check at most this number of tables.
  @param no_errors    TRUE if no error should be sent directly to the client.

  If table->grant.want_privilege != 0 then the requested privileges where
  in the set of COL_ACLS but access was not granted on the table level. As
  a consequence an extra check of column privileges is required.

  Specifically if this function returns FALSE the user has some kind of
  privilege on a combination of columns in each table.

  This function is usually preceeded by check_access which establish the
  User-, Db- and Host access rights.

  @see check_access
  @see check_table_access

Michael Widenius's avatar
Michael Widenius committed
5324 5325
  @note
     This functions assumes that either number of tables to be inspected
5326 5327 5328 5329
     by it is limited explicitly (i.e. is is not UINT_MAX) or table list
     used and thd->lex->query_tables_own_last value correspond to each
     other (the latter should be either 0 or point to next_global member
     of one of elements of this table list).
5330

Michael Widenius's avatar
Michael Widenius committed
5331 5332 5333
     We delay locking of LOCK_grant until we really need it as we assume that
     most privileges be resolved with user or db level accesses.

5334 5335 5336 5337 5338 5339 5340
   @return Access status
     @retval FALSE Access granted; But column privileges might need to be
      checked.
     @retval TRUE The user did not have the requested privileges on any of the
      tables.

*/
unknown's avatar
unknown committed
5341

unknown's avatar
unknown committed
5342
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
5343
                 bool any_combination_will_do, uint number, bool no_errors)
unknown's avatar
unknown committed
5344
{
5345 5346
  TABLE_LIST *tl;
  TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
5347
  Security_context *sctx= thd->security_ctx;
5348
  uint i;
Marc Alff's avatar
Marc Alff committed
5349
  ulong orig_want_access= want_access;
Michael Widenius's avatar
Michael Widenius committed
5350 5351
  my_bool locked= 0;
  GRANT_TABLE *grant_table;
5352
  GRANT_TABLE *grant_table_role= NULL;
5353 5354
  DBUG_ENTER("check_grant");
  DBUG_ASSERT(number > 0);
unknown's avatar
unknown committed
5355

5356
  /*
unknown's avatar
unknown committed
5357 5358 5359 5360 5361 5362 5363 5364
    Walk through the list of tables that belong to the query and save the
    requested access (orig_want_privilege) to be able to use it when
    checking access rights to the underlying tables of a view. Our grant
    system gradually eliminates checked bits from want_privilege and thus
    after all checks are done we can no longer use it.
    The check that first_not_own_table is not reached is for the case when
    the given table list refers to the list for prelocking (contains tables
    of other queries). For simple queries first_not_own_table is 0.
5365
  */
5366 5367 5368
  for (i= 0, tl= tables;
       i < number  && tl != first_not_own_table;
       tl= tl->next_global, i++)
5369
  {
5370 5371 5372 5373
    /*
      Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
      It will be checked during making view.
    */
5374
    tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
5375
  }
Michael Widenius's avatar
Michael Widenius committed
5376
  number= i;
5377

Michael Widenius's avatar
Michael Widenius committed
5378
  for (tl= tables; number-- ; tl= tl->next_global)
unknown's avatar
unknown committed
5379
  {
5380
    sctx = test(tl->security_ctx) ? tl->security_ctx : thd->security_ctx;
5381

5382 5383 5384 5385
    const ACL_internal_table_access *access=
      get_cached_table_access(&tl->grant.m_internal,
                              tl->get_db_name(),
                              tl->get_table_name());
Marc Alff's avatar
Marc Alff committed
5386 5387 5388

    if (access)
    {
5389
      switch(access->check(orig_want_access, &tl->grant.privilege))
Marc Alff's avatar
Marc Alff committed
5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408
      {
      case ACL_INTERNAL_ACCESS_GRANTED:
        /*
          Currently,
          -  the information_schema does not subclass ACL_internal_table_access,
          there are no per table privilege checks for I_S,
          - the performance schema does use per tables checks, but at most
          returns 'CHECK_GRANT', and never 'ACCESS_GRANTED'.
          so this branch is not used.
        */
        DBUG_ASSERT(0);
      case ACL_INTERNAL_ACCESS_DENIED:
        goto err;
      case ACL_INTERNAL_ACCESS_CHECK_GRANT:
        break;
      }
    }

    want_access= orig_want_access;
5409 5410 5411 5412
    want_access&= ~sctx->master_access;
    if (!want_access)
      continue;                                 // ok

5413 5414
    if (!(~tl->grant.privilege & want_access) ||
        tl->is_anonymous_derived_table() || tl->schema_table)
unknown's avatar
unknown committed
5415
    {
unknown's avatar
VIEW  
unknown committed
5416
      /*
5417
        It is subquery in the FROM clause. VIEW set tl->derived after
unknown's avatar
VIEW  
unknown committed
5418 5419
        table opening, but this function always called before table opening.
      */
5420
      if (!tl->referencing_view)
5421 5422 5423 5424 5425 5426
      {
        /*
          If it's a temporary table created for a subquery in the FROM
          clause, or an INFORMATION_SCHEMA table, drop the request for
          a privilege.
        */
5427
        tl->grant.want_privilege= 0;
5428 5429
      }
      continue;
unknown's avatar
unknown committed
5430
    }
5431

Michael Widenius's avatar
Michael Widenius committed
5432 5433 5434 5435 5436 5437
    if (!locked)
    {
      locked= 1;
      mysql_rwlock_rdlock(&LOCK_grant);
    }

5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449
    grant_table= table_hash_search(sctx->host, sctx->ip,
                                   tl->get_db_name(),
                                   sctx->priv_user,
                                   tl->get_table_name(),
                                   FALSE);
    if (sctx->priv_role[0])
      grant_table_role= table_hash_search("", "", tl->get_db_name(),
                                          sctx->priv_role,
                                          tl->get_table_name(),
                                          TRUE);

    if (!grant_table && !grant_table_role)
unknown's avatar
unknown committed
5450
    {
5451 5452
      want_access&= ~tl->grant.privilege;
      goto err;
unknown's avatar
unknown committed
5453
    }
5454 5455 5456 5457 5458 5459 5460

    /*
      For SHOW COLUMNS, SHOW INDEX it is enough to have some
      privileges on any column combination on the table.
    */
    if (any_combination_will_do)
      continue;
unknown's avatar
unknown committed
5461

5462 5463
    tl->grant.grant_table_user= grant_table; // Remember for column test
    tl->grant.grant_table_role= grant_table_role;
5464
    tl->grant.version= grant_version;
5465 5466
    tl->grant.privilege|= grant_table ? grant_table->privs : 0;
    tl->grant.privilege|= grant_table_role ? grant_table_role->privs : 0;
5467
    tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege);
unknown's avatar
unknown committed
5468

5469
    if (!(~tl->grant.privilege & want_access))
unknown's avatar
unknown committed
5470 5471
      continue;

5472
    if ((want_access&= ~((grant_table ? grant_table->cols : 0) |
5473
                        (grant_table_role ? grant_table_role->cols : 0) |
5474
                        tl->grant.privilege)))
unknown's avatar
unknown committed
5475
    {
5476
      goto err;                                 // impossible
unknown's avatar
unknown committed
5477 5478
    }
  }
Michael Widenius's avatar
Michael Widenius committed
5479 5480
  if (locked)
    mysql_rwlock_unlock(&LOCK_grant);
5481
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
5482

5483
err:
Michael Widenius's avatar
Michael Widenius committed
5484 5485
  if (locked)
    mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
5486
  if (!no_errors)				// Not a silent skip of table
unknown's avatar
unknown committed
5487
  {
5488 5489
    char command[128];
    get_privilege_desc(command, sizeof(command), want_access);
5490 5491
    status_var_increment(thd->status_var.access_denied_errors);

5492 5493
    my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
             command,
5494 5495
             sctx->priv_user,
             sctx->host_or_ip,
5496
             tl ? tl->get_table_name() : "unknown");
unknown's avatar
unknown committed
5497
  }
5498 5499 5500 5501
  DBUG_RETURN(TRUE);
}


5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519
/*
  Check column rights in given security context

  SYNOPSIS
    check_grant_column()
    thd                  thread handler
    grant                grant information structure
    db_name              db name
    table_name           table  name
    name                 column name
    length               column name length
    sctx                 security context

  RETURN
    FALSE OK
    TRUE  access denied
*/

unknown's avatar
VIEW  
unknown committed
5520
bool check_grant_column(THD *thd, GRANT_INFO *grant,
5521
			const char *db_name, const char *table_name,
5522
			const char *name, uint length,  Security_context *sctx)
unknown's avatar
unknown committed
5523 5524
{
  GRANT_TABLE *grant_table;
5525
  GRANT_TABLE *grant_table_role;
unknown's avatar
unknown committed
5526
  GRANT_COLUMN *grant_column;
unknown's avatar
VIEW  
unknown committed
5527
  ulong want_access= grant->want_privilege & ~grant->privilege;
unknown's avatar
unknown committed
5528
  DBUG_ENTER("check_grant_column");
unknown's avatar
unknown committed
5529
  DBUG_PRINT("enter", ("table: %s  want_access: %lu", table_name, want_access));
unknown's avatar
unknown committed
5530

unknown's avatar
unknown committed
5531
  if (!want_access)
unknown's avatar
unknown committed
5532
    DBUG_RETURN(0);				// Already checked
unknown's avatar
unknown committed
5533

Marc Alff's avatar
Marc Alff committed
5534
  mysql_rwlock_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
5535

5536
  /* reload table if someone has modified any grants */
unknown's avatar
unknown committed
5537

unknown's avatar
VIEW  
unknown committed
5538
  if (grant->version != grant_version)
unknown's avatar
unknown committed
5539
  {
5540
    grant->grant_table_user=
5541 5542
      table_hash_search(sctx->host, sctx->ip, db_name,
			sctx->priv_user,
unknown's avatar
unknown committed
5543
			table_name, 0);         /* purecov: inspected */
5544 5545 5546 5547
    grant->grant_table_role=
      sctx->priv_role[0] ? table_hash_search("", "", db_name,
                                             sctx->priv_role,
                                             table_name, TRUE) : NULL;
unknown's avatar
VIEW  
unknown committed
5548
    grant->version= grant_version;		/* purecov: inspected */
unknown's avatar
unknown committed
5549
  }
5550 5551
  if (!(grant_table= grant->grant_table_user) &&
      !(grant_table_role= grant->grant_table_role))
unknown's avatar
unknown committed
5552 5553
    goto err;					/* purecov: deadcode */

5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570
  if (grant_table)
  {
    grant_column= column_hash_search(grant_table, name, length);
    if (grant_column)
    {
      want_access&= ~grant_column->rights;
    }
  }
  if (grant_table_role)
  {
    grant_column= column_hash_search(grant_table_role, name, length);
    if (grant_column)
    {
      want_access&= ~grant_column->rights;
    }
  }
  if (!want_access)
unknown's avatar
unknown committed
5571
  {
Marc Alff's avatar
Marc Alff committed
5572
    mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
5573
    DBUG_RETURN(0);
unknown's avatar
unknown committed
5574 5575
  }

5576
err:
Marc Alff's avatar
Marc Alff committed
5577
  mysql_rwlock_unlock(&LOCK_grant);
5578 5579
  char command[128];
  get_privilege_desc(command, sizeof(command), want_access);
5580
  /* TODO perhaps error should print current rolename aswell */
5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622
  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
           command,
           sctx->priv_user,
           sctx->host_or_ip,
           name,
           table_name);
  DBUG_RETURN(1);
}


/*
  Check the access right to a column depending on the type of table.

  SYNOPSIS
    check_column_grant_in_table_ref()
    thd              thread handler
    table_ref        table reference where to check the field
    name             name of field to check
    length           length of name

  DESCRIPTION
    Check the access rights to a column depending on the type of table
    reference where the column is checked. The function provides a
    generic interface to check column access rights that hides the
    heterogeneity of the column representation - whether it is a view
    or a stored table colum.

  RETURN
    FALSE OK
    TRUE  access denied
*/

bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
                                     const char *name, uint length)
{
  GRANT_INFO *grant;
  const char *db_name;
  const char *table_name;
  Security_context *sctx= test(table_ref->security_ctx) ?
                          table_ref->security_ctx : thd->security_ctx;

  if (table_ref->view || table_ref->field_translation)
unknown's avatar
unknown committed
5623
  {
5624
    /* View or derived information schema table. */
5625
    ulong view_privs;
5626 5627 5628
    grant= &(table_ref->grant);
    db_name= table_ref->view_db.str;
    table_name= table_ref->view_name.str;
5629
    if (table_ref->belong_to_view &&
5630
        thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641
    {
      view_privs= get_column_grant(thd, grant, db_name, table_name, name);
      if (view_privs & VIEW_ANY_ACL)
      {
        table_ref->belong_to_view->allowed_show= TRUE;
        return FALSE;
      }
      table_ref->belong_to_view->allowed_show= FALSE;
      my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
      return TRUE;
    }
unknown's avatar
unknown committed
5642
  }
5643 5644 5645 5646 5647
  else
  {
    /* Normal or temporary table. */
    TABLE *table= table_ref->table;
    grant= &(table->grant);
unknown's avatar
unknown committed
5648 5649
    db_name= table->s->db.str;
    table_name= table->s->table_name.str;
5650 5651 5652 5653 5654 5655 5656 5657
  }

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

unknown's avatar
unknown committed
5658 5659 5660
}


5661
/**
5662 5663 5664 5665 5666 5667 5668 5669
  @brief check if a query can access a set of columns

  @param  thd  the current thread
  @param  want_access_arg  the privileges requested
  @param  fields an iterator over the fields of a table reference.
  @return Operation status
    @retval 0 Success
    @retval 1 Falure
5670
  @details This function walks over the columns of a table reference
5671
   The columns may originate from different tables, depending on the kind of
5672
   table reference, e.g. join, view.
5673 5674
   For each table it will retrieve the grant information and will use it
   to check the required access privileges for the fields requested from it.
5675 5676
*/
bool check_grant_all_columns(THD *thd, ulong want_access_arg,
5677
                             Field_iterator_table_ref *fields)
unknown's avatar
unknown committed
5678
{
5679
  Security_context *sctx= thd->security_ctx;
5680 5681
  ulong want_access= want_access_arg;
  const char *table_name= NULL;
unknown's avatar
unknown committed
5682

5683
  const char* db_name;
5684
  GRANT_INFO *grant;
5685 5686
  /* Initialized only to make gcc happy */
  GRANT_TABLE *grant_table= NULL;
5687
  GRANT_TABLE *grant_table_role= NULL;
5688
  /*
5689 5690 5691 5692
     Flag that gets set if privilege checking has to be performed on column
     level.
  */
  bool using_column_privileges= FALSE;
unknown's avatar
unknown committed
5693

Marc Alff's avatar
Marc Alff committed
5694
  mysql_rwlock_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
5695

5696
  for (; !fields->end_of_fields(); fields->next())
unknown's avatar
unknown committed
5697
  {
unknown's avatar
VIEW  
unknown committed
5698
    const char *field_name= fields->name();
unknown's avatar
unknown committed
5699

5700
    if (table_name != fields->get_table_name())
5701
    {
5702 5703
      table_name= fields->get_table_name();
      db_name= fields->get_db_name();
5704 5705 5706 5707
      grant= fields->grant();
      /* get a fresh one for each table */
      want_access= want_access_arg & ~grant->privilege;
      if (want_access)
5708
      {
5709 5710
        /* reload table if someone has modified any grants */
        if (grant->version != grant_version)
5711
        {
5712
          grant->grant_table_user=
5713 5714 5715
            table_hash_search(sctx->host, sctx->ip, db_name,
                              sctx->priv_user,
                              table_name, 0);	/* purecov: inspected */
5716 5717 5718 5719
          grant->grant_table_role=
            sctx->priv_role[0] ? table_hash_search("", "", db_name,
                                                   sctx->priv_role,
                                                   table_name, TRUE) : NULL;
5720
          grant->version= grant_version;	/* purecov: inspected */
5721 5722
        }

5723
        grant_table= grant->grant_table_user;
5724
        DBUG_ASSERT (grant_table);
5725
        grant_table_role= grant->grant_table_role;
5726 5727
      }
    }
unknown's avatar
unknown committed
5728

5729 5730
    if (want_access)
    {
5731
      GRANT_COLUMN *grant_column=
5732 5733
        column_hash_search(grant_table, field_name,
                           (uint) strlen(field_name));
5734
      if (grant_column)
5735
      {
5736
        using_column_privileges= TRUE;
5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751
        want_access&= ~grant_column->rights;
      }
      if (grant_table_role)
      {
        grant_column=
          column_hash_search(grant_table_role, field_name,
                             (uint) strlen(field_name));
        if (grant_column)
        {
          using_column_privileges= TRUE;
          want_access&= ~grant_column->rights;
        }
      }

      if (!want_access)
5752 5753
        goto err;
    }
unknown's avatar
unknown committed
5754
  }
Marc Alff's avatar
Marc Alff committed
5755
  mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
5756 5757
  return 0;

unknown's avatar
unknown committed
5758
err:
Marc Alff's avatar
Marc Alff committed
5759
  mysql_rwlock_unlock(&LOCK_grant);
5760

5761 5762
  char command[128];
  get_privilege_desc(command, sizeof(command), want_access);
5763 5764 5765 5766 5767 5768 5769
  /*
    Do not give an error message listing a column name unless the user has
    privilege to see all columns.
  */
  if (using_column_privileges)
    my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
             command, sctx->priv_user,
5770
             sctx->host_or_ip, table_name);
5771 5772 5773 5774 5775 5776 5777
  else
    my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
             command,
             sctx->priv_user,
             sctx->host_or_ip,
             fields->name(),
             table_name);
unknown's avatar
unknown committed
5778 5779 5780 5781
  return 1;
}


5782 5783 5784 5785 5786 5787
static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
{
  Security_context *sctx= thd->security_ctx;

  for (uint idx= 0; idx < hash->records; ++idx)
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
5788
    GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801

    if (strcmp(item->user, sctx->priv_user) == 0 &&
        strcmp(item->db, db) == 0 &&
        compare_hostname(&item->host, sctx->host, sctx->ip))
    {
      return FALSE;
    }
  }

  return TRUE;
}


5802
/*
unknown's avatar
unknown committed
5803
  Check if a user has the right to access a database
5804
  Access is accepted if he has a grant for any table/routine in the database
unknown's avatar
unknown committed
5805
  Return 1 if access is denied
5806
*/
unknown's avatar
unknown committed
5807 5808 5809

bool check_grant_db(THD *thd,const char *db)
{
5810
  Security_context *sctx= thd->security_ctx;
5811
  char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end;
unknown's avatar
unknown committed
5812
  uint len;
5813
  bool error= TRUE;
unknown's avatar
unknown committed
5814

5815 5816 5817 5818 5819 5820 5821
  end= strmov(helping, sctx->priv_user) + 1;
  end= strnmov(end, db, helping + sizeof(helping) - end);

  if (end >= helping + sizeof(helping)) // db name was truncated
    return 1;                           // no privileges for an invalid db name

  len= (uint) (end - helping) + 1;
5822

Marc Alff's avatar
Marc Alff committed
5823
  mysql_rwlock_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
5824

5825
  for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
unknown's avatar
unknown committed
5826
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
5827 5828 5829
    GRANT_TABLE *grant_table= (GRANT_TABLE*)
      my_hash_element(&column_priv_hash,
                      idx);
unknown's avatar
unknown committed
5830 5831
    if (len < grant_table->key_length &&
	!memcmp(grant_table->hash_key,helping,len) &&
5832
        compare_hostname(&grant_table->host, sctx->host, sctx->ip))
unknown's avatar
unknown committed
5833
    {
5834
      error= FALSE; /* Found match. */
unknown's avatar
unknown committed
5835 5836 5837
      break;
    }
  }
5838 5839 5840 5841 5842

  if (error)
    error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
           check_grant_db_routine(thd, db, &func_priv_hash);

Marc Alff's avatar
Marc Alff committed
5843
  mysql_rwlock_unlock(&LOCK_grant);
5844

unknown's avatar
unknown committed
5845 5846 5847
  return error;
}

5848 5849

/****************************************************************************
5850
  Check routine level grants
5851 5852

  SYNPOSIS
5853
   bool check_grant_routine()
5854 5855
   thd		Thread handler
   want_access  Bits of privileges user needs to have
5856 5857
   procs	List of routines to check. The user should have 'want_access'
   is_proc	True if the list is all procedures, else functions
5858 5859 5860 5861 5862 5863 5864 5865
   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
****************************************************************************/

5866
bool check_grant_routine(THD *thd, ulong want_access,
5867
			 TABLE_LIST *procs, bool is_proc, bool no_errors)
5868 5869
{
  TABLE_LIST *table;
5870
  Security_context *sctx= thd->security_ctx;
5871 5872
  char *user= sctx->priv_user;
  char *host= sctx->priv_host;
5873
  DBUG_ENTER("check_grant_routine");
5874

5875
  want_access&= ~sctx->master_access;
5876 5877 5878
  if (!want_access)
    DBUG_RETURN(0);                             // ok

Marc Alff's avatar
Marc Alff committed
5879
  mysql_rwlock_rdlock(&LOCK_grant);
5880 5881 5882
  for (table= procs; table; table= table->next_global)
  {
    GRANT_NAME *grant_proc;
5883
    if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
5884
					 table->table_name, is_proc, 0)))
5885 5886 5887 5888 5889 5890 5891 5892
      table->grant.privilege|= grant_proc->privs;

    if (want_access & ~table->grant.privilege)
    {
      want_access &= ~table->grant.privilege;
      goto err;
    }
  }
Marc Alff's avatar
Marc Alff committed
5893
  mysql_rwlock_unlock(&LOCK_grant);
5894 5895
  DBUG_RETURN(0);
err:
Marc Alff's avatar
Marc Alff committed
5896
  mysql_rwlock_unlock(&LOCK_grant);
5897 5898 5899 5900 5901
  if (!no_errors)
  {
    char buff[1024];
    const char *command="";
    if (table)
5902
      strxmov(buff, table->db, ".", table->table_name, NullS);
5903 5904 5905
    if (want_access & EXECUTE_ACL)
      command= "execute";
    else if (want_access & ALTER_PROC_ACL)
5906
      command= "alter routine";
5907 5908 5909 5910 5911 5912 5913 5914 5915
    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);
}


5916
/*
5917
  Check if routine has any of the
5918
  routine level grants
5919

5920 5921 5922 5923 5924 5925 5926
  SYNPOSIS
   bool    check_routine_level_acl()
   thd	        Thread handler
   db           Database name
   name         Routine name

  RETURN
5927
   0            Ok
5928
   1            error
5929 5930
*/

5931
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
unknown's avatar
unknown committed
5932
                             bool is_proc)
5933 5934
{
  bool no_routine_acl= 1;
5935 5936
  GRANT_NAME *grant_proc;
  Security_context *sctx= thd->security_ctx;
Marc Alff's avatar
Marc Alff committed
5937
  mysql_rwlock_rdlock(&LOCK_grant);
5938 5939 5940 5941 5942
  if ((grant_proc= routine_hash_search(sctx->priv_host,
                                       sctx->ip, db,
                                       sctx->priv_user,
                                       name, is_proc, 0)))
    no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
Marc Alff's avatar
Marc Alff committed
5943
  mysql_rwlock_unlock(&LOCK_grant);
5944 5945 5946 5947
  return no_routine_acl;
}


unknown's avatar
unknown committed
5948
/*****************************************************************************
unknown's avatar
unknown committed
5949
  Functions to retrieve the grant for a table/column  (for SHOW functions)
unknown's avatar
unknown committed
5950 5951
*****************************************************************************/

unknown's avatar
unknown committed
5952
ulong get_table_grant(THD *thd, TABLE_LIST *table)
unknown's avatar
unknown committed
5953
{
unknown's avatar
unknown committed
5954
  ulong privilege;
5955 5956
  Security_context *sctx= thd->security_ctx;
  const char *db = table->db ? table->db : thd->db;
unknown's avatar
unknown committed
5957
  GRANT_TABLE *grant_table;
5958
  GRANT_TABLE *grant_table_role= NULL;
unknown's avatar
unknown committed
5959

5960
  mysql_rwlock_rdlock(&LOCK_grant);
5961 5962
#ifdef EMBEDDED_LIBRARY
  grant_table= NULL;
5963
  grant_table_role= NULL;
5964
#else
5965
  grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
5966
				 table->table_name, 0);
5967 5968 5969
  if (sctx->priv_role[0])
    grant_table_role= table_hash_search("", "", db, sctx->priv_role,
                                        table->table_name, 0);
5970
#endif
5971 5972
  table->grant.grant_table_user= grant_table; // Remember for column test
  table->grant.grant_table_role= grant_table_role;
unknown's avatar
unknown committed
5973 5974 5975
  table->grant.version=grant_version;
  if (grant_table)
    table->grant.privilege|= grant_table->privs;
5976 5977
  if (grant_table_role)
    table->grant.privilege|= grant_table_role->privs;
5978
  privilege= table->grant.privilege;
Marc Alff's avatar
Marc Alff committed
5979
  mysql_rwlock_unlock(&LOCK_grant);
5980
  return privilege;
unknown's avatar
unknown committed
5981 5982 5983
}


unknown's avatar
unknown committed
5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001
/*
  Determine the access priviliges for a field.

  SYNOPSIS
    get_column_grant()
    thd         thread handler
    grant       grants table descriptor
    db_name     name of database that the field belongs to
    table_name  name of table that the field belongs to
    field_name  name of field

  DESCRIPTION
    The procedure may also modify: grant->grant_table and grant->version.

  RETURN
    The access priviliges for the field db_name.table_name.field_name
*/

unknown's avatar
VIEW  
unknown committed
6002 6003 6004
ulong get_column_grant(THD *thd, GRANT_INFO *grant,
                       const char *db_name, const char *table_name,
                       const char *field_name)
unknown's avatar
unknown committed
6005 6006
{
  GRANT_TABLE *grant_table;
6007
  GRANT_TABLE *grant_table_role;
unknown's avatar
unknown committed
6008
  GRANT_COLUMN *grant_column;
6009
  ulong priv= 0;
unknown's avatar
unknown committed
6010

Marc Alff's avatar
Marc Alff committed
6011
  mysql_rwlock_rdlock(&LOCK_grant);
6012
  /* reload table if someone has modified any grants */
unknown's avatar
VIEW  
unknown committed
6013
  if (grant->version != grant_version)
unknown's avatar
unknown committed
6014
  {
6015
    Security_context *sctx= thd->security_ctx;
6016
    grant->grant_table_user=
6017 6018
      table_hash_search(sctx->host, sctx->ip,
                        db_name, sctx->priv_user,
6019 6020 6021 6022 6023
                        table_name, 0);         /* purecov: inspected */
    grant->grant_table_role=
      sctx->priv_role[0] ? table_hash_search("", "", db_name,
                                             sctx->priv_role,
                                             table_name, TRUE) : NULL;
unknown's avatar
VIEW  
unknown committed
6024
    grant->version= grant_version;              /* purecov: inspected */
unknown's avatar
unknown committed
6025 6026
  }

6027 6028
  if (!(grant_table= grant->grant_table_user) &&
      !(grant_table_role= grant->grant_table_role))
unknown's avatar
VIEW  
unknown committed
6029
    priv= grant->privilege;
unknown's avatar
unknown committed
6030 6031
  else
  {
6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050
    if (grant_table)
    {
      grant_column= column_hash_search(grant_table, field_name,
                                       (uint) strlen(field_name));
      if (!grant_column)
        priv= (grant->privilege | grant_table->privs);
      else
        priv= (grant->privilege | grant_table->privs | grant_column->rights);
    }

    if (grant_table_role)
    {
      grant_column= column_hash_search(grant_table_role, field_name,
                                       (uint) strlen(field_name));
      if (!grant_column)
        priv|= (grant->privilege | grant_table_role->privs);
      else
        priv|= (grant->privilege | grant_table->privs | grant_column->rights);
    }
unknown's avatar
unknown committed
6051
  }
Marc Alff's avatar
Marc Alff committed
6052
  mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
6053 6054 6055
  return priv;
}

unknown's avatar
VIEW  
unknown committed
6056

6057
/* Help function for mysql_show_grants */
unknown's avatar
unknown committed
6058

6059 6060
static void add_user_option(String *grant, long value, const char *name,
                            my_bool is_signed)
6061 6062 6063 6064 6065 6066 6067
{
  if (value)
  {
    char buff[22], *p; // just as in int2str
    grant->append(' ');
    grant->append(name, strlen(name));
    grant->append(' ');
6068
    p=int10_to_str(value, buff, is_signed ? -10 : 10);
6069 6070 6071
    grant->append(buff,p-buff);
  }
}
unknown's avatar
unknown committed
6072 6073

static const char *command_array[]=
unknown's avatar
unknown committed
6074
{
unknown's avatar
VIEW  
unknown committed
6075 6076 6077 6078
  "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",
6079
  "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
6080
  "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE"
unknown's avatar
unknown committed
6081
};
6082

unknown's avatar
unknown committed
6083 6084
static uint command_lengths[]=
{
6085
  6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
6086
  14, 13, 11, 5, 7, 17
unknown's avatar
unknown committed
6087 6088
};

unknown's avatar
unknown committed
6089

6090 6091 6092 6093 6094
static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
                               const char *type, int typelen,
                               char *buff, int buffsize);


6095 6096 6097 6098 6099 6100 6101
/*
  SHOW GRANTS;  Send grants for a user to the client

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

unknown's avatar
unknown committed
6102
bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
unknown's avatar
unknown committed
6103
{
unknown's avatar
unknown committed
6104 6105
  ulong want_access;
  uint counter,index;
unknown's avatar
unknown committed
6106
  int  error = 0;
unknown's avatar
unknown committed
6107 6108
  ACL_USER *acl_user;
  ACL_DB *acl_db;
unknown's avatar
unknown committed
6109
  char buff[1024];
6110
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
6111
  DBUG_ENTER("mysql_show_grants");
unknown's avatar
unknown committed
6112 6113 6114 6115

  LINT_INIT(acl_user);
  if (!initialized)
  {
unknown's avatar
unknown committed
6116
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
unknown's avatar
unknown committed
6117
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
6118
  }
unknown's avatar
unknown committed
6119

Marc Alff's avatar
Marc Alff committed
6120 6121
  mysql_rwlock_rdlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
6122

6123
  acl_user= find_user_no_anon(lex_user->host.str, lex_user->user.str, TRUE);
6124
  if (!acl_user)
unknown's avatar
unknown committed
6125
  {
Marc Alff's avatar
Marc Alff committed
6126 6127
    mysql_mutex_unlock(&acl_cache->lock);
    mysql_rwlock_unlock(&LOCK_grant);
6128

unknown's avatar
unknown committed
6129 6130
    my_error(ER_NONEXISTING_GRANT, MYF(0),
             lex_user->user.str, lex_user->host.str);
unknown's avatar
unknown committed
6131
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
6132 6133
  }

unknown's avatar
unknown committed
6134
  Item_string *field=new Item_string("",0,&my_charset_latin1);
unknown's avatar
unknown committed
6135 6136 6137 6138 6139 6140
  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);
6141
  if (protocol->send_result_set_metadata(&field_list,
6142
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
6143
  {
Marc Alff's avatar
Marc Alff committed
6144 6145
    mysql_mutex_unlock(&acl_cache->lock);
    mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
6146

6147 6148
    DBUG_RETURN(TRUE);
  }
unknown's avatar
unknown committed
6149 6150 6151

  /* Add first global access grants */
  {
6152
    String global(buff,sizeof(buff),system_charset_info);
unknown's avatar
unknown committed
6153
    global.length(0);
6154
    global.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
6155

unknown's avatar
unknown committed
6156
    want_access= acl_user->access;
unknown's avatar
unknown committed
6157
    if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
6158
      global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
6159
    else if (!(want_access & ~GRANT_ACL))
6160
      global.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
6161
    else
unknown's avatar
unknown committed
6162 6163
    {
      bool found=0;
unknown's avatar
unknown committed
6164
      ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
6165 6166
      for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
      {
unknown's avatar
unknown committed
6167
	if (test_access & j)
unknown's avatar
unknown committed
6168 6169
	{
	  if (found)
6170
	    global.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
6171 6172 6173 6174 6175
	  found=1;
	  global.append(command_array[counter],command_lengths[counter]);
	}
      }
    }
6176
    global.append (STRING_WITH_LEN(" ON *.* TO '"));
6177 6178
    global.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
6179
    global.append (STRING_WITH_LEN("'@'"));
6180 6181
    global.append(lex_user->host.str,lex_user->host.length,
		  system_charset_info);
unknown's avatar
unknown committed
6182
    global.append ('\'');
6183 6184
    if (acl_user->plugin.str == native_password_plugin_name.str ||
        acl_user->plugin.str == old_password_plugin_name.str)
unknown's avatar
unknown committed
6185
    {
6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203
      if (acl_user->auth_string.length)
      {
        DBUG_ASSERT(acl_user->salt_len);
        global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
        global.append(acl_user->auth_string.str, acl_user->auth_string.length);
        global.append('\'');
      }
    }
    else
    {
        global.append(STRING_WITH_LEN(" IDENTIFIED VIA "));
        global.append(acl_user->plugin.str, acl_user->plugin.length);
        if (acl_user->auth_string.length)
        {
          global.append(STRING_WITH_LEN(" USING '"));
          global.append(acl_user->auth_string.str, acl_user->auth_string.length);
          global.append('\'');
        }
unknown's avatar
unknown committed
6204
    }
6205 6206
    /* "show grants" SSL related stuff */
    if (acl_user->ssl_type == SSL_TYPE_ANY)
6207
      global.append(STRING_WITH_LEN(" REQUIRE SSL"));
6208
    else if (acl_user->ssl_type == SSL_TYPE_X509)
6209
      global.append(STRING_WITH_LEN(" REQUIRE X509"));
6210
    else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
6211
    {
6212
      int ssl_options = 0;
6213
      global.append(STRING_WITH_LEN(" REQUIRE "));
6214 6215
      if (acl_user->x509_issuer)
      {
6216
	ssl_options++;
6217
	global.append(STRING_WITH_LEN("ISSUER \'"));
6218
	global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
6219
	global.append('\'');
6220
      }
6221 6222
      if (acl_user->x509_subject)
      {
6223 6224
	if (ssl_options++)
	  global.append(' ');
6225
	global.append(STRING_WITH_LEN("SUBJECT \'"));
6226 6227
	global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
                      system_charset_info);
6228
	global.append('\'');
unknown's avatar
unknown committed
6229
      }
6230 6231
      if (acl_user->ssl_cipher)
      {
6232 6233
	if (ssl_options++)
	  global.append(' ');
6234
	global.append(STRING_WITH_LEN("CIPHER '"));
6235 6236
	global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
                      system_charset_info);
6237
	global.append('\'');
6238 6239
      }
    }
unknown's avatar
unknown committed
6240
    if ((want_access & GRANT_ACL) ||
6241 6242 6243 6244
	(acl_user->user_resource.questions ||
         acl_user->user_resource.updates ||
         acl_user->user_resource.conn_per_hour ||
         acl_user->user_resource.user_conn))
6245
    {
6246
      global.append(STRING_WITH_LEN(" WITH"));
unknown's avatar
unknown committed
6247
      if (want_access & GRANT_ACL)
6248
	global.append(STRING_WITH_LEN(" GRANT OPTION"));
6249
      add_user_option(&global, acl_user->user_resource.questions,
6250
		      "MAX_QUERIES_PER_HOUR", 0);
6251
      add_user_option(&global, acl_user->user_resource.updates,
6252
		      "MAX_UPDATES_PER_HOUR", 0);
6253
      add_user_option(&global, acl_user->user_resource.conn_per_hour,
6254
		      "MAX_CONNECTIONS_PER_HOUR", 0);
6255
      add_user_option(&global, acl_user->user_resource.user_conn,
6256
		      "MAX_USER_CONNECTIONS", 1);
unknown's avatar
unknown committed
6257
    }
6258
    protocol->prepare_for_resend();
6259
    protocol->store(global.ptr(),global.length(),global.charset());
6260
    if (protocol->write())
unknown's avatar
unknown committed
6261
    {
unknown's avatar
unknown committed
6262
      error= -1;
6263
      goto end;
unknown's avatar
unknown committed
6264 6265 6266 6267 6268 6269
    }
  }

  /* Add database access */
  for (counter=0 ; counter < acl_dbs.elements ; counter++)
  {
unknown's avatar
unknown committed
6270
    const char *user, *host;
unknown's avatar
unknown committed
6271 6272 6273

    acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
    if (!(user=acl_db->user))
unknown's avatar
unknown committed
6274
      user= "";
unknown's avatar
unknown committed
6275
    if (!(host=acl_db->host.hostname))
unknown's avatar
unknown committed
6276
      host= "";
unknown's avatar
unknown committed
6277

6278 6279 6280 6281 6282 6283 6284
    /*
      We do not make SHOW GRANTS case-sensitive here (like REVOKE),
      but make it case-insensitive because that's the way they are
      actually applied, and showing fewer privileges than are applied
      would be wrong from a security point of view.
    */

unknown's avatar
unknown committed
6285
    if (!strcmp(lex_user->user.str,user) &&
6286
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
unknown's avatar
unknown committed
6287 6288
    {
      want_access=acl_db->access;
unknown's avatar
unknown committed
6289
      if (want_access)
unknown's avatar
unknown committed
6290
      {
6291
	String db(buff,sizeof(buff),system_charset_info);
unknown's avatar
unknown committed
6292
	db.length(0);
6293
	db.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
6294 6295

	if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
6296
	  db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
6297
	else if (!(want_access & ~GRANT_ACL))
6298
	  db.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
6299 6300 6301
	else
	{
	  int found=0, cnt;
unknown's avatar
unknown committed
6302
	  ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
6303 6304 6305 6306 6307
	  for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
	  {
	    if (test_access & j)
	    {
	      if (found)
6308
		db.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
6309 6310 6311 6312 6313
	      found = 1;
	      db.append(command_array[cnt],command_lengths[cnt]);
	    }
	  }
	}
6314
	db.append (STRING_WITH_LEN(" ON "));
6315
	append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
6316
	db.append (STRING_WITH_LEN(".* TO '"));
6317 6318
	db.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
6319
	db.append (STRING_WITH_LEN("'@'"));
6320 6321
	// host and lex_user->host are equal except for case
	db.append(host, strlen(host), system_charset_info);
unknown's avatar
unknown committed
6322
	db.append ('\'');
unknown's avatar
unknown committed
6323
	if (want_access & GRANT_ACL)
6324
	  db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
6325
	protocol->prepare_for_resend();
6326
	protocol->store(db.ptr(),db.length(),db.charset());
6327
	if (protocol->write())
unknown's avatar
unknown committed
6328
	{
unknown's avatar
unknown committed
6329
	  error= -1;
unknown's avatar
unknown committed
6330 6331 6332 6333 6334 6335
	  goto end;
	}
      }
    }
  }

6336
  /* Add table & column access */
6337
  for (index=0 ; index < column_priv_hash.records ; index++)
unknown's avatar
unknown committed
6338
  {
6339
    const char *user, *host;
Konstantin Osipov's avatar
Konstantin Osipov committed
6340 6341
    GRANT_TABLE *grant_table= (GRANT_TABLE*)
      my_hash_element(&column_priv_hash, index);
unknown's avatar
unknown committed
6342 6343

    if (!(user=grant_table->user))
6344
      user= "";
6345 6346
    if (!(host= grant_table->host.hostname))
      host= "";
unknown's avatar
unknown committed
6347

6348 6349 6350 6351 6352 6353 6354
    /*
      We do not make SHOW GRANTS case-sensitive here (like REVOKE),
      but make it case-insensitive because that's the way they are
      actually applied, and showing fewer privileges than are applied
      would be wrong from a security point of view.
    */

unknown's avatar
unknown committed
6355
    if (!strcmp(lex_user->user.str,user) &&
6356
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
unknown's avatar
unknown committed
6357
    {
6358 6359
      ulong table_access= grant_table->privs;
      if ((table_access | grant_table->cols) != 0)
unknown's avatar
unknown committed
6360
      {
6361
	String global(buff, sizeof(buff), system_charset_info);
unknown's avatar
unknown committed
6362 6363
	ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;

unknown's avatar
unknown committed
6364
	global.length(0);
6365
	global.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
6366

6367
	if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
6368
	  global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
6369
	else if (!test_access)
6370
	  global.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
6371
	else
unknown's avatar
unknown committed
6372
	{
6373
          /* Add specific column access */
6374
	  int found= 0;
unknown's avatar
unknown committed
6375
	  ulong j;
unknown's avatar
unknown committed
6376

6377
	  for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
unknown's avatar
unknown committed
6378
	  {
unknown's avatar
unknown committed
6379
	    if (test_access & j)
unknown's avatar
unknown committed
6380 6381
	    {
	      if (found)
6382
		global.append(STRING_WITH_LEN(", "));
6383
	      found= 1;
unknown's avatar
unknown committed
6384 6385
	      global.append(command_array[counter],command_lengths[counter]);

unknown's avatar
unknown committed
6386
	      if (grant_table->cols)
unknown's avatar
unknown committed
6387
	      {
6388
		uint found_col= 0;
unknown's avatar
unknown committed
6389 6390 6391 6392 6393
		for (uint col_index=0 ;
		     col_index < grant_table->hash_columns.records ;
		     col_index++)
		{
		  GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
Konstantin Osipov's avatar
Konstantin Osipov committed
6394
                    my_hash_element(&grant_table->hash_columns,col_index);
unknown's avatar
unknown committed
6395
		  if (grant_column->rights & j)
unknown's avatar
unknown committed
6396
		  {
unknown's avatar
unknown committed
6397
		    if (!found_col)
unknown's avatar
unknown committed
6398
		    {
6399 6400 6401 6402 6403 6404 6405
		      found_col= 1;
		      /*
			If we have a duplicated table level privilege, we
			must write the access privilege name again.
		      */
		      if (table_access & j)
		      {
6406
			global.append(STRING_WITH_LEN(", "));
6407 6408 6409
			global.append(command_array[counter],
				      command_lengths[counter]);
		      }
6410
		      global.append(STRING_WITH_LEN(" ("));
unknown's avatar
unknown committed
6411 6412
		    }
		    else
6413
		      global.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
6414
		    global.append(grant_column->column,
6415 6416
				  grant_column->key_length,
				  system_charset_info);
unknown's avatar
unknown committed
6417 6418 6419 6420 6421 6422 6423 6424
		  }
		}
		if (found_col)
		  global.append(')');
	      }
	    }
	  }
	}
6425
	global.append(STRING_WITH_LEN(" ON "));
6426 6427 6428 6429 6430
	append_identifier(thd, &global, grant_table->db,
			  strlen(grant_table->db));
	global.append('.');
	append_identifier(thd, &global, grant_table->tname,
			  strlen(grant_table->tname));
6431
	global.append(STRING_WITH_LEN(" TO '"));
6432 6433
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
6434
	global.append(STRING_WITH_LEN("'@'"));
6435 6436
	// host and lex_user->host are equal except for case
	global.append(host, strlen(host), system_charset_info);
unknown's avatar
unknown committed
6437
	global.append('\'');
6438
	if (table_access & GRANT_ACL)
6439
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
6440
	protocol->prepare_for_resend();
6441
	protocol->store(global.ptr(),global.length(),global.charset());
6442
	if (protocol->write())
unknown's avatar
unknown committed
6443
	{
unknown's avatar
unknown committed
6444
	  error= -1;
6445
	  break;
unknown's avatar
unknown committed
6446 6447 6448 6449
	}
      }
    }
  }
6450

6451
  if (show_routine_grants(thd, lex_user, &proc_priv_hash,
6452
                          STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
6453 6454 6455 6456 6457 6458
  {
    error= -1;
    goto end;
  }

  if (show_routine_grants(thd, lex_user, &func_priv_hash,
6459
                          STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
6460 6461 6462 6463 6464
  {
    error= -1;
    goto end;
  }

6465 6466 6467 6468 6469 6470
  if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
  {
    error= -1;
    goto end;
  }

6471
end:
Marc Alff's avatar
Marc Alff committed
6472 6473
  mysql_mutex_unlock(&acl_cache->lock);
  mysql_rwlock_unlock(&LOCK_grant);
6474

6475
  my_eof(thd);
6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487
  DBUG_RETURN(error);
}

static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
                               const char *type, int typelen,
                               char *buff, int buffsize)
{
  uint counter, index;
  int error= 0;
  Protocol *protocol= thd->protocol;
  /* Add routine access */
  for (index=0 ; index < hash->records ; index++)
6488
  {
6489
    const char *user, *host;
Konstantin Osipov's avatar
Konstantin Osipov committed
6490
    GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
6491 6492 6493

    if (!(user=grant_proc->user))
      user= "";
6494 6495
    if (!(host= grant_proc->host.hostname))
      host= "";
6496

6497 6498 6499 6500 6501 6502 6503
    /*
      We do not make SHOW GRANTS case-sensitive here (like REVOKE),
      but make it case-insensitive because that's the way they are
      actually applied, and showing fewer privileges than are applied
      would be wrong from a security point of view.
    */

6504
    if (!strcmp(lex_user->user.str,user) &&
6505
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
6506 6507 6508 6509
    {
      ulong proc_access= grant_proc->privs;
      if (proc_access != 0)
      {
6510
	String global(buff, buffsize, system_charset_info);
6511 6512 6513
	ulong test_access= proc_access & ~GRANT_ACL;

	global.length(0);
6514
	global.append(STRING_WITH_LEN("GRANT "));
6515 6516

	if (!test_access)
6517
 	  global.append(STRING_WITH_LEN("USAGE"));
6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528
	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)
6529
		global.append(STRING_WITH_LEN(", "));
6530 6531 6532 6533 6534
	      found= 1;
	      global.append(command_array[counter],command_lengths[counter]);
	    }
	  }
	}
6535
	global.append(STRING_WITH_LEN(" ON "));
6536 6537
        global.append(type,typelen);
        global.append(' ');
6538 6539 6540 6541 6542
	append_identifier(thd, &global, grant_proc->db,
			  strlen(grant_proc->db));
	global.append('.');
	append_identifier(thd, &global, grant_proc->tname,
			  strlen(grant_proc->tname));
6543
	global.append(STRING_WITH_LEN(" TO '"));
6544 6545
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
6546
	global.append(STRING_WITH_LEN("'@'"));
6547 6548
	// host and lex_user->host are equal except for case
	global.append(host, strlen(host), system_charset_info);
6549 6550
	global.append('\'');
	if (proc_access & GRANT_ACL)
6551
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
6552 6553 6554 6555 6556 6557 6558 6559 6560 6561
	protocol->prepare_for_resend();
	protocol->store(global.ptr(),global.length(),global.charset());
	if (protocol->write())
	{
	  error= -1;
	  break;
	}
      }
    }
  }
6562
  return error;
unknown's avatar
unknown committed
6563 6564
}

unknown's avatar
unknown committed
6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592
/*
  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;
}


6593
void get_mqh(const char *user, const char *host, USER_CONN *uc)
unknown's avatar
unknown committed
6594 6595
{
  ACL_USER *acl_user;
6596

Marc Alff's avatar
Marc Alff committed
6597
  mysql_mutex_lock(&acl_cache->lock);
6598

6599
  if (initialized && (acl_user= find_user_no_anon(host,user, FALSE)))
6600 6601 6602
    uc->user_resources= acl_user->user_resource;
  else
    bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
6603

Marc Alff's avatar
Marc Alff committed
6604
  mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
6605 6606
}

6607 6608 6609 6610 6611 6612
/*
  Open the grant tables.

  SYNOPSIS
    open_grant_tables()
    thd                         The current thread.
6613
    tables (out)                The 7 elements array for the opened tables.
6614 6615 6616 6617 6618 6619 6620

  DESCRIPTION
    Tables are numbered as follows:
    0 user
    1 db
    2 tables_priv
    3 columns_priv
6621 6622 6623
    4 columns_priv
    5 proxies_priv
    6 roles_mapping
6624 6625 6626 6627 6628 6629 6630

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

6631
#define GRANT_TABLES 7
6632 6633
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
6634
  Rpl_filter *rpl_filter= thd->rpl_filter;
6635 6636 6637 6638
  DBUG_ENTER("open_grant_tables");

  if (!initialized)
  {
unknown's avatar
unknown committed
6639
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
6640 6641 6642
    DBUG_RETURN(-1);
  }

6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655
  tables->init_one_table(C_STRING_WITH_LEN("mysql"),
                         C_STRING_WITH_LEN("user"), "user", TL_WRITE);
  (tables+1)->init_one_table(C_STRING_WITH_LEN("mysql"),
                             C_STRING_WITH_LEN("db"), "db", TL_WRITE);
  (tables+2)->init_one_table(C_STRING_WITH_LEN("mysql"),
                             C_STRING_WITH_LEN("tables_priv"),
                             "tables_priv", TL_WRITE);
  (tables+3)->init_one_table(C_STRING_WITH_LEN("mysql"),
                             C_STRING_WITH_LEN("columns_priv"),
                             "columns_priv", TL_WRITE);
  (tables+4)->init_one_table(C_STRING_WITH_LEN("mysql"),
                             C_STRING_WITH_LEN("procs_priv"),
                             "procs_priv", TL_WRITE);
6656
  (tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"),
6657 6658
                             C_STRING_WITH_LEN("proxies_priv"),
                             "proxies_priv", TL_WRITE);
6659 6660 6661 6662 6663 6664
  (tables+5)->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
  (tables+6)->init_one_table(C_STRING_WITH_LEN("mysql"),
                             C_STRING_WITH_LEN("roles_mapping"),
                             "roles_mapping", TL_WRITE);
  (tables+6)->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;

6665

6666 6667 6668 6669 6670
  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;
  (tables+3)->next_local= (tables+3)->next_global= tables + 4;
  (tables+4)->next_local= (tables+4)->next_global= tables + 5;
6671
  (tables+5)->next_local= (tables+5)->next_global= tables + 6;
6672 6673 6674 6675 6676 6677

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
6678
  if (thd->slave_thread && rpl_filter->is_on())
6679
  {
unknown's avatar
unknown committed
6680 6681 6682
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
6683
    */
6684 6685
    tables[0].updating= tables[1].updating= tables[2].updating=
      tables[3].updating= tables[4].updating= tables[5].updating= 1;
unknown's avatar
unknown committed
6686
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
6687
      DBUG_RETURN(1);
6688 6689
    tables[0].updating= tables[1].updating= tables[2].updating=
      tables[3].updating= tables[4].updating= tables[5].updating= 0;
6690
  }
6691 6692
#endif

6693
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
6694 6695 6696 6697 6698 6699 6700 6701
  {						// This should never happen
    DBUG_RETURN(-1);
  }

  DBUG_RETURN(0);
}

ACL_USER *check_acl_user(LEX_USER *user_name,
unknown's avatar
merge  
unknown committed
6702
			 uint *acl_acl_userdx)
6703 6704 6705 6706
{
  ACL_USER *acl_user= 0;
  uint counter;

Marc Alff's avatar
Marc Alff committed
6707
  mysql_mutex_assert_owner(&acl_cache->lock);
6708

6709 6710 6711 6712
  for (counter= 0 ; counter < acl_users.elements ; counter++)
  {
    const char *user,*host;
    acl_user= dynamic_element(&acl_users, counter, ACL_USER*);
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
6713
    if (!(user=acl_user->user.str))
unknown's avatar
unknown committed
6714
      user= "";
6715
    if (!(host=acl_user->host.hostname))
6716
      host= "";
6717 6718 6719 6720 6721 6722 6723
    if (!strcmp(user_name->user.str,user) &&
	!my_strcasecmp(system_charset_info, user_name->host.str, host))
      break;
  }
  if (counter == acl_users.elements)
    return 0;

unknown's avatar
merge  
unknown committed
6724
  *acl_acl_userdx= counter;
unknown's avatar
unknown committed
6725
  return acl_user;
6726
}
unknown's avatar
unknown committed
6727

6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749
/*
  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)
6750
{
6751 6752
  int error;
  DBUG_ENTER("modify_grant_table");
6753

6754 6755 6756 6757 6758 6759 6760 6761
  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);
6762
    if ((error= table->file->ha_update_row(table->record[1],
6763 6764
                                           table->record[0])) &&
        error != HA_ERR_RECORD_IS_THE_SAME)
6765
      table->file->print_error(error, MYF(0));
6766 6767
    else
      error= 0;
6768 6769 6770 6771
  }
  else
  {
    /* delete */
6772
    if ((error=table->file->ha_delete_row(table->record[0])))
6773 6774
      table->file->print_error(error, MYF(0));
  }
6775

6776 6777
  DBUG_RETURN(error);
}
6778

6779 6780 6781 6782 6783
/*
  Handle the roles_mappings privilege table


*/
6784 6785 6786
static int handle_roles_mappings_table(TABLE *table, bool drop,
                                       LEX_USER *user_from, LEX_USER *user_to)
{
6787
  /*
6788 6789 6790
    The first thing that needs to be checked is what we are renaming,
    a user, or a role. In order to do this, perform a hash lookup over
    acl_roles to find if a key exists.
6791

6792 6793 6794
    If the renaming involves renaming a role, all entries
    (HostFK, UserFk) that match user_from will be renamed,
    as well as all RoleFk entries that match.
6795

6796
    Otherwise, only matching (HostFk, UserFk) will be renamed.
6797 6798 6799 6800 6801 6802
  */
  DBUG_ENTER("handle_roles_mappings_table");

  int error;
  int result= 0;
  THD *thd= current_thd;
6803
  const char *host, *user, *role;
6804
  my_bool is_role= FALSE;
6805 6806 6807 6808 6809
  Field *host_field= table->field[0];
  Field *user_field= table->field[1];
  Field *role_field= table->field[2];

  if (!user_from->host.length && find_acl_role(user_from->user.str))
6810
    is_role= TRUE;
6811

6812 6813 6814 6815
  DBUG_PRINT("info", ("Rewriting %s entry in roles_mappings table: %s %s",
                      is_role ? "role" : "user",
                      user_from->user.str,
                      user_from->host.str));
6816
  table->use_all_columns();
6817
  if ((error= table->file->ha_rnd_init(1)))
6818
  {
6819 6820 6821 6822 6823 6824 6825
    table->file->print_error(error, MYF(0));
    result= -1;
  }
  else
  {
    while((error= table->file->ha_rnd_next(table->record[0])) !=
          HA_ERR_END_OF_FILE)
6826
    {
6827
      if (error)
6828
      {
6829 6830 6831
        DBUG_PRINT("info", ("scan error: %d", error));
        continue;
      }
6832

6833 6834 6835 6836
      if (! (host= get_field(thd->mem_root, host_field)))
        host= "";
      if (! (user= get_field(thd->mem_root, user_field)))
        user= "";
6837

6838 6839
      if (!(strcmp(user_from->user.str, user) ||
          my_strcasecmp(system_charset_info, user_from->host.str, host)))
6840 6841 6842
        result= ((drop || user_to) &&
                 modify_grant_table(table, host_field, user_field, user_to)) ?
          -1 : result ? result : 1; /* Error or keep result or found. */
6843
      if (is_role)
6844 6845 6846 6847 6848 6849
      {
        if (! (role= get_field(thd->mem_root, role_field)))
          role= "";

        if (strcmp(user_from->user.str, role))
          continue;
6850

6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870
        error= 0;

        if (drop) /* drop if requested */
        {
          if ((error= table->file->ha_delete_row(table->record[0])))
            table->file->print_error(error, MYF(0));
        }
        else if (user_to)
        {
          store_record(table, record[1]);
          role_field->store(user_to->user.str, user_to->user.length,
                            system_charset_info);
          if ((error= table->file->ha_update_row(table->record[1],
                                                 table->record[0])) &&
              error != HA_ERR_RECORD_IS_THE_SAME)
            table->file->print_error(error, MYF(0));
        }

        /* Error or keep result or found. */
        result= error ? -1 : result ? result : 1;
6871 6872
      }
    }
6873
    table->file->ha_rnd_end();
6874 6875
  }
  DBUG_RETURN(result);
6876
}
6877 6878 6879 6880 6881 6882
/*
  Handle a privilege table.

  SYNOPSIS
    handle_grant_table()
    tables                      The array with the four open tables.
6883
    table_no                    The number of the table to handle (0..4).
6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900
    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
6901
    4 procs_priv
6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915

  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];
6916
  Field *user_field= table->field[table_no && table_no != 5 ? 2 : 1];
6917 6918 6919 6920
  char *host_str= user_from->host.str;
  char *user_str= user_from->user.str;
  const char *host;
  const char *user;
6921
  uchar user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
6922
  uint key_prefix_length;
6923
  DBUG_ENTER("handle_grant_table");
6924
  THD *thd= current_thd;
6925

6926 6927
  if (table_no == 6)
  {
6928
    result= handle_roles_mappings_table(table, drop, user_from, user_to);
6929 6930 6931
    DBUG_RETURN(result);
  }

6932
  table->use_all_columns();
unknown's avatar
unknown committed
6933
  if (! table_no) // mysql.user table
6934
  {
6935 6936 6937 6938 6939 6940 6941 6942 6943 6944
    /*
      The 'user' table has an unique index on (host, user).
      Thus, we can handle everything with a single index access.
      The host- and user fields are consecutive in the user table records.
      So we set host- and user fields of table->record[0] and use the
      pointer to the host field as key.
      index_read_idx() will replace table->record[0] (its first argument)
      by the searched record, if it exists.
    */
    DBUG_PRINT("info",("read table: '%s'  search: '%s'@'%s'",
unknown's avatar
unknown committed
6945
                       table->s->table_name.str, user_str, host_str));
6946 6947
    host_field->store(host_str, user_from->host.length, system_charset_info);
    user_field->store(user_str, user_from->user.length, system_charset_info);
unknown's avatar
unknown committed
6948 6949 6950 6951 6952

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

6953 6954 6955
    if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
                                                   user_key, (key_part_map)3,
                                                   HA_READ_KEY_EXACT)))
6956
    {
6957
      if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
6958 6959 6960 6961
      {
        table->file->print_error(error, MYF(0));
        result= -1;
      }
6962
    }
6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979
    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)))
6980
    {
6981
      table->file->print_error(error, MYF(0));
6982
      result= -1;
6983 6984 6985 6986 6987
    }
    else
    {
#ifdef EXTRA_DEBUG
      DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
unknown's avatar
unknown committed
6988
                         table->s->table_name.str, user_str, host_str));
6989
#endif
6990
      while ((error= table->file->ha_rnd_next(table->record[0])) !=
6991 6992 6993 6994 6995 6996 6997 6998
             HA_ERR_END_OF_FILE)
      {
        if (error)
        {
          /* Most probable 'deleted record'. */
          DBUG_PRINT("info",("scan error: %d", error));
          continue;
        }
6999
        if (! (host= get_field(thd->mem_root, host_field)))
7000
          host= "";
7001
        if (! (user= get_field(thd->mem_root, user_field)))
7002 7003 7004
          user= "";

#ifdef EXTRA_DEBUG
7005 7006 7007 7008 7009 7010 7011 7012 7013
        if (table_no != 5)
        {
          DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
                             user, host,
                             get_field(thd->mem_root, table->field[1]) /*db*/,
                             get_field(thd->mem_root, table->field[3]) /*table*/,
                             get_field(thd->mem_root,
                                       table->field[4]) /*column*/));
        }
7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035
#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);
}


7036
/**
7037 7038
  Handle an in-memory privilege structure.

7039
  @param struct_no  The number of the structure to handle (0..6).
7040 7041 7042
  @param drop       If user_from is to be dropped.
  @param user_from  The the user to be searched/dropped/renamed.
  @param user_to    The new name for the user if to be renamed, NULL otherwise.
7043

7044
  @note
7045 7046 7047 7048 7049
    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.
7050 7051 7052 7053 7054 7055
    Structures are enumerated as follows:
    0 ACL_USER
    1 ACL_DB
    2 COLUMN_PRIVILEGES_HASH
    3 PROC_PRIVILEGES_HASH
    4 FUNC_PRIVILEGES_HASH
Sergei Golubchik's avatar
Sergei Golubchik committed
7056
    5 PROXY_USERS_ACL
7057
    6 ROLES_MAPPINGS_HASH
7058

7059 7060
  @retval > 0  At least one element matched.
  @retval 0    OK, but no element matched.
7061 7062
*/

7063
static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
7064 7065 7066
                               LEX_USER *user_from, LEX_USER *user_to)
{
  int result= 0;
7067 7068
  int idx;
  int elements;
7069 7070
  const char *user;
  const char *host;
7071 7072 7073
  const char *role;
  my_bool is_role= FALSE;
  uint role_not_matched= 1;
Staale Smedseng's avatar
Staale Smedseng committed
7074
  ACL_USER *acl_user= NULL;
7075
  ACL_ROLE *acl_role= NULL;
Staale Smedseng's avatar
Staale Smedseng committed
7076
  ACL_DB *acl_db= NULL;
7077
  ACL_PROXY_USER *acl_proxy_user= NULL;
Staale Smedseng's avatar
Staale Smedseng committed
7078
  GRANT_NAME *grant_name= NULL;
7079
  ROLE_GRANT_PAIR *role_grant_pair;
7080
  HASH *grant_name_hash= NULL;
7081
  HASH *roles_mappings_hash= NULL;
7082
  DBUG_ENTER("handle_grant_struct");
unknown's avatar
unknown committed
7083 7084 7085
  DBUG_PRINT("info",("scan struct: %u  search: '%s'@'%s'",
                     struct_no, user_from->user.str, user_from->host.str));

7086 7087
  LINT_INIT(user);
  LINT_INIT(host);
7088

Marc Alff's avatar
Marc Alff committed
7089
  mysql_mutex_assert_owner(&acl_cache->lock);
7090

7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119
  /* test if the current query targets a role */
  is_role= (!user_from->host.length &&
            (acl_role= find_acl_role(user_from->user.str))) ? TRUE : FALSE;
  if (is_role && (struct_no != ROLE_ACL || struct_no != ROLES_MAPPINGS_HASH))
  {
    DBUG_RETURN(0);
  }
  else if (struct_no == ROLE_ACL) //no need to scan the structures in this case
  {
    if (!is_role || (!drop && !user_to))
      DBUG_RETURN(is_role);

    /* this calls for a role update */
    char *old_key= acl_role->user.str;
    size_t old_key_length= acl_role->user.length;
    if (drop)
    {
      my_hash_delete(&acl_roles, (uchar*) acl_role);
      DBUG_RETURN(1);
    }
    acl_role->user.str= strdup_root(&mem, user_to->user.str);
    acl_role->user.length= user_to->user.length;

    my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key,
                   old_key_length);
    DBUG_RETURN(1);

  }

7120
  /* Get the number of elements in the in-memory structure. */
7121
  switch (struct_no) {
7122
  case USER_ACL:
7123 7124
    elements= acl_users.elements;
    break;
7125
  case DB_ACL:
7126 7127
    elements= acl_dbs.elements;
    break;
7128
  case COLUMN_PRIVILEGES_HASH:
7129
    grant_name_hash= &column_priv_hash;
7130
    elements= grant_name_hash->records;
7131
    break;
7132
  case PROC_PRIVILEGES_HASH:
7133
    grant_name_hash= &proc_priv_hash;
7134
    elements= grant_name_hash->records;
7135
    break;
7136
  case FUNC_PRIVILEGES_HASH:
7137
    grant_name_hash= &func_priv_hash;
7138
    elements= grant_name_hash->records;
7139
    break;
7140
  case PROXY_USERS_ACL:
7141
    elements= acl_proxy_users.elements;
7142
    break;
7143 7144 7145 7146
  case ROLES_MAPPINGS_HASH:
    roles_mappings_hash= &acl_roles_mappings;
    elements= roles_mappings_hash->records;
    break;
7147
  default:
Sergei Golubchik's avatar
Sergei Golubchik committed
7148
    DBUG_ASSERT(0);
7149
    return -1;
7150 7151 7152 7153 7154 7155
  }

#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
7156 7157
  /* Loop over all elements *backwards* (see the comment below). */
  for (idx= elements - 1; idx >= 0; idx--)
7158 7159 7160 7161
  {
    /*
      Get a pointer to the element.
    */
7162
    switch (struct_no) {
7163
    case USER_ACL:
7164
      acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
7165
      user= acl_user->user.str;
7166 7167
      host= acl_user->host.hostname;
    break;
7168

7169
    case DB_ACL:
7170 7171
      acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
      user= acl_db->user;
7172
      host= acl_db->host.hostname;
7173 7174
      break;

7175 7176 7177
    case COLUMN_PRIVILEGES_HASH:
    case PROC_PRIVILEGES_HASH:
    case FUNC_PRIVILEGES_HASH:
Georgi Kodinov's avatar
Georgi Kodinov committed
7178
      grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
7179
      user= grant_name->user;
7180
      host= grant_name->host.hostname;
7181 7182
      break;

7183
    case PROXY_USERS_ACL:
7184
      acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
7185 7186
      user= acl_proxy_user->get_user();
      host= acl_proxy_user->get_host();
7187
      break;
Georgi Kodinov's avatar
Georgi Kodinov committed
7188

7189 7190 7191 7192
    case ROLES_MAPPINGS_HASH:
      role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx);
      user= role_grant_pair->u_uname;
      host= role_grant_pair->u_hname;
7193
      role= role_grant_pair->r_uname;
7194 7195
      break;

unknown's avatar
unknown committed
7196
    default:
7197
      DBUG_ASSERT(0);
7198 7199
    }
    if (! user)
7200
      user= "";
7201 7202
    if (! host)
      host= "";
7203 7204
    if (! role)
      role= "";
7205

7206 7207 7208 7209
#ifdef EXTRA_DEBUG
    DBUG_PRINT("loop",("scan struct: %u  index: %u  user: '%s'  host: '%s'",
                       struct_no, idx, user, host));
#endif
7210

7211
    if (strcmp(user_from->user.str, user) ||
7212 7213 7214
        my_strcasecmp(system_charset_info, user_from->host.str, host) ||
        (is_role && (role_not_matched= strcmp(user_from->user.str, role)))
        )
7215
      continue;
7216 7217 7218 7219

    result= 1; /* At least one element found. */
    if ( drop )
    {
7220
      elements--;
7221
      switch ( struct_no ) {
7222
      case USER_ACL:
7223
        free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*));
7224 7225 7226
        delete_dynamic_element(&acl_users, idx);
        break;

7227
      case DB_ACL:
7228 7229 7230
        delete_dynamic_element(&acl_dbs, idx);
        break;

7231 7232 7233
      case COLUMN_PRIVILEGES_HASH:
      case PROC_PRIVILEGES_HASH:
      case FUNC_PRIVILEGES_HASH:
Georgi Kodinov's avatar
Georgi Kodinov committed
7234
        my_hash_delete(grant_name_hash, (uchar*) grant_name);
7235 7236 7237 7238 7239 7240 7241 7242 7243
        /*
          In our HASH implementation on deletion one elements
          is moved into a place where a deleted element was,
          and the last element is moved into the empty space.
          Thus we need to re-examine the current element, but
          we don't have to restart the search from the beginning.
        */
        if (idx != elements)
          idx++;
7244
	break;
7245

7246
      case PROXY_USERS_ACL:
7247 7248 7249
        delete_dynamic_element(&acl_proxy_users, idx);
        break;

7250 7251 7252 7253
      case ROLES_MAPPINGS_HASH:
        my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair);
        break;

7254 7255 7256
      default:
        DBUG_ASSERT(0);
        break;
7257
      }
7258
    }
7259 7260
    else if ( user_to )
    {
7261
      switch ( struct_no ) {
7262
      case USER_ACL:
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
7263 7264
        acl_user->user.str= strdup_root(&mem, user_to->user.str);
        acl_user->user.length= user_to->user.length;
7265 7266
        acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
        break;
7267

7268
      case DB_ACL:
7269 7270 7271 7272
        acl_db->user= strdup_root(&mem, user_to->user.str);
        acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
        break;

7273 7274 7275
      case COLUMN_PRIVILEGES_HASH:
      case PROC_PRIVILEGES_HASH:
      case FUNC_PRIVILEGES_HASH:
7276 7277
        {
          /*
7278
            Save old hash key and its length to be able to properly update
7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295
            element position in hash.
          */
          char *old_key= grant_name->hash_key;
          size_t old_key_length= grant_name->key_length;

          /*
            Update the grant structure with the new user name and host name.
          */
          grant_name->set_user_details(user_to->host.str, grant_name->db,
                                       user_to->user.str, grant_name->tname,
                                       TRUE);

          /*
            Since username is part of the hash key, when the user name
            is renamed, the hash key is changed. Update the hash to
            ensure that the position matches the new hash key value
          */
Georgi Kodinov's avatar
Georgi Kodinov committed
7296 7297
          my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
                         old_key_length);
7298
          /*
7299 7300 7301 7302 7303 7304
            hash_update() operation could have moved element from the tail or
            the head of the hash to the current position.  But it can never
            move an element from the head to the tail or from the tail to the
            head over the current element.
            So we need to examine the current element once again, but
            we don't need to restart the search from the beginning.
7305
          */
7306 7307
          if (idx != elements)
            idx++;
7308 7309
          break;
        }
7310

7311
      case PROXY_USERS_ACL:
7312 7313 7314
        acl_proxy_user->set_user (&mem, user_to->user.str);
        acl_proxy_user->set_host (&mem, user_to->host.str);
        break;
7315

7316 7317 7318
      case ROLES_MAPPINGS_HASH:
        {
          /*
7319
            Save old hash key and its length to be able to properly update
7320 7321 7322 7323 7324
            element position in hash.
          */
          char *old_key= role_grant_pair->hashkey.str;
          size_t old_key_length= role_grant_pair->hashkey.length;

7325 7326 7327 7328 7329 7330 7331 7332 7333
          if (role_not_matched)
            init_role_grant_pair(&mem, role_grant_pair,
                                 user_to->user.str, user_to->host.str,
                                 role_grant_pair->r_uname);
          else
            init_role_grant_pair(&mem, role_grant_pair,
                                 role_grant_pair->u_uname,
                                 role_grant_pair->u_hname,
                                 user_to->user.str);
7334 7335 7336

          my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair,
                         (uchar*) old_key, old_key_length);
7337
          break;
7338 7339
        }

7340 7341 7342
      default:
        DBUG_ASSERT(0);
        break;
7343
      }
7344

7345 7346
    }
    else
7347
    {
7348 7349 7350 7351 7352 7353 7354
      /* 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
7355

7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399
  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. */
Sergei Golubchik's avatar
Sergei Golubchik committed
7400
    if ((handle_grant_struct(USER_ACL, drop, user_from, user_to)) || found)
7401 7402 7403 7404 7405 7406
    {
      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;
    }
7407 7408 7409 7410 7411 7412 7413
    if ((handle_grant_struct(ROLE_ACL, drop, user_from, user_to)) || found)
    {
      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;
    }
7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424
  }

  /* 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. */
Sergei Golubchik's avatar
Sergei Golubchik committed
7425
    if (((handle_grant_struct(DB_ACL, drop, user_from, user_to) && ! result) ||
7426 7427 7428 7429 7430 7431 7432 7433 7434
         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;
    }
  }

7435
  /* Handle stored routines table. */
7436 7437 7438 7439 7440 7441 7442 7443
  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. */
Sergei Golubchik's avatar
Sergei Golubchik committed
7444
    if (((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
7445 7446 7447 7448 7449 7450 7451
         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;
    }
7452
    /* Handle funcs array. */
Sergei Golubchik's avatar
Sergei Golubchik committed
7453
    if (((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
7454 7455 7456 7457
         found) && ! result)
    {
      result= 1; /* At least one record/element found. */
      /* If search is requested, we do not need to search further. */
7458 7459 7460 7461 7462
      if (! drop && ! user_to)
        goto end;
    }
  }

7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476
  /* 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;
7477
    }
7478 7479 7480

    /* Handle columns table. */
    if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
7481
    {
7482
      /* Handle of table failed, don't touch the in-memory array. */
7483 7484
      result= -1;
    }
7485 7486 7487
    else
    {
      /* Handle columns hash. */
Sergei Golubchik's avatar
Sergei Golubchik committed
7488
      if (((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
7489 7490 7491 7492
           found) && ! result)
        result= 1; /* At least one record/element found. */
    }
  }
7493

7494
  /* Handle proxies_priv table. */
7495
  if (tables[5].table)
7496
  {
7497 7498 7499 7500 7501 7502 7503 7504
    if ((found= handle_grant_table(tables, 5, drop, user_from, user_to)) < 0)
    {
      /* Handle of table failed, don't touch the in-memory array. */
      result= -1;
    }
    else
    {
      /* Handle proxies_priv array. */
Sergei Golubchik's avatar
Sergei Golubchik committed
7505
      if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) && !result) ||
7506 7507 7508
          found)
        result= 1; /* At least one record/element found. */
    }
7509
  }
7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527

  /* Handle roles_mappings table. */
  if (tables[6].table)
  {
    if ((found= handle_grant_table(tables, 6, drop, user_from, user_to)) < 0)
    {
      /* Handle of table failed, don't touch the in-memory array. */
      result= -1;
    }
    else
    {
      /* Handle acl_roles_mappings array */
      if ((handle_grant_struct(ROLES_MAPPINGS_HASH, drop, user_from, user_to) && !result) ||
          found)
        result= 1; /* At least one record/element found */
    }
  }

7528 7529 7530 7531
 end:
  DBUG_RETURN(result);
}

unknown's avatar
unknown committed
7532

unknown's avatar
unknown committed
7533 7534 7535 7536 7537 7538
static void append_user(String *str, LEX_USER *user)
{
  if (str->length())
    str->append(',');
  str->append('\'');
  str->append(user->user.str);
7539
  str->append(STRING_WITH_LEN("'@'"));
unknown's avatar
unknown committed
7540 7541 7542
  str->append(user->host.str);
  str->append('\'');
}
7543

unknown's avatar
unknown committed
7544

7545 7546 7547 7548 7549 7550 7551
/*
  Create a list of users.

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

7553 7554 7555 7556 7557 7558 7559 7560
  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_create_user(THD *thd, List <LEX_USER> &list)
{
  int result;
unknown's avatar
unknown committed
7561
  String wrong_users;
7562
  LEX_USER *user_name, *tmp_user_name;
7563
  List_iterator <LEX_USER> user_list(list);
7564
  TABLE_LIST tables[GRANT_TABLES];
7565
  bool some_users_created= FALSE;
7566 7567 7568 7569 7570 7571
  DBUG_ENTER("mysql_create_user");

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

Marc Alff's avatar
Marc Alff committed
7572 7573
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
7574

7575
  while ((tmp_user_name= user_list++))
7576
  {
7577 7578 7579 7580
    if (!(user_name= get_current_user(thd, tmp_user_name)))
    {
      result= TRUE;
      continue;
7581 7582
    }

7583 7584 7585 7586
    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
7587
    if (handle_grant_data(tables, 0, user_name, NULL))
7588
    {
unknown's avatar
unknown committed
7589
      append_user(&wrong_users, user_name);
7590
      result= TRUE;
unknown's avatar
unknown committed
7591
      continue;
7592
    }
7593

7594
    some_users_created= TRUE;
unknown's avatar
unknown committed
7595
    if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
7596
    {
unknown's avatar
unknown committed
7597
      append_user(&wrong_users, user_name);
7598 7599 7600 7601
      result= TRUE;
    }
  }

Marc Alff's avatar
Marc Alff committed
7602
  mysql_mutex_unlock(&acl_cache->lock);
7603

7604 7605 7606
  if (result)
    my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());

7607
  if (some_users_created)
7608
    result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
7609

Marc Alff's avatar
Marc Alff committed
7610
  mysql_rwlock_unlock(&LOCK_grant);
7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630
  DBUG_RETURN(result);
}


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

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

  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
{
  int result;
unknown's avatar
unknown committed
7631
  String wrong_users;
7632
  LEX_USER *user_name, *tmp_user_name;
7633
  List_iterator <LEX_USER> user_list(list);
7634
  TABLE_LIST tables[GRANT_TABLES];
7635
  bool some_users_deleted= FALSE;
7636
  ulonglong old_sql_mode= thd->variables.sql_mode;
7637 7638
  DBUG_ENTER("mysql_drop_user");

unknown's avatar
unknown committed
7639
  /* DROP USER may be skipped on replication client. */
7640 7641 7642
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);

7643 7644
  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;

Marc Alff's avatar
Marc Alff committed
7645 7646
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
7647

7648
  while ((tmp_user_name= user_list++))
7649
  {
7650 7651 7652 7653
    if (!(user_name= get_current_user(thd, tmp_user_name)))
    {
      result= TRUE;
      continue;
7654
    }
7655
    if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
7656
    {
unknown's avatar
unknown committed
7657
      append_user(&wrong_users, user_name);
7658
      result= TRUE;
7659
      continue;
7660
    }
7661
    some_users_deleted= TRUE;
7662
  }
7663

7664 7665
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();
7666 7667 7668
  /* Rebuild every user's role_grants because the acl_user has been modified
     and some grants might now be invalid */
  rebuild_role_grants();
7669

Marc Alff's avatar
Marc Alff committed
7670
  mysql_mutex_unlock(&acl_cache->lock);
7671

7672 7673 7674
  if (result)
    my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());

7675
  if (some_users_deleted)
7676
    result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
7677

Marc Alff's avatar
Marc Alff committed
7678
  mysql_rwlock_unlock(&LOCK_grant);
7679
  thd->variables.sql_mode= old_sql_mode;
7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698
  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)
{
7699
  int result;
unknown's avatar
unknown committed
7700
  String wrong_users;
7701 7702
  LEX_USER *user_from, *tmp_user_from;
  LEX_USER *user_to, *tmp_user_to;
7703
  List_iterator <LEX_USER> user_list(list);
7704
  TABLE_LIST tables[GRANT_TABLES];
7705
  bool some_users_renamed= FALSE;
7706 7707
  DBUG_ENTER("mysql_rename_user");

unknown's avatar
unknown committed
7708
  /* RENAME USER may be skipped on replication client. */
7709 7710
  if ((result= open_grant_tables(thd, tables)))
    DBUG_RETURN(result != 1);
7711 7712

  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7713

Marc Alff's avatar
Marc Alff committed
7714 7715
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
7716

7717
  while ((tmp_user_from= user_list++))
7718
  {
7719 7720 7721 7722
    if (!(user_from= get_current_user(thd, tmp_user_from)))
    {
      result= TRUE;
      continue;
7723
    }
7724 7725 7726 7727 7728
    tmp_user_to= user_list++;
    if (!(user_to= get_current_user(thd, tmp_user_to)))
    {
      result= TRUE;
      continue;
7729
    }
7730
    DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
7731 7732 7733 7734 7735

    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
unknown's avatar
unknown committed
7736 7737
    if (handle_grant_data(tables, 0, user_to, NULL) ||
        handle_grant_data(tables, 0, user_from, user_to) <= 0)
7738
    {
unknown's avatar
unknown committed
7739
      append_user(&wrong_users, user_from);
7740
      result= TRUE;
7741
      continue;
7742
    }
7743
    some_users_renamed= TRUE;
7744
  }
7745

7746 7747
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();
7748 7749 7750
  /* Rebuild every user's role_grants because the acl_user has been modified
     and some grants might now be invalid */
  rebuild_role_grants();
7751

Marc Alff's avatar
Marc Alff committed
7752
  mysql_mutex_unlock(&acl_cache->lock);
7753

7754 7755
  if (result)
    my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
7756

7757
  if (some_users_renamed && mysql_bin_log.is_open())
7758
    result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
7759

Marc Alff's avatar
Marc Alff committed
7760
  mysql_rwlock_unlock(&LOCK_grant);
7761 7762 7763
  DBUG_RETURN(result);
}

7764

7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778
/*
  Revoke all privileges from a list of users.

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

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

unknown's avatar
unknown committed
7779
bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
7780
{
7781
  uint counter, revoked, is_proc;
7782
  int result;
unknown's avatar
unknown committed
7783
  ACL_DB *acl_db;
7784
  TABLE_LIST tables[GRANT_TABLES];
7785 7786 7787
  DBUG_ENTER("mysql_revoke_all");

  if ((result= open_grant_tables(thd, tables)))
unknown's avatar
unknown committed
7788
    DBUG_RETURN(result != 1);
7789 7790

  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7791

Marc Alff's avatar
Marc Alff committed
7792 7793
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
7794

7795
  LEX_USER *lex_user, *tmp_lex_user;
7796
  List_iterator <LEX_USER> user_list(list);
7797
  while ((tmp_lex_user= user_list++))
7798
  {
7799 7800 7801 7802
    if (!(lex_user= get_current_user(thd, tmp_lex_user)))
    {
      result= -1;
      continue;
7803
    }
7804
    if (!find_user_no_anon(lex_user->host.str, lex_user->user.str, TRUE))
7805 7806 7807 7808
    {
      result= -1;
      continue;
    }
unknown's avatar
unknown committed
7809

7810
    if (replace_user_table(thd, tables[0].table,
7811
			   *lex_user, ~(ulong)0, 1, 0, 0))
7812 7813 7814 7815 7816 7817
    {
      result= -1;
      continue;
    }

    /* Remove db access privileges */
unknown's avatar
unknown committed
7818 7819 7820 7821 7822
    /*
      Because acl_dbs and column_priv_hash shrink and may re-order
      as privileges are removed, removal occurs in a repeated loop
      until no more privileges are revoked.
     */
unknown's avatar
unknown committed
7823
    do
7824
    {
unknown's avatar
unknown committed
7825
      for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
7826
      {
unknown's avatar
unknown committed
7827
	const char *user,*host;
unknown's avatar
unknown committed
7828

unknown's avatar
unknown committed
7829 7830 7831 7832 7833
	acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
	if (!(user=acl_db->user))
	  user= "";
	if (!(host=acl_db->host.hostname))
	  host= "";
unknown's avatar
unknown committed
7834

unknown's avatar
unknown committed
7835
	if (!strcmp(lex_user->user.str,user) &&
7836
            !strcmp(lex_user->host.str, host))
unknown's avatar
unknown committed
7837
	{
7838 7839
	  if (!replace_db_table(tables[1].table, acl_db->db, *lex_user,
                                ~(ulong)0, 1))
unknown's avatar
unknown committed
7840
	  {
unknown's avatar
unknown committed
7841 7842 7843 7844 7845
	    /*
	      Don't increment counter as replace_db_table deleted the
	      current element in acl_dbs.
	     */
	    revoked= 1;
unknown's avatar
unknown committed
7846 7847
	    continue;
	  }
unknown's avatar
unknown committed
7848
	  result= -1; // Something went wrong
unknown's avatar
unknown committed
7849
	}
unknown's avatar
unknown committed
7850
	counter++;
7851
      }
unknown's avatar
unknown committed
7852
    } while (revoked);
7853 7854

    /* Remove column access */
unknown's avatar
unknown committed
7855
    do
7856
    {
unknown's avatar
unknown committed
7857
      for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
7858
      {
unknown's avatar
unknown committed
7859
	const char *user,*host;
Konstantin Osipov's avatar
Konstantin Osipov committed
7860 7861
        GRANT_TABLE *grant_table=
          (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
unknown's avatar
unknown committed
7862 7863
	if (!(user=grant_table->user))
	  user= "";
7864
	if (!(host=grant_table->host.hostname))
unknown's avatar
unknown committed
7865
	  host= "";
unknown's avatar
unknown committed
7866

unknown's avatar
unknown committed
7867
	if (!strcmp(lex_user->user.str,user) &&
7868
            !strcmp(lex_user->host.str, host))
7869
	{
unknown's avatar
unknown committed
7870 7871 7872
	  if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
				  grant_table->db,
				  grant_table->tname,
7873
				  ~(ulong)0, 0, 1))
unknown's avatar
unknown committed
7874
	  {
unknown's avatar
unknown committed
7875
	    result= -1;
unknown's avatar
unknown committed
7876
	  }
unknown's avatar
unknown committed
7877
	  else
unknown's avatar
unknown committed
7878
	  {
unknown's avatar
unknown committed
7879
	    if (!grant_table->cols)
unknown's avatar
unknown committed
7880
	    {
unknown's avatar
unknown committed
7881 7882
	      revoked= 1;
	      continue;
unknown's avatar
unknown committed
7883
	    }
unknown's avatar
unknown committed
7884 7885
	    List<LEX_COLUMN> columns;
	    if (!replace_column_table(grant_table,tables[3].table, *lex_user,
unknown's avatar
unknown committed
7886 7887 7888
				      columns,
				      grant_table->db,
				      grant_table->tname,
7889
				      ~(ulong)0, 1))
unknown's avatar
unknown committed
7890
	    {
unknown's avatar
unknown committed
7891
	      revoked= 1;
7892
	      continue;
unknown's avatar
unknown committed
7893
	    }
7894
	    result= -1;
unknown's avatar
unknown committed
7895
	  }
7896
	}
unknown's avatar
unknown committed
7897
	counter++;
7898
      }
unknown's avatar
unknown committed
7899
    } while (revoked);
7900 7901

    /* Remove procedure access */
7902 7903 7904
    for (is_proc=0; is_proc<2; is_proc++) do {
      HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
      for (counter= 0, revoked= 0 ; counter < hash->records ; )
7905 7906
      {
	const char *user,*host;
Konstantin Osipov's avatar
Konstantin Osipov committed
7907
        GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
7908 7909
	if (!(user=grant_proc->user))
	  user= "";
7910
	if (!(host=grant_proc->host.hostname))
7911 7912 7913
	  host= "";

	if (!strcmp(lex_user->user.str,user) &&
7914
            !strcmp(lex_user->host.str, host))
7915
	{
7916
	  if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
7917 7918
				  grant_proc->db,
				  grant_proc->tname,
7919
                                  is_proc,
7920
				  ~(ulong)0, 1) == 0)
7921 7922 7923 7924 7925 7926 7927 7928 7929
	  {
	    revoked= 1;
	    continue;
	  }
	  result= -1;	// Something went wrong
	}
	counter++;
      }
    } while (revoked);
7930
  }
unknown's avatar
unknown committed
7931

Marc Alff's avatar
Marc Alff committed
7932
  mysql_mutex_unlock(&acl_cache->lock);
7933

7934 7935 7936 7937
  if (result)
    my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));

  result= result |
7938
    write_bin_log(thd, FALSE, thd->query(), thd->query_length());
7939

Marc Alff's avatar
Marc Alff committed
7940
  mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
7941

7942
  DBUG_RETURN(result);
7943
}
unknown's avatar
unknown committed
7944

7945

7946

7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962 7963 7964 7965

/**
  If the defining user for a routine does not exist, then the ACL lookup
  code should raise two errors which we should intercept.  We convert the more
  descriptive error into a warning, and consume the other.

  If any other errors are raised, then we set a flag that should indicate
  that there was some failure we should complain at a higher level.
*/
class Silence_routine_definer_errors : public Internal_error_handler
{
public:
  Silence_routine_definer_errors()
    : is_grave(FALSE)
  {}

  virtual ~Silence_routine_definer_errors()
  {}

Marc Alff's avatar
Marc Alff committed
7966 7967 7968 7969 7970 7971
  virtual bool handle_condition(THD *thd,
                                uint sql_errno,
                                const char* sqlstate,
                                MYSQL_ERROR::enum_warning_level level,
                                const char* msg,
                                MYSQL_ERROR ** cond_hdl);
7972 7973 7974 7975 7976 7977 7978 7979

  bool has_errors() { return is_grave; }

private:
  bool is_grave;
};

bool
Marc Alff's avatar
Marc Alff committed
7980 7981 7982 7983 7984 7985 7986
Silence_routine_definer_errors::handle_condition(
  THD *thd,
  uint sql_errno,
  const char*,
  MYSQL_ERROR::enum_warning_level level,
  const char* msg,
  MYSQL_ERROR ** cond_hdl)
7987
{
Marc Alff's avatar
Marc Alff committed
7988
  *cond_hdl= NULL;
7989 7990 7991 7992 7993 7994
  if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
  {
    switch (sql_errno)
    {
      case ER_NONEXISTING_PROC_GRANT:
        /* Convert the error into a warning. */
Marc Alff's avatar
Marc Alff committed
7995 7996
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                     sql_errno, msg);
7997 7998 7999 8000 8001 8002 8003 8004 8005 8006 8007 8008 8009 8010 8011
        return TRUE;
      default:
        is_grave= TRUE;
    }
  }

  return FALSE;
}


/**
  Revoke privileges for all users on a stored procedure.  Use an error handler
  that converts errors about missing grants into warnings.

  @param
8012
    thd                         The current thread.
8013
  @param
8014
    db				DB of the stored procedure
8015
  @param
8016 8017
    name			Name of the stored procedure

8018
  @retval
8019
    0           OK.
8020
  @retval
8021 8022 8023
    < 0         Error. Error message not yet sent.
*/

8024 8025
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
                          bool is_proc)
8026 8027 8028 8029
{
  uint counter, revoked;
  int result;
  TABLE_LIST tables[GRANT_TABLES];
8030
  HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
8031
  Silence_routine_definer_errors error_handler;
8032 8033 8034 8035 8036
  DBUG_ENTER("sp_revoke_privileges");

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

8037 8038
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());

8039 8040 8041
  /* Be sure to pop this before exiting this scope! */
  thd->push_internal_handler(&error_handler);

Marc Alff's avatar
Marc Alff committed
8042 8043
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
8044 8045

  /* Remove procedure access */
8046 8047
  do
  {
8048
    for (counter= 0, revoked= 0 ; counter < hash->records ; )
8049
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
8050
      GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
8051
      if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
8052 8053 8054 8055 8056
	  !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);
8057 8058 8059 8060
	lex_user.host.str= grant_proc->host.hostname ?
	  grant_proc->host.hostname : (char*)"";
	lex_user.host.length= grant_proc->host.hostname ?
	  strlen(grant_proc->host.hostname) : 0;
8061 8062 8063 8064

	if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
				  grant_proc->db, grant_proc->tname,
                                  is_proc, ~(ulong)0, 1) == 0)
8065 8066 8067 8068 8069 8070 8071 8072 8073
	{
	  revoked= 1;
	  continue;
	}
      }
      counter++;
    }
  } while (revoked);

Marc Alff's avatar
Marc Alff committed
8074 8075
  mysql_mutex_unlock(&acl_cache->lock);
  mysql_rwlock_unlock(&LOCK_grant);
8076

8077
  thd->pop_internal_handler();
8078

8079
  DBUG_RETURN(error_handler.has_errors());
8080 8081 8082
}


8083
/**
8084 8085
  Grant EXECUTE,ALTER privilege for a stored procedure

8086 8087 8088 8089
  @param thd The current thread.
  @param sp_db
  @param sp_name
  @param is_proc
8090

8091 8092 8093
  @return
    @retval FALSE Success
    @retval TRUE An error occured. Error message not yet sent.
8094 8095
*/

8096
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
8097
                         bool is_proc)
8098
{
8099
  Security_context *sctx= thd->security_ctx;
8100 8101 8102 8103
  LEX_USER *combo;
  TABLE_LIST tables[1];
  List<LEX_USER> user_list;
  bool result;
8104 8105
  ACL_USER *au;
  char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
8106
  Dummy_error_handler error_handler;
8107
  DBUG_ENTER("sp_grant_privileges");
8108 8109 8110 8111

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

8112
  combo->user.str= sctx->user;
8113

Marc Alff's avatar
Marc Alff committed
8114
  mysql_mutex_lock(&acl_cache->lock);
8115

8116 8117
  if ((au= find_user_no_anon(combo->host.str=(char*)sctx->host_or_ip,
                             combo->user.str,FALSE)))
8118
    goto found_acl;
8119 8120
  if ((au= find_user_no_anon(combo->host.str=(char*)sctx->host,
                             combo->user.str,FALSE)))
8121
    goto found_acl;
8122 8123
  if ((au= find_user_no_anon(combo->host.str=(char*)sctx->ip,
                             combo->user.str,FALSE)))
8124
    goto found_acl;
8125 8126
  if ((au= find_user_no_anon(combo->host.str=(char*)"%",
                             combo->user.str, FALSE)))
8127 8128
    goto found_acl;

Marc Alff's avatar
Marc Alff committed
8129
  mysql_mutex_unlock(&acl_cache->lock);
8130 8131 8132
  DBUG_RETURN(TRUE);

 found_acl:
Marc Alff's avatar
Marc Alff committed
8133
  mysql_mutex_unlock(&acl_cache->lock);
8134 8135 8136 8137 8138

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

  tables->db= (char*)sp_db;
8139
  tables->table_name= tables->alias= (char*)sp_name;
8140

8141 8142
  thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str));
  thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str));
8143

8144 8145 8146
  combo->password= empty_lex_str;
  combo->plugin= empty_lex_str;
  combo->auth= empty_lex_str;
8147

8148
  if(au)
8149
  {
8150
    if (au->salt_len)
8151
    {
8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168
      if (au->salt_len == SCRAMBLE_LENGTH)
      {
        make_password_from_salt(passwd_buff, au->salt);
        combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
      }
      else if (au->salt_len == SCRAMBLE_LENGTH_323)
      {
        make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
        combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
      }
      else
      {
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PASSWD_LENGTH,
                            ER(ER_PASSWD_LENGTH), SCRAMBLED_PASSWORD_CHAR_LENGTH);
        return TRUE;
      }
      combo->password.str= passwd_buff;
8169
    }
8170 8171 8172

    if (au->plugin.str != native_password_plugin_name.str &&
        au->plugin.str != old_password_plugin_name.str)
8173
    {
8174 8175
      combo->plugin= au->plugin;
      combo->auth= au->auth_string;
8176 8177
    }
  }
8178 8179 8180 8181 8182

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

  thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
8183
  thd->lex->ssl_cipher= thd->lex->x509_subject= thd->lex->x509_issuer= 0;
8184
  bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
8185

8186 8187 8188 8189 8190
  /*
    Only care about whether the operation failed or succeeded
    as all errors will be handled later.
  */
  thd->push_internal_handler(&error_handler);
8191
  result= mysql_routine_grant(thd, tables, is_proc, user_list,
8192 8193
                              DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
  thd->pop_internal_handler();
8194 8195 8196 8197
  DBUG_RETURN(result);
}


8198 8199 8200 8201 8202
/**
  Validate if a user can proxy as another user

  @thd                     current thread
  @param user              the logged in user (proxy user)
8203
  @param authenticated_as  the effective user a plugin is trying to
8204
                           impersonate as (proxied user)
8205 8206 8207
  @return                  proxy user definition
    @retval NULL           proxy user definition not found or not applicable
    @retval non-null       the proxy user data
8208 8209
*/

8210
static ACL_PROXY_USER *
8211
acl_find_proxy_user(const char *user, const char *host, const char *ip,
8212 8213 8214 8215
                    const char *authenticated_as, bool *proxy_used)
{
  uint i;
  /* if the proxied and proxy user are the same return OK */
8216 8217 8218
  DBUG_ENTER("acl_find_proxy_user");
  DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
                      user, host, ip, authenticated_as));
8219

8220
  if (!strcmp(authenticated_as, user))
8221 8222
  {
    DBUG_PRINT ("info", ("user is the same as authenticated_as"));
8223
    DBUG_RETURN (NULL);
8224 8225
  }

8226
  *proxy_used= TRUE;
8227
  for (i=0; i < acl_proxy_users.elements; i++)
8228
  {
8229
    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
8230 8231
                                           ACL_PROXY_USER *);
    if (proxy->matches(host, user, ip, authenticated_as))
8232
      DBUG_RETURN(proxy);
8233 8234
  }

8235
  DBUG_RETURN(NULL);
8236 8237 8238 8239
}


bool
8240 8241
acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
                             bool with_grant)
8242
{
8243
  DBUG_ENTER("acl_check_proxy_grant_access");
8244
  DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
8245
                      (int) with_grant));
8246 8247 8248 8249 8250 8251 8252 8253 8254
  if (!initialized)
  {
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
    DBUG_RETURN(1);
  }

  /* replication slave thread can do anything */
  if (thd->slave_thread)
  {
8255
    DBUG_PRINT("info", ("replication slave"));
8256 8257 8258
    DBUG_RETURN(FALSE);
  }

8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271
  /*
    one can grant proxy for self to others.
    Security context in THD contains two pairs of (user,host):
    1. (user,host) pair referring to inbound connection.
    2. (priv_user,priv_host) pair obtained from mysql.user table after doing
        authnetication of incoming connection.
    Privileges should be checked wrt (priv_user, priv_host) tuple, because
    (user,host) pair obtained from inbound connection may have different
    values than what is actually stored in mysql.user table and while granting
    or revoking proxy privilege, user is expected to provide entries mentioned
    in mysql.user table.
  */
  if (!strcmp(thd->security_ctx->priv_user, user) &&
8272
      !my_strcasecmp(system_charset_info, host,
8273
                     thd->security_ctx->priv_host))
8274
  {
8275
    DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
8276 8277
                        thd->security_ctx->priv_user, user,
                        host, thd->security_ctx->priv_host));
8278 8279 8280 8281
    DBUG_RETURN(FALSE);
  }

  /* check for matching WITH PROXY rights */
8282 8283
  for (uint i=0; i < acl_proxy_users.elements; i++)
  {
8284
    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
8285 8286 8287 8288 8289
                                           ACL_PROXY_USER *);
    if (proxy->matches(thd->security_ctx->host,
                       thd->security_ctx->user,
                       thd->security_ctx->ip,
                       user) &&
8290 8291
        proxy->get_with_grant())
    {
8292
      DBUG_PRINT("info", ("found"));
8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304
      DBUG_RETURN(FALSE);
    }
  }

  my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
           thd->security_ctx->user,
           thd->security_ctx->host_or_ip);
  DBUG_RETURN(TRUE);
}


static bool
8305
show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
8306 8307 8308 8309
{
  Protocol *protocol= thd->protocol;
  int error= 0;

8310
  for (uint i=0; i < acl_proxy_users.elements; i++)
8311
  {
8312 8313
    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
                                           ACL_PROXY_USER *);
8314 8315 8316 8317 8318 8319
    if (proxy->granted_on(user->host.str, user->user.str))
    {
      String global(buff, buffsize, system_charset_info);
      global.length(0);
      proxy->print_grant(&global);
      protocol->prepare_for_resend();
8320
      protocol->store(global.ptr(), global.length(), global.charset());
8321 8322 8323 8324 8325 8326 8327 8328 8329 8330 8331
      if (protocol->write())
      {
        error= -1;
        break;
      }
    }
  }
  return error;
}


unknown's avatar
unknown committed
8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377
#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');
}

8378

Sergey Glukhov's avatar
Sergey Glukhov committed
8379
#ifndef NO_EMBEDDED_ACCESS_CHECKS
8380 8381 8382 8383 8384
static bool update_schema_privilege(THD *thd, 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)
8385 8386 8387
{
  int i= 2;
  CHARSET_INFO *cs= system_charset_info;
8388
  restore_record(table, s->default_values);
8389
  table->field[0]->store(buff, (uint) strlen(buff), cs);
8390
  table->field[1]->store(STRING_WITH_LEN("def"), cs);
8391
  if (db)
8392
    table->field[i++]->store(db, (uint) strlen(db), cs);
8393
  if (t_name)
8394
    table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
8395 8396 8397 8398
  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);
8399
  return schema_table_store_record(thd, table);
8400
}
Sergey Glukhov's avatar
Sergey Glukhov committed
8401
#endif
8402 8403 8404 8405 8406


int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
8407
  int error= 0;
8408 8409 8410 8411 8412
  uint counter;
  ACL_USER *acl_user;
  ulong want_access;
  char buff[100];
  TABLE *table= tables->table;
Marc Alff's avatar
Marc Alff committed
8413 8414
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
                                      NULL, NULL, 1, 1);
8415
  char *curr_host= thd->security_ctx->priv_host_name();
8416
  DBUG_ENTER("fill_schema_user_privileges");
8417

8418 8419
  if (!initialized)
    DBUG_RETURN(0);
Marc Alff's avatar
Marc Alff committed
8420
  mysql_mutex_lock(&acl_cache->lock);
8421

8422 8423 8424 8425
  for (counter=0 ; counter < acl_users.elements ; counter++)
  {
    const char *user,*host, *is_grantable="YES";
    acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
8426
    if (!(user=acl_user->user.str))
8427 8428 8429
      user= "";
    if (!(host=acl_user->host.hostname))
      host= "";
8430 8431

    if (no_global_access &&
8432
        (strcmp(thd->security_ctx->priv_user, user) ||
8433 8434
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;
8435

8436 8437 8438 8439 8440 8441
    want_access= acl_user->access;
    if (!(want_access & GRANT_ACL))
      is_grantable= "NO";

    strxmov(buff,"'",user,"'@'",host,"'",NullS);
    if (!(want_access & ~GRANT_ACL))
8442 8443 8444 8445 8446 8447 8448 8449
    {
      if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
                                  STRING_WITH_LEN("USAGE"), is_grantable))
      {
        error= 1;
        goto err;
      }
    }
8450 8451 8452 8453 8454 8455 8456
    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)
8457
        {
8458
          if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
8459 8460 8461 8462 8463 8464 8465
                                      command_array[priv_id],
                                      command_lengths[priv_id], is_grantable))
          {
            error= 1;
            goto err;
          }
        }
8466 8467 8468
      }
    }
  }
8469
err:
Marc Alff's avatar
Marc Alff committed
8470
  mysql_mutex_unlock(&acl_cache->lock);
8471

8472
  DBUG_RETURN(error);
8473 8474 8475
#else
  return(0);
#endif
8476 8477 8478 8479 8480 8481
}


int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
8482
  int error= 0;
8483 8484 8485 8486 8487
  uint counter;
  ACL_DB *acl_db;
  ulong want_access;
  char buff[100];
  TABLE *table= tables->table;
Marc Alff's avatar
Marc Alff committed
8488 8489
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
                                      NULL, NULL, 1, 1);
8490
  char *curr_host= thd->security_ctx->priv_host_name();
8491 8492
  DBUG_ENTER("fill_schema_schema_privileges");

8493 8494
  if (!initialized)
    DBUG_RETURN(0);
Marc Alff's avatar
Marc Alff committed
8495
  mysql_mutex_lock(&acl_cache->lock);
8496

8497 8498 8499 8500 8501 8502 8503 8504 8505 8506
  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= "";

8507
    if (no_global_access &&
8508
        (strcmp(thd->security_ctx->priv_user, user) ||
8509 8510 8511
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;

8512 8513 8514 8515 8516 8517 8518 8519 8520
    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))
8521 8522 8523 8524 8525 8526 8527 8528
      {
        if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0,
                                    0, STRING_WITH_LEN("USAGE"), is_grantable))
        {
          error= 1;
          goto err;
        }
      }
8529 8530 8531 8532 8533 8534
      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)
8535 8536 8537 8538 8539 8540 8541 8542 8543
          {
            if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0, 0,
                                        command_array[cnt], command_lengths[cnt],
                                        is_grantable))
            {
              error= 1;
              goto err;
            }
          }
8544 8545 8546
      }
    }
  }
8547
err:
Marc Alff's avatar
Marc Alff committed
8548
  mysql_mutex_unlock(&acl_cache->lock);
8549

8550
  DBUG_RETURN(error);
8551 8552 8553
#else
  return (0);
#endif
8554 8555 8556 8557 8558 8559
}


int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
8560
  int error= 0;
8561 8562 8563
  uint index;
  char buff[100];
  TABLE *table= tables->table;
Marc Alff's avatar
Marc Alff committed
8564 8565
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
                                      NULL, NULL, 1, 1);
8566
  char *curr_host= thd->security_ctx->priv_host_name();
8567 8568
  DBUG_ENTER("fill_schema_table_privileges");

Marc Alff's avatar
Marc Alff committed
8569
  mysql_rwlock_rdlock(&LOCK_grant);
8570

8571 8572
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
8573
    const char *user, *host, *is_grantable= "YES";
Konstantin Osipov's avatar
Konstantin Osipov committed
8574
    GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
8575 8576 8577
							  index);
    if (!(user=grant_table->user))
      user= "";
8578 8579
    if (!(host= grant_table->host.hostname))
      host= "";
8580 8581

    if (no_global_access &&
8582
        (strcmp(thd->security_ctx->priv_user, user) ||
8583
         my_strcasecmp(system_charset_info, curr_host, host)))
8584 8585
      continue;

8586
    ulong table_access= grant_table->privs;
8587
    if (table_access)
8588 8589
    {
      ulong test_access= table_access & ~GRANT_ACL;
unknown's avatar
unknown committed
8590 8591 8592 8593
      /*
        We should skip 'usage' privilege on table if
        we have any privileges on column(s) of this table
      */
8594 8595
      if (!test_access && grant_table->cols)
        continue;
8596 8597 8598
      if (!(table_access & GRANT_ACL))
        is_grantable= "NO";

8599
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
8600
      if (!test_access)
8601 8602 8603 8604 8605 8606 8607 8608 8609
      {
        if (update_schema_privilege(thd, table, buff, grant_table->db,
                                    grant_table->tname, 0, 0,
                                    STRING_WITH_LEN("USAGE"), is_grantable))
        {
          error= 1;
          goto err;
        }
      }
8610 8611 8612 8613 8614 8615 8616
      else
      {
        ulong j;
        int cnt;
        for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
        {
          if (test_access & j)
8617 8618 8619 8620 8621 8622 8623 8624 8625 8626
          {
            if (update_schema_privilege(thd, table, buff, grant_table->db,
                                        grant_table->tname, 0, 0,
                                        command_array[cnt],
                                        command_lengths[cnt], is_grantable))
            {
              error= 1;
              goto err;
            }
          }
8627 8628
        }
      }
8629
    }
8630
  }
8631
err:
Marc Alff's avatar
Marc Alff committed
8632
  mysql_rwlock_unlock(&LOCK_grant);
8633

8634
  DBUG_RETURN(error);
8635 8636 8637
#else
  return (0);
#endif
8638 8639 8640 8641 8642 8643
}


int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
8644
  int error= 0;
8645 8646 8647
  uint index;
  char buff[100];
  TABLE *table= tables->table;
Marc Alff's avatar
Marc Alff committed
8648 8649
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
                                      NULL, NULL, 1, 1);
8650
  char *curr_host= thd->security_ctx->priv_host_name();
8651 8652
  DBUG_ENTER("fill_schema_table_privileges");

Marc Alff's avatar
Marc Alff committed
8653
  mysql_rwlock_rdlock(&LOCK_grant);
8654

8655 8656
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
8657
    const char *user, *host, *is_grantable= "YES";
Konstantin Osipov's avatar
Konstantin Osipov committed
8658
    GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
8659 8660 8661
							  index);
    if (!(user=grant_table->user))
      user= "";
8662 8663
    if (!(host= grant_table->host.hostname))
      host= "";
8664 8665

    if (no_global_access &&
8666
        (strcmp(thd->security_ctx->priv_user, user) ||
8667
         my_strcasecmp(system_charset_info, curr_host, host)))
8668 8669
      continue;

8670 8671 8672
    ulong table_access= grant_table->cols;
    if (table_access != 0)
    {
unknown's avatar
unknown committed
8673
      if (!(grant_table->privs & GRANT_ACL))
8674 8675
        is_grantable= "NO";

unknown's avatar
unknown committed
8676
      ulong test_access= table_access & ~GRANT_ACL;
8677
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
8678 8679 8680 8681 8682 8683 8684 8685 8686 8687 8688 8689 8690 8691 8692
      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*)
Konstantin Osipov's avatar
Konstantin Osipov committed
8693
                my_hash_element(&grant_table->hash_columns,col_index);
8694
              if ((grant_column->rights & j) && (table_access & j))
8695 8696 8697 8698 8699 8700 8701 8702 8703 8704 8705 8706
              {
                if (update_schema_privilege(thd, table, buff, grant_table->db,
                                            grant_table->tname,
                                            grant_column->column,
                                            grant_column->key_length,
                                            command_array[cnt],
                                            command_lengths[cnt], is_grantable))
                {
                  error= 1;
                  goto err;
                }
              }
8707 8708 8709 8710 8711 8712
            }
          }
        }
      }
    }
  }
8713
err:
Marc Alff's avatar
Marc Alff committed
8714
  mysql_rwlock_unlock(&LOCK_grant);
8715

8716
  DBUG_RETURN(error);
8717 8718 8719
#else
  return (0);
#endif
8720 8721 8722
}


unknown's avatar
VIEW  
unknown committed
8723 8724 8725 8726 8727
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
  fill effective privileges for table

  SYNOPSIS
8728 8729
    fill_effective_table_privileges()
    thd     thread handler
unknown's avatar
VIEW  
unknown committed
8730 8731 8732 8733 8734 8735 8736 8737
    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)
{
8738
  Security_context *sctx= thd->security_ctx;
8739 8740 8741 8742 8743
  DBUG_ENTER("fill_effective_table_privileges");
  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
                       sctx->priv_host, (sctx->ip ? sctx->ip : "(NULL)"),
                       (sctx->priv_user ? sctx->priv_user : "(NULL)"),
                       db, table));
8744 8745 8746
  /* --skip-grants */
  if (!initialized)
  {
8747
    DBUG_PRINT("info", ("skip grants"));
8748
    grant->privilege= ~NO_ACCESS;             // everything is allowed
8749 8750
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;
8751 8752
  }

unknown's avatar
VIEW  
unknown committed
8753
  /* global privileges */
8754
  grant->privilege= sctx->master_access;
8755

8756
  if (!sctx->priv_user)
8757 8758 8759 8760
  {
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;                         // it is slave
  }
8761

8762
  /* db privileges */
8763
  grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
8764 8765 8766
  /* db privileges for role */
  if (sctx->priv_role[0])
    grant->privilege|= acl_get("", "", sctx->priv_role, db, 0);
8767

unknown's avatar
VIEW  
unknown committed
8768
  /* table privileges */
Marc Alff's avatar
Marc Alff committed
8769
  mysql_rwlock_rdlock(&LOCK_grant);
unknown's avatar
VIEW  
unknown committed
8770 8771
  if (grant->version != grant_version)
  {
8772
    grant->grant_table_user=
8773
      table_hash_search(sctx->host, sctx->ip, db,
8774 8775 8776 8777 8778 8779
                        sctx->priv_user,
                        table, 0);              /* purecov: inspected */
    grant->grant_table_role=
      sctx->priv_role[0] ? table_hash_search("", "", db,
                                             sctx->priv_role,
                                             table, TRUE) : NULL;
unknown's avatar
VIEW  
unknown committed
8780 8781
    grant->version= grant_version;              /* purecov: inspected */
  }
8782 8783 8784 8785 8786
  if (grant->grant_table_user != 0)
  {
    grant->privilege|= grant->grant_table_user->privs;
  }
  if (grant->grant_table_role != 0)
unknown's avatar
VIEW  
unknown committed
8787
  {
8788
    grant->privilege|= grant->grant_table_role->privs;
unknown's avatar
VIEW  
unknown committed
8789
  }
Marc Alff's avatar
Marc Alff committed
8790
  mysql_rwlock_unlock(&LOCK_grant);
8791

8792 8793
  DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
  DBUG_VOID_RETURN;
unknown's avatar
VIEW  
unknown committed
8794
}
8795 8796 8797 8798 8799 8800 8801

#else /* NO_EMBEDDED_ACCESS_CHECKS */

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

unknown's avatar
unknown committed
8802 8803
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
                             bool is_proc)
8804 8805 8806 8807
{
  return FALSE;
}

unknown's avatar
VIEW  
unknown committed
8808
#endif
Marc Alff's avatar
Marc Alff committed
8809 8810 8811 8812 8813 8814 8815 8816 8817 8818 8819 8820 8821 8822 8823 8824 8825 8826 8827 8828 8829 8830 8831 8832 8833 8834 8835 8836 8837 8838 8839 8840 8841 8842 8843 8844 8845 8846 8847 8848 8849 8850 8851 8852 8853 8854 8855 8856 8857 8858 8859 8860 8861 8862 8863 8864 8865 8866 8867 8868 8869 8870 8871 8872 8873 8874 8875 8876 8877 8878 8879 8880 8881 8882 8883 8884 8885 8886 8887 8888 8889 8890 8891 8892 8893 8894 8895 8896 8897 8898 8899 8900 8901 8902 8903 8904 8905 8906 8907 8908 8909

struct ACL_internal_schema_registry_entry
{
  const LEX_STRING *m_name;
  const ACL_internal_schema_access *m_access;
};

/**
  Internal schema registered.
  Currently, this is only:
  - performance_schema
  - information_schema,
  This can be reused later for:
  - mysql
*/
static ACL_internal_schema_registry_entry registry_array[2];
static uint m_registry_array_size= 0;

/**
  Add an internal schema to the registry.
  @param name the schema name
  @param access the schema ACL specific rules
*/
void ACL_internal_schema_registry::register_schema
  (const LEX_STRING *name, const ACL_internal_schema_access *access)
{
  DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));

  /* Not thread safe, and does not need to be. */
  registry_array[m_registry_array_size].m_name= name;
  registry_array[m_registry_array_size].m_access= access;
  m_registry_array_size++;
}

/**
  Search per internal schema ACL by name.
  @param name a schema name
  @return per schema rules, or NULL
*/
const ACL_internal_schema_access *
ACL_internal_schema_registry::lookup(const char *name)
{
  DBUG_ASSERT(name != NULL);

  uint i;

  for (i= 0; i<m_registry_array_size; i++)
  {
    if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
                      name) == 0)
      return registry_array[i].m_access;
  }
  return NULL;
}

/**
  Get a cached internal schema access.
  @param grant_internal_info the cache
  @param schema_name the name of the internal schema
*/
const ACL_internal_schema_access *
get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
                         const char *schema_name)
{
  if (grant_internal_info)
  {
    if (! grant_internal_info->m_schema_lookup_done)
    {
      grant_internal_info->m_schema_access=
        ACL_internal_schema_registry::lookup(schema_name);
      grant_internal_info->m_schema_lookup_done= TRUE;
    }
    return grant_internal_info->m_schema_access;
  }
  return ACL_internal_schema_registry::lookup(schema_name);
}

/**
  Get a cached internal table access.
  @param grant_internal_info the cache
  @param schema_name the name of the internal schema
  @param table_name the name of the internal table
*/
const ACL_internal_table_access *
get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
                        const char *schema_name,
                        const char *table_name)
{
  DBUG_ASSERT(grant_internal_info);
  if (! grant_internal_info->m_table_lookup_done)
  {
    const ACL_internal_schema_access *schema_access;
    schema_access= get_cached_schema_access(grant_internal_info, schema_name);
    if (schema_access)
      grant_internal_info->m_table_access= schema_access->lookup(table_name);
    grant_internal_info->m_table_lookup_done= TRUE;
  }
  return grant_internal_info->m_table_access;
}


8910 8911 8912 8913 8914 8915 8916 8917 8918 8919 8920 8921 8922
/****************************************************************************
   AUTHENTICATION CODE
   including initial connect handshake, invoking appropriate plugins,
   client-server plugin negotiation, COM_CHANGE_USER, and native
   MySQL authentication plugins.
****************************************************************************/

/* few defines to have less ifdef's in the code below */
#ifdef EMBEDDED_LIBRARY
#undef HAVE_OPENSSL
#ifdef NO_EMBEDDED_ACCESS_CHECKS
#define initialized 0
#define check_for_max_user_connections(X,Y)   0
8923
#define get_or_create_user_conn(A,B,C,D) 0
8924 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934
#endif
#endif
#ifndef HAVE_OPENSSL
#define ssl_acceptor_fd 0
#define sslaccept(A,B,C,D) 1
#endif

/**
  The internal version of what plugins know as MYSQL_PLUGIN_VIO,
  basically the context of the authentication session
*/
8935
struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
8936 8937 8938
{
  MYSQL_SERVER_AUTH_INFO auth_info;
  THD *thd;
Sergei Golubchik's avatar
Sergei Golubchik committed
8939
  ACL_USER *acl_user;       ///< a copy, independent from acl_users array
8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953
  plugin_ref plugin;        ///< what plugin we're under
  LEX_STRING db;            ///< db name from the handshake packet
  /** when restarting a plugin this caches the last client reply */
  struct {
    char *plugin, *pkt;     ///< pointers into NET::buff
    uint pkt_len;
  } cached_client_reply;
  /** this caches the first plugin packet for restart request on the client */
  struct {
    char *pkt;
    uint pkt_len;
  } cached_server_packet;
  int packets_read, packets_written; ///< counters for send/received packets
  uint connect_errors;      ///< if there were connect errors for this host
8954
  bool make_it_fail;
8955 8956 8957 8958 8959 8960 8961
  /** when plugin returns a failure this tells us what really happened */
  enum { SUCCESS, FAILURE, RESTART } status;
};

/**
  a helper function to report an access denied error in all the proper places
*/
8962
static void login_failed_error(THD *thd)
8963
{
Sergei Golubchik's avatar
Sergei Golubchik committed
8964
  my_error(access_denied_error_code(thd->password), MYF(0),
8965 8966
           thd->main_security_ctx.user,
           thd->main_security_ctx.host_or_ip,
8967
           thd->password ? ER(ER_YES) : ER(ER_NO));
Sergei Golubchik's avatar
Sergei Golubchik committed
8968 8969
  general_log_print(thd, COM_CONNECT,
                    ER(access_denied_error_code(thd->password)),
8970 8971
                    thd->main_security_ctx.user,
                    thd->main_security_ctx.host_or_ip,
8972
                    thd->password ? ER(ER_YES) : ER(ER_NO));
8973
  status_var_increment(thd->status_var.access_denied_errors);
8974
  /*
8975
    Log access denied messages to the error log when log-warnings = 2
8976
    so that the overhead of the general query log is not required to track
8977 8978 8979 8980
    failed connections.
  */
  if (global_system_variables.log_warnings > 1)
  {
Sergei Golubchik's avatar
Sergei Golubchik committed
8981
    sql_print_warning(ER(access_denied_error_code(thd->password)),
8982 8983
                      thd->main_security_ctx.user,
                      thd->main_security_ctx.host_or_ip,
8984
                      thd->password ? ER(ER_YES) : ER(ER_NO));
8985 8986 8987 8988 8989 8990 8991 8992
  }
}

/**
  sends a server handshake initialization packet, the very first packet
  after the connection was established

  Packet format:
8993

8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019
    Bytes       Content
    -----       ----
    1           protocol version (always 10)
    n           server version string, \0-terminated
    4           thread id
    8           first 8 bytes of the plugin provided data (scramble)
    1           \0 byte, terminating the first part of a scramble
    2           server capabilities (two lower bytes)
    1           server character set
    2           server status
    2           server capabilities (two upper bytes)
    1           length of the scramble
    10          reserved, always 0
    n           rest of the plugin provided data (at least 12 bytes)
    1           \0 byte, terminating the second part of a scramble

  @retval 0 ok
  @retval 1 error
*/
static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
                                         const char *data, uint data_len)
{
  DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
  DBUG_ASSERT(data_len <= 255);

  THD *thd= mpvio->thd;
Sergei Golubchik's avatar
Sergei Golubchik committed
9020
  char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + 1 + data_len + 64);
9021 9022
  char scramble_buf[SCRAMBLE_LENGTH];
  char *end= buff;
9023
  DBUG_ENTER("send_server_handshake_packet");
9024 9025 9026 9027 9028

  *end++= protocol_version;

  thd->client_capabilities= CLIENT_BASIC_FLAGS;

9029
  if (opt_using_transactions)
Sergei Golubchik's avatar
Sergei Golubchik committed
9030
    thd->client_capabilities|= CLIENT_TRANSACTIONS;
9031

Sergei Golubchik's avatar
Sergei Golubchik committed
9032
  thd->client_capabilities|= CAN_CLIENT_COMPRESS;
9033 9034 9035

  if (ssl_acceptor_fd)
  {
Sergei Golubchik's avatar
Sergei Golubchik committed
9036 9037
    thd->client_capabilities |= CLIENT_SSL;
    thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
9038 9039
  }

9040 9041 9042 9043 9044 9045 9046 9047 9048
  if (data_len)
  {
    mpvio->cached_server_packet.pkt= (char*)thd->memdup(data, data_len);
    mpvio->cached_server_packet.pkt_len= data_len;
  }

  if (data_len < SCRAMBLE_LENGTH)
  {
    if (data_len)
9049 9050
    {
      /*
9051 9052 9053 9054
        the first packet *must* have at least 20 bytes of a scramble.
        if a plugin provided less, we pad it to 20 with zeros
      */
      memcpy(scramble_buf, data, data_len);
9055
      bzero(scramble_buf + data_len, SCRAMBLE_LENGTH - data_len);
9056 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074
      data= scramble_buf;
    }
    else
    {
      /*
        if the default plugin does not provide the data for the scramble at
        all, we generate a scramble internally anyway, just in case the
        user account (that will be known only later) uses a
        native_password_plugin (which needs a scramble). If we don't send a
        scramble now - wasting 20 bytes in the packet -
        native_password_plugin will have to send it in a separate packet,
        adding one more round trip.
      */
      create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
      data= thd->scramble;
    }
    data_len= SCRAMBLE_LENGTH;
  }

9075
  end= strxnmov(end, SERVER_VERSION_LENGTH, RPL_VERSION_HACK, server_version, NullS) + 1;
9076 9077 9078 9079 9080 9081 9082 9083
  int4store((uchar*) end, mpvio->thd->thread_id);
  end+= 4;

  /*
    Old clients does not understand long scrambles, but can ignore packet
    tail: that's why first part of the scramble is placed here, and second
    part at the end of packet.
  */
9084
  end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
9085 9086
  end+= SCRAMBLE_LENGTH_323;
  *end++= 0;
9087

9088 9089
  int2store(end, thd->client_capabilities);
  /* write server characteristics: up to 16 bytes allowed */
9090
  end[2]= (char) default_charset_info->number;
9091 9092 9093
  int2store(end+3, mpvio->thd->server_status);
  int2store(end+5, thd->client_capabilities >> 16);
  end[7]= data_len;
9094
  DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
9095
  bzero(end + 8, 10);
9096 9097
  end+= 18;
  /* write scramble tail */
9098 9099
  end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
                      data_len - SCRAMBLE_LENGTH_323);
9100 9101 9102 9103
  end+= data_len - SCRAMBLE_LENGTH_323;
  end= strmake(end, plugin_name(mpvio->plugin)->str,
                    plugin_name(mpvio->plugin)->length);

Sergei Golubchik's avatar
Sergei Golubchik committed
9104 9105
  int res= my_net_write(&mpvio->thd->net, (uchar*) buff,
                        (size_t) (end - buff + 1)) ||
9106 9107
           net_flush(&mpvio->thd->net);
  my_afree(buff);
9108
  DBUG_RETURN (res);
9109 9110 9111 9112 9113 9114 9115 9116
}

static bool secure_auth(THD *thd)
{
  if (!opt_secure_auth)
    return 0;

  /*
9117
    If the server is running in secure auth mode, short scrambles are
9118 9119 9120 9121 9122 9123 9124 9125 9126 9127 9128 9129 9130 9131 9132 9133 9134 9135 9136 9137 9138 9139 9140 9141
    forbidden. Extra juggling to report the same error as the old code.
  */
  if (thd->client_capabilities & CLIENT_PROTOCOL_41)
  {
    my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
             thd->security_ctx->user,
             thd->security_ctx->host_or_ip);
    general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
                      thd->security_ctx->user,
                      thd->security_ctx->host_or_ip);
  }
  else
  {
    my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
    general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
  }
  return 1;
}

/**
  sends a "change plugin" packet, requesting a client to restart authentication
  using a different authentication plugin

  Packet format:
9142

9143 9144 9145 9146 9147 9148 9149 9150 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 9162 9163 9164
    Bytes       Content
    -----       ----
    1           byte with the value 254
    n           client plugin to use, \0-terminated
    n           plugin provided data

  In a special case of switching from native_password_plugin to
  old_password_plugin, the packet contains only one - the first - byte,
  plugin name is omitted, plugin data aren't needed as the scramble was
  already sent. This one-byte packet is identical to the "use the short
  scramble" packet in the protocol before plugins were introduced.

  @retval 0 ok
  @retval 1 error
*/
static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
                                       const uchar *data, uint data_len)
{
  DBUG_ASSERT(mpvio->packets_written == 1);
  DBUG_ASSERT(mpvio->packets_read == 1);
  NET *net= &mpvio->thd->net;
  static uchar switch_plugin_request_buf[]= { 254 };
9165
  DBUG_ENTER("send_plugin_request_packet");
9166 9167 9168 9169

  mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART

  const char *client_auth_plugin=
9170
    ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
9171 9172 9173 9174 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187

  DBUG_ASSERT(client_auth_plugin);

  /*
    we send an old "short 4.0 scramble request", if we need to request a
    client to use 4.0 auth plugin (short scramble) and the scramble was
    already sent to the client

    below, cached_client_reply.plugin is the plugin name that client has used,
    client_auth_plugin is derived from mysql.user table, for the given
    user account, it's the plugin that the client need to use to login.
  */
  bool switch_from_long_to_short_scramble=
    native_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
    client_auth_plugin == old_password_plugin_name.str;

  if (switch_from_long_to_short_scramble)
Sergei Golubchik's avatar
Sergei Golubchik committed
9188
    DBUG_RETURN (secure_auth(mpvio->thd) ||
9189 9190
                 my_net_write(net, switch_plugin_request_buf, 1) ||
                 net_flush(net));
9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204

  /*
    We never request a client to switch from a short to long scramble.
    Plugin-aware clients can do that, but traditionally it meant to
    ask an old 4.0 client to use the new 4.1 authentication protocol.
  */
  bool switch_from_short_to_long_scramble=
    old_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
    client_auth_plugin == native_password_plugin_name.str;

  if (switch_from_short_to_long_scramble)
  {
    my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
    general_log_print(mpvio->thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
9205
    DBUG_RETURN (1);
9206 9207
  }

9208
  DBUG_PRINT("info", ("requesting client to use the %s plugin",
9209 9210 9211 9212 9213
                      client_auth_plugin));
  DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
                                (uchar*) client_auth_plugin,
                                strlen(client_auth_plugin) + 1,
                                (uchar*) data, data_len));
9214 9215 9216 9217 9218
}

#ifndef NO_EMBEDDED_ACCESS_CHECKS
/**
   Finds acl entry in user database for authentication purposes.
9219

9220 9221
   Finds a user and copies it into mpvio. Creates a fake user
   if no matching user account is found.
9222

9223
   @note find_user_no_anon is not the same, because it doesn't take into
9224 9225 9226
   account the case when user is not empty, but acl_user->user is empty

   @retval 0    found
9227
   @retval 1    error
9228
*/
9229
static bool find_mpvio_user(MPVIO_EXT *mpvio)
9230
{
Sergei Golubchik's avatar
Sergei Golubchik committed
9231
  Security_context *sctx= mpvio->thd->security_ctx;
9232
  DBUG_ENTER("find_mpvio_user");
9233 9234
  DBUG_ASSERT(mpvio->acl_user == 0);

9235
  mysql_mutex_lock(&acl_cache->lock);
9236 9237 9238 9239 9240

  ACL_USER *user= find_user(sctx->host, sctx->user, sctx->ip);
  if (user)
    mpvio->acl_user= user->copy(&mem);

9241
  mysql_mutex_unlock(&acl_cache->lock);
9242 9243 9244

  if (!mpvio->acl_user)
  {
9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258
    /*
      A matching user was not found. Fake it. Take any user, make the
      authentication fail later.
      This way we get a realistically looking failure, with occasional
      "change auth plugin" requests even for nonexistent users. The ratio
      of "change auth plugin" request will be the same for real and
      nonexistent users.
      Note, that we cannot pick any user at random, it must always be
      the same user account for the incoming sctx->user name.
    */
    ulong nr1=1, nr2=4;
    CHARSET_INFO *cs= &my_charset_latin1;
    cs->coll->hash_sort(cs, (uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2);

Sergei Golubchik's avatar
Sergei Golubchik committed
9259
    mysql_mutex_lock(&acl_cache->lock);
9260 9261
    if (!acl_users.elements)
    {
Sergei Golubchik's avatar
Sergei Golubchik committed
9262
      mysql_mutex_unlock(&acl_cache->lock);
9263
      login_failed_error(mpvio->thd);
Sergei Golubchik's avatar
Sergei Golubchik committed
9264
      DBUG_RETURN(1);
9265
    }
9266 9267 9268
    uint i= nr1 % acl_users.elements;
    ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
    mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root);
Sergei Golubchik's avatar
Sergei Golubchik committed
9269
    mysql_mutex_unlock(&acl_cache->lock);
9270 9271

    mpvio->make_it_fail= true;
9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284
  }

  /* user account requires non-default plugin and the client is too old */
  if (mpvio->acl_user->plugin.str != native_password_plugin_name.str &&
      mpvio->acl_user->plugin.str != old_password_plugin_name.str &&
      !(mpvio->thd->client_capabilities & CLIENT_PLUGIN_AUTH))
  {
    DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
                              native_password_plugin_name.str));
    DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
                              old_password_plugin_name.str));
    my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
    general_log_print(mpvio->thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
9285
    DBUG_RETURN (1);
9286 9287 9288
  }

  mpvio->auth_info.user_name= sctx->user;
Sergei Golubchik's avatar
Sergei Golubchik committed
9289
  mpvio->auth_info.user_name_length= strlen(sctx->user);
9290
  mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
9291
  mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length;
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
9292 9293
  strmake_buf(mpvio->auth_info.authenticated_as, mpvio->acl_user->user.str ?
              mpvio->acl_user->user.str : "");
9294

9295 9296 9297 9298 9299 9300 9301
  DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
                      "plugin=%s",
                      mpvio->auth_info.user_name,
                      mpvio->auth_info.auth_string,
                      mpvio->auth_info.authenticated_as,
                      mpvio->acl_user->plugin.str));
  DBUG_RETURN(0);
9302 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314
}
#endif

/* the packet format is described in send_change_user_packet() */
static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
{
  THD *thd= mpvio->thd;
  NET *net= &thd->net;
  Security_context *sctx= thd->security_ctx;

  char *user= (char*) net->read_pos;
  char *end= user + packet_length;
  /* Safe because there is always a trailing \0 at the end of the packet */
9315
  char *passwd= strend(user) + 1;
9316 9317
  uint user_len= passwd - user - 1;
  char *db= passwd;
Sergei Golubchik's avatar
Sergei Golubchik committed
9318
  char db_buff[SAFE_NAME_LEN + 1];            // buffer to store db in utf8
9319 9320
  char user_buff[USERNAME_LENGTH + 1];	      // buffer to store user in utf8
  uint dummy_errors;
9321
  DBUG_ENTER ("parse_com_change_user_packet");
9322 9323 9324 9325

  if (passwd >= end)
  {
    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
9326
    DBUG_RETURN (1);
9327 9328 9329 9330 9331 9332 9333 9334 9335 9336 9337 9338 9339
  }

  /*
    Old clients send null-terminated string as password; new clients send
    the size (1 byte) + string (not null-terminated). Hence in case of empty
    password both send '\0'.

    This strlen() can't be easily deleted without changing protocol.

    Cast *passwd to an unsigned char, so that it doesn't extend the sign for
    *passwd > 127 and become 2**32-127+ after casting to uint.
  */
  uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
9340
                    (uchar) (*passwd++) : strlen(passwd));
9341 9342 9343 9344 9345 9346 9347 9348 9349

  db+= passwd_len + 1;
  /*
    Database name is always NUL-terminated, so in case of empty database
    the packet must contain at least the trailing '\0'.
  */
  if (db >= end)
  {
    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
9350
    DBUG_RETURN (1);
9351 9352 9353 9354 9355 9356
  }

  uint db_len= strlen(db);

  char *ptr= db + db_len + 1;

9357
  if (ptr + 1 < end)
9358
  {
Sergei Golubchik's avatar
Sergei Golubchik committed
9359
    if (thd_init_client_charset(thd, uint2korr(ptr)))
9360
      DBUG_RETURN(1);
9361 9362 9363 9364
    thd->update_charset();
  }

  /* Convert database and user names to utf8 */
9365
  db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
9366 9367
                           db, db_len, thd->charset(), &dummy_errors);

9368
  user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
9369 9370 9371 9372
                             system_charset_info, user, user_len,
                             thd->charset(), &dummy_errors);

  if (!(sctx->user= my_strndup(user_buff, user_len, MYF(MY_WME))))
Sergei Golubchik's avatar
Sergei Golubchik committed
9373
    DBUG_RETURN(1);
9374 9375 9376

  /* Clear variables that are allocated */
  thd->user_connect= 0;
9377
  strmake_buf(sctx->priv_user, sctx->user);
9378

9379
  if (thd->make_lex_string(&mpvio->db, db_buff, db_len) == 0)
9380
    DBUG_RETURN(1); /* The error is set by make_lex_string(). */
9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392

  /*
    Clear thd->db as it points to something, that will be freed when
    connection is closed. We don't want to accidentally free a wrong
    pointer if connect failed.
  */
  thd->reset_db(NULL, 0);

  if (!initialized)
  {
    // if mysqld's been started with --skip-grant-tables option
    mpvio->status= MPVIO_EXT::SUCCESS;
9393
    DBUG_RETURN(0);
9394 9395 9396
  }

#ifndef NO_EMBEDDED_ACCESS_CHECKS
9397
  thd->password= passwd_len > 0;
9398
  if (find_mpvio_user(mpvio))
9399
    DBUG_RETURN(1);
9400 9401 9402 9403 9404 9405 9406 9407

  char *client_plugin;
  if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
  {
    client_plugin= ptr + 2;
    if (client_plugin >= end)
    {
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
9408
      DBUG_RETURN(1);
9409
    }
9410
    client_plugin= fix_plugin_ptr(client_plugin);
9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 9421
  }
  else
  {
    if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
      client_plugin= native_password_plugin_name.str;
    else
    {
      client_plugin=  old_password_plugin_name.str;
      /*
        For a passwordless accounts we use native_password_plugin.
        But when an old 4.0 client connects to it, we change it to
9422
        old_password_plugin, otherwise MySQL will think that server
9423 9424 9425 9426 9427 9428
        and client plugins don't match.
      */
      if (mpvio->acl_user->auth_string.length == 0)
        mpvio->acl_user->plugin= old_password_plugin_name;
    }
  }
9429

9430
  DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
9431 9432 9433
  /*
    Remember the data part of the packet, to present it to plugin in
    read_packet()
9434
  */
9435 9436 9437 9438 9439 9440
  mpvio->cached_client_reply.pkt= passwd;
  mpvio->cached_client_reply.pkt_len= passwd_len;
  mpvio->cached_client_reply.plugin= client_plugin;
  mpvio->status= MPVIO_EXT::RESTART;
#endif

9441
  DBUG_RETURN (0);
9442 9443 9444 9445 9446 9447 9448 9449 9450 9451 9452 9453 9454 9455 9456 9457
}


/* the packet format is described in send_client_reply_packet() */
static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
                                           uchar **buff, ulong pkt_len)
{
#ifndef EMBEDDED_LIBRARY
  THD *thd= mpvio->thd;
  NET *net= &thd->net;
  char *end;
  DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);

  if (pkt_len < MIN_HANDSHAKE_SIZE)
    return packet_error;

Michael Widenius's avatar
Michael Widenius committed
9458 9459 9460 9461 9462 9463
  /*
    Protocol buffer is guaranteed to always end with \0. (see my_net_read())
    As the code below depends on this, lets check that.
  */
  DBUG_ASSERT(net->read_pos[pkt_len] == 0);

9464
  if (mpvio->connect_errors)
Sergei Golubchik's avatar
Sergei Golubchik committed
9465
    reset_host_errors(thd->main_security_ctx.ip);
9466 9467 9468 9469

  ulong client_capabilities= uint2korr(net->read_pos);
  if (client_capabilities & CLIENT_PROTOCOL_41)
  {
9470
    if (pkt_len < 4)
Michael Widenius's avatar
Michael Widenius committed
9471
      return packet_error;
9472 9473 9474 9475 9476 9477 9478 9479 9480
    client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
  }

  /* Disable those bits which are not supported by the client. */
  thd->client_capabilities&= client_capabilities;

  DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
  if (thd->client_capabilities & CLIENT_SSL)
  {
Sergei Golubchik's avatar
Sergei Golubchik committed
9481
    unsigned long errptr __attribute__((unused));
9482 9483 9484 9485 9486 9487

    /* Do the SSL layering. */
    if (!ssl_acceptor_fd)
      return packet_error;

    DBUG_PRINT("info", ("IO layer change in progress..."));
9488
    if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr))
9489 9490 9491 9492 9493 9494 9495 9496 9497 9498 9499 9500 9501 9502 9503
    {
      DBUG_PRINT("error", ("Failed to accept new SSL connection"));
      return packet_error;
    }

    DBUG_PRINT("info", ("Reading user information over SSL layer"));
    pkt_len= my_net_read(net);
    if (pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE)
    {
      DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
			   pkt_len));
      return packet_error;
    }
  }

9504 9505 9506 9507 9508 9509 9510 9511 9512 9513 9514 9515 9516 9517 9518 9519 9520 9521 9522
  if (client_capabilities & CLIENT_PROTOCOL_41)
  {
    if (pkt_len < 32)
      return packet_error;
    thd->max_client_packet_length= uint4korr(net->read_pos+4);
    DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
    if (thd_init_client_charset(thd, (uint) net->read_pos[8]))
      return packet_error;
    thd->update_charset();
    end= (char*) net->read_pos+32;
  }
  else
  {
    if (pkt_len < 5)
      return packet_error;
    thd->max_client_packet_length= uint3korr(net->read_pos+2);
    end= (char*) net->read_pos+5;
  }

9523 9524 9525
  if (end >= (char*) net->read_pos+ pkt_len +2)
    return packet_error;

Sergei Golubchik's avatar
Sergei Golubchik committed
9526 9527 9528 9529 9530
  if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
    thd->variables.sql_mode|= MODE_IGNORE_SPACE;
  if (thd->client_capabilities & CLIENT_INTERACTIVE)
    thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;

9531 9532 9533 9534 9535 9536 9537 9538 9539 9540 9541
  if (end >= (char*) net->read_pos+ pkt_len +2)
    return packet_error;

  if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
      opt_using_transactions)
    net->return_status= &thd->server_status;

  char *user= end;
  char *passwd= strend(user)+1;
  uint user_len= passwd - user - 1, db_len;
  char *db= passwd;
Sergei Golubchik's avatar
Sergei Golubchik committed
9542
  char db_buff[SAFE_NAME_LEN + 1];      // buffer to store db in utf8
9543 9544 9545 9546 9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558
  char user_buff[USERNAME_LENGTH + 1];	// buffer to store user in utf8
  uint dummy_errors;

  /*
    Old clients send null-terminated string as password; new clients send
    the size (1 byte) + string (not null-terminated). Hence in case of empty
    password both send '\0'.

    This strlen() can't be easily deleted without changing protocol.

    Cast *passwd to an unsigned char, so that it doesn't extend the sign for
    *passwd > 127 and become 2**32-127+ after casting to uint.
  */
  uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
                   (uchar)(*passwd++) : strlen(passwd);

Sergei Golubchik's avatar
Sergei Golubchik committed
9559 9560
  db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
    db + passwd_len + 1 : 0;
9561

Sergei Golubchik's avatar
Sergei Golubchik committed
9562
  if (passwd + passwd_len + test(db) > (char *)net->read_pos + pkt_len)
9563 9564
    return packet_error;

Sergei Golubchik's avatar
Sergei Golubchik committed
9565 9566 9567
  /* strlen() can't be easily deleted without changing protocol */
  db_len= db ? strlen(db) : 0;

9568 9569 9570 9571 9572
  char *client_plugin= passwd + passwd_len + (db ? db_len + 1 : 0);

  /* Since 4.1 all database names are stored in utf8 */
  if (db)
  {
9573
    db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
9574 9575 9576 9577
                             db, db_len, thd->charset(), &dummy_errors);
    db= db_buff;
  }

9578
  user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
9579 9580 9581 9582 9583 9584 9585 9586 9587 9588 9589
                             system_charset_info, user, user_len,
                             thd->charset(), &dummy_errors);
  user= user_buff;

  /* If username starts and ends in "'", chop them off */
  if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
  {
    user++;
    user_len-= 2;
  }

9590 9591
  /*
    Clip username to allowed length in characters (not bytes).  This is
9592 9593
    mostly for backward compatibility (to truncate long usernames, as
    old 5.1 did)
9594 9595 9596 9597 9598 9599
  */
  {
    CHARSET_INFO *cs= system_charset_info;
    int           err;

    user_len= (uint) cs->cset->well_formed_len(cs, user, user + user_len,
9600
                                               username_char_length, &err);
9601 9602 9603
    user[user_len]= '\0';
  }

9604 9605
  Security_context *sctx= thd->security_ctx;

9606
  if (thd->make_lex_string(&mpvio->db, db, db_len) == 0)
9607
    return packet_error; /* The error is set by make_lex_string(). */
Sergei Golubchik's avatar
Sergei Golubchik committed
9608
  my_free(sctx->user);
9609 9610 9611
  if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME))))
    return packet_error; /* The error is set by my_strdup(). */

Sergei Golubchik's avatar
Sergei Golubchik committed
9612

9613 9614 9615 9616 9617 9618 9619 9620 9621 9622 9623 9624 9625 9626
  /*
    Clear thd->db as it points to something, that will be freed when
    connection is closed. We don't want to accidentally free a wrong
    pointer if connect failed.
  */
  thd->reset_db(NULL, 0);

  if (!initialized)
  {
    // if mysqld's been started with --skip-grant-tables option
    mpvio->status= MPVIO_EXT::SUCCESS;
    return packet_error;
  }

9627
  thd->password= passwd_len > 0;
9628
  if (find_mpvio_user(mpvio))
9629 9630
    return packet_error;

9631 9632
  if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) &&
      (client_plugin < (char *)net->read_pos + pkt_len))
9633
  {
9634
    client_plugin= fix_plugin_ptr(client_plugin);
9635 9636 9637
  }
  else
  {
9638 9639 9640
    /* Some clients lie. Sad, but true */
    thd->client_capabilities &= ~CLIENT_PLUGIN_AUTH;

9641 9642 9643 9644 9645 9646 9647 9648
    if (thd->client_capabilities & CLIENT_SECURE_CONNECTION)
      client_plugin= native_password_plugin_name.str;
    else
    {
      client_plugin=  old_password_plugin_name.str;
      /*
        For a passwordless accounts we use native_password_plugin.
        But when an old 4.0 client connects to it, we change it to
9649
        old_password_plugin, otherwise MySQL will think that server
9650 9651 9652 9653 9654 9655
        and client plugins don't match.
      */
      if (mpvio->acl_user->auth_string.length == 0)
        mpvio->acl_user->plugin= old_password_plugin_name;
    }
  }
9656

9657 9658 9659 9660 9661 9662 9663 9664 9665 9666 9667 9668 9669 9670 9671 9672 9673 9674 9675 9676 9677 9678 9679 9680 9681 9682
  /*
    if the acl_user needs a different plugin to authenticate
    (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
    we need to restart the authentication in the server.
    But perhaps the client has already used the correct plugin -
    in that case the authentication on the client may not need to be
    restarted and a server auth plugin will read the data that the client
    has just send. Cache them to return in the next server_mpvio_read_packet().
  */
  if (my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
                    plugin_name(mpvio->plugin)->str) != 0)
  {
    mpvio->cached_client_reply.pkt= passwd;
    mpvio->cached_client_reply.pkt_len= passwd_len;
    mpvio->cached_client_reply.plugin= client_plugin;
    mpvio->status= MPVIO_EXT::RESTART;
    return packet_error;
  }

  /*
    ok, we don't need to restart the authentication on the server.
    but if the client used the wrong plugin, we need to restart
    the authentication on the client. Do it here, the server plugin
    doesn't need to know.
  */
  const char *client_auth_plugin=
9683
    ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
9684 9685 9686 9687 9688 9689

  if (client_auth_plugin &&
      my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
  {
    mpvio->cached_client_reply.plugin= client_plugin;
    if (send_plugin_request_packet(mpvio,
9690
                                   (uchar*) mpvio->cached_server_packet.pkt,
9691 9692 9693
                                   mpvio->cached_server_packet.pkt_len))
      return packet_error;

9694 9695
    passwd_len= my_net_read(&thd->net);
    passwd= (char*)thd->net.read_pos;
9696 9697
  }

9698
  *buff= (uchar*) passwd;
9699 9700 9701 9702 9703 9704 9705 9706 9707 9708 9709 9710 9711 9712 9713 9714 9715 9716 9717 9718
  return passwd_len;
#else
  return 0;
#endif
}


/**
  vio->write_packet() callback method for server authentication plugins

  This function is called by a server authentication plugin, when it wants
  to send data to the client.

  It transparently wraps the data into a handshake packet,
  and handles plugin negotiation with the client. If necessary,
  it escapes the plugin data, if it starts with a mysql protocol packet byte.
*/
static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
                                   const uchar *packet, int packet_len)
{
9719
  MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
9720
  int res;
9721
  DBUG_ENTER("server_mpvio_write_packet");
9722 9723 9724

  /* reset cached_client_reply */
  mpvio->cached_client_reply.pkt= 0;
Sergei Golubchik's avatar
Sergei Golubchik committed
9725

9726 9727
  /* for the 1st packet we wrap plugin data into the handshake packet */
  if (mpvio->packets_written == 0)
9728
    res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
9729 9730 9731 9732 9733 9734 9735 9736 9737 9738 9739 9740 9741 9742 9743 9744 9745 9746 9747
  else if (mpvio->status == MPVIO_EXT::RESTART)
    res= send_plugin_request_packet(mpvio, packet, packet_len);
  else if (packet_len > 0 && (*packet == 1 || *packet == 255 || *packet == 254))
  {
    /*
      we cannot allow plugin data packet to start from 255 or 254 -
      as the client will treat it as an error or "change plugin" packet.
      We'll escape these bytes with \1. Consequently, we
      have to escape \1 byte too.
    */
    res= net_write_command(&mpvio->thd->net, 1, (uchar*)"", 0,
                           packet, packet_len);
  }
  else
  {
    res= my_net_write(&mpvio->thd->net, packet, packet_len) ||
         net_flush(&mpvio->thd->net);
  }
  mpvio->packets_written++;
9748
  DBUG_RETURN(res);
9749 9750 9751 9752 9753 9754 9755 9756 9757 9758 9759 9760 9761 9762
}

/**
  vio->read_packet() callback method for server authentication plugins

  This function is called by a server authentication plugin, when it wants
  to read data from the client.

  It transparently extracts the client plugin data, if embedded into
  a client authentication handshake packet, and handles plugin negotiation
  with the client, if necessary.
*/
static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
{
9763
  MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
9764
  ulong pkt_len;
9765
  DBUG_ENTER("server_mpvio_read_packet");
9766 9767 9768 9769 9770 9771 9772 9773 9774 9775 9776
  if (mpvio->packets_written == 0)
  {
    /*
      plugin wants to read the data without sending anything first.
      send an empty packet to force a server handshake packet to be sent
    */
    if (server_mpvio_write_packet(mpvio, 0, 0))
      pkt_len= packet_error;
    else
      pkt_len= my_net_read(&mpvio->thd->net);
  }
9777
  else if (mpvio->cached_client_reply.pkt)
9778 9779 9780 9781 9782 9783 9784 9785 9786 9787
  {
    DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
    DBUG_ASSERT(mpvio->packets_read > 0);
    /*
      if the have the data cached from the last server_mpvio_read_packet
      (which can be the case if it's a restarted authentication)
      and a client has used the correct plugin, then we can return the
      cached data straight away and avoid one round trip.
    */
    const char *client_auth_plugin=
9788
      ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
9789 9790 9791 9792 9793
    if (client_auth_plugin == 0 ||
        my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
                      client_auth_plugin) == 0)
    {
      mpvio->status= MPVIO_EXT::FAILURE;
9794
      *buf= (uchar*) mpvio->cached_client_reply.pkt;
9795 9796
      mpvio->cached_client_reply.pkt= 0;
      mpvio->packets_read++;
9797 9798 9799 9800

      if (mpvio->make_it_fail)
        goto err;

9801
      DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
9802
    }
9803

9804 9805 9806 9807 9808 9809 9810 9811 9812 9813 9814 9815 9816 9817 9818 9819 9820 9821 9822 9823 9824 9825 9826 9827 9828 9829 9830 9831 9832
    /*
      But if the client has used the wrong plugin, the cached data are
      useless. Furthermore, we have to send a "change plugin" request
      to the client.
    */
    if (server_mpvio_write_packet(mpvio, 0, 0))
      pkt_len= packet_error;
    else
      pkt_len= my_net_read(&mpvio->thd->net);
  }
  else
    pkt_len= my_net_read(&mpvio->thd->net);

  if (pkt_len == packet_error)
    goto err;

  mpvio->packets_read++;

  /*
    the 1st packet has the plugin data wrapped into the client authentication
    handshake packet
  */
  if (mpvio->packets_read == 1)
  {
    pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
    if (pkt_len == packet_error)
      goto err;
  }
  else
Sergei Golubchik's avatar
Sergei Golubchik committed
9833
    *buf= mpvio->thd->net.read_pos;
9834

9835 9836 9837
  if (mpvio->make_it_fail)
    goto err;

9838
  DBUG_RETURN((int)pkt_len);
9839 9840

err:
9841
  if (mpvio->status == MPVIO_EXT::FAILURE)
9842
  {
Sergei Golubchik's avatar
Sergei Golubchik committed
9843
    inc_host_errors(mpvio->thd->security_ctx->ip);
Sergei Golubchik's avatar
Sergei Golubchik committed
9844
    if (!mpvio->thd->is_error())
Sergei Golubchik's avatar
Sergei Golubchik committed
9845 9846 9847 9848 9849 9850
    {
      if (mpvio->make_it_fail)
        login_failed_error(mpvio->thd);
      else
        my_error(ER_HANDSHAKE_ERROR, MYF(0));
    }
9851
  }
9852
  DBUG_RETURN(-1);
9853 9854 9855 9856 9857 9858 9859 9860 9861
}

/**
  fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
  connection
*/
static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
                              MYSQL_PLUGIN_VIO_INFO *info)
{
9862
  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
9863 9864 9865
  mpvio_info(mpvio->thd->net.vio, info);
}

9866
static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
9867
{
Sergei Golubchik's avatar
Sergei Golubchik committed
9868
#ifdef HAVE_OPENSSL
9869 9870
  Vio *vio= thd->net.vio;
  SSL *ssl= (SSL *) vio->ssl_arg;
9871 9872 9873 9874 9875 9876 9877 9878 9879 9880 9881 9882 9883
  X509 *cert;
#endif

  /*
    At this point we know that user is allowed to connect
    from given host by given username/password pair. Now
    we check if SSL is required, if user is using SSL and
    if X509 certificate attributes are OK
  */
  switch (acl_user->ssl_type) {
  case SSL_TYPE_NOT_SPECIFIED:                  // Impossible
  case SSL_TYPE_NONE:                           // SSL is not required
    return 0;
Sergei Golubchik's avatar
Sergei Golubchik committed
9884
#ifdef HAVE_OPENSSL
9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 9901 9902 9903 9904 9905 9906 9907 9908 9909
  case SSL_TYPE_ANY:                            // Any kind of SSL is ok
    return vio_type(vio) != VIO_TYPE_SSL;
  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.

      We need to check for absence of SSL because without SSL
      we should reject connection.
    */
    if (vio_type(vio) == VIO_TYPE_SSL &&
        SSL_get_verify_result(ssl) == X509_V_OK &&
        (cert= SSL_get_peer_certificate(ssl)))
    {
      X509_free(cert);
      return 0;
    }
    return 1;
  case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
    /* If a cipher name is specified, we compare it to actual cipher in use. */
    if (vio_type(vio) != VIO_TYPE_SSL ||
        SSL_get_verify_result(ssl) != X509_V_OK)
      return 1;
    if (acl_user->ssl_cipher)
    {
9910 9911 9912
      DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
                         acl_user->ssl_cipher, SSL_get_cipher(ssl)));
      if (strcmp(acl_user->ssl_cipher, SSL_get_cipher(ssl)))
9913 9914 9915 9916 9917 9918 9919 9920 9921 9922 9923 9924 9925
      {
        if (global_system_variables.log_warnings)
          sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
                            acl_user->ssl_cipher, SSL_get_cipher(ssl));
        return 1;
      }
    }
    /* Prepare certificate (if exists) */
    if (!(cert= SSL_get_peer_certificate(ssl)))
      return 1;
    /* If X509 issuer is specified, we check it... */
    if (acl_user->x509_issuer)
    {
9926 9927
      char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
      DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943
                         acl_user->x509_issuer, ptr));
      if (strcmp(acl_user->x509_issuer, ptr))
      {
        if (global_system_variables.log_warnings)
          sql_print_information("X509 issuer mismatch: should be '%s' "
                            "but is '%s'", acl_user->x509_issuer, ptr);
        free(ptr);
        X509_free(cert);
        return 1;
      }
      free(ptr);
    }
    /* X509 subject is specified, we check it .. */
    if (acl_user->x509_subject)
    {
      char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
9944
      DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
9945
                         acl_user->x509_subject, ptr));
9946
      if (strcmp(acl_user->x509_subject, ptr))
9947 9948 9949 9950 9951 9952 9953 9954 9955 9956 9957 9958 9959 9960 9961
      {
        if (global_system_variables.log_warnings)
          sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
                          acl_user->x509_subject, ptr);
        free(ptr);
        X509_free(cert);
        return 1;
      }
      free(ptr);
    }
    X509_free(cert);
    return 0;
#else  /* HAVE_OPENSSL */
  default:
    /*
9962
      If we don't have SSL but SSL is required for this user the
9963 9964 9965 9966 9967 9968 9969 9970
      authentication should fail.
    */
    return 1;
#endif /* HAVE_OPENSSL */
  }
  return 1;
}

9971 9972

static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
9973 9974 9975 9976
                        MPVIO_EXT *mpvio)
{
  int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
  bool unlock_plugin= false;
Sergei Golubchik's avatar
Sergei Golubchik committed
9977
  plugin_ref plugin= NULL;
9978 9979 9980

  if (auth_plugin_name->str == native_password_plugin_name.str)
    plugin= native_password_plugin;
9981
#ifndef EMBEDDED_LIBRARY
Michael Widenius's avatar
Michael Widenius committed
9982
  else if (auth_plugin_name->str == old_password_plugin_name.str)
9983
    plugin= old_password_plugin;
Michael Widenius's avatar
Michael Widenius committed
9984 9985
  else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
                                           MYSQL_AUTHENTICATION_PLUGIN)))
9986 9987 9988 9989 9990 9991 9992 9993
    unlock_plugin= true;
#endif

  mpvio->plugin= plugin;
  old_status= mpvio->status;

  if (plugin)
  {
9994
    st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
9995 9996 9997 9998 9999 10000 10001 10002 10003 10004 10005 10006 10007 10008
    switch (auth->interface_version) {
    case 0x0200:
      res= auth->authenticate_user(mpvio, &mpvio->auth_info);
      break;
    case 0x0100:
      {
        MYSQL_SERVER_AUTH_INFO_0x0100 compat;
        compat.downgrade(&mpvio->auth_info);
        res= auth->authenticate_user(mpvio, (MYSQL_SERVER_AUTH_INFO *)&compat);
        compat.upgrade(&mpvio->auth_info);
      }
      break;
    default: DBUG_ASSERT(0);
    }
10009 10010 10011 10012 10013 10014 10015 10016 10017 10018 10019 10020 10021 10022 10023 10024 10025 10026 10027 10028 10029 10030 10031 10032 10033

    if (unlock_plugin)
      plugin_unlock(thd, plugin);
  }
  else
  {
    /* Server cannot load the required plugin. */
    my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
    res= CR_ERROR;
  }

  /*
    If the status was MPVIO_EXT::RESTART before the authenticate_user() call
    it can never be MPVIO_EXT::RESTART after the call, because any call
    to write_packet() or read_packet() will reset the status.

    But (!) if a plugin never called a read_packet() or write_packet(), the
    status will stay unchanged. We'll fix it, by resetting the status here.
  */
  if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART)
    mpvio->status= MPVIO_EXT::FAILURE; // reset to the default

  return res;
}

10034

10035 10036 10037 10038 10039 10040 10041 10042 10043 10044 10045 10046 10047 10048
/**
  Perform the handshake, authorize the client and update thd sctx variables.

  @param thd                     thread handle
  @param connect_errors          number of previous failed connect attemps
                                 from this host
  @param com_change_user_pkt_len size of the COM_CHANGE_USER packet
                                 (without the first, command, byte) or 0
                                 if it's not a COM_CHANGE_USER (that is, if
                                 it's a new connection)

  @retval 0  success, thd is updated.
  @retval 1  error
*/
Sergei Golubchik's avatar
Sergei Golubchik committed
10049 10050
bool acl_authenticate(THD *thd, uint connect_errors,
                      uint com_change_user_pkt_len)
10051 10052 10053
{
  int res= CR_OK;
  MPVIO_EXT mpvio;
10054
  const LEX_STRING *auth_plugin_name= default_auth_plugin_name;
10055 10056 10057 10058 10059 10060 10061 10062 10063 10064 10065
  enum  enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
                                                             : COM_CONNECT;
  DBUG_ENTER("acl_authenticate");

  bzero(&mpvio, sizeof(mpvio));
  mpvio.read_packet= server_mpvio_read_packet;
  mpvio.write_packet= server_mpvio_write_packet;
  mpvio.info= server_mpvio_info;
  mpvio.thd= thd;
  mpvio.connect_errors= connect_errors;
  mpvio.status= MPVIO_EXT::FAILURE;
10066
  mpvio.make_it_fail= false;
Sergei Golubchik's avatar
Sergei Golubchik committed
10067
  mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
10068
  mpvio.auth_info.host_or_ip_length=
Sergei Golubchik's avatar
Sergei Golubchik committed
10069
    (unsigned int) strlen(thd->security_ctx->host_or_ip);
10070

10071
  DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
10072

10073 10074 10075 10076 10077 10078 10079 10080 10081 10082 10083 10084 10085 10086 10087 10088 10089 10090 10091 10092 10093 10094
  if (command == COM_CHANGE_USER)
  {
    mpvio.packets_written++; // pretend that a server handshake packet was sent
    mpvio.packets_read++;    // take COM_CHANGE_USER packet into account

    if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
      DBUG_RETURN(1);

    DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
                mpvio.status == MPVIO_EXT::SUCCESS);
  }
  else
  {
    /* mark the thd as having no scramble yet */
    thd->scramble[SCRAMBLE_LENGTH]= 1;

    /*
      perform the first authentication attempt, with the default plugin.
      This sends the server handshake packet, reads the client reply
      with a user name, and performs the authentication if everyone has used
      the correct plugin.
    */
10095

10096
    res= do_auth_once(thd, auth_plugin_name, &mpvio);
10097 10098 10099 10100 10101 10102 10103 10104 10105 10106 10107 10108 10109 10110 10111 10112 10113
  }

  /*
    retry the authentication, if - after receiving the user name -
    we found that we need to switch to a non-default plugin
  */
  if (mpvio.status == MPVIO_EXT::RESTART)
  {
    DBUG_ASSERT(mpvio.acl_user);
    DBUG_ASSERT(command == COM_CHANGE_USER ||
                my_strcasecmp(system_charset_info, auth_plugin_name->str,
                              mpvio.acl_user->plugin.str));
    auth_plugin_name= &mpvio.acl_user->plugin;
    res= do_auth_once(thd, auth_plugin_name, &mpvio);
  }

  Security_context *sctx= thd->security_ctx;
10114
  const ACL_USER *acl_user= mpvio.acl_user;
10115

10116
  thd->password= mpvio.auth_info.password_used;  // remember for error messages
10117 10118 10119 10120 10121 10122 10123 10124 10125 10126 10127 10128 10129 10130 10131 10132 10133 10134 10135 10136 10137 10138 10139 10140 10141 10142 10143

  /*
    Log the command here so that the user can check the log
    for the tried logins and also to detect break-in attempts.

    if sctx->user is unset it's protocol failure, bad packet.
  */
  if (sctx->user)
  {
    if (strcmp(sctx->priv_user, sctx->user))
    {
      general_log_print(thd, command, "%s@%s as %s on %s",
                        sctx->user, sctx->host_or_ip,
                        sctx->priv_user[0] ? sctx->priv_user : "anonymous",
                        mpvio.db.str ? mpvio.db.str : (char*) "");
    }
    else
      general_log_print(thd, command, (char*) "%s@%s on %s",
                        sctx->user, sctx->host_or_ip,
                        mpvio.db.str ? mpvio.db.str : (char*) "");
  }

  if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
  {
    DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);

    if (!thd->is_error())
10144
      login_failed_error(thd);
10145 10146 10147
    DBUG_RETURN(1);
  }

10148 10149
  sctx->proxy_user[0]= 0;

10150 10151
  if (initialized) // if not --skip-grant-tables
  {
10152 10153
#ifndef NO_EMBEDDED_ACCESS_CHECKS
    bool is_proxy_user= FALSE;
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
10154
    const char *auth_user = acl_user->user.str ? acl_user->user.str : "";
10155
    ACL_PROXY_USER *proxy_user;
10156
    /* check if the user is allowed to proxy as another user */
10157 10158 10159 10160
    proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
                                    mpvio.auth_info.authenticated_as,
                                          &is_proxy_user);
    if (is_proxy_user)
10161
    {
10162 10163 10164 10165 10166 10167
      ACL_USER *acl_proxy_user;

      /* we need to find the proxy user, but there was none */
      if (!proxy_user)
      {
        if (!thd->is_error())
Sergei Golubchik's avatar
Sergei Golubchik committed
10168
          login_failed_error(thd);
10169 10170
        DBUG_RETURN(1);
      }
10171

10172
      my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
10173 10174
                  "'%s'@'%s'", auth_user,
                  acl_user->host.hostname ? acl_user->host.hostname : "");
10175 10176 10177

      /* we're proxying : find the proxy user definition */
      mysql_mutex_lock(&acl_cache->lock);
10178 10179 10180 10181
      acl_proxy_user= find_user_no_anon(proxy_user->get_proxied_host() ?
                                          proxy_user->get_proxied_host() : "",
                                        mpvio.auth_info.authenticated_as,
                                        TRUE);
10182 10183 10184
      if (!acl_proxy_user)
      {
        if (!thd->is_error())
Sergei Golubchik's avatar
Sergei Golubchik committed
10185
          login_failed_error(thd);
10186 10187 10188 10189 10190 10191
        mysql_mutex_unlock(&acl_cache->lock);
        DBUG_RETURN(1);
      }
      acl_user= acl_proxy_user->copy(thd->mem_root);
      mysql_mutex_unlock(&acl_cache->lock);
    }
10192 10193
#endif

10194
    sctx->master_access= acl_user->access;
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
10195 10196
    if (acl_user->user.str)
      strmake_buf(sctx->priv_user, acl_user->user.str);
10197 10198 10199
    else
      *sctx->priv_user= 0;

10200
    if (acl_user->host.hostname)
10201
      strmake_buf(sctx->priv_host, acl_user->host.hostname);
10202 10203 10204 10205 10206 10207 10208 10209 10210 10211
    else
      *sctx->priv_host= 0;

    /*
      OK. Let's check the SSL. Historically it was checked after the password,
      as an additional layer, not instead of the password
      (in which case it would've been a plugin too).
    */
    if (acl_check_ssl(thd, acl_user))
    {
10212
      login_failed_error(thd);
10213 10214 10215
      DBUG_RETURN(1);
    }

10216 10217 10218 10219 10220 10221 10222 10223
    /*
      Don't allow the user to connect if he has done too many queries.
      As we are testing max_user_connections == 0 here, it means that we
      can't let the user change max_user_connections from 0 in the server
      without a restart as it would lead to wrong connect counting.
    */
    if ((acl_user->user_resource.questions ||
         acl_user->user_resource.updates ||
10224
         acl_user->user_resource.conn_per_hour ||
10225
         acl_user->user_resource.user_conn || max_user_connections_checking) &&
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
10226 10227 10228 10229
         get_or_create_user_conn(thd,
           (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
           (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
           &acl_user->user_resource))
10230 10231 10232 10233 10234 10235 10236 10237
      DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
  }
  else
    sctx->skip_grants();

  if (thd->user_connect &&
      (thd->user_connect->user_resources.conn_per_hour ||
       thd->user_connect->user_resources.user_conn ||
10238
       max_user_connections_checking) &&
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
10239
       check_for_max_user_connections(thd, thd->user_connect))
10240
  {
10241 10242
    /* Ensure we don't decrement thd->user_connections->connections twice */
    thd->user_connect= 0;
10243 10244 10245 10246 10247 10248 10249 10250 10251 10252 10253 10254 10255 10256 10257 10258
    status_var_increment(denied_connections);
    DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
  }

  DBUG_PRINT("info",
             ("Capabilities: %lu  packet_length: %ld  Host: '%s'  "
              "Login user: '%s' Priv_user: '%s'  Using password: %s "
              "Access: %lu  db: '%s'",
              thd->client_capabilities, thd->max_client_packet_length,
              sctx->host_or_ip, sctx->user, sctx->priv_user,
              thd->password ? "yes": "no",
              sctx->master_access, mpvio.db.str));

  if (command == COM_CONNECT &&
      !(thd->main_security_ctx.master_access & SUPER_ACL))
  {
10259
    mysql_mutex_lock(&LOCK_connection_count);
10260 10261
    bool count_ok= (*thd->scheduler->connection_count <=
                    *thd->scheduler->max_connections);
10262
    mysql_mutex_unlock(&LOCK_connection_count);
10263 10264 10265 10266 10267 10268 10269 10270 10271 10272 10273 10274 10275 10276 10277 10278 10279 10280 10281 10282
    if (!count_ok)
    {                                         // too many connections
      my_error(ER_CON_COUNT_ERROR, MYF(0));
      DBUG_RETURN(1);
    }
  }

  /*
    This is the default access rights for the current database.  It's
    set to 0 here because we don't have an active database yet (and we
    may not have an active database to set.
  */
  sctx->db_access=0;

  /* Change a database if necessary */
  if (mpvio.db.length)
  {
    if (mysql_change_db(thd, &mpvio.db, FALSE))
    {
      /* mysql_change_db() has pushed the error message. */
10283
      status_var_increment(thd->status_var.access_denied_errors);
10284 10285 10286 10287 10288 10289
      DBUG_RETURN(1);
    }
  }

  thd->net.net_skip_rest_factor= 2;  // skip at most 2*max_packet_size

10290 10291 10292
  if (mpvio.auth_info.external_user[0])
    sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));

10293
  if (res == CR_OK_HANDSHAKE_COMPLETE)
10294
    thd->stmt_da->disable_status();
10295 10296 10297 10298 10299 10300 10301 10302 10303 10304 10305 10306 10307 10308 10309
  else
    my_ok(thd);

  /* Ready to handle queries */
  DBUG_RETURN(0);
}

/**
  MySQL Server Password Authentication Plugin

  In the MySQL authentication protocol:
  1. the server sends the random scramble to the client
  2. client sends the encrypted password back to the server
  3. the server checks the password.
*/
10310
static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
10311 10312 10313 10314
                                        MYSQL_SERVER_AUTH_INFO *info)
{
  uchar *pkt;
  int pkt_len;
10315
  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
10316
  THD *thd=mpvio->thd;
10317
  DBUG_ENTER("native_password_authenticate");
10318 10319 10320

  /* generate the scramble, or reuse the old one */
  if (thd->scramble[SCRAMBLE_LENGTH])
10321
  {
10322
    create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
10323 10324
    /* and send it to the client */
    if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
Sergei Golubchik's avatar
Sergei Golubchik committed
10325
      DBUG_RETURN(CR_ERROR);
10326
  }
10327 10328 10329 10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 10348 10349 10350 10351 10352 10353 10354 10355 10356 10357 10358 10359 10360 10361 10362 10363 10364 10365 10366

  /* reply and authenticate */

  /*
    <digression>
      This is more complex than it looks.

      The plugin (we) may be called right after the client was connected -
      and will need to send a scramble, read reply, authenticate.

      Or the plugin may be called after another plugin has sent a scramble,
      and read the reply. If the client has used the correct client-plugin,
      we won't need to read anything here from the client, the client
      has already sent a reply with everything we need for authentication.

      Or the plugin may be called after another plugin has sent a scramble,
      and read the reply, but the client has used the wrong client-plugin.
      We'll need to sent a "switch to another plugin" packet to the
      client and read the reply. "Use the short scramble" packet is a special
      case of "switch to another plugin" packet.

      Or, perhaps, the plugin may be called after another plugin has
      done the handshake but did not send a useful scramble. We'll need
      to send a scramble (and perhaps a "switch to another plugin" packet)
      and read the reply.

      Besides, a client may be an old one, that doesn't understand plugins.
      Or doesn't even understand 4.0 scramble.

      And we want to keep the same protocol on the wire  unless non-native
      plugins are involved.

      Anyway, it still looks simple from a plugin point of view:
      "send the scramble, read the reply and authenticate".
      All the magic is transparently handled by the server.
    </digression>
  */

  /* read the reply with the encrypted password */
  if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
10367 10368
    DBUG_RETURN(CR_ERROR);
  DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
10369 10370

#ifdef NO_EMBEDDED_ACCESS_CHECKS
10371
  DBUG_RETURN(CR_OK);
10372 10373 10374
#endif

  if (pkt_len == 0) /* no password */
Sergei Golubchik's avatar
Sergei Golubchik committed
10375
    DBUG_RETURN(info->auth_string[0] ? CR_ERROR : CR_OK);
10376

10377
  info->password_used= PASSWORD_USED_YES;
10378
  if (pkt_len == SCRAMBLE_LENGTH)
10379 10380 10381 10382
  {
    if (!mpvio->acl_user->salt_len)
      DBUG_RETURN(CR_ERROR);

Sergei Golubchik's avatar
Sergei Golubchik committed
10383 10384 10385 10386
    if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt))
      DBUG_RETURN(CR_ERROR);
    else
      DBUG_RETURN(CR_OK);
10387
  }
10388

Sergei Golubchik's avatar
Sergei Golubchik committed
10389
  inc_host_errors(mpvio->thd->security_ctx->ip);
Guilhem Bichot's avatar
Guilhem Bichot committed
10390
  my_error(ER_HANDSHAKE_ERROR, MYF(0));
10391
  DBUG_RETURN(CR_ERROR);
10392 10393
}

10394
static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
10395 10396 10397 10398
                                     MYSQL_SERVER_AUTH_INFO *info)
{
  uchar *pkt;
  int pkt_len;
10399
  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
10400 10401 10402 10403
  THD *thd=mpvio->thd;

  /* generate the scramble, or reuse the old one */
  if (thd->scramble[SCRAMBLE_LENGTH])
10404
  {
10405
    create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
10406 10407 10408 10409
    /* and send it to the client */
    if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
      return CR_ERROR;
  }
10410 10411 10412 10413 10414 10415 10416 10417 10418 10419 10420 10421 10422 10423

  /* read the reply and authenticate */
  if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
    return CR_ERROR;

#ifdef NO_EMBEDDED_ACCESS_CHECKS
  return CR_OK;
#endif

  /*
    legacy: if switch_from_long_to_short_scramble,
    the password is sent \0-terminated, the pkt_len is always 9 bytes.
    We need to figure out the correct scramble length here.
  */
10424
  if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
10425 10426 10427 10428 10429 10430 10431 10432
    pkt_len= strnlen((char*)pkt, pkt_len);

  if (pkt_len == 0) /* no password */
    return info->auth_string[0] ? CR_ERROR : CR_OK;

  if (secure_auth(thd))
    return CR_ERROR;

10433
  info->password_used= PASSWORD_USED_YES;
10434 10435

  if (pkt_len == SCRAMBLE_LENGTH_323)
10436 10437 10438 10439
  {
    if (!mpvio->acl_user->salt_len)
      return CR_ERROR;

Sergei Golubchik's avatar
Sergei Golubchik committed
10440
    return check_scramble_323(pkt, thd->scramble,
10441
                             (ulong *) mpvio->acl_user->salt) ?
10442
                             CR_ERROR : CR_OK;
10443
  }
10444

Sergei Golubchik's avatar
Sergei Golubchik committed
10445
  inc_host_errors(mpvio->thd->security_ctx->ip);
Guilhem Bichot's avatar
Guilhem Bichot committed
10446
  my_error(ER_HANDSHAKE_ERROR, MYF(0));
10447 10448 10449 10450 10451 10452 10453 10454 10455 10456 10457 10458 10459 10460 10461 10462 10463
  return CR_ERROR;
}

static struct st_mysql_auth native_password_handler=
{
  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
  native_password_plugin_name.str,
  native_password_authenticate
};

static struct st_mysql_auth old_password_handler=
{
  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
  old_password_plugin_name.str,
  old_password_authenticate
};

unknown's avatar
unknown committed
10464 10465 10466 10467 10468 10469 10470 10471 10472 10473 10474 10475 10476 10477 10478 10479 10480 10481 10482 10483 10484 10485 10486 10487 10488 10489 10490 10491 10492 10493 10494 10495
maria_declare_plugin(mysql_password)
{
  MYSQL_AUTHENTICATION_PLUGIN,                  /* type constant    */
  &native_password_handler,                     /* type descriptor  */
  native_password_plugin_name.str,              /* Name             */
  "R.J.Silk, Sergei Golubchik",                 /* Author           */
  "Native MySQL authentication",                /* Description      */
  PLUGIN_LICENSE_GPL,                           /* License          */
  NULL,                                         /* Init function    */
  NULL,                                         /* Deinit function  */
  0x0100,                                       /* Version (1.0)    */
  NULL,                                         /* status variables */
  NULL,                                         /* system variables */
  "1.0",                                        /* String version   */
  MariaDB_PLUGIN_MATURITY_BETA                  /* Maturity         */
},
{
  MYSQL_AUTHENTICATION_PLUGIN,                  /* type constant    */
  &old_password_handler,                        /* type descriptor  */
  old_password_plugin_name.str,                 /* Name             */
  "R.J.Silk, Sergei Golubchik",                 /* Author           */
  "Old MySQL-4.0 authentication",               /* Description      */
  PLUGIN_LICENSE_GPL,                           /* License          */
  NULL,                                         /* Init function    */
  NULL,                                         /* Deinit function  */
  0x0100,                                       /* Version (1.0)    */
  NULL,                                         /* status variables */
  NULL,                                         /* system variables */
  "1.0",                                        /* String version   */
  MariaDB_PLUGIN_MATURITY_BETA                  /* Maturity         */
}
maria_declare_plugin_end;