sql_acl.cc 288 KB
Newer Older
1
/* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
2
   Copyright (c) 2009, 2018, MariaDB
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
  {
unknown's avatar
unknown committed
62 63
    { C_STRING_WITH_LEN("Host") },            
    { C_STRING_WITH_LEN("char(60)") },
64 65 66
    {NULL, 0}
  }, 
  {
unknown's avatar
unknown committed
67 68
    { C_STRING_WITH_LEN("Db") },            
    { C_STRING_WITH_LEN("char(64)") },
69 70 71
    {NULL, 0}
  }, 
  {
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 174
const TABLE_FIELD_DEF
  mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields};
175

176 177 178 179 180 181 182 183 184 185 186
static LEX_STRING native_password_plugin_name= {
  C_STRING_WITH_LEN("mysql_native_password")
};
  
static LEX_STRING old_password_plugin_name= {
  C_STRING_WITH_LEN("mysql_old_password")
};
  
/// @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 215 216 217 218 219 220 221
};

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

class ACL_USER :public ACL_ACCESS
{
public:
  acl_host_and_ip host;
  uint hostname_length;
  USER_RESOURCES user_resource;
  char *user;
222
  uint8 salt[SCRAMBLE_LENGTH + 1];       // scrambled password in binary form
223 224 225 226 227 228 229 230
  uint8 salt_len;        // 0 - no password, 4 - 3.20, 8 - 4.0,  20 - 4.1.1 
  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)
  {
231
    ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
232 233 234 235 236 237 238 239 240 241 242 243
    if (!dst)
      return 0;
    *dst= *this;
    dst->user= safe_strdup_root(root, user);
    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);
244
    dst->auth_string.str= safe_strdup_root(root, auth_string.str);
245 246 247 248 249 250 251 252 253 254 255 256
    dst->host.hostname= safe_strdup_root(root, host.hostname);
    return dst;
  }
};

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

257

258
#ifndef NO_EMBEDDED_ACCESS_CHECKS
259 260
static void update_hostname(acl_host_and_ip *host, const char *hostname);
static ulong get_sort(uint count,...);
261
static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
262
			     const char *ip);
263
static bool show_proxy_grants (THD *thd, LEX_USER *user,
264 265 266 267 268 269 270 271 272 273 274
                               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;

  typedef enum { 
275 276 277 278 279 280 281
    MYSQL_PROXIES_PRIV_HOST, 
    MYSQL_PROXIES_PRIV_USER, 
    MYSQL_PROXIES_PRIV_PROXIED_HOST,
    MYSQL_PROXIES_PRIV_PROXIED_USER, 
    MYSQL_PROXIES_PRIV_WITH_GRANT,
    MYSQL_PROXIES_PRIV_GRANTOR,
    MYSQL_PROXIES_PRIV_TIMESTAMP } old_acl_proxy_users;
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
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;
    update_hostname (&host, 
                     (host_arg && *host_arg) ? host_arg : NULL);
    proxied_user= (proxied_user_arg && *proxied_user_arg) ? 
      proxied_user_arg : NULL;
    update_hostname (&proxied_host, 
                     (proxied_host_arg && *proxied_host_arg) ?
                     proxied_host_arg : NULL);
    with_grant= with_grant_arg;
298 299
    sort= get_sort(4, host.hostname, user,
                   proxied_host.hostname, proxied_user);
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
  }

  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,
          (proxied_host_arg && *proxied_host_arg) ? 
            strdup_root (mem, proxied_host_arg) : NULL,
          (proxied_user_arg && *proxied_user_arg) ? 
            strdup_root (mem, proxied_user_arg) : NULL,
          with_grant_arg);
  }

  void init(TABLE *table, MEM_ROOT *mem)
  {
317 318 319 320 321
    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);
322 323 324 325 326
  }

  bool get_with_grant() { return with_grant; }
  const char *get_user() { return user; }
  const char *get_host() { return host.hostname; }
327 328
  const char *get_proxied_user() { return proxied_user; }
  const char *get_proxied_host() { return proxied_host.hostname; }
329 330
  void set_user(MEM_ROOT *mem, const char *user_arg) 
  { 
331
    user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
332 333 334
  }
  void set_host(MEM_ROOT *mem, const char *host_arg) 
  { 
335 336 337
    update_hostname(&host, 
                    (host_arg && *host_arg) ? 
                    strdup_root(mem, host_arg) : NULL);
338 339
  }

340
  bool check_validity(bool check_no_resolve)
341 342 343 344 345
  {
    if (check_no_resolve && 
        (hostname_requires_resolving(host.hostname) ||
         hostname_requires_resolving(proxied_host.hostname)))
    {
346
      sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
347 348 349 350 351
                        "ignored in --skip-name-resolve mode.",
                        proxied_user ? proxied_user : "",
                        proxied_host.hostname ? proxied_host.hostname : "",
                        user ? user : "",
                        host.hostname ? host.hostname : "");
352 353 354 355 356
      return TRUE;
    }
    return FALSE;
  }

357
  bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
358 359
                const char *proxied_user_arg)
  {
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
    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))) &&
                (!proxied_user || 
                 (proxied_user && !wild_compare(proxied_user_arg, 
                                                proxied_user, TRUE))));
382 383
  }

384 385 386 387 388 389 390

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


391
  bool pk_equals(ACL_PROXY_USER *grant)
392
  {
393 394 395 396 397 398 399 400 401 402 403 404 405 406
    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>",
                        grant->proxied_host.hostname ? 
                        grant->proxied_host.hostname : "<NULL>"));
407 408 409 410 411 412

    DBUG_RETURN(auth_element_equals(user, grant->user) &&
                auth_element_equals(proxied_user, grant->proxied_user) &&
                auth_element_equals(host.hostname, grant->host.hostname) &&
                auth_element_equals(proxied_host.hostname, 
                                    grant->proxied_host.hostname));
413 414
  }

415

416
  bool granted_on(const char *host_arg, const char *user_arg)
417 418
  {
    return (((!user && (!user_arg || !user_arg[0])) ||
419
             (user && user_arg && !strcmp(user, user_arg))) &&
420
            ((!host.hostname && (!host_arg || !host_arg[0])) ||
421
             (host.hostname && host_arg && !strcmp(host.hostname, host_arg))));
422 423
  }

424

425
  void print_grant(String *str)
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
  {
    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"));
  }

444
  void set_data(ACL_PROXY_USER *grant)
445 446 447 448
  {
    with_grant= grant->with_grant;
  }

449 450 451 452 453
  static int store_pk(TABLE *table, 
                      const LEX_STRING *host, 
                      const LEX_STRING *user,
                      const LEX_STRING *proxied_host, 
                      const LEX_STRING *proxied_user)
454
  {
455 456 457 458 459 460
    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>"));
461
    if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str, 
462 463 464
                                                   host->length,
                                                   system_charset_info))
      DBUG_RETURN(TRUE);
465
    if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str, 
466 467 468
                                                   user->length,
                                                   system_charset_info))
      DBUG_RETURN(TRUE);
469
    if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str,
470 471 472
                                                           proxied_host->length,
                                                           system_charset_info))
      DBUG_RETURN(TRUE);
473
    if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str,
474 475 476 477 478 479 480
                                                           proxied_user->length,
                                                           system_charset_info))
      DBUG_RETURN(TRUE);

    DBUG_RETURN(FALSE);
  }

481 482
  static int store_data_record(TABLE *table,
                               const LEX_STRING *host,
483
                               const LEX_STRING *user,
484
                               const LEX_STRING *proxied_host,
485
                               const LEX_STRING *proxied_user,
486 487
                               bool with_grant,
                               const char *grantor)
488
  {
489 490
    DBUG_ENTER("ACL_PROXY_USER::store_pk");
    if (store_pk(table,  host, user, proxied_host, proxied_user))
491
      DBUG_RETURN(TRUE);
492 493
    DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
    if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0, 
494 495
                                                         TRUE))
      DBUG_RETURN(TRUE);
496 497 498 499
    if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor, 
                                                        strlen(grantor),
                                                        system_charset_info))
      DBUG_RETURN(TRUE);
500 501 502 503

    DBUG_RETURN(FALSE);
  }
};
504 505 506

#define FIRST_NON_YN_FIELD 26

unknown's avatar
unknown committed
507 508 509
class acl_entry :public hash_filo_element
{
public:
unknown's avatar
unknown committed
510
  ulong access;
unknown's avatar
unknown committed
511 512 513 514
  uint16 length;
  char key[1];					// Key will be stored here
};

unknown's avatar
unknown committed
515

516 517
static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
                                my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
518 519
{
  *length=(uint) entry->length;
520
  return (uchar*) entry->key;
unknown's avatar
unknown committed
521 522
}

523 524 525
#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
526

527 528 529
#if defined(HAVE_OPENSSL)
/*
  Without SSL the handshake consists of one packet. This packet
530
  has both client capabilities and scrambled password.
531 532 533
  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
534
  is in the second packet and client_capabilities field will be ignored.
535 536 537 538 539 540 541 542
  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 */
543
#define NORMAL_HANDSHAKE_SIZE   6
544

545
static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users;
unknown's avatar
unknown committed
546 547 548
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
549
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
unknown's avatar
unknown committed
550 551
static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
552
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
unknown's avatar
unknown committed
553
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
unknown's avatar
unknown committed
554 555 556
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
557
static void rebuild_check_host(void);
558 559
static ACL_USER *find_acl_user(const char *host, const char *user,
                               my_bool exact);
560 561
static bool update_user_table(THD *, TABLE *, const char *, const char *, const
                              char *, uint, bool);
562
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
563
static my_bool grant_load(THD *thd, TABLE_LIST *tables);
564
static inline void get_grantor(THD *thd, char* grantor);
565 566 567 568 569 570 571 572 573
/*
 Enumeration of various ACL's and Hashes used in handle_grant_struct()
*/
enum enum_acl_lists
{
  USER_ACL= 0,
  DB_ACL,
  COLUMN_PRIVILEGES_HASH,
  PROC_PRIVILEGES_HASH,
Sergei Golubchik's avatar
Sergei Golubchik committed
574
  FUNC_PRIVILEGES_HASH,
575
  PROXY_USERS_ACL
576
};
unknown's avatar
unknown committed
577

578 579 580 581 582 583 584 585 586 587 588 589 590 591
/*
  Convert scrambled password to binary form, according to scramble type, 
  Binary form is stored in user.salt.
*/

static
void
set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
{
  if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
  {
    get_salt_from_password(acl_user->salt, password);
    acl_user->salt_len= SCRAMBLE_LENGTH;
  }
592
  else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
593 594
  {
    get_salt_from_password_323((ulong *) acl_user->salt, password);
595
    acl_user->salt_len= SCRAMBLE_LENGTH_323;
596 597 598 599 600
  }
  else
    acl_user->salt_len= 0;
}

601 602 603 604 605 606 607 608 609 610 611 612 613
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;
}

614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
/**
  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
629
  user->salt_len= 0;
630 631 632 633 634 635 636 637 638 639 640 641 642 643
  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;
  
  set_user_salt(user, user->auth_string.str, user->auth_string.length);
  return false;
}

644
/*
645 646
  Initialize structures responsible for user/db-level privilege checking and
  load privilege information for them from tables in the 'mysql' database.
647 648 649

  SYNOPSIS
    acl_init()
650 651 652 653 654 655
      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().
656 657 658 659 660 661

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

662
my_bool acl_init(bool dont_read_acl_tables)
unknown's avatar
unknown committed
663
{
unknown's avatar
unknown committed
664
  THD  *thd;
665
  my_bool return_val;
unknown's avatar
unknown committed
666 667
  DBUG_ENTER("acl_init");

668
  acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
Konstantin Osipov's avatar
Konstantin Osipov committed
669 670
                           (my_hash_get_key) acl_entry_get_key,
                           (my_hash_free_key) free,
671
                           &my_charset_utf8_bin);
672 673 674 675 676 677 678 679 680 681 682 683 684

  /*
    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
685
  if (dont_read_acl_tables)
686
  {
unknown's avatar
unknown committed
687
    DBUG_RETURN(0); /* purecov: tested */
unknown's avatar
unknown committed
688 689
  }

690 691 692
  /*
    To be able to run this from boot, we allocate a temporary THD
  */
unknown's avatar
unknown committed
693 694
  if (!(thd=new THD))
    DBUG_RETURN(1); /* purecov: inspected */
695
  thd->thread_stack= (char*) &thd;
696
  thd->store_globals();
697 698 699 700 701 702 703 704 705 706 707 708
  /*
    It is safe to call acl_reload() since acl_* arrays and hashes which
    will be freed there are global static objects and thus are initialized
    by zeros at startup.
  */
  return_val= acl_reload(thd);
  delete thd;
  /* Remember that we don't have a THD */
  my_pthread_setspecific_ptr(THR_THD,  0);
  DBUG_RETURN(return_val);
}

709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
/**
  Choose from either native or old password plugins when assigning a password
*/

static bool
set_user_plugin (ACL_USER *user, int password_len)
{
  switch (password_len) 
  {
  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'; "
                      "Ignoring user", user->user ? user->user : "",
                      user->host.hostname ? user->host.hostname : "");
    return TRUE;
  }
}

733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752

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

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

  RETURN VALUES
    FALSE  Success
    TRUE   Error
*/

static my_bool acl_load(THD *thd, TABLE_LIST *tables)
{
  TABLE *table;
  READ_RECORD read_record_info;
753
  my_bool return_val= TRUE;
754
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
755
  char tmp_name[SAFE_NAME_LEN+1];
756
  int password_length;
757
  ulonglong old_sql_mode= thd->variables.sql_mode;
758 759
  DBUG_ENTER("acl_load");

760 761
  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;

762
  grant_version++; /* Privileges updated */
763

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

766
  init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
767 768 769 770
  if (init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0, 
                       FALSE))
    goto end;

771
  table->use_all_columns();
unknown's avatar
unknown committed
772 773 774
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_HOST host;
775 776
    update_hostname(&host.host,get_field(&mem, table->field[0]));
    host.db=	 get_field(&mem, table->field[1]);
777
    if (lower_case_table_names && host.db)
778 779
    {
      /*
780 781
        convert db to lower case and give a warning if the db wasn't
        already in lower case
782
      */
783 784 785 786 787 788
      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;
      }
789
      my_casedn_str(files_charset_info, host.db);
790 791 792
      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 "
793 794
                          "lower_case_table_names is set. It will not be "
                          "possible to remove this privilege using REVOKE.",
795 796
                          host.host.hostname ? host.host.hostname : "",
                          host.db ? host.db : "");
797
    }
unknown's avatar
unknown committed
798 799
    host.access= get_access(table,2);
    host.access= fix_rights_for_db(host.access);
800
    host.sort=	 get_sort(2,host.host.hostname,host.db);
unknown's avatar
SCRUM  
unknown committed
801 802
    if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
    {
unknown's avatar
unknown committed
803
      sql_print_warning("'host' entry '%s|%s' "
unknown's avatar
SCRUM  
unknown committed
804
		      "ignored in --skip-name-resolve mode.",
805 806
			host.host.hostname ? host.host.hostname : "",
			host.db ? host.db : "");
unknown's avatar
SCRUM  
unknown committed
807 808
      continue;
    }
unknown's avatar
unknown committed
809
#ifndef TO_BE_REMOVED
810
    if (table->s->fields == 8)
unknown's avatar
unknown committed
811 812
    {						// Without grant
      if (host.access & CREATE_ACL)
813
	host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
unknown's avatar
unknown committed
814 815
    }
#endif
Konstantin Osipov's avatar
Konstantin Osipov committed
816
    (void) push_dynamic(&acl_hosts,(uchar*) &host);
unknown's avatar
unknown committed
817
  }
818 819
  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
820 821 822
  end_read_record(&read_record_info);
  freeze_size(&acl_hosts);

823 824 825 826
  if (init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0,
                       FALSE))
    goto end;

827
  table->use_all_columns();
828
  username_char_length= min(table->field[1]->char_length(), USERNAME_CHAR_LENGTH);
829 830 831
  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
832
  {
833 834 835
    sql_print_error("Fatal error: mysql.user table is damaged or in "
                    "unsupported 3.20 format.");
    goto end;
unknown's avatar
unknown committed
836 837
  }

838
  DBUG_PRINT("info",("user table fields: %d, password length: %d",
839
		     table->s->fields, password_length));
840

Marc Alff's avatar
Marc Alff committed
841
  mysql_mutex_lock(&LOCK_global_system_variables);
842
  if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
843
  {
844 845
    if (opt_secure_auth)
    {
Marc Alff's avatar
Marc Alff committed
846
      mysql_mutex_unlock(&LOCK_global_system_variables);
847 848 849 850
      sql_print_error("Fatal error: mysql.user table is in old format, "
                      "but server started with --secure-auth option.");
      goto end;
    }
851
    mysql_user_table_is_in_short_password_format= true;
852
    if (global_system_variables.old_passwords)
Marc Alff's avatar
Marc Alff committed
853
      mysql_mutex_unlock(&LOCK_global_system_variables);
854 855 856
    else
    {
      global_system_variables.old_passwords= 1;
Marc Alff's avatar
Marc Alff committed
857
      mysql_mutex_unlock(&LOCK_global_system_variables);
858 859 860
      sql_print_warning("mysql.user table is not updated to new password format; "
                        "Disabling new password usage until "
                        "mysql_fix_privilege_tables is run");
861 862 863 864
    }
    thd->variables.old_passwords= 1;
  }
  else
865
  {
866
    mysql_user_table_is_in_short_password_format= false;
Marc Alff's avatar
Marc Alff committed
867
    mysql_mutex_unlock(&LOCK_global_system_variables);
868 869
  }

unknown's avatar
unknown committed
870 871 872 873
  allow_all_hosts=0;
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_USER user;
874
    bzero(&user, sizeof(user));
875 876
    update_hostname(&user.host, get_field(&mem, table->field[0]));
    user.user= get_field(&mem, table->field[1]);
unknown's avatar
SCRUM  
unknown committed
877 878
    if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
    {
unknown's avatar
unknown committed
879 880
      sql_print_warning("'user' entry '%s@%s' "
                        "ignored in --skip-name-resolve mode.",
881 882
			user.user ? user.user : "",
			user.host.hostname ? user.host.hostname : "");
unknown's avatar
SCRUM  
unknown committed
883 884 885
      continue;
    }

886
    char *password= get_field(&mem, table->field[2]);
887
    uint password_len= password ? strlen(password) : 0;
888 889
    user.auth_string.str= password ? password : const_cast<char*>("");
    user.auth_string.length= password_len;
890
    set_user_salt(&user, password, password_len);
891

892
    if (set_user_plugin(&user, password_len))
893 894
      continue;
    
unknown's avatar
unknown committed
895
    {
unknown's avatar
unknown committed
896 897
      uint next_field;
      user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
898 899 900 901
      /*
        if it is pre 5.0.1 privilege table then map CREATE privilege on
        CREATE VIEW & SHOW VIEW privileges
      */
902
      if (table->s->fields <= 31 && (user.access & CREATE_ACL))
903
        user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
904 905 906 907 908

      /*
        if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
        CREATE PROCEDURE & ALTER PROCEDURE privileges
      */
909
      if (table->s->fields <= 33 && (user.access & CREATE_ACL))
910
        user.access|= CREATE_PROC_ACL;
911
      if (table->s->fields <= 33 && (user.access & ALTER_ACL))
912 913
        user.access|= ALTER_PROC_ACL;

914 915 916 917 918 919
      /*
        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
920 921

      /*
unknown's avatar
unknown committed
922
        if it is pre 5.1.6 privilege table then map CREATE privilege on
unknown's avatar
unknown committed
923 924
        CREATE|ALTER|DROP|EXECUTE EVENT
      */
unknown's avatar
unknown committed
925
      if (table->s->fields <= 37 && (user.access & SUPER_ACL))
unknown's avatar
unknown committed
926 927
        user.access|= EVENT_ACL;

928 929 930 931 932 933
      /*
        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;

934 935 936
      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
937

938 939
      /* Starting from 4.0.2 we have more fields */
      if (table->s->fields >= 31)
940
      {
941
        char *ssl_type=get_field(thd->mem_root, table->field[next_field++]);
942 943 944 945 946 947 948 949 950
        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
951 952 953
        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++]);
954

955
        char *ptr = get_field(thd->mem_root, table->field[next_field++]);
unknown's avatar
unknown committed
956
        user.user_resource.questions=ptr ? atoi(ptr) : 0;
957
        ptr = get_field(thd->mem_root, table->field[next_field++]);
unknown's avatar
unknown committed
958
        user.user_resource.updates=ptr ? atoi(ptr) : 0;
959
        ptr = get_field(thd->mem_root, table->field[next_field++]);
960
        user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
961
        if (user.user_resource.questions || user.user_resource.updates ||
962
            user.user_resource.conn_per_hour)
963
          mqh_used=1;
964

965
        if (table->s->fields >= 36)
966 967
        {
          /* Starting from 5.0.3 we have max_user_connections field */
968
          ptr= get_field(thd->mem_root, table->field[next_field++]);
969 970
          user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
        }
971 972 973 974 975 976 977

        if (table->s->fields >= 41)
        {
          /* We may have plugin & auth_String fields */
          char *tmpstr= get_field(&mem, table->field[next_field++]);
          if (tmpstr)
          {
978 979
            user.plugin.str= tmpstr;
            user.plugin.length= strlen(user.plugin.str);
980 981 982 983 984 985 986 987 988 989 990 991
            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.",
                                user.user ? user.user : "",
                                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);
992 993

            fix_user_plugin_ptr(&user);
994 995
          }
        }
996
      }
997 998 999
      else
      {
        user.ssl_type=SSL_TYPE_NONE;
unknown's avatar
unknown committed
1000
#ifndef TO_BE_REMOVED
1001
        if (table->s->fields <= 13)
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
        {						// 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
1012
#endif
1013
      }
Konstantin Osipov's avatar
Konstantin Osipov committed
1014
      (void) push_dynamic(&acl_users,(uchar*) &user);
1015 1016
      if (!user.host.hostname ||
	  (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
1017
        allow_all_hosts=1;			// Anyone can connect
unknown's avatar
unknown committed
1018
    }
unknown's avatar
unknown committed
1019
  }
1020 1021
  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
1022 1023
  end_read_record(&read_record_info);
  freeze_size(&acl_users);
unknown's avatar
unknown committed
1024

1025 1026 1027 1028
  if (init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0,
                       FALSE))
    goto end;

1029
  table->use_all_columns();
unknown's avatar
unknown committed
1030 1031 1032
  while (!(read_record_info.read_record(&read_record_info)))
  {
    ACL_DB db;
1033 1034
    update_hostname(&db.host,get_field(&mem, table->field[MYSQL_DB_FIELD_HOST]));
    db.db=get_field(&mem, table->field[MYSQL_DB_FIELD_DB]);
1035 1036
    if (!db.db)
    {
unknown's avatar
unknown committed
1037
      sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
1038
      continue;
1039
    }
1040
    db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]);
unknown's avatar
SCRUM  
unknown committed
1041 1042
    if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
    {
unknown's avatar
unknown committed
1043 1044
      sql_print_warning("'db' entry '%s %s@%s' "
		        "ignored in --skip-name-resolve mode.",
1045 1046 1047
		        db.db,
			db.user ? db.user : "",
			db.host.hostname ? db.host.hostname : "");
unknown's avatar
SCRUM  
unknown committed
1048 1049
      continue;
    }
unknown's avatar
unknown committed
1050 1051
    db.access=get_access(table,3);
    db.access=fix_rights_for_db(db.access);
1052 1053 1054
    if (lower_case_table_names)
    {
      /*
1055 1056
        convert db to lower case and give a warning if the db wasn't
        already in lower case
1057
      */
1058 1059 1060 1061 1062 1063
      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;
      }
1064
      my_casedn_str(files_charset_info, db.db);
1065 1066 1067 1068
      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 "
1069 1070
                          "lower_case_table_names is set. It will not be "
                          "possible to remove this privilege using REVOKE.",
1071 1072 1073
		          db.db,
			  db.user ? db.user : "",
			  db.host.hostname ? db.host.hostname : "");
1074 1075
      }
    }
unknown's avatar
unknown committed
1076 1077
    db.sort=get_sort(3,db.host.hostname,db.db,db.user);
#ifndef TO_BE_REMOVED
1078
    if (table->s->fields <=  9)
unknown's avatar
unknown committed
1079 1080 1081 1082 1083
    {						// Without grant
      if (db.access & CREATE_ACL)
	db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
    }
#endif
Konstantin Osipov's avatar
Konstantin Osipov committed
1084
    (void) push_dynamic(&acl_dbs,(uchar*) &db);
unknown's avatar
unknown committed
1085
  }
1086 1087
  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
1088 1089
  end_read_record(&read_record_info);
  freeze_size(&acl_dbs);
1090

1091
  if (tables[3].table)
1092
  {
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
    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");
1117 1118 1119
  }
  freeze_size(&acl_proxy_users);

unknown's avatar
unknown committed
1120 1121
  init_check_host();

1122
  initialized=1;
1123
  return_val= FALSE;
1124 1125

end:
Arun Kuruvila's avatar
Arun Kuruvila committed
1126
  end_read_record(&read_record_info);
1127
  thd->variables.sql_mode= old_sql_mode;
1128
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
1129 1130 1131 1132 1133
}


void acl_free(bool end)
{
1134
  free_root(&mem,MYF(0));
unknown's avatar
unknown committed
1135 1136 1137 1138
  delete_dynamic(&acl_hosts);
  delete_dynamic(&acl_users);
  delete_dynamic(&acl_dbs);
  delete_dynamic(&acl_wild_hosts);
1139
  delete_dynamic(&acl_proxy_users);
Konstantin Osipov's avatar
Konstantin Osipov committed
1140
  my_hash_free(&acl_check_hosts);
unknown's avatar
unknown committed
1141 1142 1143 1144
  if (!end)
    acl_cache->clear(1); /* purecov: inspected */
  else
  {
Arun Kuruvila's avatar
Arun Kuruvila committed
1145 1146
    plugin_unlock(0, native_password_plugin);
    plugin_unlock(0, old_password_plugin);
unknown's avatar
unknown committed
1147 1148 1149 1150 1151
    delete acl_cache;
    acl_cache=0;
  }
}

1152 1153

/*
1154 1155
  Forget current user/db-level privileges and read new privileges
  from the privilege tables.
1156 1157 1158

  SYNOPSIS
    acl_reload()
1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169
      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
1170
*/
unknown's avatar
unknown committed
1171

1172
my_bool acl_reload(THD *thd)
unknown's avatar
unknown committed
1173
{
1174
  TABLE_LIST tables[4];
1175
  DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users;
unknown's avatar
unknown committed
1176 1177
  MEM_ROOT old_mem;
  bool old_initialized;
1178
  my_bool return_val= TRUE;
unknown's avatar
unknown committed
1179 1180
  DBUG_ENTER("acl_reload");

1181 1182 1183 1184
  /*
    To avoid deadlocks we should obtain table locks before
    obtaining acl_cache->lock mutex.
  */
1185 1186 1187 1188 1189 1190
  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);
1191
  tables[3].init_one_table(C_STRING_WITH_LEN("mysql"),
1192 1193
                           C_STRING_WITH_LEN("proxies_priv"), 
                           "proxies_priv", TL_READ);
1194 1195 1196
  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;
1197 1198
  tables[0].open_type= tables[1].open_type= tables[2].open_type= 
  tables[3].open_type= OT_BASE_ONLY;
1199
  tables[3].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
1200

1201
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
1202
  {
1203 1204 1205 1206
    /*
      Execution might have been interrupted; only print the error message
      if an error condition has been raised.
    */
1207
    if (thd->stmt_da->is_error())
1208
      sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
1209
                      thd->stmt_da->message());
1210 1211 1212
    goto end;
  }

unknown's avatar
unknown committed
1213
  if ((old_initialized=initialized))
Marc Alff's avatar
Marc Alff committed
1214
    mysql_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
1215

1216 1217 1218 1219
  old_acl_hosts= acl_hosts;
  old_acl_users= acl_users;
  old_acl_proxy_users= acl_proxy_users;
  old_acl_dbs= acl_dbs;
Arun Kuruvila's avatar
Arun Kuruvila committed
1220 1221 1222 1223
  my_init_dynamic_array(&acl_hosts, sizeof(ACL_HOST), 20, 50);
  my_init_dynamic_array(&acl_users, sizeof(ACL_USER), 50, 100);
  my_init_dynamic_array(&acl_dbs, sizeof(ACL_DB), 50, 100);
  my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), 50, 100);
1224
  old_mem= mem;
unknown's avatar
unknown committed
1225
  delete_dynamic(&acl_wild_hosts);
Konstantin Osipov's avatar
Konstantin Osipov committed
1226
  my_hash_free(&acl_check_hosts);
unknown's avatar
unknown committed
1227

1228
  if ((return_val= acl_load(thd, tables)))
unknown's avatar
unknown committed
1229
  {					// Error. Revert to old list
1230
    DBUG_PRINT("error",("Reverting to old privileges"));
1231
    acl_free();				/* purecov: inspected */
1232 1233 1234 1235 1236
    acl_hosts= old_acl_hosts;
    acl_users= old_acl_users;
    acl_proxy_users= old_acl_proxy_users;
    acl_dbs= old_acl_dbs;
    mem= old_mem;
unknown's avatar
unknown committed
1237 1238 1239 1240
    init_check_host();
  }
  else
  {
1241
    free_root(&old_mem,MYF(0));
unknown's avatar
unknown committed
1242 1243
    delete_dynamic(&old_acl_hosts);
    delete_dynamic(&old_acl_users);
1244
    delete_dynamic(&old_acl_proxy_users);
unknown's avatar
unknown committed
1245 1246 1247
    delete_dynamic(&old_acl_dbs);
  }
  if (old_initialized)
Marc Alff's avatar
Marc Alff committed
1248
    mysql_mutex_unlock(&acl_cache->lock);
1249
end:
1250
  close_mysql_tables(thd);
1251
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
1252 1253 1254
}


unknown's avatar
unknown committed
1255 1256
/*
  Get all access bits from table after fieldnr
unknown's avatar
unknown committed
1257 1258

  IMPLEMENTATION
unknown's avatar
unknown committed
1259 1260
  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
1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271

  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
1272
*/
unknown's avatar
unknown committed
1273

unknown's avatar
unknown committed
1274
static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
unknown's avatar
unknown committed
1275
{
unknown's avatar
unknown committed
1276
  ulong access_bits=0,bit;
unknown's avatar
unknown committed
1277
  char buff[2];
unknown's avatar
unknown committed
1278
  String res(buff,sizeof(buff),&my_charset_latin1);
unknown's avatar
unknown committed
1279 1280
  Field **pos;

unknown's avatar
unknown committed
1281
  for (pos=form->field+fieldnr, bit=1;
1282
       *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
unknown's avatar
unknown committed
1283
	 ((Field_enum*) (*pos))->typelib->count == 2 ;
unknown's avatar
unknown committed
1284
       pos++, fieldnr++, bit<<=1)
unknown's avatar
unknown committed
1285
  {
1286
    (*pos)->val_str(&res);
unknown's avatar
unknown committed
1287
    if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
unknown's avatar
unknown committed
1288
      access_bits|= bit;
unknown's avatar
unknown committed
1289
  }
unknown's avatar
unknown committed
1290 1291
  if (next_field)
    *next_field=fieldnr;
unknown's avatar
unknown committed
1292 1293 1294 1295 1296
  return access_bits;
}


/*
unknown's avatar
unknown committed
1297 1298 1299 1300 1301
  Return a number which, if sorted 'desc', puts strings in this order:
    no wildcards
    wildcards
    empty string
*/
unknown's avatar
unknown committed
1302 1303 1304 1305 1306 1307 1308

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

1309 1310 1311
  /* Should not use this function with more than 4 arguments for compare. */
  DBUG_ASSERT(count <= 4);

unknown's avatar
unknown committed
1312 1313
  while (count--)
  {
1314 1315 1316
    char *start, *str= va_arg(args,char*);
    uint chars= 0;
    uint wild_pos= 0;           /* first wildcard position */
unknown's avatar
unknown committed
1317

unknown's avatar
unknown committed
1318
    if ((start= str))
unknown's avatar
unknown committed
1319 1320 1321
    {
      for (; *str ; str++)
      {
1322 1323 1324
        if (*str == wild_prefix && str[1])
          str++;
        else if (*str == wild_many || *str == wild_one)
1325
        {
unknown's avatar
unknown committed
1326
          wild_pos= (uint) (str - start) + 1;
1327 1328
          break;
        }
unknown's avatar
unknown committed
1329
        chars= 128;                             // Marker that chars existed
unknown's avatar
unknown committed
1330 1331
      }
    }
unknown's avatar
unknown committed
1332
    sort= (sort << 8) + (wild_pos ? min(wild_pos, 127) : chars);
unknown's avatar
unknown committed
1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347
  }
  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;
}

1348

1349
/*
1350
  Gets user credentials without authentication and resource limit checks.
unknown's avatar
unknown committed
1351

1352
  SYNOPSIS
1353
    acl_getroot()
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364
      sctx               Context which should be initialized
      user               user name
      host               host name
      ip                 IP
      db                 current data base name

  RETURN
    FALSE  OK
    TRUE   Error
*/

1365 1366
bool acl_getroot(Security_context *sctx, char *user, char *host,
                 char *ip, char *db)
1367 1368
{
  int res= 1;
1369
  uint i;
1370
  ACL_USER *acl_user= 0;
1371
  DBUG_ENTER("acl_getroot");
1372

1373 1374
  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
                       (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
1375
                       user, (db ? db : "(NULL)")));
1376 1377 1378 1379 1380
  sctx->user= user;
  sctx->host= host;
  sctx->ip= ip;
  sctx->host_or_ip= host ? host : (ip ? ip : "");

1381 1382
  if (!initialized)
  {
1383
    /*
1384 1385
      here if mysqld's been started with --skip-grant-tables option.
    */
1386
    sctx->skip_grants();
1387
    DBUG_RETURN(FALSE);
1388 1389
  }

Marc Alff's avatar
Marc Alff committed
1390
  mysql_mutex_lock(&acl_cache->lock);
1391

1392 1393
  sctx->master_access= 0;
  sctx->db_access= 0;
1394
  *sctx->priv_user= *sctx->priv_host= 0;
1395

1396 1397 1398
  /*
     Find acl entry in user database.
     This is specially tailored to suit the check we do for CALL of
1399
     a stored procedure; user is set to what is actually a
1400 1401
     priv_user, which can be ''.
  */
1402
  for (i=0 ; i < acl_users.elements ; i++)
1403
  {
1404 1405 1406
    ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
    if ((!acl_user_tmp->user && !user[0]) ||
        (acl_user_tmp->user && strcmp(user, acl_user_tmp->user) == 0))
1407
    {
1408
      if (compare_hostname(&acl_user_tmp->host, host, ip))
1409
      {
1410 1411 1412
        acl_user= acl_user_tmp;
        res= 0;
        break;
1413 1414 1415 1416 1417 1418
      }
    }
  }

  if (acl_user)
  {
1419 1420 1421 1422
    for (i=0 ; i < acl_dbs.elements ; i++)
    {
      ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
      if (!acl_db->user ||
1423
	  (user && user[0] && !strcmp(user, acl_db->user)))
1424
      {
1425
	if (compare_hostname(&acl_db->host, host, ip))
1426
	{
1427
	  if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
1428
	  {
1429
	    sctx->db_access= acl_db->access;
1430 1431 1432 1433 1434
	    break;
	  }
	}
      }
    }
1435
    sctx->master_access= acl_user->access;
1436 1437

    if (acl_user->user)
1438
      strmake_buf(sctx->priv_user, user);
1439 1440
    else
      *sctx->priv_user= 0;
1441 1442

    if (acl_user->host.hostname)
1443
      strmake_buf(sctx->priv_host, acl_user->host.hostname);
1444
    else
1445
      *sctx->priv_host= 0;
1446
  }
Marc Alff's avatar
Marc Alff committed
1447
  mysql_mutex_unlock(&acl_cache->lock);
1448 1449 1450
  DBUG_RETURN(res);
}

1451 1452
static uchar* check_get_key(ACL_USER *buff, size_t *length,
                            my_bool not_used __attribute__((unused)))
unknown's avatar
unknown committed
1453 1454
{
  *length=buff->hostname_length;
1455
  return (uchar*) buff->host.hostname;
unknown's avatar
unknown committed
1456 1457
}

1458

unknown's avatar
unknown committed
1459
static void acl_update_user(const char *user, const char *host,
1460
			    const char *password, uint password_len,
1461 1462 1463 1464
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
unknown's avatar
unknown committed
1465
			    USER_RESOURCES  *mqh,
1466 1467 1468
			    ulong privileges,
			    const LEX_STRING *plugin,
			    const LEX_STRING *auth)
unknown's avatar
unknown committed
1469
{
Marc Alff's avatar
Marc Alff committed
1470
  mysql_mutex_assert_owner(&acl_cache->lock);
1471

unknown's avatar
unknown committed
1472 1473 1474
  for (uint i=0 ; i < acl_users.elements ; i++)
  {
    ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
1475 1476
    if ((!acl_user->user && !user[0]) ||
	(acl_user->user && !strcmp(user,acl_user->user)))
unknown's avatar
unknown committed
1477
    {
1478 1479
      if ((!acl_user->host.hostname && !host[0]) ||
	  (acl_user->host.hostname &&
1480
           !my_strcasecmp(system_charset_info, host, acl_user->host.hostname)))
unknown's avatar
unknown committed
1481
      {
1482 1483
        if (plugin->str[0])
        {
1484
          acl_user->plugin= *plugin;
1485 1486 1487
          acl_user->auth_string.str= auth->str ?
            strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
          acl_user->auth_string.length= auth->length;
1488 1489
          if (fix_user_plugin_ptr(acl_user))
            acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
1490
        }
1491
        else
Sergei Golubchik's avatar
Sergei Golubchik committed
1492
          if (password[0])
1493 1494 1495 1496
          {
            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
1497
            set_user_plugin(acl_user, password_len);
1498
          }
unknown's avatar
unknown committed
1499
	acl_user->access=privileges;
1500
	if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1501
	  acl_user->user_resource.questions=mqh->questions;
1502
	if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1503
	  acl_user->user_resource.updates=mqh->updates;
1504 1505 1506 1507
	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;
1508 1509 1510 1511 1512 1513 1514 1515 1516 1517
	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);
	}
1518
        /* search complete: */
unknown's avatar
unknown committed
1519 1520 1521 1522 1523 1524 1525 1526
	break;
      }
    }
  }
}


static void acl_insert_user(const char *user, const char *host,
1527
			    const char *password, uint password_len,
1528 1529 1530 1531
			    enum SSL_type ssl_type,
			    const char *ssl_cipher,
			    const char *x509_issuer,
			    const char *x509_subject,
1532
			    USER_RESOURCES *mqh,
1533 1534 1535
			    ulong privileges,
			    const LEX_STRING *plugin,
			    const LEX_STRING *auth)
unknown's avatar
unknown committed
1536 1537
{
  ACL_USER acl_user;
1538

Marc Alff's avatar
Marc Alff committed
1539
  mysql_mutex_assert_owner(&acl_cache->lock);
1540

1541
  acl_user.user=*user ? strdup_root(&mem,user) : 0;
unknown's avatar
unknown committed
1542
  update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
1543 1544
  if (plugin->str[0])
  {
1545
    acl_user.plugin= *plugin;
1546 1547 1548
    acl_user.auth_string.str= auth->str ?
      strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
    acl_user.auth_string.length= auth->length;
1549 1550
    if (fix_user_plugin_ptr(&acl_user))
      acl_user.plugin.str= strmake_root(&mem, plugin->str, plugin->length);
1551 1552 1553 1554 1555
  }
  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
1556 1557
    set_user_salt(&acl_user, password, password_len);
    set_user_plugin(&acl_user, password_len);
1558 1559
  }

unknown's avatar
unknown committed
1560
  acl_user.access=privileges;
1561
  acl_user.user_resource = *mqh;
unknown's avatar
unknown committed
1562
  acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
1563
  acl_user.hostname_length=(uint) strlen(host);
1564 1565 1566 1567 1568
  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;
1569

Konstantin Osipov's avatar
Konstantin Osipov committed
1570
  (void) push_dynamic(&acl_users,(uchar*) &acl_user);
1571 1572
  if (!acl_user.host.hostname ||
      (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
unknown's avatar
unknown committed
1573
    allow_all_hosts=1;		// Anyone can connect /* purecov: tested */
1574 1575
  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
1576

1577 1578
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();
unknown's avatar
unknown committed
1579 1580 1581 1582
}


static void acl_update_db(const char *user, const char *host, const char *db,
unknown's avatar
unknown committed
1583
			  ulong privileges)
unknown's avatar
unknown committed
1584
{
Marc Alff's avatar
Marc Alff committed
1585
  mysql_mutex_assert_owner(&acl_cache->lock);
1586

unknown's avatar
unknown committed
1587 1588 1589
  for (uint i=0 ; i < acl_dbs.elements ; i++)
  {
    ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
1590 1591 1592
    if ((!acl_db->user && !user[0]) ||
	(acl_db->user &&
	!strcmp(user,acl_db->user)))
unknown's avatar
unknown committed
1593
    {
1594 1595
      if ((!acl_db->host.hostname && !host[0]) ||
	  (acl_db->host.hostname &&
Sergey Petrunya's avatar
Sergey Petrunya committed
1596
	   !strcmp(host, acl_db->host.hostname)))
unknown's avatar
unknown committed
1597
      {
1598 1599
	if ((!acl_db->db && !db[0]) ||
	    (acl_db->db && !strcmp(db,acl_db->db)))
Sergey Petrunya's avatar
Sergey Petrunya committed
1600

unknown's avatar
unknown committed
1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612
	{
	  if (privileges)
	    acl_db->access=privileges;
	  else
	    delete_dynamic_element(&acl_dbs,i);
	}
      }
    }
  }
}


1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626
/*
  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
1627
static void acl_insert_db(const char *user, const char *host, const char *db,
unknown's avatar
unknown committed
1628
			  ulong privileges)
unknown's avatar
unknown committed
1629 1630
{
  ACL_DB acl_db;
Marc Alff's avatar
Marc Alff committed
1631
  mysql_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
1632
  acl_db.user=strdup_root(&mem,user);
1633
  update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
unknown's avatar
unknown committed
1634 1635 1636
  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
1637
  (void) push_dynamic(&acl_dbs,(uchar*) &acl_db);
1638 1639
  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
1640 1641 1642
}


1643 1644 1645

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

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

1651
ulong acl_get(const char *host, const char *ip,
1652
              const char *user, const char *db, my_bool db_is_pattern)
unknown's avatar
unknown committed
1653
{
1654
  ulong host_access= ~(ulong)0, db_access= 0;
1655 1656
  uint i;
  size_t key_length;
unknown's avatar
unknown committed
1657
  char key[ACL_KEY_LENGTH],*tmp_db,*end;
unknown's avatar
unknown committed
1658
  acl_entry *entry;
unknown's avatar
unknown committed
1659
  DBUG_ENTER("acl_get");
unknown's avatar
unknown committed
1660

1661 1662 1663 1664 1665 1666
  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
1667 1668
  if (lower_case_table_names)
  {
1669
    my_casedn_str(files_charset_info, tmp_db);
unknown's avatar
unknown committed
1670 1671
    db=tmp_db;
  }
1672
  key_length= (size_t) (end-key);
1673

Sergei Golubchik's avatar
Sergei Golubchik committed
1674
  mysql_mutex_lock(&acl_cache->lock);
1675 1676
  if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key,
                                                              key_length)))
unknown's avatar
unknown committed
1677 1678
  {
    db_access=entry->access;
Marc Alff's avatar
Marc Alff committed
1679
    mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
1680 1681
    DBUG_PRINT("exit", ("access: 0x%lx", db_access));
    DBUG_RETURN(db_access);
unknown's avatar
unknown committed
1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693
  }

  /*
    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))
      {
1694
	if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
unknown's avatar
unknown committed
1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715
	{
	  db_access=acl_db->access;
	  if (acl_db->host.hostname)
	    goto exit;				// Fully specified. Take it
	  break; /* purecov: tested */
	}
      }
    }
  }
  if (!db_access)
    goto exit;					// Can't be better

  /*
    No host specified for user. Get hostdata from host table
  */
  host_access=0;				// Host must be found
  for (i=0 ; i < acl_hosts.elements ; i++)
  {
    ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
    if (compare_hostname(&acl_host->host,host,ip))
    {
1716
      if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
unknown's avatar
unknown committed
1717 1718 1719 1720 1721 1722 1723 1724
      {
	host_access=acl_host->access;		// Fully specified. Take it
	break;
      }
    }
  }
exit:
  /* Save entry in cache for quick retrieval */
1725 1726
  if (!db_is_pattern &&
      (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
unknown's avatar
unknown committed
1727 1728 1729
  {
    entry->access=(db_access & host_access);
    entry->length=key_length;
1730
    memcpy((uchar*) entry->key,key,key_length);
unknown's avatar
unknown committed
1731 1732
    acl_cache->add(entry);
  }
Marc Alff's avatar
Marc Alff committed
1733
  mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
1734 1735
  DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
  DBUG_RETURN(db_access & host_access);
unknown's avatar
unknown committed
1736 1737
}

1738 1739 1740 1741 1742 1743 1744
/*
  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
1745 1746 1747 1748

static void init_check_host(void)
{
  DBUG_ENTER("init_check_host");
Konstantin Osipov's avatar
Konstantin Osipov committed
1749 1750 1751 1752 1753
  (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
			  acl_users.elements,1);
  (void) my_hash_init(&acl_check_hosts,system_charset_info,
                      acl_users.elements, 0, 0,
                      (my_hash_get_key) check_get_key, 0, 0);
unknown's avatar
unknown committed
1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767
  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 *);
1768
	  if (!my_strcasecmp(system_charset_info,
1769
                             acl_user->host.hostname, acl->hostname))
unknown's avatar
unknown committed
1770 1771 1772
	    break;				// already stored
	}
	if (j == acl_wild_hosts.elements)	// If new
1773
	  (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
unknown's avatar
unknown committed
1774
      }
Konstantin Osipov's avatar
Konstantin Osipov committed
1775 1776 1777
      else if (!my_hash_search(&acl_check_hosts,(uchar*)
                               acl_user->host.hostname,
                               strlen(acl_user->host.hostname)))
unknown's avatar
unknown committed
1778
      {
1779
	if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
unknown's avatar
unknown committed
1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792
	{					// 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;
}


1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
/*
  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
1804
  my_hash_free(&acl_check_hosts);
1805 1806 1807 1808
  init_check_host();
}


unknown's avatar
unknown committed
1809 1810 1811 1812 1813 1814
/* 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
1815
  mysql_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
1816

Konstantin Osipov's avatar
Konstantin Osipov committed
1817 1818
  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
1819
  {
Marc Alff's avatar
Marc Alff committed
1820
    mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
1821 1822 1823 1824 1825 1826 1827
    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
1828
      mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
1829 1830 1831
      return 0;					// Host ok
    }
  }
Marc Alff's avatar
Marc Alff committed
1832
  mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
1833 1834 1835 1836
  return 1;					// Host is not allowed
}


unknown's avatar
unknown committed
1837 1838 1839 1840 1841 1842 1843 1844
/*
  Check if the user is allowed to change password

  SYNOPSIS:
    check_change_password()
    thd		THD
    host	hostname for the user
    user	user name
1845 1846 1847 1848
    new_password new password

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

unknown's avatar
unknown committed
1850
    RETURN VALUE
1851 1852
      0		OK
      1		ERROR  ; In this case the error is sent to the client.
unknown's avatar
unknown committed
1853 1854
*/

1855
int check_change_password(THD *thd, const char *host, const char *user,
1856
                           char *new_password, uint new_password_len)
unknown's avatar
unknown committed
1857
{
unknown's avatar
unknown committed
1858 1859
  if (!initialized)
  {
unknown's avatar
unknown committed
1860
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1861
    return(1);
unknown's avatar
unknown committed
1862
  }
unknown's avatar
unknown committed
1863
  if (!thd->slave_thread &&
1864 1865 1866
      (strcmp(thd->security_ctx->user, user) ||
       my_strcasecmp(system_charset_info, host,
                     thd->security_ctx->priv_host)))
unknown's avatar
unknown committed
1867
  {
Marc Alff's avatar
Marc Alff committed
1868
    if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
unknown's avatar
unknown committed
1869
      return(1);
unknown's avatar
unknown committed
1870
  }
1871
  if (!thd->slave_thread && !thd->security_ctx->user[0])
unknown's avatar
unknown committed
1872
  {
unknown's avatar
unknown committed
1873 1874
    my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
               MYF(0));
unknown's avatar
unknown committed
1875
    return(1);
unknown's avatar
unknown committed
1876
  }
1877
  size_t len= strlen(new_password);
unknown's avatar
unknown committed
1878
  if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
1879 1880
      len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
  {
unknown's avatar
unknown committed
1881
    my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
1882 1883
    return -1;
  }
unknown's avatar
unknown committed
1884 1885 1886 1887
  return(0);
}


1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900
/*
  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
1901
*/
1902

unknown's avatar
unknown committed
1903 1904 1905
bool change_password(THD *thd, const char *host, const char *user,
		     char *new_password)
{
1906 1907 1908 1909 1910
  TABLE_LIST tables;
  TABLE *table;
  /* Buffer should be extended when password length is extended. */
  char buff[512];
  ulong query_length;
1911
  bool save_binlog_row_based;
1912
  uint new_password_len= (uint) strlen(new_password);
1913
  bool result= 1;
1914
  bool use_salt= 0;
unknown's avatar
unknown committed
1915 1916 1917 1918 1919
  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

1920
  if (check_change_password(thd, host, user, new_password, new_password_len))
unknown's avatar
unknown committed
1921 1922
    DBUG_RETURN(1);

Konstantin Osipov's avatar
Konstantin Osipov committed
1923
  tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
1924 1925 1926 1927 1928 1929

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
1930
  if (thd->slave_thread && rpl_filter->is_on())
1931 1932 1933 1934 1935 1936 1937
  {
    /*
      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 */
1938
    if (!(thd->spcont || rpl_filter->tables_ok(0, &tables)))
1939 1940 1941
      DBUG_RETURN(0);
  }
#endif
1942
  if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
1943 1944
    DBUG_RETURN(1);

1945 1946 1947 1948 1949 1950 1951 1952
  /*
    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.
  */
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
    thd->clear_current_stmt_binlog_format_row();

Marc Alff's avatar
Marc Alff committed
1953
  mysql_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
1954
  ACL_USER *acl_user;
1955
  if (!(acl_user= find_acl_user(host, user, TRUE)))
unknown's avatar
unknown committed
1956
  {
Marc Alff's avatar
Marc Alff committed
1957
    mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
1958
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
1959
    goto end;
unknown's avatar
unknown committed
1960
  }
1961

1962
  /* update loaded acl entry: */
1963 1964 1965 1966 1967 1968
  if (acl_user->plugin.str == native_password_plugin_name.str || 
      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
1969
    set_user_plugin(acl_user, new_password_len);
1970
    use_salt= 1;
1971
  }
Sergei Golubchik's avatar
Sergei Golubchik committed
1972
  else
1973 1974
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
                 ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
1975

1976
  if (update_user_table(thd, table,
unknown's avatar
unknown committed
1977
			acl_user->host.hostname ? acl_user->host.hostname : "",
unknown's avatar
unknown committed
1978
			acl_user->user ? acl_user->user : "",
1979
			new_password, new_password_len, use_salt))
unknown's avatar
unknown committed
1980
  {
Marc Alff's avatar
Marc Alff committed
1981
    mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
1982
    goto end;
unknown's avatar
unknown committed
1983
  }
unknown's avatar
unknown committed
1984

unknown's avatar
unknown committed
1985
  acl_cache->clear(1);				// Clear locked hostname cache
Marc Alff's avatar
Marc Alff committed
1986
  mysql_mutex_unlock(&acl_cache->lock);
1987 1988 1989
  result= 0;
  if (mysql_bin_log.is_open())
  {
1990
    query_length=
Sergei Golubchik's avatar
Sergei Golubchik committed
1991 1992 1993 1994
      sprintf(buff,"SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
              acl_user->user ? acl_user->user : "",
              acl_user->host.hostname ? acl_user->host.hostname : "",
              new_password);
1995
    thd->clear_error();
1996 1997
    result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
                              FALSE, FALSE, FALSE, 0);
1998 1999
  }
end:
2000 2001 2002 2003 2004 2005 2006
  close_mysql_tables(thd);

  /* Restore the state of binlog format */
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
  if (save_binlog_row_based)
    thd->set_current_stmt_binlog_format_row();

2007
  DBUG_RETURN(result);
unknown's avatar
unknown committed
2008 2009 2010
}


2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026
/*
  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;
2027 2028 2029 2030 2031

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

Marc Alff's avatar
Marc Alff committed
2032
  mysql_mutex_lock(&acl_cache->lock);
2033
  res= find_acl_user(host, user, TRUE) != NULL;
Marc Alff's avatar
Marc Alff committed
2034
  mysql_mutex_unlock(&acl_cache->lock);
2035 2036 2037 2038
  return res;
}


unknown's avatar
unknown committed
2039 2040 2041 2042 2043
/*
  Find first entry that matches the current user
*/

static ACL_USER *
2044
find_acl_user(const char *host, const char *user, my_bool exact)
unknown's avatar
unknown committed
2045
{
unknown's avatar
unknown committed
2046
  DBUG_ENTER("find_acl_user");
2047
  DBUG_PRINT("enter",("host: '%s'  user: '%s'",host,user));
2048

Marc Alff's avatar
Marc Alff committed
2049
  mysql_mutex_assert_owner(&acl_cache->lock);
2050

unknown's avatar
unknown committed
2051 2052 2053
  for (uint i=0 ; i < acl_users.elements ; i++)
  {
    ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
unknown's avatar
unknown committed
2054
    DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
2055 2056 2057 2058
                       user, acl_user->user ? acl_user->user : "",
                       host,
                       acl_user->host.hostname ? acl_user->host.hostname :
                       ""));
2059 2060
    if ((!acl_user->user && !user[0]) ||
	(acl_user->user && !strcmp(user,acl_user->user)))
unknown's avatar
unknown committed
2061
    {
2062
      if (exact ? !my_strcasecmp(system_charset_info, host,
2063 2064
                                 acl_user->host.hostname ?
				 acl_user->host.hostname : "") :
2065
          compare_hostname(&acl_user->host,host,host))
unknown's avatar
unknown committed
2066 2067 2068
      {
	DBUG_RETURN(acl_user);
      }
unknown's avatar
unknown committed
2069 2070
    }
  }
unknown's avatar
unknown committed
2071
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2072 2073 2074
}


2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085
/*
  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
2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107

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)
{
2108
  host->hostname=(char*) hostname;             // This will not be modified!
2109
  if (!hostname ||
unknown's avatar
unknown committed
2110 2111 2112
      (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
       !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
  {
2113
    host->ip= host->ip_mask=0;			// Not a masked ip
unknown's avatar
unknown committed
2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126
  }
}


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 ||
2127
	  (hostname && !wild_case_compare(system_charset_info,
2128 2129
                                          hostname, host->hostname)) ||
	  (ip && !wild_compare(ip, host->hostname, 0)));
unknown's avatar
unknown committed
2130 2131
}

2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157
/**
  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
2158 2159 2160
bool hostname_requires_resolving(const char *hostname)
{
  if (!hostname)
unknown's avatar
unknown committed
2161
    return FALSE;
2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173

  /* 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
2174
    return FALSE;
2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185
  }

  /*
    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
2186
  {
2187 2188 2189 2190 2191 2192 2193
    switch (*p) {
      case ':':
      case '%':
      case '_':
      case '/':
        return FALSE;
    }
unknown's avatar
SCRUM  
unknown committed
2194
  }
2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208

  /*
    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
2209
}
unknown's avatar
unknown committed
2210

2211

2212
/*
2213 2214 2215 2216 2217 2218 2219 2220 2221 2222
  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
2223
*/
unknown's avatar
unknown committed
2224

2225 2226
static bool update_user_table(THD *thd, TABLE *table,
                              const char *host, const char *user,
2227 2228
			      const char *new_password, uint new_password_len,
                              bool reset_plugin)
unknown's avatar
unknown committed
2229
{
2230
  char user_key[MAX_KEY_LENGTH];
2231
  int error;
unknown's avatar
unknown committed
2232 2233 2234
  DBUG_ENTER("update_user_table");
  DBUG_PRINT("enter",("user: %s  host: %s",user,host));

2235
  table->use_all_columns();
2236 2237
  table->field[0]->store(host,(uint) strlen(host), system_charset_info);
  table->field[1]->store(user,(uint) strlen(user), system_charset_info);
2238
  key_copy((uchar *) user_key, table->record[0], table->key_info,
2239
           table->key_info->key_length);
unknown's avatar
unknown committed
2240

2241 2242 2243
  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
2244
  {
unknown's avatar
unknown committed
2245 2246
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
               MYF(0));	/* purecov: deadcode */
unknown's avatar
unknown committed
2247 2248
    DBUG_RETURN(1);				/* purecov: deadcode */
  }
unknown's avatar
unknown committed
2249
  store_record(table,record[1]);
2250
  table->field[2]->store(new_password, new_password_len, system_charset_info);
2251 2252 2253 2254 2255
  if (reset_plugin && table->s->fields >= 41)
  {
    table->field[40]->reset();
    table->field[41]->reset();
  }
2256 2257
  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
2258 2259
  {
    table->file->print_error(error,MYF(0));	/* purecov: deadcode */
2260
    DBUG_RETURN(1);
unknown's avatar
unknown committed
2261
  }
2262
  DBUG_RETURN(0);
unknown's avatar
unknown committed
2263 2264
}

unknown's avatar
unknown committed
2265

2266 2267 2268 2269 2270 2271
/*
  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
2272 2273 2274

static bool test_if_create_new_users(THD *thd)
{
2275
  Security_context *sctx= thd->security_ctx;
2276
  bool create_new_users= test(sctx->master_access & INSERT_ACL) ||
2277
                         (!opt_safe_user_create &&
2278
                          test(sctx->master_access & CREATE_USER_ACL));
2279
  if (!create_new_users)
unknown's avatar
unknown committed
2280 2281
  {
    TABLE_LIST tl;
unknown's avatar
unknown committed
2282
    ulong db_access;
2283 2284
    tl.init_one_table(C_STRING_WITH_LEN("mysql"),
                      C_STRING_WITH_LEN("user"), "user", TL_WRITE);
2285
    create_new_users= 1;
unknown's avatar
unknown committed
2286

2287 2288
    db_access=acl_get(sctx->host, sctx->ip,
		      sctx->priv_user, tl.db, 0);
unknown's avatar
unknown committed
2289 2290
    if (!(db_access & INSERT_ACL))
    {
2291
      if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
unknown's avatar
unknown committed
2292 2293 2294 2295 2296 2297 2298
	create_new_users=0;
    }
  }
  return create_new_users;
}


unknown's avatar
unknown committed
2299
/****************************************************************************
2300
  Handle GRANT commands
unknown's avatar
unknown committed
2301 2302
****************************************************************************/

Sergei Golubchik's avatar
Sergei Golubchik committed
2303
static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
unknown's avatar
unknown committed
2304
			      ulong rights, bool revoke_grant,
unknown's avatar
unknown committed
2305
			      bool can_create_user, bool no_auto_create)
unknown's avatar
unknown committed
2306 2307
{
  int error = -1;
unknown's avatar
unknown committed
2308
  bool old_row_exists=0;
unknown's avatar
unknown committed
2309
  char what= (revoke_grant) ? 'N' : 'Y';
2310
  uchar user_key[MAX_KEY_LENGTH];
2311
  LEX *lex= thd->lex;
unknown's avatar
unknown committed
2312
  DBUG_ENTER("replace_user_table");
unknown's avatar
unknown committed
2313

Marc Alff's avatar
Marc Alff committed
2314
  mysql_mutex_assert_owner(&acl_cache->lock);
unknown's avatar
unknown committed
2315 2316

  if (combo.password.str && combo.password.str[0])
2317
  {
2318 2319
    if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
        combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
2320
    {
2321
      my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
unknown's avatar
unknown committed
2322
      DBUG_RETURN(-1);
2323 2324
    }
  }
Sergei Golubchik's avatar
Sergei Golubchik committed
2325 2326
  else
    combo.password= empty_lex_str;
unknown's avatar
unknown committed
2327

2328 2329 2330 2331 2332
  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);
2333 2334 2335
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

2336 2337 2338
  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
2339
  {
2340 2341
    /* what == 'N' means revoke */
    if (what == 'N')
unknown's avatar
unknown committed
2342
    {
2343 2344 2345 2346
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
      goto end;
    }
    /*
2347 2348
      There are four options which affect the process of creation of
      a new user (mysqld option --safe-create-user, 'insert' privilege
2349 2350 2351 2352 2353 2354 2355
      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
2356 2357

      see also test_if_create_new_users()
2358
    */
Sergei Golubchik's avatar
Sergei Golubchik committed
2359
    else if (!combo.password.length && !combo.plugin.length && no_auto_create)
unknown's avatar
unknown committed
2360
    {
2361
      my_error(ER_PASSWORD_NO_MATCH, MYF(0));
unknown's avatar
unknown committed
2362 2363 2364
      goto end;
    }
    else if (!can_create_user)
2365
    {
2366
      my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
unknown's avatar
unknown committed
2367 2368
      goto end;
    }
2369 2370 2371 2372 2373 2374 2375 2376 2377
    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
2378
    old_row_exists = 0;
2379
    restore_record(table,s->default_values);
2380
    table->field[0]->store(combo.host.str,combo.host.length,
2381
                           system_charset_info);
2382
    table->field[1]->store(combo.user.str,combo.user.length,
2383
                           system_charset_info);
unknown's avatar
unknown committed
2384 2385 2386
  }
  else
  {
unknown's avatar
unknown committed
2387
    old_row_exists = 1;
unknown's avatar
unknown committed
2388
    store_record(table,record[1]);			// Save copy for update
unknown's avatar
unknown committed
2389 2390
  }

unknown's avatar
unknown committed
2391 2392 2393 2394
  /* Update table columns with new privileges */

  Field **tmp_field;
  ulong priv;
2395
  uint next_field;
unknown's avatar
unknown committed
2396
  for (tmp_field= table->field+3, priv = SELECT_ACL;
2397
       *tmp_field && (*tmp_field)->real_type() == MYSQL_TYPE_ENUM &&
unknown's avatar
unknown committed
2398 2399
	 ((Field_enum*) (*tmp_field))->typelib->count == 2 ;
       tmp_field++, priv <<= 1)
unknown's avatar
unknown committed
2400
  {
unknown's avatar
unknown committed
2401
    if (priv & rights)				 // set requested privileges
unknown's avatar
unknown committed
2402
      (*tmp_field)->store(&what, 1, &my_charset_latin1);
unknown's avatar
unknown committed
2403
  }
2404
  rights= get_access(table, 3, &next_field);
2405
  DBUG_PRINT("info",("table fields: %d",table->s->fields));
Sergei Golubchik's avatar
Sergei Golubchik committed
2406 2407
  if (combo.password.str[0])
    table->field[2]->store(combo.password.str, combo.password.length, system_charset_info);
2408
  if (table->s->fields >= 31)		/* From 4.0.0 we have more fields */
2409
  {
unknown's avatar
unknown committed
2410
    /* We write down SSL related ACL stuff */
2411
    switch (lex->ssl_type) {
2412
    case SSL_TYPE_ANY:
2413 2414
      table->field[next_field]->store(STRING_WITH_LEN("ANY"),
                                      &my_charset_latin1);
2415 2416 2417
      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);
2418 2419
      break;
    case SSL_TYPE_X509:
2420 2421
      table->field[next_field]->store(STRING_WITH_LEN("X509"),
                                      &my_charset_latin1);
2422 2423 2424
      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);
2425 2426
      break;
    case SSL_TYPE_SPECIFIED:
2427 2428
      table->field[next_field]->store(STRING_WITH_LEN("SPECIFIED"),
                                      &my_charset_latin1);
2429 2430 2431
      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);
2432
      if (lex->ssl_cipher)
unknown's avatar
unknown committed
2433 2434
        table->field[next_field+1]->store(lex->ssl_cipher,
                                strlen(lex->ssl_cipher), system_charset_info);
2435
      if (lex->x509_issuer)
unknown's avatar
unknown committed
2436 2437
        table->field[next_field+2]->store(lex->x509_issuer,
                                strlen(lex->x509_issuer), system_charset_info);
2438
      if (lex->x509_subject)
unknown's avatar
unknown committed
2439 2440
        table->field[next_field+3]->store(lex->x509_subject,
                                strlen(lex->x509_subject), system_charset_info);
2441
      break;
unknown's avatar
unknown committed
2442
    case SSL_TYPE_NOT_SPECIFIED:
unknown's avatar
unknown committed
2443 2444
      break;
    case SSL_TYPE_NONE:
2445 2446 2447 2448
      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
2449
      break;
2450
    }
unknown's avatar
unknown committed
2451
    next_field+=4;
unknown's avatar
unknown committed
2452

2453
    USER_RESOURCES mqh= lex->mqh;
2454
    if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
2455
      table->field[next_field]->store((longlong) mqh.questions, TRUE);
2456
    if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
2457
      table->field[next_field+1]->store((longlong) mqh.updates, TRUE);
2458
    if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
2459
      table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE);
2460
    if (table->s->fields >= 36 &&
2461
        (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
2462
      table->field[next_field+3]->store((longlong) mqh.user_conn, FALSE);
2463
    mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
2464

2465
    next_field+= 4;
Sergei Golubchik's avatar
Sergei Golubchik committed
2466
    if (table->s->fields >= 41)
2467
    {
Sergei Golubchik's avatar
Sergei Golubchik committed
2468 2469
      table->field[next_field]->set_notnull();
      table->field[next_field + 1]->set_notnull();
Sergei Golubchik's avatar
Sergei Golubchik committed
2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484
      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();
      }
2485
    }
2486
  }
2487

unknown's avatar
unknown committed
2488
  if (old_row_exists)
unknown's avatar
unknown committed
2489 2490 2491 2492 2493
  {
    /*
      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!
    */
2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505
    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)
      {						// This should never happen
        table->file->print_error(error,MYF(0));	/* purecov: deadcode */
        error= -1;				/* purecov: deadcode */
        goto end;				/* purecov: deadcode */
      }
      else
        error= 0;
unknown's avatar
unknown committed
2506 2507
    }
  }
2508
  else if ((error=table->file->ha_write_row(table->record[0]))) // insert
unknown's avatar
unknown committed
2509
  {						// This should never happen
2510
    if (table->file->is_fatal_error(error, HA_CHECK_DUP))
unknown's avatar
unknown committed
2511 2512 2513 2514 2515 2516 2517 2518
    {
      table->file->print_error(error,MYF(0));	/* purecov: deadcode */
      error= -1;				/* purecov: deadcode */
      goto end;					/* purecov: deadcode */
    }
  }
  error=0;					// Privileges granted / revoked

2519
end:
unknown's avatar
unknown committed
2520 2521 2522
  if (!error)
  {
    acl_cache->clear(1);			// Clear privilege cache
unknown's avatar
unknown committed
2523
    if (old_row_exists)
2524
      acl_update_user(combo.user.str, combo.host.str,
Sergei Golubchik's avatar
Sergei Golubchik committed
2525
                      combo.password.str, combo.password.length,
2526 2527 2528 2529 2530
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
2531 2532 2533
		      rights,
		      &combo.plugin,
		      &combo.auth);
unknown's avatar
unknown committed
2534
    else
Sergei Golubchik's avatar
Sergei Golubchik committed
2535 2536
      acl_insert_user(combo.user.str, combo.host.str,
                      combo.password.str, combo.password.length,
2537 2538 2539 2540 2541
		      lex->ssl_type,
		      lex->ssl_cipher,
		      lex->x509_issuer,
		      lex->x509_subject,
		      &lex->mqh,
2542 2543 2544
		      rights,
		      &combo.plugin,
		      &combo.auth);
unknown's avatar
unknown committed
2545 2546 2547 2548 2549 2550
  }
  DBUG_RETURN(error);
}


/*
unknown's avatar
unknown committed
2551
  change grants in the mysql.db table
unknown's avatar
unknown committed
2552 2553 2554 2555
*/

static int replace_db_table(TABLE *table, const char *db,
			    const LEX_USER &combo,
unknown's avatar
unknown committed
2556
			    ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
2557
{
unknown's avatar
unknown committed
2558 2559
  uint i;
  ulong priv,store_rights;
unknown's avatar
unknown committed
2560
  bool old_row_exists=0;
unknown's avatar
unknown committed
2561
  int error;
unknown's avatar
unknown committed
2562
  char what= (revoke_grant) ? 'N' : 'Y';
2563
  uchar user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
2564 2565
  DBUG_ENTER("replace_db_table");

2566 2567
  if (!initialized)
  {
unknown's avatar
unknown committed
2568
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2569 2570 2571
    DBUG_RETURN(-1);
  }

2572
  /* Check if there is such a user in user table in memory? */
2573
  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
unknown's avatar
unknown committed
2574
  {
unknown's avatar
unknown committed
2575
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
unknown's avatar
unknown committed
2576 2577 2578
    DBUG_RETURN(-1);
  }

2579 2580 2581
  table->use_all_columns();
  table->field[0]->store(combo.host.str,combo.host.length,
                         system_charset_info);
2582
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
2583 2584
  table->field[2]->store(combo.user.str,combo.user.length,
                         system_charset_info);
2585 2586 2587
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);

2588 2589 2590
  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
2591 2592 2593
  {
    if (what == 'N')
    { // no row, no revoke
unknown's avatar
unknown committed
2594
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
unknown's avatar
unknown committed
2595 2596
      goto abort;
    }
unknown's avatar
unknown committed
2597
    old_row_exists = 0;
2598
    restore_record(table, s->default_values);
2599 2600
    table->field[0]->store(combo.host.str,combo.host.length,
                           system_charset_info);
2601
    table->field[1]->store(db,(uint) strlen(db), system_charset_info);
2602 2603
    table->field[2]->store(combo.user.str,combo.user.length,
                           system_charset_info);
unknown's avatar
unknown committed
2604 2605 2606
  }
  else
  {
unknown's avatar
unknown committed
2607
    old_row_exists = 1;
unknown's avatar
unknown committed
2608
    store_record(table,record[1]);
unknown's avatar
unknown committed
2609 2610 2611
  }

  store_rights=get_rights_for_db(rights);
2612
  for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
unknown's avatar
unknown committed
2613
  {
unknown's avatar
unknown committed
2614
    if (priv & store_rights)			// do it if priv is chosen
unknown's avatar
unknown committed
2615
      table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
unknown's avatar
unknown committed
2616 2617 2618 2619
  }
  rights=get_access(table,3);
  rights=fix_rights_for_db(rights);

unknown's avatar
unknown committed
2620
  if (old_row_exists)
unknown's avatar
unknown committed
2621
  {
2622
    /* update old existing row */
unknown's avatar
unknown committed
2623 2624
    if (rights)
    {
2625
      if ((error= table->file->ha_update_row(table->record[1],
2626 2627
                                             table->record[0])) &&
          error != HA_ERR_RECORD_IS_THE_SAME)
unknown's avatar
unknown committed
2628 2629 2630 2631
	goto table_error;			/* purecov: deadcode */
    }
    else	/* must have been a revoke of all privileges */
    {
2632
      if ((error= table->file->ha_delete_row(table->record[1])))
unknown's avatar
unknown committed
2633 2634 2635
	goto table_error;			/* purecov: deadcode */
    }
  }
2636
  else if (rights && (error= table->file->ha_write_row(table->record[0])))
unknown's avatar
unknown committed
2637
  {
2638
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
unknown's avatar
unknown committed
2639 2640 2641 2642
      goto table_error; /* purecov: deadcode */
  }

  acl_cache->clear(1);				// Clear privilege cache
unknown's avatar
unknown committed
2643
  if (old_row_exists)
unknown's avatar
unknown committed
2644 2645
    acl_update_db(combo.user.str,combo.host.str,db,rights);
  else
2646
  if (rights)
unknown's avatar
unknown committed
2647 2648 2649 2650
    acl_insert_db(combo.user.str,combo.host.str,db,rights);
  DBUG_RETURN(0);

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

2654
abort:
unknown's avatar
unknown committed
2655 2656 2657 2658
  DBUG_RETURN(-1);
}


2659 2660 2661 2662 2663
static void  
acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
{
  mysql_mutex_assert_owner(&acl_cache->lock);

2664 2665
  DBUG_ENTER("acl_update_proxy_user");
  for (uint i= 0; i < acl_proxy_users.elements; i++)
2666 2667 2668 2669 2670 2671 2672 2673
  {
    ACL_PROXY_USER *acl_user= 
      dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);

    if (acl_user->pk_equals(new_value))
    {
      if (is_revoke)
      {
2674
        DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
2675 2676 2677 2678
        delete_dynamic_element(&acl_proxy_users, i);
      }
      else
      {
2679
        DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691
        acl_user->set_data(new_value);
      }
      break;
    }
  }
  DBUG_VOID_RETURN;
}


static void  
acl_insert_proxy_user(ACL_PROXY_USER *new_value)
{
2692
  DBUG_ENTER("acl_insert_proxy_user");
2693 2694
  mysql_mutex_assert_owner(&acl_cache->lock);
  (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
2695
  my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
2696
           acl_proxy_users.elements,
2697
           sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
2698 2699 2700 2701 2702
  DBUG_VOID_RETURN;
}


static int 
2703
replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
2704 2705 2706
                         const LEX_USER *proxied_user, bool with_grant_arg, 
                         bool revoke_grant)
{
2707
  bool old_row_exists= 0;
2708 2709 2710
  int error;
  uchar user_key[MAX_KEY_LENGTH];
  ACL_PROXY_USER new_grant;
2711
  char grantor[USER_HOST_BUFF_SIZE];
2712

2713
  DBUG_ENTER("replace_proxies_priv_table");
2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734

  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? */
  if (!find_acl_user(user->host.str,user->user.str, FALSE))
  {
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
    DBUG_RETURN(-1);
  }

  table->use_all_columns();
  ACL_PROXY_USER::store_pk (table, &user->host, &user->user, 
                            &proxied_user->host, &proxied_user->user);

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

2735 2736
  get_grantor(thd, grantor);

2737 2738 2739 2740 2741 2742 2743
  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
2744 2745 2746
  if (table->file->ha_index_read_map(table->record[0], user_key,
                                     HA_WHOLE_KEY,
                                     HA_READ_KEY_EXACT))
2747 2748 2749 2750 2751 2752 2753
  {
    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;
    }
2754 2755 2756 2757 2758
    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,
2759 2760
                                      with_grant_arg,
                                      grantor);
2761 2762 2763
  }
  else
  {
2764 2765 2766
    DBUG_PRINT("info", ("Row found"));
    old_row_exists= 1;
    store_record(table, record[1]);
2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786
  }

  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])))
  {
2787
    DBUG_PRINT("info", ("error inserting the row"));
2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801
    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
  {
2802 2803 2804
    new_grant.init(&mem, user->host.str, user->user.str,
                   proxied_user->host.str, proxied_user->user.str,
                   with_grant_arg);
2805 2806 2807 2808 2809 2810 2811 2812
    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:
2813 2814
  DBUG_PRINT("info", ("table error"));
  table->file->print_error(error, MYF(0));	/* purecov: inspected */
2815 2816

abort:
2817
  DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
2818 2819 2820 2821 2822
  table->file->ha_index_end();
  DBUG_RETURN(-1);
}


unknown's avatar
unknown committed
2823 2824 2825 2826
class GRANT_COLUMN :public Sql_alloc
{
public:
  char *column;
unknown's avatar
unknown committed
2827 2828 2829
  ulong rights;
  uint key_length;
  GRANT_COLUMN(String &c,  ulong y) :rights (y)
unknown's avatar
unknown committed
2830
  {
2831
    column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length());
unknown's avatar
unknown committed
2832 2833 2834
  }
};

unknown's avatar
unknown committed
2835

2836
static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
unknown's avatar
unknown committed
2837 2838 2839
			    my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
2840
  return (uchar*) buff->column;
unknown's avatar
unknown committed
2841 2842
}

unknown's avatar
unknown committed
2843

2844
class GRANT_NAME :public Sql_alloc
unknown's avatar
unknown committed
2845 2846
{
public:
2847 2848
  acl_host_and_ip host;
  char *db, *user, *tname, *hash_key;
2849
  ulong privs;
2850
  ulong sort;
2851
  size_t key_length;
2852
  GRANT_NAME(const char *h, const char *d,const char *u,
2853 2854
             const char *t, ulong p, bool is_routine);
  GRANT_NAME (TABLE *form, bool is_routine);
2855 2856
  virtual ~GRANT_NAME() {};
  virtual bool ok() { return privs != 0; }
2857
  void set_user_details(const char *h, const char *d,
2858 2859
                        const char *u, const char *t,
                        bool is_routine);
2860 2861 2862 2863 2864 2865 2866
};


class GRANT_TABLE :public GRANT_NAME
{
public:
  ulong cols;
unknown's avatar
unknown committed
2867
  HASH hash_columns;
unknown's avatar
unknown committed
2868 2869 2870 2871

  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);
2872
  ~GRANT_TABLE();
2873 2874
  bool ok() { return privs != 0 || cols != 0; }
};
unknown's avatar
unknown committed
2875

2876

2877
void GRANT_NAME::set_user_details(const char *h, const char *d,
2878 2879
                                  const char *u, const char *t,
                                  bool is_routine)
2880 2881
{
  /* Host given by user */
2882
  update_hostname(&host, strdup_root(&memex, h));
2883 2884 2885 2886 2887 2888
  if (db != d)
  {
    db= strdup_root(&memex, d);
    if (lower_case_table_names)
      my_casedn_str(files_charset_info, db);
  }
2889
  user = strdup_root(&memex,u);
2890
  sort=  get_sort(3,host.hostname,db,user);
2891
  if (tname != t)
unknown's avatar
unknown committed
2892
  {
2893
    tname= strdup_root(&memex, t);
2894
    if (lower_case_table_names || is_routine)
2895
      my_casedn_str(files_charset_info, tname);
2896
  }
2897 2898
  key_length= strlen(d) + strlen(u)+ strlen(t)+3;
  hash_key=   (char*) alloc_root(&memex,key_length);
2899
  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
2900 2901
}

2902
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
2903
                       const char *t, ulong p, bool is_routine)
2904 2905
  :db(0), tname(0), privs(p)
{
2906
  set_user_details(h, d, u, t, is_routine);
2907
}
2908 2909 2910

GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
                	 const char *t, ulong p, ulong c)
2911
  :GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
2912
{
Konstantin Osipov's avatar
Konstantin Osipov committed
2913 2914
  (void) my_hash_init2(&hash_columns,4,system_charset_info,
                   0,0,0, (my_hash_get_key) get_key_column,0,0);
2915
}
unknown's avatar
unknown committed
2916

2917

2918
GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
2919
{
2920
  update_hostname(&host, get_field(&memex, form->field[0]));
unknown's avatar
unknown committed
2921 2922
  db=    get_field(&memex,form->field[1]);
  user=  get_field(&memex,form->field[2]);
2923 2924
  if (!user)
    user= (char*) "";
2925
  sort=  get_sort(3, host.hostname, db, user);
unknown's avatar
unknown committed
2926
  tname= get_field(&memex,form->field[3]);
2927 2928 2929
  if (!db || !tname)
  {
    /* Wrong table row; Ignore it */
2930
    privs= 0;
2931 2932 2933 2934
    return;					/* purecov: inspected */
  }
  if (lower_case_table_names)
  {
2935
    my_casedn_str(files_charset_info, db);
2936 2937 2938
  }
  if (lower_case_table_names || is_routine)
  {
2939
    my_casedn_str(files_charset_info, tname);
2940
  }
2941 2942
  key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
  hash_key=   (char*) alloc_root(&memex, key_length);
2943 2944 2945
  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
  privs = (ulong) form->field[6]->val_int();
  privs = fix_rights_for_table(privs);
2946 2947 2948 2949
}


GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
2950
  :GRANT_NAME(form, FALSE)
2951
{
2952
  uchar key[MAX_KEY_LENGTH];
2953 2954 2955 2956

  if (!db || !tname)
  {
    /* Wrong table row; Ignore it */
Konstantin Osipov's avatar
Konstantin Osipov committed
2957
    my_hash_clear(&hash_columns);               /* allow for destruction */
2958 2959 2960 2961
    cols= 0;
    return;
  }
  cols= (ulong) form->field[7]->val_int();
2962 2963
  cols =  fix_rights_for_column(cols);

Konstantin Osipov's avatar
Konstantin Osipov committed
2964 2965
  (void) my_hash_init2(&hash_columns,4,system_charset_info,
                   0,0,0, (my_hash_get_key) get_key_column,0,0);
2966 2967
  if (cols)
  {
2968 2969
    uint key_prefix_len;
    KEY_PART_INFO *key_part= col_privs->key_info->key_part;
2970
    col_privs->field[0]->store(host.hostname,
2971 2972
                               host.hostname ? (uint) strlen(host.hostname) :
                               0,
2973 2974 2975 2976
                               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);
2977 2978 2979 2980 2981 2982

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

2985 2986 2987 2988 2989 2990
    if (col_privs->file->ha_index_init(0, 1))
    {
      cols= 0;
      return;
    }

2991 2992 2993
    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
2994
    {
2995
      cols = 0; /* purecov: deadcode */
2996
      col_privs->file->ha_index_end();
2997
      return;
unknown's avatar
unknown committed
2998
    }
2999
    do
unknown's avatar
unknown committed
3000
    {
3001 3002 3003
      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
3004
      res=col_privs->field[4]->val_str(&column_name);
3005 3006 3007
      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
3008
      {
3009 3010 3011
        /* Don't use this entry */
        privs = cols = 0;			/* purecov: deadcode */
        return;				/* purecov: deadcode */
unknown's avatar
unknown committed
3012
      }
3013 3014 3015 3016 3017 3018
      if (my_hash_insert(&hash_columns, (uchar *) mem_check))
      {
        /* Invalidate this entry */
        privs= cols= 0;
        return;
      }
3019
    } while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
3020
             !key_cmp_if_same(col_privs,key,0,key_prefix_len));
3021
    col_privs->file->ha_index_end();
unknown's avatar
unknown committed
3022
  }
3023
}
unknown's avatar
unknown committed
3024

unknown's avatar
unknown committed
3025

3026 3027
GRANT_TABLE::~GRANT_TABLE()
{
Konstantin Osipov's avatar
Konstantin Osipov committed
3028
  my_hash_free(&hash_columns);
3029 3030 3031
}


3032
static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
unknown's avatar
unknown committed
3033 3034 3035
			     my_bool not_used __attribute__((unused)))
{
  *length=buff->key_length;
3036
  return (uchar*) buff->hash_key;
unknown's avatar
unknown committed
3037 3038
}

unknown's avatar
unknown committed
3039

unknown's avatar
unknown committed
3040 3041
void free_grant_table(GRANT_TABLE *grant_table)
{
Konstantin Osipov's avatar
Konstantin Osipov committed
3042
  my_hash_free(&grant_table->hash_columns);
unknown's avatar
unknown committed
3043 3044
}

unknown's avatar
unknown committed
3045

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

3048
static GRANT_NAME *name_hash_search(HASH *name_hash,
unknown's avatar
unknown committed
3049 3050 3051
                                    const char *host,const char* ip,
                                    const char *db,
                                    const char *user, const char *tname,
Georgi Kodinov's avatar
merge  
Georgi Kodinov committed
3052
                                    bool exact, bool name_tolower)
unknown's avatar
unknown committed
3053
{
3054 3055
  char helping[SAFE_NAME_LEN*2+USERNAME_LENGTH+3];
  char *hend = helping + sizeof(helping);
unknown's avatar
unknown committed
3056
  uint len;
3057
  GRANT_NAME *grant_name,*found=0;
3058
  HASH_SEARCH_STATE state;
unknown's avatar
unknown committed
3059

3060 3061 3062 3063 3064 3065 3066 3067 3068
  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);
3069
  if (name_tolower)
3070
    my_casedn_str(files_charset_info, tname_ptr);
Konstantin Osipov's avatar
Konstantin Osipov committed
3071 3072
  for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
                                               len, &state);
3073
       grant_name ;
Konstantin Osipov's avatar
Konstantin Osipov committed
3074 3075
       grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
                                              len, &state))
unknown's avatar
unknown committed
3076 3077 3078
  {
    if (exact)
    {
3079 3080
      if (!grant_name->host.hostname ||
          (host &&
3081
	   !my_strcasecmp(system_charset_info, host,
unknown's avatar
unknown committed
3082 3083
                          grant_name->host.hostname)) ||
	  (ip && !strcmp(ip, grant_name->host.hostname)))
3084
	return grant_name;
unknown's avatar
unknown committed
3085 3086 3087
    }
    else
    {
3088
      if (compare_hostname(&grant_name->host, host, ip) &&
3089 3090
          (!found || found->sort < grant_name->sort))
	found=grant_name;					// Host ok
unknown's avatar
unknown committed
3091 3092 3093 3094 3095 3096
    }
  }
  return found;
}


3097
inline GRANT_NAME *
3098 3099
routine_hash_search(const char *host, const char *ip, const char *db,
                 const char *user, const char *tname, bool proc, bool exact)
3100
{
3101 3102
  return (GRANT_TABLE*)
    name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
3103
		     host, ip, db, user, tname, exact, TRUE);
3104 3105 3106 3107 3108 3109 3110 3111
}


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,
3112
					 user, tname, exact, FALSE);
3113 3114
}

unknown's avatar
unknown committed
3115

unknown's avatar
unknown committed
3116
inline GRANT_COLUMN *
unknown's avatar
unknown committed
3117
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
unknown's avatar
unknown committed
3118
{
Konstantin Osipov's avatar
Konstantin Osipov committed
3119 3120
  return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
                                        (uchar*) cname, length);
unknown's avatar
unknown committed
3121 3122 3123 3124 3125 3126 3127
}


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
3128
				ulong rights, bool revoke_grant)
unknown's avatar
unknown committed
3129
{
3130
  int result=0;
3131
  uchar key[MAX_KEY_LENGTH];
3132 3133
  uint key_prefix_length;
  KEY_PART_INFO *key_part= table->key_info->key_part;
unknown's avatar
unknown committed
3134 3135
  DBUG_ENTER("replace_column_table");

3136
  table->use_all_columns();
unknown's avatar
unknown committed
3137 3138 3139 3140 3141 3142 3143 3144
  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
3145

3146
  /* Get length of 4 first key parts */
3147 3148 3149
  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
3150

3151
  rights&= COL_ACLS;				// Only ACL for columns
unknown's avatar
unknown committed
3152 3153 3154 3155

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

  List_iterator <LEX_COLUMN> iter(columns);
unknown's avatar
unknown committed
3156
  class LEX_COLUMN *column;
3157 3158 3159 3160 3161 3162 3163
  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
3164
  while ((column= iter++))
unknown's avatar
unknown committed
3165
  {
unknown's avatar
unknown committed
3166
    ulong privileges= column->rights;
unknown's avatar
unknown committed
3167
    bool old_row_exists=0;
3168
    uchar user_key[MAX_KEY_LENGTH];
3169 3170 3171

    key_restore(table->record[0],key,table->key_info,
                key_prefix_length);
unknown's avatar
unknown committed
3172
    table->field[4]->store(column->column.ptr(), column->column.length(),
3173
                           system_charset_info);
3174 3175 3176
    /* 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
3177

3178 3179
    if (table->file->ha_index_read_map(table->record[0], user_key,
                                       HA_WHOLE_KEY, HA_READ_KEY_EXACT))
unknown's avatar
unknown committed
3180 3181 3182
    {
      if (revoke_grant)
      {
unknown's avatar
unknown committed
3183
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
3184
                 combo.user.str, combo.host.str,
unknown's avatar
unknown committed
3185 3186 3187
                 table_name);                   /* purecov: inspected */
	result= -1;                             /* purecov: inspected */
	continue;                               /* purecov: inspected */
unknown's avatar
unknown committed
3188
      }
unknown's avatar
unknown committed
3189
      old_row_exists = 0;
3190
      restore_record(table, s->default_values);		// Get empty record
3191 3192
      key_restore(table->record[0],key,table->key_info,
                  key_prefix_length);
unknown's avatar
unknown committed
3193
      table->field[4]->store(column->column.ptr(),column->column.length(),
3194
                             system_charset_info);
unknown's avatar
unknown committed
3195 3196 3197
    }
    else
    {
unknown's avatar
unknown committed
3198
      ulong tmp= (ulong) table->field[6]->val_int();
unknown's avatar
unknown committed
3199 3200 3201 3202 3203 3204
      tmp=fix_rights_for_column(tmp);

      if (revoke_grant)
	privileges = tmp & ~(privileges | rights);
      else
	privileges |= tmp;
unknown's avatar
unknown committed
3205
      old_row_exists = 1;
unknown's avatar
unknown committed
3206
      store_record(table,record[1]);			// copy original row
unknown's avatar
unknown committed
3207 3208
    }

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

unknown's avatar
unknown committed
3211
    if (old_row_exists)
unknown's avatar
unknown committed
3212
    {
unknown's avatar
unknown committed
3213
      GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
3214
      if (privileges)
3215
	error=table->file->ha_update_row(table->record[1],table->record[0]);
unknown's avatar
unknown committed
3216
      else
3217
	error=table->file->ha_delete_row(table->record[1]);
3218
      if (error && error != HA_ERR_RECORD_IS_THE_SAME)
unknown's avatar
unknown committed
3219 3220 3221 3222 3223
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
3224 3225
      else
        error= 0;
unknown's avatar
unknown committed
3226 3227
      grant_column= column_hash_search(g_t, column->column.ptr(),
                                       column->column.length());
unknown's avatar
unknown committed
3228
      if (grant_column)				// Should always be true
unknown's avatar
unknown committed
3229
	grant_column->rights= privileges;	// Update hash
unknown's avatar
unknown committed
3230 3231 3232
    }
    else					// new grant
    {
unknown's avatar
unknown committed
3233
      GRANT_COLUMN *grant_column;
3234
      if ((error=table->file->ha_write_row(table->record[0])))
unknown's avatar
unknown committed
3235 3236 3237 3238 3239
      {
	table->file->print_error(error,MYF(0)); /* purecov: inspected */
	result= -1;				/* purecov: inspected */
	goto end;				/* purecov: inspected */
      }
unknown's avatar
unknown committed
3240
      grant_column= new GRANT_COLUMN(column->column,privileges);
3241 3242 3243 3244 3245
      if (my_hash_insert(&g_t->hash_columns,(uchar*) grant_column))
      {
        result= -1;
        goto end;
      }
unknown's avatar
unknown committed
3246 3247 3248 3249 3250 3251 3252 3253 3254 3255
    }
  }

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

  if (revoke_grant)
  {
3256
    uchar user_key[MAX_KEY_LENGTH];
3257
    key_copy(user_key, table->record[0], table->key_info,
unknown's avatar
unknown committed
3258 3259
             key_prefix_length);

3260 3261 3262
    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
3263 3264
      goto end;

3265
    /* Scan through all rows with the same host,db,user and table */
unknown's avatar
unknown committed
3266 3267
    do
    {
unknown's avatar
unknown committed
3268
      ulong privileges = (ulong) table->field[6]->val_int();
unknown's avatar
unknown committed
3269
      privileges=fix_rights_for_column(privileges);
unknown's avatar
unknown committed
3270
      store_record(table,record[1]);
unknown's avatar
unknown committed
3271 3272 3273 3274 3275

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

	privileges&= ~rights;
	table->field[6]->store((longlong)
3281
			       get_rights_for_column(privileges), TRUE);
3282
	table->field[4]->val_str(&column_name);
unknown's avatar
unknown committed
3283 3284 3285 3286 3287 3288
	grant_column = column_hash_search(g_t,
					  column_name.ptr(),
					  column_name.length());
	if (privileges)
	{
	  int tmp_error;
3289
	  if ((tmp_error=table->file->ha_update_row(table->record[1],
3290 3291
						    table->record[0])) &&
              tmp_error != HA_ERR_RECORD_IS_THE_SAME)
unknown's avatar
unknown committed
3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302
	  {					/* 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;
3303
	  if ((tmp_error = table->file->ha_delete_row(table->record[1])))
unknown's avatar
unknown committed
3304 3305 3306 3307 3308 3309
	  {					/* 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
3310
	    my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
unknown's avatar
unknown committed
3311 3312
	}
      }
3313
    } while (!table->file->ha_index_next(table->record[0]) &&
3314
	     !key_cmp_if_same(table, key, 0, key_prefix_length));
unknown's avatar
unknown committed
3315 3316
  }

3317
end:
unknown's avatar
unknown committed
3318
  table->file->ha_index_end();
unknown's avatar
unknown committed
3319 3320 3321
  DBUG_RETURN(result);
}

3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335
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
3336 3337 3338 3339

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
3340 3341
			       ulong rights, ulong col_rights,
			       bool revoke_grant)
unknown's avatar
unknown committed
3342
{
3343
  char grantor[USER_HOST_BUFF_SIZE];
unknown's avatar
unknown committed
3344
  int old_row_exists = 1;
unknown's avatar
unknown committed
3345
  int error=0;
unknown's avatar
unknown committed
3346
  ulong store_table_rights, store_col_rights;
3347
  uchar user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
3348 3349
  DBUG_ENTER("replace_table_table");

3350
  get_grantor(thd, grantor);
unknown's avatar
unknown committed
3351 3352 3353 3354
  /*
    The following should always succeed as new users are created before
    this function is called!
  */
3355
  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
unknown's avatar
unknown committed
3356
  {
unknown's avatar
unknown committed
3357 3358
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
               MYF(0));	/* purecov: deadcode */
unknown's avatar
unknown committed
3359 3360 3361
    DBUG_RETURN(-1);				/* purecov: deadcode */
  }

3362
  table->use_all_columns();
3363
  restore_record(table, s->default_values);     // Get empty record
3364 3365
  table->field[0]->store(combo.host.str,combo.host.length,
                         system_charset_info);
3366
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
3367 3368 3369 3370
  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
3371
  store_record(table,record[1]);			// store at pos 1
3372 3373
  key_copy(user_key, table->record[0], table->key_info,
           table->key_info->key_length);
unknown's avatar
unknown committed
3374

3375 3376 3377
  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
3378 3379 3380 3381 3382 3383 3384 3385
  {
    /*
      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
3386 3387
      my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
               combo.user.str, combo.host.str,
3388
               table_name);		        /* purecov: deadcode */
unknown's avatar
unknown committed
3389 3390
      DBUG_RETURN(-1);				/* purecov: deadcode */
    }
unknown's avatar
unknown committed
3391
    old_row_exists = 0;
unknown's avatar
unknown committed
3392
    restore_record(table,record[1]);			// Get saved record
unknown's avatar
unknown committed
3393 3394
  }

unknown's avatar
unknown committed
3395 3396
  store_table_rights= get_rights_for_table(rights);
  store_col_rights=   get_rights_for_column(col_rights);
unknown's avatar
unknown committed
3397
  if (old_row_exists)
unknown's avatar
unknown committed
3398
  {
unknown's avatar
unknown committed
3399
    ulong j,k;
unknown's avatar
unknown committed
3400
    store_record(table,record[1]);
unknown's avatar
unknown committed
3401 3402
    j = (ulong) table->field[6]->val_int();
    k = (ulong) table->field[7]->val_int();
unknown's avatar
unknown committed
3403 3404 3405

    if (revoke_grant)
    {
3406
      /* column rights are already fixed in mysql_table_grant */
unknown's avatar
unknown committed
3407 3408 3409 3410
      store_table_rights=j & ~store_table_rights;
    }
    else
    {
unknown's avatar
unknown committed
3411 3412
      store_table_rights|= j;
      store_col_rights|=   k;
unknown's avatar
unknown committed
3413 3414 3415
    }
  }

3416
  table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
3417 3418
  table->field[6]->store((longlong) store_table_rights, TRUE);
  table->field[7]->store((longlong) store_col_rights, TRUE);
unknown's avatar
unknown committed
3419
  rights=fix_rights_for_table(store_table_rights);
unknown's avatar
unknown committed
3420
  col_rights=fix_rights_for_column(store_col_rights);
unknown's avatar
unknown committed
3421

unknown's avatar
unknown committed
3422
  if (old_row_exists)
unknown's avatar
unknown committed
3423 3424 3425
  {
    if (store_table_rights || store_col_rights)
    {
3426 3427 3428
      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
3429 3430
	goto table_error;			/* purecov: deadcode */
    }
3431
    else if ((error = table->file->ha_delete_row(table->record[1])))
unknown's avatar
unknown committed
3432 3433 3434 3435
      goto table_error;				/* purecov: deadcode */
  }
  else
  {
3436
    error=table->file->ha_write_row(table->record[0]);
3437
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
unknown's avatar
unknown committed
3438 3439 3440
      goto table_error;				/* purecov: deadcode */
  }

unknown's avatar
unknown committed
3441
  if (rights | col_rights)
unknown's avatar
unknown committed
3442
  {
unknown's avatar
unknown committed
3443
    grant_table->privs= rights;
3444
    grant_table->cols=	col_rights;
unknown's avatar
unknown committed
3445 3446 3447
  }
  else
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
3448
    my_hash_delete(&column_priv_hash,(uchar*) grant_table);
unknown's avatar
unknown committed
3449 3450 3451
  }
  DBUG_RETURN(0);

3452 3453
  /* This should never happen */
table_error:
unknown's avatar
unknown committed
3454 3455 3456 3457 3458
  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
  DBUG_RETURN(-1); /* purecov: deadcode */
}


3459 3460 3461 3462
/**
  @retval       0  success
  @retval      -1  error
*/
3463
static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
3464
			      TABLE *table, const LEX_USER &combo,
3465 3466
			      const char *db, const char *routine_name,
			      bool is_proc, ulong rights, bool revoke_grant)
3467
{
3468
  char grantor[USER_HOST_BUFF_SIZE];
3469 3470 3471
  int old_row_exists= 1;
  int error=0;
  ulong store_proc_rights;
3472
  DBUG_ENTER("replace_routine_table");
3473 3474 3475 3476 3477 3478 3479

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

3480
  get_grantor(thd, grantor);
3481
  /*
3482 3483 3484 3485
    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.
3486 3487
  */

3488
  table->use_all_columns();
3489
  restore_record(table, s->default_values);		// Get empty record
3490 3491 3492
  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);
3493 3494
  table->field[3]->store(routine_name,(uint) strlen(routine_name),
                         &my_charset_latin1);
unknown's avatar
unknown committed
3495
  table->field[4]->store((longlong)(is_proc ?
3496 3497
                                    TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION),
                         TRUE);
3498 3499
  store_record(table,record[1]);			// store at pos 1

3500 3501 3502 3503
  if (table->file->ha_index_read_idx_map(table->record[0], 0,
                                         (uchar*) table->field[0]->ptr,
                                         HA_WHOLE_KEY,
                                         HA_READ_KEY_EXACT))
3504 3505 3506 3507 3508 3509 3510 3511 3512
  {
    /*
      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),
3513
               combo.user.str, combo.host.str, routine_name);
3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537
      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;
    }
  }

3538
  table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
3539
  table->field[6]->store((longlong) store_proc_rights, TRUE);
3540 3541 3542 3543 3544 3545
  rights=fix_rights_for_procedure(store_proc_rights);

  if (old_row_exists)
  {
    if (store_proc_rights)
    {
3546 3547 3548
      if ((error=table->file->ha_update_row(table->record[1],
                                            table->record[0])) &&
          error != HA_ERR_RECORD_IS_THE_SAME)
3549 3550
	goto table_error;
    }
3551
    else if ((error= table->file->ha_delete_row(table->record[1])))
3552 3553 3554 3555
      goto table_error;
  }
  else
  {
3556
    error=table->file->ha_write_row(table->record[0]);
3557
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
3558 3559 3560 3561 3562 3563 3564 3565 3566
      goto table_error;
  }

  if (rights)
  {
    grant_name->privs= rights;
  }
  else
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
3567 3568
    my_hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*)
                   grant_name);
3569 3570 3571 3572 3573 3574 3575 3576 3577 3578
  }
  DBUG_RETURN(0);

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


3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591
/*
  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
3592 3593
    FALSE ok
    TRUE  error
3594 3595
*/

3596
int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
unknown's avatar
unknown committed
3597 3598 3599
		      List <LEX_USER> &user_list,
		      List <LEX_COLUMN> &columns, ulong rights,
		      bool revoke_grant)
unknown's avatar
unknown committed
3600
{
3601
  ulong column_priv= 0;
unknown's avatar
unknown committed
3602
  List_iterator <LEX_USER> str_list (user_list);
3603
  LEX_USER *Str, *tmp_Str;
unknown's avatar
unknown committed
3604
  TABLE_LIST tables[3];
unknown's avatar
unknown committed
3605
  bool create_new_users=0;
3606
  char *db_name, *table_name;
3607
  bool save_binlog_row_based;
unknown's avatar
unknown committed
3608 3609 3610 3611
  DBUG_ENTER("mysql_table_grant");

  if (!initialized)
  {
unknown's avatar
unknown committed
3612 3613
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: inspected */
unknown's avatar
unknown committed
3614
    DBUG_RETURN(TRUE);				/* purecov: inspected */
unknown's avatar
unknown committed
3615 3616 3617
  }
  if (rights & ~TABLE_ACLS)
  {
unknown's avatar
unknown committed
3618 3619
    my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
               MYF(0));
unknown's avatar
unknown committed
3620
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
3621 3622
  }

3623
  if (!revoke_grant)
unknown's avatar
unknown committed
3624
  {
unknown's avatar
unknown committed
3625
    if (columns.elements)
unknown's avatar
unknown committed
3626
    {
3627 3628
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
unknown's avatar
unknown committed
3629

Sergei Golubchik's avatar
Sergei Golubchik committed
3630
      if (open_normal_and_derived_tables(thd, table_list, 0, DT_PREPARE))
unknown's avatar
unknown committed
3631
        DBUG_RETURN(TRUE);
3632 3633

      while ((column = column_iter++))
unknown's avatar
unknown committed
3634
      {
unknown's avatar
unknown committed
3635
        uint unused_field_idx= NO_CACHED_FIELD_INDEX;
unknown's avatar
unknown committed
3636 3637
        TABLE_LIST *dummy;
        Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
3638
                                         column->column.length(),
unknown's avatar
unknown committed
3639
                                         column->column.ptr(), NULL, NULL,
3640
                                         NULL, TRUE, FALSE,
unknown's avatar
unknown committed
3641
                                         &unused_field_idx, FALSE, &dummy);
unknown's avatar
unknown committed
3642
        if (f == (Field*)0)
3643
        {
unknown's avatar
unknown committed
3644 3645
          my_error(ER_BAD_FIELD_ERROR, MYF(0),
                   column->column.c_ptr(), table_list->alias);
unknown's avatar
unknown committed
3646
          DBUG_RETURN(TRUE);
3647
        }
unknown's avatar
unknown committed
3648 3649
        if (f == (Field *)-1)
          DBUG_RETURN(TRUE);
3650
        column_priv|= column->rights;
unknown's avatar
unknown committed
3651
      }
3652
      close_mysql_tables(thd);
unknown's avatar
unknown committed
3653
    }
3654
    else
unknown's avatar
unknown committed
3655
    {
3656 3657
      if (!(rights & CREATE_ACL))
      {
3658 3659
        char buf[FN_REFLEN + 1];
        build_table_filename(buf, sizeof(buf) - 1, table_list->db,
3660
                             table_list->table_name, reg_ext, 0);
3661 3662
        fn_format(buf, buf, "", "", MY_UNPACK_FILENAME  | MY_RESOLVE_SYMLINKS |
                                    MY_RETURN_REAL_PATH | MY_APPEND_EXT);
3663 3664
        if (access(buf,F_OK))
        {
unknown's avatar
unknown committed
3665
          my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
unknown's avatar
unknown committed
3666
          DBUG_RETURN(TRUE);
3667 3668 3669 3670 3671 3672 3673 3674
        }
      }
      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),
3675 3676
                 command, thd->security_ctx->priv_user,
                 thd->security_ctx->host_or_ip, table_list->alias);
3677 3678
        DBUG_RETURN(-1);
      }
unknown's avatar
unknown committed
3679 3680 3681 3682 3683
    }
  }

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

3684 3685 3686 3687 3688 3689 3690 3691
  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
3692
  tables[0].next_local= tables[0].next_global= tables+1;
unknown's avatar
unknown committed
3693
  /* Don't open column table if we don't need it ! */
3694 3695
  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
3696

3697 3698 3699 3700 3701
  /*
    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.
  */
3702 3703
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
    thd->clear_current_stmt_binlog_format_row();
3704

3705 3706 3707 3708 3709
#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
3710
  if (thd->slave_thread && rpl_filter->is_on())
3711
  {
unknown's avatar
unknown committed
3712 3713 3714
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
3715
    */
3716
    tables[0].updating= tables[1].updating= tables[2].updating= 1;
unknown's avatar
unknown committed
3717
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
3718 3719
    {
      /* Restore the state of binlog format */
3720
      DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3721 3722
      if (save_binlog_row_based)
        thd->set_current_stmt_binlog_format_row();
unknown's avatar
unknown committed
3723
      DBUG_RETURN(FALSE);
3724
    }
3725
  }
3726 3727
#endif

3728 3729 3730 3731 3732 3733
  /* 
    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);
3734 3735 3736 3737 3738 3739
  /*
    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;
3740
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
unknown's avatar
unknown committed
3741
  {						// Should never happen
3742
    /* Restore the state of binlog format */
3743
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3744
    thd->lex->restore_backup_query_tables_list(&backup);
3745 3746
    if (save_binlog_row_based)
      thd->set_current_stmt_binlog_format_row();
unknown's avatar
unknown committed
3747
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
unknown's avatar
unknown committed
3748 3749
  }

unknown's avatar
unknown committed
3750 3751
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
unknown's avatar
unknown committed
3752
  bool result= FALSE;
Marc Alff's avatar
Marc Alff committed
3753 3754
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
3755 3756
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;
3757
  grant_version++;
unknown's avatar
unknown committed
3758

3759
  while ((tmp_Str = str_list++))
unknown's avatar
unknown committed
3760
  {
3761
    int error;
unknown's avatar
unknown committed
3762
    GRANT_TABLE *grant_table;
3763 3764 3765 3766 3767
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
    }  
unknown's avatar
unknown committed
3768
    /* Create user if needed */
unknown's avatar
unknown committed
3769
    error=replace_user_table(thd, tables[0].table, *Str,
unknown's avatar
unknown committed
3770
			     0, revoke_grant, create_new_users,
unknown's avatar
unknown committed
3771 3772
                             test(thd->variables.sql_mode &
                                  MODE_NO_AUTO_CREATE_USER));
3773
    if (error)
unknown's avatar
unknown committed
3774
    {
unknown's avatar
unknown committed
3775
      result= TRUE;				// Remember error
unknown's avatar
unknown committed
3776 3777 3778
      continue;					// Add next user
    }

3779 3780
    db_name= table_list->get_db_name();
    table_name= table_list->get_table_name();
unknown's avatar
VIEW  
unknown committed
3781

unknown's avatar
unknown committed
3782
    /* Find/create cached table grant */
unknown's avatar
VIEW  
unknown committed
3783
    grant_table= table_hash_search(Str->host.str, NullS, db_name,
3784
				   Str->user.str, table_name, 1);
unknown's avatar
unknown committed
3785 3786 3787 3788
    if (!grant_table)
    {
      if (revoke_grant)
      {
unknown's avatar
unknown committed
3789
	my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
3790
                 Str->user.str, Str->host.str, table_list->table_name);
unknown's avatar
unknown committed
3791
	result= TRUE;
unknown's avatar
unknown committed
3792 3793
	continue;
      }
unknown's avatar
VIEW  
unknown committed
3794
      grant_table = new GRANT_TABLE (Str->host.str, db_name,
3795
				     Str->user.str, table_name,
unknown's avatar
unknown committed
3796 3797
				     rights,
				     column_priv);
3798
      if (!grant_table ||
Kristofer Pettersson's avatar
merge  
Kristofer Pettersson committed
3799
        my_hash_insert(&column_priv_hash,(uchar*) grant_table))
unknown's avatar
unknown committed
3800
      {
unknown's avatar
unknown committed
3801
	result= TRUE;				/* purecov: deadcode */
unknown's avatar
unknown committed
3802 3803 3804 3805 3806 3807 3808
	continue;				/* purecov: deadcode */
      }
    }

    /* If revoke_grant, calculate the new column privilege for tables_priv */
    if (revoke_grant)
    {
3809 3810
      class LEX_COLUMN *column;
      List_iterator <LEX_COLUMN> column_iter(columns);
unknown's avatar
unknown committed
3811 3812 3813
      GRANT_COLUMN *grant_column;

      /* Fix old grants */
3814
      while ((column = column_iter++))
unknown's avatar
unknown committed
3815 3816
      {
	grant_column = column_hash_search(grant_table,
3817 3818
					  column->column.ptr(),
					  column->column.length());
unknown's avatar
unknown committed
3819
	if (grant_column)
3820
	  grant_column->rights&= ~(column->rights | rights);
unknown's avatar
unknown committed
3821 3822
      }
      /* scan trough all columns to get new column grant */
3823
      column_priv= 0;
unknown's avatar
unknown committed
3824 3825
      for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
      {
Konstantin Osipov's avatar
Konstantin Osipov committed
3826 3827
        grant_column= (GRANT_COLUMN*)
          my_hash_element(&grant_table->hash_columns, idx);
unknown's avatar
unknown committed
3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839
	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
3840
    if (replace_table_table(thd, grant_table, tables[1].table, *Str,
3841
			    db_name, table_name,
unknown's avatar
unknown committed
3842
			    rights, column_priv, revoke_grant))
3843 3844
    {
      /* Should only happen if table is crashed */
unknown's avatar
unknown committed
3845
      result= TRUE;			       /* purecov: deadcode */
unknown's avatar
unknown committed
3846 3847 3848
    }
    else if (tables[2].table)
    {
unknown's avatar
VIEW  
unknown committed
3849
      if ((replace_column_table(grant_table, tables[2].table, *Str,
unknown's avatar
unknown committed
3850
				columns,
3851
				db_name, table_name,
unknown's avatar
unknown committed
3852 3853
				rights, revoke_grant)))
      {
unknown's avatar
unknown committed
3854
	result= TRUE;
unknown's avatar
unknown committed
3855 3856 3857
      }
    }
  }
unknown's avatar
unknown committed
3858
  thd->mem_root= old_root;
Marc Alff's avatar
Marc Alff committed
3859
  mysql_mutex_unlock(&acl_cache->lock);
3860 3861 3862

  if (!result) /* success */
  {
3863
    result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
3864 3865
  }

Marc Alff's avatar
Marc Alff committed
3866
  mysql_rwlock_unlock(&LOCK_grant);
3867 3868

  if (!result) /* success */
3869
    my_ok(thd);
3870

3871
  /* Tables are automatically closed */
3872
  thd->lex->restore_backup_query_tables_list(&backup);
3873
  /* Restore the state of binlog format */
3874
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3875 3876
  if (save_binlog_row_based)
    thd->set_current_stmt_binlog_format_row();
unknown's avatar
unknown committed
3877 3878 3879 3880
  DBUG_RETURN(result);
}


3881
/**
3882
  Store routine level grants in the privilege tables
3883

3884 3885 3886 3887 3888 3889
  @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?
3890

3891 3892 3893
  @return
    @retval FALSE Success.
    @retval TRUE An error occurred.
3894 3895
*/

3896 3897
bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
			 List <LEX_USER> &user_list, ulong rights,
3898
			 bool revoke_grant, bool write_to_binlog)
3899 3900
{
  List_iterator <LEX_USER> str_list (user_list);
3901
  LEX_USER *Str, *tmp_Str;
3902 3903
  TABLE_LIST tables[2];
  bool create_new_users=0, result=0;
3904
  char *db_name, *table_name;
3905
  bool save_binlog_row_based;
3906
  DBUG_ENTER("mysql_routine_grant");
3907 3908 3909

  if (!initialized)
  {
3910 3911
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");
3912 3913 3914 3915
    DBUG_RETURN(TRUE);
  }
  if (rights & ~PROC_ACLS)
  {
3916 3917
    my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
               MYF(0));
3918 3919 3920 3921 3922
    DBUG_RETURN(TRUE);
  }

  if (!revoke_grant)
  {
3923
    if (sp_exist_routines(thd, table_list, is_proc))
3924 3925 3926 3927 3928
      DBUG_RETURN(TRUE);
  }

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

3929 3930 3931 3932
  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);
3933 3934
  tables[0].next_local= tables[0].next_global= tables+1;

3935 3936 3937 3938 3939
  /*
    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.
  */
3940 3941
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
    thd->clear_current_stmt_binlog_format_row();
3942

3943 3944 3945 3946 3947
#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
3948
  if (thd->slave_thread && rpl_filter->is_on())
3949 3950 3951 3952 3953 3954
  {
    /*
      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
3955
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
3956 3957
    {
      /* Restore the state of binlog format */
3958
      DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3959 3960
      if (save_binlog_row_based)
        thd->set_current_stmt_binlog_format_row();
3961
      DBUG_RETURN(FALSE);
3962
    }
3963 3964 3965
  }
#endif

3966
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
3967
  {						// Should never happen
3968
    /* Restore the state of binlog format */
3969
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3970 3971
    if (save_binlog_row_based)
      thd->set_current_stmt_binlog_format_row();
3972 3973 3974 3975 3976
    DBUG_RETURN(TRUE);
  }

  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
Marc Alff's avatar
Marc Alff committed
3977 3978
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
3979 3980 3981 3982 3983
  MEM_ROOT *old_root= thd->mem_root;
  thd->mem_root= &memex;

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

3984
  while ((tmp_Str= str_list++))
3985 3986 3987
  {
    int error;
    GRANT_NAME *grant_name;
3988 3989 3990 3991 3992
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
    }  
3993 3994
    /* Create user if needed */
    error=replace_user_table(thd, tables[0].table, *Str,
unknown's avatar
unknown committed
3995
			     0, revoke_grant, create_new_users,
unknown's avatar
unknown committed
3996 3997
                             test(thd->variables.sql_mode &
                                  MODE_NO_AUTO_CREATE_USER));
3998 3999 4000 4001 4002 4003 4004
    if (error)
    {
      result= TRUE;				// Remember error
      continue;					// Add next user
    }

    db_name= table_list->db;
4005
    table_name= table_list->table_name;
4006

4007 4008
    grant_name= routine_hash_search(Str->host.str, NullS, db_name,
                                    Str->user.str, table_name, is_proc, 1);
4009 4010 4011 4012
    if (!grant_name)
    {
      if (revoke_grant)
      {
4013 4014
        my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
	         Str->user.str, Str->host.str, table_name);
4015 4016 4017 4018
	result= TRUE;
	continue;
      }
      grant_name= new GRANT_NAME(Str->host.str, db_name,
4019
				 Str->user.str, table_name,
4020
				 rights, TRUE);
4021
      if (!grant_name ||
Kristofer Pettersson's avatar
merge  
Kristofer Pettersson committed
4022 4023
        my_hash_insert(is_proc ?
                       &proc_priv_hash : &func_priv_hash,(uchar*) grant_name))
4024 4025 4026 4027 4028
      {
        result= TRUE;
	continue;
      }
    }
4029

4030
    if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
4031 4032
                              db_name, table_name, is_proc, rights, 
                              revoke_grant) != 0)
4033 4034 4035 4036 4037 4038
    {
      result= TRUE;
      continue;
    }
  }
  thd->mem_root= old_root;
Marc Alff's avatar
Marc Alff committed
4039
  mysql_mutex_unlock(&acl_cache->lock);
4040 4041

  if (write_to_binlog)
4042
  {
4043
    if (write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
4044
      result= TRUE;
4045 4046
  }

Marc Alff's avatar
Marc Alff committed
4047
  mysql_rwlock_unlock(&LOCK_grant);
4048
  /* Restore the state of binlog format */
4049
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4050 4051
  if (save_binlog_row_based)
    thd->set_current_stmt_binlog_format_row();
4052

4053 4054 4055 4056 4057
  /* Tables are automatically closed */
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
4058
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
4059
                 ulong rights, bool revoke_grant, bool is_proxy)
unknown's avatar
unknown committed
4060 4061
{
  List_iterator <LEX_USER> str_list (list);
4062
  LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
4063
  char tmp_db[SAFE_NAME_LEN+1];
unknown's avatar
unknown committed
4064
  bool create_new_users=0;
unknown's avatar
unknown committed
4065
  TABLE_LIST tables[2];
4066
  bool save_binlog_row_based;
unknown's avatar
unknown committed
4067 4068 4069
  DBUG_ENTER("mysql_grant");
  if (!initialized)
  {
unknown's avatar
unknown committed
4070 4071
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
             "--skip-grant-tables");	/* purecov: tested */
unknown's avatar
unknown committed
4072
    DBUG_RETURN(TRUE);				/* purecov: tested */
unknown's avatar
unknown committed
4073 4074
  }

unknown's avatar
unknown committed
4075 4076
  if (lower_case_table_names && db)
  {
4077 4078 4079 4080 4081 4082
    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);
    }
4083
    my_casedn_str(files_charset_info, tmp_db);
unknown's avatar
unknown committed
4084 4085
    db=tmp_db;
  }
unknown's avatar
unknown committed
4086

4087 4088
  if (is_proxy)
  {
4089
    DBUG_ASSERT(!db);
4090 4091 4092
    proxied_user= str_list++;
  }

4093
  /* open the mysql.user and mysql.db or mysql.proxies_priv tables */
4094 4095
  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("user"), "user", TL_WRITE);
4096 4097 4098
  if (is_proxy)

    tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
4099 4100
                             C_STRING_WITH_LEN("proxies_priv"),
                             "proxies_priv", 
4101 4102 4103 4104 4105 4106
                             TL_WRITE);
  else
    tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
                             C_STRING_WITH_LEN("db"), 
                             "db", 
                             TL_WRITE);
unknown's avatar
VIEW  
unknown committed
4107
  tables[0].next_local= tables[0].next_global= tables+1;
4108

4109 4110 4111 4112 4113
  /*
    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.
  */
4114 4115
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
    thd->clear_current_stmt_binlog_format_row();
4116

4117 4118 4119 4120 4121
#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
4122
  if (thd->slave_thread && rpl_filter->is_on())
4123
  {
unknown's avatar
unknown committed
4124 4125 4126
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
4127
    */
4128
    tables[0].updating= tables[1].updating= 1;
unknown's avatar
unknown committed
4129
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
4130 4131
    {
      /* Restore the state of binlog format */
4132
      DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4133 4134
      if (save_binlog_row_based)
        thd->set_current_stmt_binlog_format_row();
unknown's avatar
unknown committed
4135
      DBUG_RETURN(FALSE);
4136
    }
4137
  }
4138 4139
#endif

4140
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
unknown's avatar
unknown committed
4141
  {						// This should never happen
4142
    /* Restore the state of binlog format */
4143
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4144 4145
    if (save_binlog_row_based)
      thd->set_current_stmt_binlog_format_row();
unknown's avatar
unknown committed
4146
    DBUG_RETURN(TRUE);				/* purecov: deadcode */
unknown's avatar
unknown committed
4147 4148
  }

unknown's avatar
unknown committed
4149 4150
  if (!revoke_grant)
    create_new_users= test_if_create_new_users(thd);
unknown's avatar
unknown committed
4151

4152
  /* go through users in user_list */
Marc Alff's avatar
Marc Alff committed
4153 4154
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
unknown's avatar
unknown committed
4155 4156 4157
  grant_version++;

  int result=0;
4158
  while ((tmp_Str = str_list++))
unknown's avatar
unknown committed
4159
  {
4160 4161 4162 4163
    if (!(Str= get_current_user(thd, tmp_Str)))
    {
      result= TRUE;
      continue;
unknown's avatar
unknown committed
4164
    }
4165 4166 4167 4168 4169 4170 4171
    /*
      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
4172 4173
    if (replace_user_table(thd, tables[0].table, *Str,
                           (!db ? rights : 0), revoke_grant, create_new_users,
unknown's avatar
unknown committed
4174 4175
                           test(thd->variables.sql_mode &
                                MODE_NO_AUTO_CREATE_USER)))
4176
      result= -1;
unknown's avatar
unknown committed
4177
    else if (db)
unknown's avatar
unknown committed
4178
    {
unknown's avatar
unknown committed
4179 4180 4181 4182 4183 4184 4185 4186 4187
      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
4188
	my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
unknown's avatar
unknown committed
4189
	result= -1;
unknown's avatar
unknown committed
4190
      }
unknown's avatar
unknown committed
4191
    }
4192 4193
    else if (is_proxy)
    {
4194
      if (replace_proxies_priv_table (thd, tables[1].table, Str, proxied_user,
4195 4196 4197 4198
                                    rights & GRANT_ACL ? TRUE : FALSE, 
                                    revoke_grant))
        result= -1;
    }
unknown's avatar
unknown committed
4199
  }
Marc Alff's avatar
Marc Alff committed
4200
  mysql_mutex_unlock(&acl_cache->lock);
4201 4202 4203

  if (!result)
  {
4204
    result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
4205 4206
  }

Marc Alff's avatar
Marc Alff committed
4207
  mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4208 4209

  if (!result)
4210
    my_ok(thd);
4211
  /* Restore the state of binlog format */
4212
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4213 4214
  if (save_binlog_row_based)
    thd->set_current_stmt_binlog_format_row();
4215

unknown's avatar
unknown committed
4216 4217 4218
  DBUG_RETURN(result);
}

unknown's avatar
unknown committed
4219 4220

/* Free grant array if possible */
unknown's avatar
unknown committed
4221 4222 4223 4224

void  grant_free(void)
{
  DBUG_ENTER("grant_free");
Konstantin Osipov's avatar
Konstantin Osipov committed
4225 4226 4227
  my_hash_free(&column_priv_hash);
  my_hash_free(&proc_priv_hash);
  my_hash_free(&func_priv_hash);
4228
  free_root(&memex,MYF(0));
unknown's avatar
unknown committed
4229 4230 4231 4232
  DBUG_VOID_RETURN;
}


4233 4234 4235
/**
  @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
4236

4237 4238 4239
  @return Error status
    @retval 0 OK
    @retval 1 Could not initialize grant subsystem.
4240
*/
unknown's avatar
unknown committed
4241

4242
my_bool grant_init()
unknown's avatar
unknown committed
4243
{
unknown's avatar
unknown committed
4244
  THD  *thd;
4245 4246 4247 4248 4249
  my_bool return_val;
  DBUG_ENTER("grant_init");

  if (!(thd= new THD))
    DBUG_RETURN(1);				/* purecov: deadcode */
4250
  thd->thread_stack= (char*) &thd;
4251 4252 4253 4254 4255 4256 4257 4258 4259
  thd->store_globals();
  return_val=  grant_reload(thd);
  delete thd;
  /* Remember that we don't have a THD */
  my_pthread_setspecific_ptr(THR_THD,  0);
  DBUG_RETURN(return_val);
}


4260 4261
/**
  @brief Helper function to grant_reload_procs_priv
4262

4263
  Reads the procs_priv table into memory hash.
4264

4265 4266 4267 4268 4269 4270 4271 4272
  @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
4273 4274
*/

4275
static my_bool grant_load_procs_priv(TABLE *p_table)
4276
{
unknown's avatar
unknown committed
4277
  MEM_ROOT *memex_ptr;
4278
  my_bool return_val= 1;
unknown's avatar
SCRUM  
unknown committed
4279
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
4280 4281
  MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
                                                           THR_MALLOC);
4282
  DBUG_ENTER("grant_load_procs_priv");
4283
  (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
Konstantin Osipov's avatar
Konstantin Osipov committed
4284 4285
                      0,0,0, (my_hash_get_key) get_grant_table,
                      0,0);
4286
  (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
Konstantin Osipov's avatar
Konstantin Osipov committed
4287 4288
                      0,0,0, (my_hash_get_key) get_grant_table,
                      0,0);
4289 4290 4291 4292

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

4293
  p_table->use_all_columns();
4294

4295
  if (!p_table->file->ha_index_first(p_table->record[0]))
unknown's avatar
unknown committed
4296
  {
4297 4298 4299 4300
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
    {
4301 4302
      GRANT_NAME *mem_check;
      HASH *hash;
4303
      if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE)))
4304
      {
4305 4306
        /* This could only happen if we are out memory */
        goto end_unlock;
4307
      }
unknown's avatar
unknown committed
4308

4309 4310
      if (check_no_resolve)
      {
4311
	if (hostname_requires_resolving(mem_check->host.hostname))
4312
	{
4313
          sql_print_warning("'procs_priv' entry '%s %s@%s' "
4314
                            "ignored in --skip-name-resolve mode.",
4315
                            mem_check->tname, mem_check->user,
4316 4317
                            mem_check->host.hostname ?
                            mem_check->host.hostname : "");
4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335
          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;
4336 4337
      }

4338
      mem_check->privs= fix_rights_for_procedure(mem_check->privs);
4339
      if (! mem_check->ok())
4340 4341
        delete mem_check;
      else if (my_hash_insert(hash, (uchar*) mem_check))
4342
      {
4343 4344
        delete mem_check;
        goto end_unlock;
4345 4346
      }
    }
4347
    while (!p_table->file->ha_index_next(p_table->record[0]));
4348
  }
4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373
  /* 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
*/

4374
static my_bool grant_load(THD *thd, TABLE_LIST *tables)
4375 4376 4377 4378 4379 4380 4381
{
  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);
4382
  ulonglong old_sql_mode= thd->variables.sql_mode;
4383
  DBUG_ENTER("grant_load");
4384 4385 4386

  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;

4387
  (void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
Konstantin Osipov's avatar
Konstantin Osipov committed
4388 4389
                      0,0,0, (my_hash_get_key) get_grant_table,
                      (my_hash_free_key) free_grant_table,0);
4390 4391 4392

  t_table = tables[0].table;
  c_table = tables[1].table;
4393 4394 4395 4396

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

4397 4398 4399
  t_table->use_all_columns();
  c_table->use_all_columns();

4400
  if (!t_table->file->ha_index_first(t_table->record[0]))
unknown's avatar
unknown committed
4401
  {
4402 4403 4404
    memex_ptr= &memex;
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
    do
unknown's avatar
unknown committed
4405
    {
4406 4407
      GRANT_TABLE *mem_check;
      if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table)))
4408 4409 4410 4411
      {
	/* This could only happen if we are out memory */
	goto end_unlock;
      }
unknown's avatar
SCRUM  
unknown committed
4412

4413
      if (check_no_resolve)
unknown's avatar
SCRUM  
unknown committed
4414
      {
unknown's avatar
unknown committed
4415
	if (hostname_requires_resolving(mem_check->host.hostname))
4416
	{
4417
          sql_print_warning("'tables_priv' entry '%s %s@%s' "
4418
                            "ignored in --skip-name-resolve mode.",
4419 4420
                            mem_check->tname,
                            mem_check->user ? mem_check->user : "",
4421 4422
                            mem_check->host.hostname ?
                            mem_check->host.hostname : "");
4423 4424
	  continue;
	}
unknown's avatar
SCRUM  
unknown committed
4425 4426
      }

4427 4428
      if (! mem_check->ok())
	delete mem_check;
4429
      else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
4430 4431 4432 4433
      {
	delete mem_check;
	goto end_unlock;
      }
unknown's avatar
SCRUM  
unknown committed
4434
    }
4435
    while (!t_table->file->ha_index_next(t_table->record[0]));
unknown's avatar
unknown committed
4436
  }
4437

4438 4439 4440
  return_val=0;					// Return ok

end_unlock:
unknown's avatar
unknown committed
4441
  t_table->file->ha_index_end();
4442
  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
4443 4444
end_index_init:
  thd->variables.sql_mode= old_sql_mode;
4445
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
4446 4447 4448
}


4449 4450 4451
/**
  @brief Helper function to grant_reload. Reloads procs_priv table is it
    exists.
4452

4453
  @param thd A pointer to the thread handler object.
4454
  @param table A pointer to the table list.
4455

4456
  @see grant_reload
4457

4458 4459 4460 4461 4462
  @return Error state
    @retval FALSE Success
    @retval TRUE An error has occurred.
*/

4463
static my_bool grant_reload_procs_priv(THD *thd, TABLE_LIST *table)
4464 4465 4466 4467 4468 4469 4470 4471 4472
{
  HASH old_proc_priv_hash, old_func_priv_hash;
  my_bool return_val= FALSE;
  DBUG_ENTER("grant_reload_procs_priv");

  /* 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;

4473
  if ((return_val= grant_load_procs_priv(table->table)))
4474 4475 4476
  {
    /* Error; Reverting to old hash */
    DBUG_PRINT("error",("Reverting to old privileges"));
4477 4478
    my_hash_free(&proc_priv_hash);
    my_hash_free(&func_priv_hash);
4479 4480 4481 4482 4483
    proc_priv_hash= old_proc_priv_hash;
    func_priv_hash= old_func_priv_hash;
  }
  else
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
4484 4485
    my_hash_free(&old_proc_priv_hash);
    my_hash_free(&old_func_priv_hash);
4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504
  }

  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
4505
*/
unknown's avatar
unknown committed
4506

4507
my_bool grant_reload(THD *thd)
unknown's avatar
unknown committed
4508
{
4509
  TABLE_LIST tables[3];
4510
  HASH old_column_priv_hash;
unknown's avatar
unknown committed
4511
  MEM_ROOT old_mem;
4512
  my_bool return_val= 1;
unknown's avatar
unknown committed
4513 4514
  DBUG_ENTER("grant_reload");

4515 4516 4517 4518
  /* Don't do anything if running with --skip-grant-tables */
  if (!initialized)
    DBUG_RETURN(0);

4519 4520 4521 4522 4523 4524
  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);
4525 4526 4527 4528
  tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
                           C_STRING_WITH_LEN("procs_priv"),
                           "procs_priv", TL_READ);

4529
  tables[0].next_local= tables[0].next_global= tables+1;
4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552
  tables[1].next_local= tables[1].next_global= tables+2;
  tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;

  /*
    Reload will work in the following manner:-

                             proc_priv_hash structure
                              /                     \
                    not initialized                 initialized
                   /               \                     |
    mysql.procs_priv table        Server Startup         |
        is missing                      \                |
             |                         open_and_lock_tables()
    Assume we are working on           /success             \failure
    pre 4.1 system tables.        Normal Scenario.          An error is thrown.
    A warning is printed          Reload column privilege.  Retain the old hash.
    and continue with             Reload function and
    reloading the column          procedure privileges,
    privileges.                   if available.
  */

  if (!(my_hash_inited(&proc_priv_hash)))
    tables[2].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
4553

4554 4555 4556 4557
  /*
    To avoid deadlocks we should obtain table locks before
    obtaining LOCK_grant rwlock.
  */
4558
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
4559 4560 4561 4562 4563 4564
  {
    if (thd->stmt_da->is_error())
    {
      sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
                      thd->stmt_da->message());
    }
4565
    goto end;
4566 4567 4568 4569 4570 4571 4572 4573 4574 4575
  }

  if (tables[2].table == NULL)
  {
    sql_print_warning("Table 'mysql.procs_priv' does not exist. "
                      "Please run mysql_upgrade.");
    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NO_SUCH_TABLE,
                        ER(ER_NO_SUCH_TABLE), tables[2].db,
                        tables[2].table_name);
  }
4576

Marc Alff's avatar
Marc Alff committed
4577
  mysql_rwlock_wrlock(&LOCK_grant);
4578
  old_column_priv_hash= column_priv_hash;
4579 4580 4581 4582 4583

  /*
    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
4584
  old_mem= memex;
4585
  init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
unknown's avatar
unknown committed
4586

4587 4588 4589 4590 4591 4592 4593 4594
  /*
    tables[2].table i.e. procs_priv can be null if we are working with
    pre 4.1 privilage tables
  */
  if ((return_val= (grant_load(thd, tables) ||
                    (tables[2].table != NULL &&
                     grant_reload_procs_priv(thd, &tables[2])))
     ))
unknown's avatar
unknown committed
4595
  {						// Error. Revert to old hash
4596
    DBUG_PRINT("error",("Reverting to old privileges"));
4597 4598
    my_hash_free(&column_priv_hash);
    free_root(&memex,MYF(0));
4599
    column_priv_hash= old_column_priv_hash;	/* purecov: deadcode */
unknown's avatar
unknown committed
4600
    memex= old_mem;				/* purecov: deadcode */
unknown's avatar
unknown committed
4601 4602 4603
  }
  else
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
4604
    my_hash_free(&old_column_priv_hash);
4605
    free_root(&old_mem,MYF(0));
4606
    grant_version++;
unknown's avatar
unknown committed
4607
  }
Marc Alff's avatar
Marc Alff committed
4608
  mysql_rwlock_unlock(&LOCK_grant);
4609 4610

end:
4611
  close_mysql_tables(thd);
4612
  DBUG_RETURN(return_val);
unknown's avatar
unknown committed
4613 4614
}

4615

4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641
/**
  @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

  @note This functions assumes that either number of tables to be inspected
4642 4643 4644 4645
     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).
4646 4647 4648 4649 4650 4651 4652 4653

   @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
4654

unknown's avatar
unknown committed
4655
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
4656
                 bool any_combination_will_do, uint number, bool no_errors)
unknown's avatar
unknown committed
4657
{
4658 4659
  TABLE_LIST *tl;
  TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
4660
  Security_context *sctx= thd->security_ctx;
4661
  uint i;
Marc Alff's avatar
Marc Alff committed
4662
  ulong orig_want_access= want_access;
4663 4664
  DBUG_ENTER("check_grant");
  DBUG_ASSERT(number > 0);
unknown's avatar
unknown committed
4665

4666
  /*
unknown's avatar
unknown committed
4667 4668 4669 4670 4671 4672 4673 4674
    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.
4675
  */
4676 4677 4678
  for (i= 0, tl= tables;
       i < number  && tl != first_not_own_table;
       tl= tl->next_global, i++)
4679
  {
4680 4681 4682 4683
    /*
      Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
      It will be checked during making view.
    */
4684
    tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
4685 4686
  }

Marc Alff's avatar
Marc Alff committed
4687
  mysql_rwlock_rdlock(&LOCK_grant);
4688 4689 4690
  for (tl= tables;
       tl && number-- && tl != first_not_own_table;
       tl= tl->next_global)
unknown's avatar
unknown committed
4691
  {
4692 4693 4694 4695
    TABLE_LIST *const t_ref=
      tl->correspondent_table ? tl->correspondent_table : tl;
    sctx = test(t_ref->security_ctx) ? t_ref->security_ctx :
                                       thd->security_ctx;
4696

4697
    const ACL_internal_table_access *access=
4698 4699 4700
      get_cached_table_access(&t_ref->grant.m_internal,
                              t_ref->get_db_name(),
                              t_ref->get_table_name());
Marc Alff's avatar
Marc Alff committed
4701 4702 4703

    if (access)
    {
4704
      switch(access->check(orig_want_access, &t_ref->grant.privilege))
Marc Alff's avatar
Marc Alff committed
4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723
      {
      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;
4724 4725 4726 4727
    want_access&= ~sctx->master_access;
    if (!want_access)
      continue;                                 // ok

4728 4729
    if (!(~t_ref->grant.privilege & want_access) ||
        t_ref->is_anonymous_derived_table() || t_ref->schema_table)
unknown's avatar
unknown committed
4730
    {
unknown's avatar
VIEW  
unknown committed
4731
      /*
4732
        It is subquery in the FROM clause. VIEW set t_ref->derived after
unknown's avatar
VIEW  
unknown committed
4733 4734
        table opening, but this function always called before table opening.
      */
4735
      if (!t_ref->referencing_view)
4736 4737 4738 4739 4740 4741
      {
        /*
          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.
        */
4742
        t_ref->grant.want_privilege= 0;
4743 4744
      }
      continue;
unknown's avatar
unknown committed
4745
    }
4746
    GRANT_TABLE *grant_table= table_hash_search(sctx->host, sctx->ip,
4747
                                                t_ref->get_db_name(),
4748
                                                sctx->priv_user,
4749
                                                t_ref->get_table_name(),
4750 4751 4752
                                                FALSE);

    if (!grant_table)
unknown's avatar
unknown committed
4753
    {
4754
      want_access &= ~t_ref->grant.privilege;
unknown's avatar
unknown committed
4755 4756
      goto err;					// No grants
    }
4757 4758 4759 4760 4761 4762 4763

    /*
      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
4764

4765 4766 4767 4768
    t_ref->grant.grant_table= grant_table; // Remember for column test
    t_ref->grant.version= grant_version;
    t_ref->grant.privilege|= grant_table->privs;
    t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege);
unknown's avatar
unknown committed
4769

4770
    if (!(~t_ref->grant.privilege & want_access))
unknown's avatar
unknown committed
4771 4772
      continue;

4773
    if (want_access & ~(grant_table->cols | t_ref->grant.privilege))
unknown's avatar
unknown committed
4774
    {
4775
      want_access &= ~(grant_table->cols | t_ref->grant.privilege);
unknown's avatar
unknown committed
4776 4777 4778
      goto err;					// impossible
    }
  }
Marc Alff's avatar
Marc Alff committed
4779
  mysql_rwlock_unlock(&LOCK_grant);
4780
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
4781

4782
err:
Marc Alff's avatar
Marc Alff committed
4783
  mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4784
  if (!no_errors)				// Not a silent skip of table
unknown's avatar
unknown committed
4785
  {
4786 4787
    char command[128];
    get_privilege_desc(command, sizeof(command), want_access);
4788 4789
    status_var_increment(thd->status_var.access_denied_errors);

4790 4791
    my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
             command,
4792 4793
             sctx->priv_user,
             sctx->host_or_ip,
4794
             tl ? tl->get_table_name() : "unknown");
unknown's avatar
unknown committed
4795
  }
4796 4797 4798 4799
  DBUG_RETURN(TRUE);
}


4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817
/*
  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
4818
bool check_grant_column(THD *thd, GRANT_INFO *grant,
4819
			const char *db_name, const char *table_name,
4820
			const char *name, uint length,  Security_context *sctx)
unknown's avatar
unknown committed
4821 4822 4823
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
unknown's avatar
VIEW  
unknown committed
4824
  ulong want_access= grant->want_privilege & ~grant->privilege;
unknown's avatar
unknown committed
4825
  DBUG_ENTER("check_grant_column");
unknown's avatar
unknown committed
4826
  DBUG_PRINT("enter", ("table: %s  want_access: %lu", table_name, want_access));
unknown's avatar
unknown committed
4827

unknown's avatar
unknown committed
4828
  if (!want_access)
unknown's avatar
unknown committed
4829
    DBUG_RETURN(0);				// Already checked
unknown's avatar
unknown committed
4830

Marc Alff's avatar
Marc Alff committed
4831
  mysql_rwlock_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
4832

4833
  /* reload table if someone has modified any grants */
unknown's avatar
unknown committed
4834

unknown's avatar
VIEW  
unknown committed
4835
  if (grant->version != grant_version)
unknown's avatar
unknown committed
4836
  {
unknown's avatar
VIEW  
unknown committed
4837
    grant->grant_table=
4838 4839
      table_hash_search(sctx->host, sctx->ip, db_name,
			sctx->priv_user,
unknown's avatar
unknown committed
4840
			table_name, 0);         /* purecov: inspected */
unknown's avatar
VIEW  
unknown committed
4841
    grant->version= grant_version;		/* purecov: inspected */
unknown's avatar
unknown committed
4842
  }
unknown's avatar
VIEW  
unknown committed
4843
  if (!(grant_table= grant->grant_table))
unknown's avatar
unknown committed
4844 4845 4846 4847 4848
    goto err;					/* purecov: deadcode */

  grant_column=column_hash_search(grant_table, name, length);
  if (grant_column && !(~grant_column->rights & want_access))
  {
Marc Alff's avatar
Marc Alff committed
4849
    mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
4850
    DBUG_RETURN(0);
unknown's avatar
unknown committed
4851 4852
  }

4853
err:
Marc Alff's avatar
Marc Alff committed
4854
  mysql_rwlock_unlock(&LOCK_grant);
4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898
  char command[128];
  get_privilege_desc(command, sizeof(command), want_access);
  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
           command,
           sctx->priv_user,
           sctx->host_or_ip,
           name,
           table_name);
  DBUG_RETURN(1);
}


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

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

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

  RETURN
    FALSE OK
    TRUE  access denied
*/

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

  if (table_ref->view || table_ref->field_translation)
unknown's avatar
unknown committed
4899
  {
4900
    /* View or derived information schema table. */
4901
    ulong view_privs;
4902 4903 4904
    grant= &(table_ref->grant);
    db_name= table_ref->view_db.str;
    table_name= table_ref->view_name.str;
4905
    if (table_ref->belong_to_view && 
4906
        thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917
    {
      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
4918
  }
4919 4920 4921 4922 4923
  else
  {
    /* Normal or temporary table. */
    TABLE *table= table_ref->table;
    grant= &(table->grant);
unknown's avatar
unknown committed
4924 4925
    db_name= table->s->db.str;
    table_name= table->s->table_name.str;
4926 4927 4928 4929 4930 4931 4932 4933
  }

  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
4934 4935 4936
}


4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947
/** 
  @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
  @details This function walks over the columns of a table reference 
   The columns may originate from different tables, depending on the kind of
4948
   table reference, e.g. join, view.
4949 4950 4951 4952 4953
   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.
*/    
bool check_grant_all_columns(THD *thd, ulong want_access_arg, 
                             Field_iterator_table_ref *fields)
unknown's avatar
unknown committed
4954
{
4955
  Security_context *sctx= thd->security_ctx;
4956 4957
  ulong want_access= want_access_arg;
  const char *table_name= NULL;
unknown's avatar
unknown committed
4958

4959 4960
  const char* db_name; 
  GRANT_INFO *grant;
4961 4962
  /* Initialized only to make gcc happy */
  GRANT_TABLE *grant_table= NULL;
4963 4964 4965 4966 4967
  /* 
     Flag that gets set if privilege checking has to be performed on column
     level.
  */
  bool using_column_privileges= FALSE;
unknown's avatar
unknown committed
4968

Marc Alff's avatar
Marc Alff committed
4969
  mysql_rwlock_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
4970

4971
  for (; !fields->end_of_fields(); fields->next())
unknown's avatar
unknown committed
4972
  {
unknown's avatar
VIEW  
unknown committed
4973
    const char *field_name= fields->name();
unknown's avatar
unknown committed
4974

4975
    if (table_name != fields->get_table_name())
4976
    {
4977 4978
      table_name= fields->get_table_name();
      db_name= fields->get_db_name();
4979 4980 4981 4982
      grant= fields->grant();
      /* get a fresh one for each table */
      want_access= want_access_arg & ~grant->privilege;
      if (want_access)
4983
      {
4984 4985
        /* reload table if someone has modified any grants */
        if (grant->version != grant_version)
4986
        {
4987 4988 4989 4990 4991
          grant->grant_table=
            table_hash_search(sctx->host, sctx->ip, db_name,
                              sctx->priv_user,
                              table_name, 0);	/* purecov: inspected */
          grant->version= grant_version;	/* purecov: inspected */
4992 4993
        }

4994 4995
        grant_table= grant->grant_table;
        DBUG_ASSERT (grant_table);
4996 4997
      }
    }
unknown's avatar
unknown committed
4998

4999 5000 5001 5002 5003
    if (want_access)
    {
      GRANT_COLUMN *grant_column= 
        column_hash_search(grant_table, field_name,
                           (uint) strlen(field_name));
5004 5005
      if (grant_column)
        using_column_privileges= TRUE;
5006 5007 5008
      if (!grant_column || (~grant_column->rights & want_access))
        goto err;
    }
unknown's avatar
unknown committed
5009
  }
Marc Alff's avatar
Marc Alff committed
5010
  mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
5011 5012
  return 0;

unknown's avatar
unknown committed
5013
err:
Marc Alff's avatar
Marc Alff committed
5014
  mysql_rwlock_unlock(&LOCK_grant);
5015

5016 5017
  char command[128];
  get_privilege_desc(command, sizeof(command), want_access);
5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032
  /*
    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,
             sctx->host_or_ip, table_name); 
  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
5033 5034 5035 5036
  return 1;
}


5037 5038 5039 5040 5041 5042
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
5043
    GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056

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


5057
/*
unknown's avatar
unknown committed
5058
  Check if a user has the right to access a database
5059
  Access is accepted if he has a grant for any table/routine in the database
unknown's avatar
unknown committed
5060
  Return 1 if access is denied
5061
*/
unknown's avatar
unknown committed
5062 5063 5064

bool check_grant_db(THD *thd,const char *db)
{
5065
  Security_context *sctx= thd->security_ctx;
5066
  char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end;
unknown's avatar
unknown committed
5067
  uint len;
5068
  bool error= TRUE;
unknown's avatar
unknown committed
5069

5070 5071 5072 5073 5074 5075 5076
  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;
5077

Marc Alff's avatar
Marc Alff committed
5078
  mysql_rwlock_rdlock(&LOCK_grant);
unknown's avatar
unknown committed
5079

5080
  for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
unknown's avatar
unknown committed
5081
  {
Konstantin Osipov's avatar
Konstantin Osipov committed
5082 5083 5084
    GRANT_TABLE *grant_table= (GRANT_TABLE*)
      my_hash_element(&column_priv_hash,
                      idx);
unknown's avatar
unknown committed
5085 5086
    if (len < grant_table->key_length &&
	!memcmp(grant_table->hash_key,helping,len) &&
5087
        compare_hostname(&grant_table->host, sctx->host, sctx->ip))
unknown's avatar
unknown committed
5088
    {
5089
      error= FALSE; /* Found match. */
unknown's avatar
unknown committed
5090 5091 5092
      break;
    }
  }
5093 5094 5095 5096 5097

  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
5098
  mysql_rwlock_unlock(&LOCK_grant);
5099

unknown's avatar
unknown committed
5100 5101 5102
  return error;
}

5103 5104

/****************************************************************************
5105
  Check routine level grants
5106 5107

  SYNPOSIS
5108
   bool check_grant_routine()
5109 5110
   thd		Thread handler
   want_access  Bits of privileges user needs to have
5111 5112
   procs	List of routines to check. The user should have 'want_access'
   is_proc	True if the list is all procedures, else functions
5113 5114 5115 5116 5117 5118 5119 5120
   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
****************************************************************************/

5121
bool check_grant_routine(THD *thd, ulong want_access,
5122
			 TABLE_LIST *procs, bool is_proc, bool no_errors)
5123 5124
{
  TABLE_LIST *table;
5125
  Security_context *sctx= thd->security_ctx;
5126 5127
  char *user= sctx->priv_user;
  char *host= sctx->priv_host;
5128
  DBUG_ENTER("check_grant_routine");
5129

5130
  want_access&= ~sctx->master_access;
5131 5132 5133
  if (!want_access)
    DBUG_RETURN(0);                             // ok

Marc Alff's avatar
Marc Alff committed
5134
  mysql_rwlock_rdlock(&LOCK_grant);
5135 5136 5137
  for (table= procs; table; table= table->next_global)
  {
    GRANT_NAME *grant_proc;
5138
    if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
5139
					 table->table_name, is_proc, 0)))
5140 5141 5142 5143 5144 5145 5146 5147
      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
5148
  mysql_rwlock_unlock(&LOCK_grant);
5149 5150
  DBUG_RETURN(0);
err:
Marc Alff's avatar
Marc Alff committed
5151
  mysql_rwlock_unlock(&LOCK_grant);
5152 5153 5154 5155 5156
  if (!no_errors)
  {
    char buff[1024];
    const char *command="";
    if (table)
5157
      strxmov(buff, table->db, ".", table->table_name, NullS);
5158 5159 5160
    if (want_access & EXECUTE_ACL)
      command= "execute";
    else if (want_access & ALTER_PROC_ACL)
5161
      command= "alter routine";
5162 5163 5164 5165 5166 5167 5168 5169 5170
    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);
}


5171 5172
/*
  Check if routine has any of the 
5173
  routine level grants
5174 5175 5176 5177 5178 5179 5180 5181 5182
  
  SYNPOSIS
   bool    check_routine_level_acl()
   thd	        Thread handler
   db           Database name
   name         Routine name

  RETURN
   0            Ok 
5183
   1            error
5184 5185
*/

unknown's avatar
unknown committed
5186 5187
bool check_routine_level_acl(THD *thd, const char *db, const char *name, 
                             bool is_proc)
5188 5189
{
  bool no_routine_acl= 1;
5190 5191
  GRANT_NAME *grant_proc;
  Security_context *sctx= thd->security_ctx;
Marc Alff's avatar
Marc Alff committed
5192
  mysql_rwlock_rdlock(&LOCK_grant);
5193 5194 5195 5196 5197
  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
5198
  mysql_rwlock_unlock(&LOCK_grant);
5199 5200 5201 5202
  return no_routine_acl;
}


unknown's avatar
unknown committed
5203
/*****************************************************************************
unknown's avatar
unknown committed
5204
  Functions to retrieve the grant for a table/column  (for SHOW functions)
unknown's avatar
unknown committed
5205 5206
*****************************************************************************/

unknown's avatar
unknown committed
5207
ulong get_table_grant(THD *thd, TABLE_LIST *table)
unknown's avatar
unknown committed
5208
{
unknown's avatar
unknown committed
5209
  ulong privilege;
5210 5211
  Security_context *sctx= thd->security_ctx;
  const char *db = table->db ? table->db : thd->db;
unknown's avatar
unknown committed
5212 5213
  GRANT_TABLE *grant_table;

5214
  mysql_rwlock_rdlock(&LOCK_grant);
5215 5216 5217
#ifdef EMBEDDED_LIBRARY
  grant_table= NULL;
#else
5218
  grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
5219
				 table->table_name, 0);
5220
#endif
unknown's avatar
unknown committed
5221 5222 5223 5224
  table->grant.grant_table=grant_table; // Remember for column test
  table->grant.version=grant_version;
  if (grant_table)
    table->grant.privilege|= grant_table->privs;
5225
  privilege= table->grant.privilege;
Marc Alff's avatar
Marc Alff committed
5226
  mysql_rwlock_unlock(&LOCK_grant);
5227
  return privilege;
unknown's avatar
unknown committed
5228 5229 5230
}


unknown's avatar
unknown committed
5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248
/*
  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
5249 5250 5251
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
5252 5253 5254
{
  GRANT_TABLE *grant_table;
  GRANT_COLUMN *grant_column;
unknown's avatar
unknown committed
5255
  ulong priv;
unknown's avatar
unknown committed
5256

Marc Alff's avatar
Marc Alff committed
5257
  mysql_rwlock_rdlock(&LOCK_grant);
5258
  /* reload table if someone has modified any grants */
unknown's avatar
VIEW  
unknown committed
5259
  if (grant->version != grant_version)
unknown's avatar
unknown committed
5260
  {
5261
    Security_context *sctx= thd->security_ctx;
unknown's avatar
VIEW  
unknown committed
5262
    grant->grant_table=
5263 5264
      table_hash_search(sctx->host, sctx->ip,
                        db_name, sctx->priv_user,
unknown's avatar
VIEW  
unknown committed
5265 5266
			table_name, 0);	        /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
unknown's avatar
unknown committed
5267 5268
  }

unknown's avatar
VIEW  
unknown committed
5269 5270
  if (!(grant_table= grant->grant_table))
    priv= grant->privilege;
unknown's avatar
unknown committed
5271 5272
  else
  {
unknown's avatar
VIEW  
unknown committed
5273 5274
    grant_column= column_hash_search(grant_table, field_name,
                                     (uint) strlen(field_name));
unknown's avatar
unknown committed
5275
    if (!grant_column)
5276
      priv= (grant->privilege | grant_table->privs);
unknown's avatar
unknown committed
5277
    else
5278
      priv= (grant->privilege | grant_table->privs | grant_column->rights);
unknown's avatar
unknown committed
5279
  }
Marc Alff's avatar
Marc Alff committed
5280
  mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
5281 5282 5283
  return priv;
}

unknown's avatar
VIEW  
unknown committed
5284

5285
/* Help function for mysql_show_grants */
unknown's avatar
unknown committed
5286

5287 5288
static void add_user_option(String *grant, long value, const char *name,
                            my_bool is_signed)
5289 5290 5291 5292 5293 5294 5295
{
  if (value)
  {
    char buff[22], *p; // just as in int2str
    grant->append(' ');
    grant->append(name, strlen(name));
    grant->append(' ');
5296
    p=int10_to_str(value, buff, is_signed ? -10 : 10);
5297 5298 5299
    grant->append(buff,p-buff);
  }
}
unknown's avatar
unknown committed
5300 5301

static const char *command_array[]=
unknown's avatar
unknown committed
5302
{
unknown's avatar
VIEW  
unknown committed
5303 5304 5305 5306
  "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",
5307
  "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
5308
  "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE"
unknown's avatar
unknown committed
5309
};
5310

unknown's avatar
unknown committed
5311 5312
static uint command_lengths[]=
{
5313
  6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
5314
  14, 13, 11, 5, 7, 17
unknown's avatar
unknown committed
5315 5316
};

unknown's avatar
unknown committed
5317

5318 5319 5320 5321 5322
static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
                               const char *type, int typelen,
                               char *buff, int buffsize);


5323 5324 5325 5326 5327 5328 5329
/*
  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
5330
bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
unknown's avatar
unknown committed
5331
{
unknown's avatar
unknown committed
5332 5333
  ulong want_access;
  uint counter,index;
unknown's avatar
unknown committed
5334
  int  error = 0;
unknown's avatar
unknown committed
5335 5336
  ACL_USER *acl_user;
  ACL_DB *acl_db;
unknown's avatar
unknown committed
5337
  char buff[1024];
5338
  Protocol *protocol= thd->protocol;
unknown's avatar
unknown committed
5339
  DBUG_ENTER("mysql_show_grants");
unknown's avatar
unknown committed
5340 5341 5342 5343

  LINT_INIT(acl_user);
  if (!initialized)
  {
unknown's avatar
unknown committed
5344
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
unknown's avatar
unknown committed
5345
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
5346
  }
unknown's avatar
unknown committed
5347

Marc Alff's avatar
Marc Alff committed
5348 5349
  mysql_rwlock_rdlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
5350 5351 5352

  acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE);
  if (!acl_user)
unknown's avatar
unknown committed
5353
  {
Marc Alff's avatar
Marc Alff committed
5354 5355
    mysql_mutex_unlock(&acl_cache->lock);
    mysql_rwlock_unlock(&LOCK_grant);
5356

unknown's avatar
unknown committed
5357 5358
    my_error(ER_NONEXISTING_GRANT, MYF(0),
             lex_user->user.str, lex_user->host.str);
unknown's avatar
unknown committed
5359
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
5360 5361
  }

unknown's avatar
unknown committed
5362
  Item_string *field=new Item_string("",0,&my_charset_latin1);
unknown's avatar
unknown committed
5363 5364 5365 5366 5367 5368
  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);
5369
  if (protocol->send_result_set_metadata(&field_list,
5370
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
5371
  {
Marc Alff's avatar
Marc Alff committed
5372 5373
    mysql_mutex_unlock(&acl_cache->lock);
    mysql_rwlock_unlock(&LOCK_grant);
unknown's avatar
unknown committed
5374

5375 5376
    DBUG_RETURN(TRUE);
  }
unknown's avatar
unknown committed
5377 5378 5379

  /* Add first global access grants */
  {
5380
    String global(buff,sizeof(buff),system_charset_info);
unknown's avatar
unknown committed
5381
    global.length(0);
5382
    global.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
5383

unknown's avatar
unknown committed
5384
    want_access= acl_user->access;
unknown's avatar
unknown committed
5385
    if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
5386
      global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
5387
    else if (!(want_access & ~GRANT_ACL))
5388
      global.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
5389
    else
unknown's avatar
unknown committed
5390 5391
    {
      bool found=0;
unknown's avatar
unknown committed
5392
      ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
5393 5394
      for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
      {
unknown's avatar
unknown committed
5395
	if (test_access & j)
unknown's avatar
unknown committed
5396 5397
	{
	  if (found)
5398
	    global.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
5399 5400 5401 5402 5403
	  found=1;
	  global.append(command_array[counter],command_lengths[counter]);
	}
      }
    }
5404
    global.append (STRING_WITH_LEN(" ON *.* TO '"));
5405 5406
    global.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
5407
    global.append (STRING_WITH_LEN("'@'"));
5408 5409
    global.append(lex_user->host.str,lex_user->host.length,
		  system_charset_info);
unknown's avatar
unknown committed
5410
    global.append ('\'');
5411 5412
    if (acl_user->plugin.str == native_password_plugin_name.str ||
        acl_user->plugin.str == old_password_plugin_name.str)
unknown's avatar
unknown committed
5413
    {
5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431
      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
5432
    }
5433 5434
    /* "show grants" SSL related stuff */
    if (acl_user->ssl_type == SSL_TYPE_ANY)
5435
      global.append(STRING_WITH_LEN(" REQUIRE SSL"));
5436
    else if (acl_user->ssl_type == SSL_TYPE_X509)
5437
      global.append(STRING_WITH_LEN(" REQUIRE X509"));
5438
    else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
5439
    {
5440
      int ssl_options = 0;
5441
      global.append(STRING_WITH_LEN(" REQUIRE "));
5442 5443
      if (acl_user->x509_issuer)
      {
5444
	ssl_options++;
5445
	global.append(STRING_WITH_LEN("ISSUER \'"));
5446
	global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
5447
	global.append('\'');
5448
      }
5449 5450
      if (acl_user->x509_subject)
      {
5451 5452
	if (ssl_options++)
	  global.append(' ');
5453
	global.append(STRING_WITH_LEN("SUBJECT \'"));
5454 5455
	global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
                      system_charset_info);
5456
	global.append('\'');
unknown's avatar
unknown committed
5457
      }
5458 5459
      if (acl_user->ssl_cipher)
      {
5460 5461
	if (ssl_options++)
	  global.append(' ');
5462
	global.append(STRING_WITH_LEN("CIPHER '"));
5463 5464
	global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
                      system_charset_info);
5465
	global.append('\'');
5466 5467
      }
    }
unknown's avatar
unknown committed
5468
    if ((want_access & GRANT_ACL) ||
5469 5470 5471 5472
	(acl_user->user_resource.questions ||
         acl_user->user_resource.updates ||
         acl_user->user_resource.conn_per_hour ||
         acl_user->user_resource.user_conn))
5473
    {
5474
      global.append(STRING_WITH_LEN(" WITH"));
unknown's avatar
unknown committed
5475
      if (want_access & GRANT_ACL)
5476
	global.append(STRING_WITH_LEN(" GRANT OPTION"));
5477
      add_user_option(&global, acl_user->user_resource.questions,
5478
		      "MAX_QUERIES_PER_HOUR", 0);
5479
      add_user_option(&global, acl_user->user_resource.updates,
5480
		      "MAX_UPDATES_PER_HOUR", 0);
5481
      add_user_option(&global, acl_user->user_resource.conn_per_hour,
5482
		      "MAX_CONNECTIONS_PER_HOUR", 0);
5483
      add_user_option(&global, acl_user->user_resource.user_conn,
5484
		      "MAX_USER_CONNECTIONS", 1);
unknown's avatar
unknown committed
5485
    }
5486
    protocol->prepare_for_resend();
5487
    protocol->store(global.ptr(),global.length(),global.charset());
5488
    if (protocol->write())
unknown's avatar
unknown committed
5489
    {
unknown's avatar
unknown committed
5490
      error= -1;
5491
      goto end;
unknown's avatar
unknown committed
5492 5493 5494 5495 5496 5497
    }
  }

  /* Add database access */
  for (counter=0 ; counter < acl_dbs.elements ; counter++)
  {
unknown's avatar
unknown committed
5498
    const char *user, *host;
unknown's avatar
unknown committed
5499 5500 5501

    acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
    if (!(user=acl_db->user))
unknown's avatar
unknown committed
5502
      user= "";
unknown's avatar
unknown committed
5503
    if (!(host=acl_db->host.hostname))
unknown's avatar
unknown committed
5504
      host= "";
unknown's avatar
unknown committed
5505

5506 5507 5508 5509 5510 5511 5512
    /*
      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
5513
    if (!strcmp(lex_user->user.str,user) &&
5514
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
unknown's avatar
unknown committed
5515 5516
    {
      want_access=acl_db->access;
unknown's avatar
unknown committed
5517
      if (want_access)
unknown's avatar
unknown committed
5518
      {
5519
	String db(buff,sizeof(buff),system_charset_info);
unknown's avatar
unknown committed
5520
	db.length(0);
5521
	db.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
5522 5523

	if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
5524
	  db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
5525
	else if (!(want_access & ~GRANT_ACL))
5526
	  db.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
5527 5528 5529
	else
	{
	  int found=0, cnt;
unknown's avatar
unknown committed
5530
	  ulong j,test_access= want_access & ~GRANT_ACL;
unknown's avatar
unknown committed
5531 5532 5533 5534 5535
	  for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
	  {
	    if (test_access & j)
	    {
	      if (found)
5536
		db.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
5537 5538 5539 5540 5541
	      found = 1;
	      db.append(command_array[cnt],command_lengths[cnt]);
	    }
	  }
	}
5542
	db.append (STRING_WITH_LEN(" ON "));
5543
	append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
5544
	db.append (STRING_WITH_LEN(".* TO '"));
5545 5546
	db.append(lex_user->user.str, lex_user->user.length,
		  system_charset_info);
5547
	db.append (STRING_WITH_LEN("'@'"));
5548 5549
	// host and lex_user->host are equal except for case
	db.append(host, strlen(host), system_charset_info);
unknown's avatar
unknown committed
5550
	db.append ('\'');
unknown's avatar
unknown committed
5551
	if (want_access & GRANT_ACL)
5552
	  db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
5553
	protocol->prepare_for_resend();
5554
	protocol->store(db.ptr(),db.length(),db.charset());
5555
	if (protocol->write())
unknown's avatar
unknown committed
5556
	{
unknown's avatar
unknown committed
5557
	  error= -1;
unknown's avatar
unknown committed
5558 5559 5560 5561 5562 5563
	  goto end;
	}
      }
    }
  }

5564
  /* Add table & column access */
5565
  for (index=0 ; index < column_priv_hash.records ; index++)
unknown's avatar
unknown committed
5566
  {
5567
    const char *user, *host;
Konstantin Osipov's avatar
Konstantin Osipov committed
5568 5569
    GRANT_TABLE *grant_table= (GRANT_TABLE*)
      my_hash_element(&column_priv_hash, index);
unknown's avatar
unknown committed
5570 5571

    if (!(user=grant_table->user))
5572
      user= "";
5573 5574
    if (!(host= grant_table->host.hostname))
      host= "";
unknown's avatar
unknown committed
5575

5576 5577 5578 5579 5580 5581 5582
    /*
      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
5583
    if (!strcmp(lex_user->user.str,user) &&
5584
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
unknown's avatar
unknown committed
5585
    {
5586 5587
      ulong table_access= grant_table->privs;
      if ((table_access | grant_table->cols) != 0)
unknown's avatar
unknown committed
5588
      {
5589
	String global(buff, sizeof(buff), system_charset_info);
unknown's avatar
unknown committed
5590 5591
	ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;

unknown's avatar
unknown committed
5592
	global.length(0);
5593
	global.append(STRING_WITH_LEN("GRANT "));
unknown's avatar
unknown committed
5594

5595
	if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
5596
	  global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
unknown's avatar
unknown committed
5597
	else if (!test_access)
5598
	  global.append(STRING_WITH_LEN("USAGE"));
unknown's avatar
unknown committed
5599
	else
unknown's avatar
unknown committed
5600
	{
5601
          /* Add specific column access */
5602
	  int found= 0;
unknown's avatar
unknown committed
5603
	  ulong j;
unknown's avatar
unknown committed
5604

5605
	  for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
unknown's avatar
unknown committed
5606
	  {
unknown's avatar
unknown committed
5607
	    if (test_access & j)
unknown's avatar
unknown committed
5608 5609
	    {
	      if (found)
5610
		global.append(STRING_WITH_LEN(", "));
5611
	      found= 1;
unknown's avatar
unknown committed
5612 5613
	      global.append(command_array[counter],command_lengths[counter]);

unknown's avatar
unknown committed
5614
	      if (grant_table->cols)
unknown's avatar
unknown committed
5615
	      {
5616
		uint found_col= 0;
unknown's avatar
unknown committed
5617 5618 5619 5620 5621
		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
5622
                    my_hash_element(&grant_table->hash_columns,col_index);
unknown's avatar
unknown committed
5623
		  if (grant_column->rights & j)
unknown's avatar
unknown committed
5624
		  {
unknown's avatar
unknown committed
5625
		    if (!found_col)
unknown's avatar
unknown committed
5626
		    {
5627 5628 5629 5630 5631 5632 5633
		      found_col= 1;
		      /*
			If we have a duplicated table level privilege, we
			must write the access privilege name again.
		      */
		      if (table_access & j)
		      {
5634
			global.append(STRING_WITH_LEN(", "));
5635 5636 5637
			global.append(command_array[counter],
				      command_lengths[counter]);
		      }
5638
		      global.append(STRING_WITH_LEN(" ("));
unknown's avatar
unknown committed
5639 5640
		    }
		    else
5641
		      global.append(STRING_WITH_LEN(", "));
unknown's avatar
unknown committed
5642
		    global.append(grant_column->column,
5643 5644
				  grant_column->key_length,
				  system_charset_info);
unknown's avatar
unknown committed
5645 5646 5647 5648 5649 5650 5651 5652
		  }
		}
		if (found_col)
		  global.append(')');
	      }
	    }
	  }
	}
5653
	global.append(STRING_WITH_LEN(" ON "));
5654 5655 5656 5657 5658
	append_identifier(thd, &global, grant_table->db,
			  strlen(grant_table->db));
	global.append('.');
	append_identifier(thd, &global, grant_table->tname,
			  strlen(grant_table->tname));
5659
	global.append(STRING_WITH_LEN(" TO '"));
5660 5661
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
5662
	global.append(STRING_WITH_LEN("'@'"));
5663 5664
	// host and lex_user->host are equal except for case
	global.append(host, strlen(host), system_charset_info);
unknown's avatar
unknown committed
5665
	global.append('\'');
5666
	if (table_access & GRANT_ACL)
5667
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
5668
	protocol->prepare_for_resend();
5669
	protocol->store(global.ptr(),global.length(),global.charset());
5670
	if (protocol->write())
unknown's avatar
unknown committed
5671
	{
unknown's avatar
unknown committed
5672
	  error= -1;
5673
	  break;
unknown's avatar
unknown committed
5674 5675 5676 5677
	}
      }
    }
  }
5678

5679
  if (show_routine_grants(thd, lex_user, &proc_priv_hash, 
5680
                          STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
5681 5682 5683 5684 5685 5686
  {
    error= -1;
    goto end;
  }

  if (show_routine_grants(thd, lex_user, &func_priv_hash,
5687
                          STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
5688 5689 5690 5691 5692
  {
    error= -1;
    goto end;
  }

5693 5694 5695 5696 5697 5698
  if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
  {
    error= -1;
    goto end;
  }

5699
end:
Marc Alff's avatar
Marc Alff committed
5700 5701
  mysql_mutex_unlock(&acl_cache->lock);
  mysql_rwlock_unlock(&LOCK_grant);
5702

5703
  my_eof(thd);
5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715
  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++)
5716
  {
5717
    const char *user, *host;
Konstantin Osipov's avatar
Konstantin Osipov committed
5718
    GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
5719 5720 5721

    if (!(user=grant_proc->user))
      user= "";
5722 5723
    if (!(host= grant_proc->host.hostname))
      host= "";
5724

5725 5726 5727 5728 5729 5730 5731
    /*
      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.
    */

5732
    if (!strcmp(lex_user->user.str,user) &&
5733
	!my_strcasecmp(system_charset_info, lex_user->host.str, host))
5734 5735 5736 5737
    {
      ulong proc_access= grant_proc->privs;
      if (proc_access != 0)
      {
5738
	String global(buff, buffsize, system_charset_info);
5739 5740 5741
	ulong test_access= proc_access & ~GRANT_ACL;

	global.length(0);
5742
	global.append(STRING_WITH_LEN("GRANT "));
5743 5744

	if (!test_access)
5745
 	  global.append(STRING_WITH_LEN("USAGE"));
5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756
	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)
5757
		global.append(STRING_WITH_LEN(", "));
5758 5759 5760 5761 5762
	      found= 1;
	      global.append(command_array[counter],command_lengths[counter]);
	    }
	  }
	}
5763
	global.append(STRING_WITH_LEN(" ON "));
5764 5765
        global.append(type,typelen);
        global.append(' ');
5766 5767 5768 5769 5770
	append_identifier(thd, &global, grant_proc->db,
			  strlen(grant_proc->db));
	global.append('.');
	append_identifier(thd, &global, grant_proc->tname,
			  strlen(grant_proc->tname));
5771
	global.append(STRING_WITH_LEN(" TO '"));
5772 5773
	global.append(lex_user->user.str, lex_user->user.length,
		      system_charset_info);
5774
	global.append(STRING_WITH_LEN("'@'"));
5775 5776
	// host and lex_user->host are equal except for case
	global.append(host, strlen(host), system_charset_info);
5777 5778
	global.append('\'');
	if (proc_access & GRANT_ACL)
5779
	  global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
5780 5781 5782 5783 5784 5785 5786 5787 5788 5789
	protocol->prepare_for_resend();
	protocol->store(global.ptr(),global.length(),global.charset());
	if (protocol->write())
	{
	  error= -1;
	  break;
	}
      }
    }
  }
5790
  return error;
unknown's avatar
unknown committed
5791 5792
}

unknown's avatar
unknown committed
5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820
/*
  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;
}


5821
void get_mqh(const char *user, const char *host, USER_CONN *uc)
unknown's avatar
unknown committed
5822 5823
{
  ACL_USER *acl_user;
5824

Marc Alff's avatar
Marc Alff committed
5825
  mysql_mutex_lock(&acl_cache->lock);
5826

5827
  if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
5828 5829 5830
    uc->user_resources= acl_user->user_resource;
  else
    bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
5831

Marc Alff's avatar
Marc Alff committed
5832
  mysql_mutex_unlock(&acl_cache->lock);
unknown's avatar
unknown committed
5833 5834
}

5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855
/*
  Open the grant tables.

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

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

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

5856
#define GRANT_TABLES 6
5857 5858 5859 5860 5861 5862
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
  DBUG_ENTER("open_grant_tables");

  if (!initialized)
  {
unknown's avatar
unknown committed
5863
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
5864 5865 5866
    DBUG_RETURN(-1);
  }

5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879
  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);
5880
  (tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"),
5881 5882
                             C_STRING_WITH_LEN("proxies_priv"),
                             "proxies_priv", TL_WRITE);
5883 5884
  tables[5].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;

5885 5886 5887 5888 5889
  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;
5890 5891 5892 5893 5894 5895

#ifdef HAVE_REPLICATION
  /*
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
    some kind of updates to the mysql.% tables.
  */
5896
  if (thd->slave_thread && rpl_filter->is_on())
5897
  {
unknown's avatar
unknown committed
5898 5899 5900
    /*
      The tables must be marked "updating" so that tables_ok() takes them into
      account in tests.
5901
    */
5902 5903
    tables[0].updating= tables[1].updating= tables[2].updating=
      tables[3].updating= tables[4].updating= tables[5].updating= 1;
unknown's avatar
unknown committed
5904
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
5905
      DBUG_RETURN(1);
5906 5907
    tables[0].updating= tables[1].updating= tables[2].updating=
      tables[3].updating= tables[4].updating= tables[5].updating= 0;
5908
  }
5909 5910
#endif

5911
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5912 5913 5914 5915 5916 5917 5918 5919
  {						// 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
5920
			 uint *acl_acl_userdx)
5921 5922 5923 5924
{
  ACL_USER *acl_user= 0;
  uint counter;

Marc Alff's avatar
Marc Alff committed
5925
  mysql_mutex_assert_owner(&acl_cache->lock);
5926

5927 5928 5929 5930 5931
  for (counter= 0 ; counter < acl_users.elements ; counter++)
  {
    const char *user,*host;
    acl_user= dynamic_element(&acl_users, counter, ACL_USER*);
    if (!(user=acl_user->user))
unknown's avatar
unknown committed
5932
      user= "";
5933
    if (!(host=acl_user->host.hostname))
5934
      host= "";
5935 5936 5937 5938 5939 5940 5941
    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
5942
  *acl_acl_userdx= counter;
unknown's avatar
unknown committed
5943
  return acl_user;
5944
}
unknown's avatar
unknown committed
5945

5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967
/*
  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)
5968
{
5969 5970
  int error;
  DBUG_ENTER("modify_grant_table");
5971

5972 5973 5974 5975 5976 5977 5978 5979
  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);
5980 5981 5982
    if ((error= table->file->ha_update_row(table->record[1], 
                                           table->record[0])) &&
        error != HA_ERR_RECORD_IS_THE_SAME)
5983
      table->file->print_error(error, MYF(0));
5984 5985
    else
      error= 0;
5986 5987 5988 5989
  }
  else
  {
    /* delete */
5990
    if ((error=table->file->ha_delete_row(table->record[0])))
5991 5992
      table->file->print_error(error, MYF(0));
  }
5993

5994 5995
  DBUG_RETURN(error);
}
5996

5997 5998 5999 6000 6001 6002
/*
  Handle a privilege table.

  SYNOPSIS
    handle_grant_table()
    tables                      The array with the four open tables.
6003
    table_no                    The number of the table to handle (0..4).
6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020
    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
6021
    4 procs_priv
6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035

  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];
6036
  Field *user_field= table->field[table_no && table_no != 5 ? 2 : 1];
6037 6038 6039 6040
  char *host_str= user_from->host.str;
  char *user_str= user_from->user.str;
  const char *host;
  const char *user;
6041
  uchar user_key[MAX_KEY_LENGTH];
unknown's avatar
unknown committed
6042
  uint key_prefix_length;
6043
  DBUG_ENTER("handle_grant_table");
6044
  THD *thd= current_thd;
6045

6046
  table->use_all_columns();
unknown's avatar
unknown committed
6047
  if (! table_no) // mysql.user table
6048
  {
6049 6050 6051 6052 6053 6054 6055 6056 6057 6058
    /*
      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
6059
                       table->s->table_name.str, user_str, host_str));
6060 6061
    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
6062 6063 6064 6065 6066

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

6067 6068 6069
    if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
                                                   user_key, (key_part_map)3,
                                                   HA_READ_KEY_EXACT)))
6070
    {
6071
      if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
6072 6073 6074 6075
      {
        table->file->print_error(error, MYF(0));
        result= -1;
      }
6076
    }
6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093
    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)))
6094
    {
6095
      table->file->print_error(error, MYF(0));
6096
      result= -1;
6097 6098 6099 6100 6101
    }
    else
    {
#ifdef EXTRA_DEBUG
      DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
unknown's avatar
unknown committed
6102
                         table->s->table_name.str, user_str, host_str));
6103
#endif
6104
      while ((error= table->file->ha_rnd_next(table->record[0])) != 
6105 6106 6107 6108 6109 6110 6111 6112
             HA_ERR_END_OF_FILE)
      {
        if (error)
        {
          /* Most probable 'deleted record'. */
          DBUG_PRINT("info",("scan error: %d", error));
          continue;
        }
6113
        if (! (host= get_field(thd->mem_root, host_field)))
6114
          host= "";
6115
        if (! (user= get_field(thd->mem_root, user_field)))
6116 6117 6118
          user= "";

#ifdef EXTRA_DEBUG
6119 6120 6121 6122 6123 6124 6125 6126 6127
        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*/));
        }
6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149
#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);
}


6150
/**
6151 6152
  Handle an in-memory privilege structure.

Georgi Kodinov's avatar
Georgi Kodinov committed
6153
  @param struct_no  The number of the structure to handle (0..5).
6154 6155 6156
  @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.
6157

6158
  @note
6159 6160 6161 6162 6163
    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.
6164 6165 6166 6167 6168 6169
    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
6170
    5 PROXY_USERS_ACL
6171

6172 6173
  @retval > 0  At least one element matched.
  @retval 0    OK, but no element matched.
6174 6175
*/

6176
static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
6177 6178 6179
                               LEX_USER *user_from, LEX_USER *user_to)
{
  int result= 0;
6180 6181
  int idx;
  int elements;
6182 6183
  const char *user;
  const char *host;
Staale Smedseng's avatar
Staale Smedseng committed
6184 6185
  ACL_USER *acl_user= NULL;
  ACL_DB *acl_db= NULL;
6186
  ACL_PROXY_USER *acl_proxy_user= NULL;
Staale Smedseng's avatar
Staale Smedseng committed
6187
  GRANT_NAME *grant_name= NULL;
6188
  HASH *grant_name_hash= NULL;
6189
  DBUG_ENTER("handle_grant_struct");
unknown's avatar
unknown committed
6190 6191 6192
  DBUG_PRINT("info",("scan struct: %u  search: '%s'@'%s'",
                     struct_no, user_from->user.str, user_from->host.str));

6193 6194
  LINT_INIT(user);
  LINT_INIT(host);
6195

Marc Alff's avatar
Marc Alff committed
6196
  mysql_mutex_assert_owner(&acl_cache->lock);
6197

6198
  /* Get the number of elements in the in-memory structure. */
6199
  switch (struct_no) {
6200
  case USER_ACL:
6201 6202
    elements= acl_users.elements;
    break;
6203
  case DB_ACL:
6204 6205
    elements= acl_dbs.elements;
    break;
6206
  case COLUMN_PRIVILEGES_HASH:
6207
    grant_name_hash= &column_priv_hash;
6208
    elements= grant_name_hash->records;
6209
    break;
6210
  case PROC_PRIVILEGES_HASH:
6211
    grant_name_hash= &proc_priv_hash;
6212
    elements= grant_name_hash->records;
6213
    break;
6214
  case FUNC_PRIVILEGES_HASH:
6215
    grant_name_hash= &func_priv_hash;
6216
    elements= grant_name_hash->records;
6217
    break;
6218
  case PROXY_USERS_ACL:
6219
    elements= acl_proxy_users.elements;
6220 6221
    break;
  default:
Sergei Golubchik's avatar
Sergei Golubchik committed
6222
    DBUG_ASSERT(0);
6223
    return -1;
6224 6225 6226 6227 6228 6229
  }

#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
6230 6231
  /* Loop over all elements *backwards* (see the comment below). */
  for (idx= elements - 1; idx >= 0; idx--)
6232 6233 6234 6235
  {
    /*
      Get a pointer to the element.
    */
6236
    switch (struct_no) {
6237
    case USER_ACL:
6238 6239
      acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
      user= acl_user->user;
6240 6241
      host= acl_user->host.hostname;
    break;
6242

6243
    case DB_ACL:
6244 6245
      acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
      user= acl_db->user;
6246
      host= acl_db->host.hostname;
6247 6248
      break;

6249 6250 6251
    case COLUMN_PRIVILEGES_HASH:
    case PROC_PRIVILEGES_HASH:
    case FUNC_PRIVILEGES_HASH:
Georgi Kodinov's avatar
Georgi Kodinov committed
6252
      grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
6253
      user= grant_name->user;
6254
      host= grant_name->host.hostname;
6255 6256
      break;

6257
    case PROXY_USERS_ACL:
6258
      acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
6259 6260
      user= acl_proxy_user->get_user();
      host= acl_proxy_user->get_host();
6261
      break;
Georgi Kodinov's avatar
Georgi Kodinov committed
6262

unknown's avatar
unknown committed
6263
    default:
6264
      DBUG_ASSERT(0);
6265 6266
    }
    if (! user)
6267
      user= "";
6268 6269 6270
    if (! host)
      host= "";

6271 6272 6273 6274 6275 6276
#ifdef EXTRA_DEBUG
    DBUG_PRINT("loop",("scan struct: %u  index: %u  user: '%s'  host: '%s'",
                       struct_no, idx, user, host));
#endif
    if (strcmp(user_from->user.str, user) ||
        my_strcasecmp(system_charset_info, user_from->host.str, host))
6277
      continue;
6278 6279 6280 6281

    result= 1; /* At least one element found. */
    if ( drop )
    {
6282
      elements--;
6283
      switch ( struct_no ) {
6284
      case USER_ACL:
6285 6286 6287
        delete_dynamic_element(&acl_users, idx);
        break;

6288
      case DB_ACL:
6289 6290 6291
        delete_dynamic_element(&acl_dbs, idx);
        break;

6292 6293 6294
      case COLUMN_PRIVILEGES_HASH:
      case PROC_PRIVILEGES_HASH:
      case FUNC_PRIVILEGES_HASH:
Georgi Kodinov's avatar
Georgi Kodinov committed
6295
        my_hash_delete(grant_name_hash, (uchar*) grant_name);
6296 6297 6298 6299 6300 6301 6302 6303 6304
        /*
          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++;
6305
	break;
6306

6307
      case PROXY_USERS_ACL:
6308 6309 6310
        delete_dynamic_element(&acl_proxy_users, idx);
        break;

6311
      }
6312
    }
6313 6314
    else if ( user_to )
    {
6315
      switch ( struct_no ) {
6316
      case USER_ACL:
6317
        acl_user->user= strdup_root(&mem, user_to->user.str);
6318
        update_hostname(&acl_user->host, strdup_root(&mem, user_to->host.str));
6319
        break;
6320

6321
      case DB_ACL:
6322
        acl_db->user= strdup_root(&mem, user_to->user.str);
6323
        update_hostname(&acl_db->host, strdup_root(&mem, user_to->host.str));
6324 6325
        break;

6326 6327 6328
      case COLUMN_PRIVILEGES_HASH:
      case PROC_PRIVILEGES_HASH:
      case FUNC_PRIVILEGES_HASH:
6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348
        {
          /*
            Save old hash key and its length to be able properly update
            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
6349 6350
          my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
                         old_key_length);
6351
          /*
6352 6353 6354 6355 6356 6357
            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.
6358
          */
6359 6360
          if (idx != elements)
            idx++;
6361 6362
          break;
        }
6363

6364
      case PROXY_USERS_ACL:
6365 6366 6367
        acl_proxy_user->set_user (&mem, user_to->user.str);
        acl_proxy_user->set_host (&mem, user_to->host.str);
        break;
6368 6369 6370
      }
    }
    else
6371
    {
6372 6373 6374 6375 6376 6377 6378
      /* 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
6379

6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423
  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
6424
    if ((handle_grant_struct(USER_ACL, drop, user_from, user_to)) || found)
6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441
    {
      result= 1; /* At least one record/element found. */
      /* If search is requested, we do not need to search further. */
      if (! drop && ! user_to)
        goto end;
    }
  }

  /* Handle db table. */
  if ((found= handle_grant_table(tables, 1, drop, user_from, user_to)) < 0)
  {
    /* Handle of table failed, don't touch the in-memory array. */
    result= -1;
  }
  else
  {
    /* Handle db array. */
Sergei Golubchik's avatar
Sergei Golubchik committed
6442
    if (((handle_grant_struct(DB_ACL, drop, user_from, user_to) && ! result) ||
6443 6444 6445 6446 6447 6448 6449 6450 6451
         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;
    }
  }

6452
  /* Handle stored routines table. */
6453 6454 6455 6456 6457 6458 6459 6460
  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
6461
    if (((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
6462 6463 6464 6465 6466 6467 6468
         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;
    }
6469
    /* Handle funcs array. */
Sergei Golubchik's avatar
Sergei Golubchik committed
6470
    if (((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
6471 6472 6473 6474
         found) && ! result)
    {
      result= 1; /* At least one record/element found. */
      /* If search is requested, we do not need to search further. */
6475 6476 6477 6478 6479
      if (! drop && ! user_to)
        goto end;
    }
  }

6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493
  /* 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;
6494
    }
6495 6496 6497

    /* Handle columns table. */
    if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
6498
    {
6499
      /* Handle of table failed, don't touch the in-memory array. */
6500 6501
      result= -1;
    }
6502 6503 6504
    else
    {
      /* Handle columns hash. */
Sergei Golubchik's avatar
Sergei Golubchik committed
6505
      if (((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
6506 6507 6508 6509
           found) && ! result)
        result= 1; /* At least one record/element found. */
    }
  }
6510

6511
  /* Handle proxies_priv table. */
6512
  if (tables[5].table)
6513
  {
6514 6515 6516 6517 6518 6519 6520 6521
    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
6522
      if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) && !result) ||
6523 6524 6525
          found)
        result= 1; /* At least one record/element found. */
    }
6526
  }
6527 6528 6529 6530
 end:
  DBUG_RETURN(result);
}

unknown's avatar
unknown committed
6531

unknown's avatar
unknown committed
6532 6533 6534 6535 6536 6537
static void append_user(String *str, LEX_USER *user)
{
  if (str->length())
    str->append(',');
  str->append('\'');
  str->append(user->user.str);
6538
  str->append(STRING_WITH_LEN("'@'"));
unknown's avatar
unknown committed
6539 6540 6541
  str->append(user->host.str);
  str->append('\'');
}
6542

unknown's avatar
unknown committed
6543

6544 6545 6546 6547 6548 6549 6550
/*
  Create a list of users.

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

6552 6553 6554 6555 6556 6557 6558 6559
  RETURN
    FALSE       OK.
    TRUE        Error.
*/

bool mysql_create_user(THD *thd, List <LEX_USER> &list)
{
  int result;
unknown's avatar
unknown committed
6560
  String wrong_users;
6561
  LEX_USER *user_name, *tmp_user_name;
6562
  List_iterator <LEX_USER> user_list(list);
6563
  TABLE_LIST tables[GRANT_TABLES];
6564
  bool some_users_created= FALSE;
6565
  bool save_binlog_row_based;
6566 6567
  DBUG_ENTER("mysql_create_user");

6568 6569 6570 6571 6572
  /*
    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.
  */
6573 6574
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
    thd->clear_current_stmt_binlog_format_row();
6575

6576 6577
  /* CREATE USER may be skipped on replication client. */
  if ((result= open_grant_tables(thd, tables)))
6578 6579
  {
    /* Restore the state of binlog format */
6580
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6581 6582
    if (save_binlog_row_based)
      thd->set_current_stmt_binlog_format_row();
6583
    DBUG_RETURN(result != 1);
6584
  }
6585

Marc Alff's avatar
Marc Alff committed
6586 6587
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
6588

6589
  while ((tmp_user_name= user_list++))
6590
  {
6591 6592 6593 6594
    if (!(user_name= get_current_user(thd, tmp_user_name)))
    {
      result= TRUE;
      continue;
6595 6596
    }

6597 6598 6599 6600
    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
6601
    if (handle_grant_data(tables, 0, user_name, NULL))
6602
    {
unknown's avatar
unknown committed
6603
      append_user(&wrong_users, user_name);
6604
      result= TRUE;
unknown's avatar
unknown committed
6605
      continue;
6606
    }
6607

6608
    some_users_created= TRUE;
unknown's avatar
unknown committed
6609
    if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
6610
    {
unknown's avatar
unknown committed
6611
      append_user(&wrong_users, user_name);
6612 6613 6614 6615
      result= TRUE;
    }
  }

Marc Alff's avatar
Marc Alff committed
6616
  mysql_mutex_unlock(&acl_cache->lock);
6617

6618 6619 6620
  if (result)
    my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());

6621
  if (some_users_created)
6622
    result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
6623

Marc Alff's avatar
Marc Alff committed
6624
  mysql_rwlock_unlock(&LOCK_grant);
6625
  /* Restore the state of binlog format */
6626
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6627 6628
  if (save_binlog_row_based)
    thd->set_current_stmt_binlog_format_row();
6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648
  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
6649
  String wrong_users;
6650
  LEX_USER *user_name, *tmp_user_name;
6651
  List_iterator <LEX_USER> user_list(list);
6652
  TABLE_LIST tables[GRANT_TABLES];
6653
  bool some_users_deleted= FALSE;
6654
  ulonglong old_sql_mode= thd->variables.sql_mode;
6655
  bool save_binlog_row_based;
6656 6657
  DBUG_ENTER("mysql_drop_user");

6658 6659 6660 6661 6662
  /*
    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.
  */
6663 6664
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
    thd->clear_current_stmt_binlog_format_row();
6665

unknown's avatar
unknown committed
6666
  /* DROP USER may be skipped on replication client. */
6667
  if ((result= open_grant_tables(thd, tables)))
6668 6669
  {
    /* Restore the state of binlog format */
6670
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6671 6672
    if (save_binlog_row_based)
      thd->set_current_stmt_binlog_format_row();
6673
    DBUG_RETURN(result != 1);
6674
  }
6675

6676 6677
  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;

Marc Alff's avatar
Marc Alff committed
6678 6679
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
6680

6681
  while ((tmp_user_name= user_list++))
6682
  {
6683 6684 6685 6686 6687
    if (!(user_name= get_current_user(thd, tmp_user_name)))
    {
      result= TRUE;
      continue;
    }  
6688
    if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
6689
    {
unknown's avatar
unknown committed
6690
      append_user(&wrong_users, user_name);
6691
      result= TRUE;
6692
      continue;
6693
    }
6694
    some_users_deleted= TRUE;
6695
  }
6696

6697 6698 6699
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

Marc Alff's avatar
Marc Alff committed
6700
  mysql_mutex_unlock(&acl_cache->lock);
6701

6702 6703 6704
  if (result)
    my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());

6705
  if (some_users_deleted)
6706
    result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
6707

Marc Alff's avatar
Marc Alff committed
6708
  mysql_rwlock_unlock(&LOCK_grant);
6709
  thd->variables.sql_mode= old_sql_mode;
6710
  /* Restore the state of binlog format */
6711
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6712 6713
  if (save_binlog_row_based)
    thd->set_current_stmt_binlog_format_row();
6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732
  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)
{
6733
  int result;
unknown's avatar
unknown committed
6734
  String wrong_users;
6735 6736
  LEX_USER *user_from, *tmp_user_from;
  LEX_USER *user_to, *tmp_user_to;
6737
  List_iterator <LEX_USER> user_list(list);
6738
  TABLE_LIST tables[GRANT_TABLES];
6739
  bool some_users_renamed= FALSE;
6740
  bool save_binlog_row_based;
6741 6742
  DBUG_ENTER("mysql_rename_user");

6743 6744 6745 6746 6747
  /*
    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.
  */
6748 6749
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
    thd->clear_current_stmt_binlog_format_row();
6750

unknown's avatar
unknown committed
6751
  /* RENAME USER may be skipped on replication client. */
6752
  if ((result= open_grant_tables(thd, tables)))
6753 6754
  {
    /* Restore the state of binlog format */
6755
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6756 6757
    if (save_binlog_row_based)
      thd->set_current_stmt_binlog_format_row();
6758
    DBUG_RETURN(result != 1);
6759
  }
6760

Marc Alff's avatar
Marc Alff committed
6761 6762
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
6763

6764
  while ((tmp_user_from= user_list++))
6765
  {
6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776
    if (!(user_from= get_current_user(thd, tmp_user_from)))
    {
      result= TRUE;
      continue;
    }  
    tmp_user_to= user_list++;
    if (!(user_to= get_current_user(thd, tmp_user_to)))
    {
      result= TRUE;
      continue;
    }  
6777
    DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
6778 6779 6780 6781 6782

    /*
      Search all in-memory structures and grant tables
      for a mention of the new user name.
    */
unknown's avatar
unknown committed
6783 6784
    if (handle_grant_data(tables, 0, user_to, NULL) ||
        handle_grant_data(tables, 0, user_from, user_to) <= 0)
6785
    {
unknown's avatar
unknown committed
6786
      append_user(&wrong_users, user_from);
6787
      result= TRUE;
6788
      continue;
6789
    }
6790
    some_users_renamed= TRUE;
6791
  }
unknown's avatar
unknown committed
6792
  
6793 6794 6795
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
  rebuild_check_host();

Marc Alff's avatar
Marc Alff committed
6796
  mysql_mutex_unlock(&acl_cache->lock);
6797

6798 6799 6800 6801
  if (result)
    my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
  
  if (some_users_renamed && mysql_bin_log.is_open())
6802
    result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
6803

Marc Alff's avatar
Marc Alff committed
6804
  mysql_rwlock_unlock(&LOCK_grant);
6805
  /* Restore the state of binlog format */
6806
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6807 6808
  if (save_binlog_row_based)
    thd->set_current_stmt_binlog_format_row();
6809 6810 6811
  DBUG_RETURN(result);
}

6812

6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826
/*
  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
6827
bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
6828
{
6829
  uint counter, revoked, is_proc;
6830
  int result;
unknown's avatar
unknown committed
6831
  ACL_DB *acl_db;
6832
  TABLE_LIST tables[GRANT_TABLES];
6833
  bool save_binlog_row_based;
6834 6835
  DBUG_ENTER("mysql_revoke_all");

6836 6837 6838 6839 6840
  /*
    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.
  */
6841 6842
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
    thd->clear_current_stmt_binlog_format_row();
6843

6844
  if ((result= open_grant_tables(thd, tables)))
6845 6846
  {
    /* Restore the state of binlog format */
6847
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6848 6849
    if (save_binlog_row_based)
      thd->set_current_stmt_binlog_format_row();
unknown's avatar
unknown committed
6850
    DBUG_RETURN(result != 1);
6851
  }
6852

Marc Alff's avatar
Marc Alff committed
6853 6854
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
6855

6856
  LEX_USER *lex_user, *tmp_lex_user;
6857
  List_iterator <LEX_USER> user_list(list);
6858
  while ((tmp_lex_user= user_list++))
6859
  {
6860 6861 6862 6863 6864
    if (!(lex_user= get_current_user(thd, tmp_lex_user)))
    {
      result= -1;
      continue;
    }  
6865
    if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
6866 6867 6868 6869
    {
      result= -1;
      continue;
    }
unknown's avatar
unknown committed
6870

6871
    if (replace_user_table(thd, tables[0].table,
6872
			   *lex_user, ~(ulong)0, 1, 0, 0))
6873 6874 6875 6876 6877 6878
    {
      result= -1;
      continue;
    }

    /* Remove db access privileges */
unknown's avatar
unknown committed
6879 6880 6881 6882 6883
    /*
      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
6884
    do
6885
    {
unknown's avatar
unknown committed
6886
      for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
6887
      {
unknown's avatar
unknown committed
6888
	const char *user,*host;
unknown's avatar
unknown committed
6889

unknown's avatar
unknown committed
6890 6891 6892 6893 6894
	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
6895

unknown's avatar
unknown committed
6896
	if (!strcmp(lex_user->user.str,user) &&
6897
            !strcmp(lex_user->host.str, host))
unknown's avatar
unknown committed
6898
	{
6899 6900
	  if (!replace_db_table(tables[1].table, acl_db->db, *lex_user,
                                ~(ulong)0, 1))
unknown's avatar
unknown committed
6901
	  {
unknown's avatar
unknown committed
6902 6903 6904 6905 6906
	    /*
	      Don't increment counter as replace_db_table deleted the
	      current element in acl_dbs.
	     */
	    revoked= 1;
unknown's avatar
unknown committed
6907 6908
	    continue;
	  }
unknown's avatar
unknown committed
6909
	  result= -1; // Something went wrong
unknown's avatar
unknown committed
6910
	}
unknown's avatar
unknown committed
6911
	counter++;
6912
      }
unknown's avatar
unknown committed
6913
    } while (revoked);
6914 6915

    /* Remove column access */
unknown's avatar
unknown committed
6916
    do
6917
    {
unknown's avatar
unknown committed
6918
      for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
6919
      {
unknown's avatar
unknown committed
6920
	const char *user,*host;
Konstantin Osipov's avatar
Konstantin Osipov committed
6921 6922
        GRANT_TABLE *grant_table=
          (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
unknown's avatar
unknown committed
6923 6924
	if (!(user=grant_table->user))
	  user= "";
6925
	if (!(host=grant_table->host.hostname))
unknown's avatar
unknown committed
6926
	  host= "";
unknown's avatar
unknown committed
6927

unknown's avatar
unknown committed
6928
	if (!strcmp(lex_user->user.str,user) &&
6929
            !strcmp(lex_user->host.str, host))
6930
	{
unknown's avatar
unknown committed
6931 6932 6933
	  if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
				  grant_table->db,
				  grant_table->tname,
6934
				  ~(ulong)0, 0, 1))
unknown's avatar
unknown committed
6935
	  {
unknown's avatar
unknown committed
6936
	    result= -1;
unknown's avatar
unknown committed
6937
	  }
unknown's avatar
unknown committed
6938
	  else
unknown's avatar
unknown committed
6939
	  {
unknown's avatar
unknown committed
6940
	    if (!grant_table->cols)
unknown's avatar
unknown committed
6941
	    {
unknown's avatar
unknown committed
6942 6943
	      revoked= 1;
	      continue;
unknown's avatar
unknown committed
6944
	    }
unknown's avatar
unknown committed
6945 6946
	    List<LEX_COLUMN> columns;
	    if (!replace_column_table(grant_table,tables[3].table, *lex_user,
unknown's avatar
unknown committed
6947 6948 6949
				      columns,
				      grant_table->db,
				      grant_table->tname,
6950
				      ~(ulong)0, 1))
unknown's avatar
unknown committed
6951
	    {
unknown's avatar
unknown committed
6952
	      revoked= 1;
6953
	      continue;
unknown's avatar
unknown committed
6954
	    }
6955
	    result= -1;
unknown's avatar
unknown committed
6956
	  }
6957
	}
unknown's avatar
unknown committed
6958
	counter++;
6959
      }
unknown's avatar
unknown committed
6960
    } while (revoked);
6961 6962

    /* Remove procedure access */
6963 6964 6965
    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 ; )
6966 6967
      {
	const char *user,*host;
Konstantin Osipov's avatar
Konstantin Osipov committed
6968
        GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
6969 6970
	if (!(user=grant_proc->user))
	  user= "";
6971
	if (!(host=grant_proc->host.hostname))
6972 6973 6974
	  host= "";

	if (!strcmp(lex_user->user.str,user) &&
6975
            !strcmp(lex_user->host.str, host))
6976
	{
6977
	  if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
6978 6979
				  grant_proc->db,
				  grant_proc->tname,
6980
                                  is_proc,
6981
				  ~(ulong)0, 1) == 0)
6982 6983 6984 6985 6986 6987 6988 6989 6990
	  {
	    revoked= 1;
	    continue;
	  }
	  result= -1;	// Something went wrong
	}
	counter++;
      }
    } while (revoked);
6991
  }
unknown's avatar
unknown committed
6992

Marc Alff's avatar
Marc Alff committed
6993
  mysql_mutex_unlock(&acl_cache->lock);
6994

6995 6996 6997 6998
  if (result)
    my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));

  result= result |
6999
    write_bin_log(thd, FALSE, thd->query(), thd->query_length());
7000

Marc Alff's avatar
Marc Alff committed
7001
  mysql_rwlock_unlock(&LOCK_grant);
7002
  /* Restore the state of binlog format */
7003
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7004 7005
  if (save_binlog_row_based)
    thd->set_current_stmt_binlog_format_row();
unknown's avatar
unknown committed
7006

7007
  DBUG_RETURN(result);
7008
}
unknown's avatar
unknown committed
7009

7010

7011

7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030

/**
  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
7031 7032 7033 7034 7035 7036
  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);
7037 7038 7039 7040 7041 7042 7043 7044

  bool has_errors() { return is_grave; }

private:
  bool is_grave;
};

bool
Marc Alff's avatar
Marc Alff committed
7045 7046 7047 7048 7049 7050 7051
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)
7052
{
Marc Alff's avatar
Marc Alff committed
7053
  *cond_hdl= NULL;
7054 7055 7056 7057 7058 7059
  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
7060 7061
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                     sql_errno, msg);
7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076
        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
7077
    thd                         The current thread.
7078
  @param
7079
    db				DB of the stored procedure
7080
  @param
7081 7082
    name			Name of the stored procedure

7083
  @retval
7084
    0           OK.
7085
  @retval
7086 7087 7088
    < 0         Error. Error message not yet sent.
*/

7089 7090
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
                          bool is_proc)
7091 7092 7093 7094
{
  uint counter, revoked;
  int result;
  TABLE_LIST tables[GRANT_TABLES];
7095
  HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
7096
  Silence_routine_definer_errors error_handler;
7097
  bool save_binlog_row_based;
7098 7099 7100 7101 7102
  DBUG_ENTER("sp_revoke_privileges");

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

7103 7104 7105
  /* Be sure to pop this before exiting this scope! */
  thd->push_internal_handler(&error_handler);

Marc Alff's avatar
Marc Alff committed
7106 7107
  mysql_rwlock_wrlock(&LOCK_grant);
  mysql_mutex_lock(&acl_cache->lock);
7108

7109 7110 7111 7112 7113
  /*
    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.
  */
7114 7115
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
    thd->clear_current_stmt_binlog_format_row();
7116

7117
  /* Remove procedure access */
7118 7119
  do
  {
7120
    for (counter= 0, revoked= 0 ; counter < hash->records ; )
7121
    {
Konstantin Osipov's avatar
Konstantin Osipov committed
7122
      GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
7123
      if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
7124 7125 7126 7127 7128
	  !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);
7129 7130 7131 7132
	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;
7133 7134 7135 7136

	if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
				  grant_proc->db, grant_proc->tname,
                                  is_proc, ~(ulong)0, 1) == 0)
7137 7138 7139 7140 7141 7142 7143 7144 7145
	{
	  revoked= 1;
	  continue;
	}
      }
      counter++;
    }
  } while (revoked);

Marc Alff's avatar
Marc Alff committed
7146 7147
  mysql_mutex_unlock(&acl_cache->lock);
  mysql_rwlock_unlock(&LOCK_grant);
7148

7149
  thd->pop_internal_handler();
7150
  /* Restore the state of binlog format */
7151
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
7152 7153
  if (save_binlog_row_based)
    thd->set_current_stmt_binlog_format_row();
7154

7155
  DBUG_RETURN(error_handler.has_errors());
7156 7157 7158
}


7159
/**
7160 7161
  Grant EXECUTE,ALTER privilege for a stored procedure

7162 7163 7164 7165
  @param thd The current thread.
  @param sp_db
  @param sp_name
  @param is_proc
7166

7167 7168 7169
  @return
    @retval FALSE Success
    @retval TRUE An error occured. Error message not yet sent.
7170 7171
*/

7172
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
7173
                         bool is_proc)
7174
{
7175
  Security_context *sctx= thd->security_ctx;
7176 7177 7178 7179
  LEX_USER *combo;
  TABLE_LIST tables[1];
  List<LEX_USER> user_list;
  bool result;
7180 7181
  ACL_USER *au;
  char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
7182
  Dummy_error_handler error_handler;
7183
  DBUG_ENTER("sp_grant_privileges");
7184 7185 7186 7187

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

7188
  combo->user.str= (char *) sctx->priv_user;
7189

Marc Alff's avatar
Marc Alff committed
7190
  mysql_mutex_lock(&acl_cache->lock);
7191

7192 7193
 if ((au= find_acl_user(combo->host.str= (char *) sctx->priv_host,
                        combo->user.str, FALSE)))
7194 7195
    goto found_acl;

Marc Alff's avatar
Marc Alff committed
7196
  mysql_mutex_unlock(&acl_cache->lock);
7197 7198 7199
  DBUG_RETURN(TRUE);

 found_acl:
Marc Alff's avatar
Marc Alff committed
7200
  mysql_mutex_unlock(&acl_cache->lock);
7201 7202 7203 7204 7205

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

  tables->db= (char*)sp_db;
7206
  tables->table_name= tables->alias= (char*)sp_name;
7207

7208 7209 7210 7211
  thd->make_lex_string(&combo->user,
                       combo->user.str, strlen(combo->user.str), 0);
  thd->make_lex_string(&combo->host,
                       combo->host.str, strlen(combo->host.str), 0);
7212

7213 7214 7215
  combo->password= empty_lex_str;
  combo->plugin= empty_lex_str;
  combo->auth= empty_lex_str;
7216

7217
  if(au)
7218
  {
7219
    if (au->salt_len)
7220
    {
7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237
      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;
7238
    }
7239 7240 7241

    if (au->plugin.str != native_password_plugin_name.str &&
        au->plugin.str != old_password_plugin_name.str)
7242
    {
7243 7244
      combo->plugin= au->plugin;
      combo->auth= au->auth_string;
7245 7246
    }
  }
7247 7248 7249 7250 7251

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

  thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
7252
  thd->lex->ssl_cipher= thd->lex->x509_subject= thd->lex->x509_issuer= 0;
7253
  bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
7254

7255 7256 7257 7258 7259
  /*
    Only care about whether the operation failed or succeeded
    as all errors will be handled later.
  */
  thd->push_internal_handler(&error_handler);
7260
  result= mysql_routine_grant(thd, tables, is_proc, user_list,
7261 7262
                              DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
  thd->pop_internal_handler();
7263 7264 7265 7266
  DBUG_RETURN(result);
}


unknown's avatar
unknown committed
7267
/*****************************************************************************
unknown's avatar
unknown committed
7268
  Instantiate used templates
unknown's avatar
unknown committed
7269 7270
*****************************************************************************/

7271
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
unknown's avatar
unknown committed
7272 7273 7274 7275 7276
template class List_iterator<LEX_COLUMN>;
template class List_iterator<LEX_USER>;
template class List<LEX_COLUMN>;
template class List<LEX_USER>;
#endif
unknown's avatar
unknown committed
7277

7278 7279 7280 7281 7282 7283 7284
/**
  Validate if a user can proxy as another user

  @thd                     current thread
  @param user              the logged in user (proxy user)
  @param authenticated_as  the effective user a plugin is trying to 
                           impersonate as (proxied user)
7285 7286 7287
  @return                  proxy user definition
    @retval NULL           proxy user definition not found or not applicable
    @retval non-null       the proxy user data
7288 7289
*/

7290
static ACL_PROXY_USER *
7291 7292 7293 7294 7295
acl_find_proxy_user(const char *user, const char *host, const char *ip, 
                    const char *authenticated_as, bool *proxy_used)
{
  uint i;
  /* if the proxied and proxy user are the same return OK */
7296 7297 7298
  DBUG_ENTER("acl_find_proxy_user");
  DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
                      user, host, ip, authenticated_as));
7299

7300
  if (!strcmp(authenticated_as, user))
7301 7302
  {
    DBUG_PRINT ("info", ("user is the same as authenticated_as"));
7303
    DBUG_RETURN (NULL);
7304 7305 7306
  }

  *proxy_used= TRUE; 
7307
  for (i=0; i < acl_proxy_users.elements; i++)
7308
  {
7309 7310 7311
    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i, 
                                           ACL_PROXY_USER *);
    if (proxy->matches(host, user, ip, authenticated_as))
7312
      DBUG_RETURN(proxy);
7313 7314
  }

7315
  DBUG_RETURN(NULL);
7316 7317 7318 7319
}


bool
7320 7321
acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
                             bool with_grant)
7322
{
7323 7324 7325
  DBUG_ENTER("acl_check_proxy_grant_access");
  DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host, 
                      (int) with_grant));
7326 7327 7328 7329 7330 7331 7332 7333 7334
  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)
  {
7335
    DBUG_PRINT("info", ("replication slave"));
7336 7337 7338
    DBUG_RETURN(FALSE);
  }

7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351
  /*
    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) &&
7352
      !my_strcasecmp(system_charset_info, host,
7353
                     thd->security_ctx->priv_host))
7354
  {
7355
    DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal", 
7356 7357
                        thd->security_ctx->priv_user, user,
                        host, thd->security_ctx->priv_host));
7358 7359 7360
    DBUG_RETURN(FALSE);
  }

7361 7362
  mysql_mutex_lock(&acl_cache->lock);

7363
  /* check for matching WITH PROXY rights */
7364 7365 7366 7367 7368 7369 7370 7371
  for (uint i=0; i < acl_proxy_users.elements; i++)
  {
    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i, 
                                           ACL_PROXY_USER *);
    if (proxy->matches(thd->security_ctx->host,
                       thd->security_ctx->user,
                       thd->security_ctx->ip,
                       user) &&
7372 7373
        proxy->get_with_grant())
    {
7374
      DBUG_PRINT("info", ("found"));
7375
      mysql_mutex_unlock(&acl_cache->lock);
7376 7377 7378 7379
      DBUG_RETURN(FALSE);
    }
  }

7380
  mysql_mutex_unlock(&acl_cache->lock);
7381 7382 7383 7384 7385 7386 7387 7388
  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
7389
show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
7390 7391 7392 7393
{
  Protocol *protocol= thd->protocol;
  int error= 0;

7394
  for (uint i=0; i < acl_proxy_users.elements; i++)
7395
  {
7396 7397
    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
                                           ACL_PROXY_USER *);
7398 7399 7400 7401 7402 7403
    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();
7404
      protocol->store(global.ptr(), global.length(), global.charset());
7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415
      if (protocol->write())
      {
        error= -1;
        break;
      }
    }
  }
  return error;
}


unknown's avatar
unknown committed
7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461
#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');
}

7462

Sergey Glukhov's avatar
Sergey Glukhov committed
7463
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7464 7465 7466 7467 7468
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)
7469 7470 7471
{
  int i= 2;
  CHARSET_INFO *cs= system_charset_info;
7472
  restore_record(table, s->default_values);
7473
  table->field[0]->store(buff, (uint) strlen(buff), cs);
7474
  table->field[1]->store(STRING_WITH_LEN("def"), cs);
7475
  if (db)
7476
    table->field[i++]->store(db, (uint) strlen(db), cs);
7477
  if (t_name)
7478
    table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
7479 7480 7481 7482
  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);
7483
  return schema_table_store_record(thd, table);
7484
}
Sergey Glukhov's avatar
Sergey Glukhov committed
7485
#endif
7486 7487 7488 7489 7490


int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7491
  int error= 0;
7492 7493 7494 7495 7496
  uint counter;
  ACL_USER *acl_user;
  ulong want_access;
  char buff[100];
  TABLE *table= tables->table;
Marc Alff's avatar
Marc Alff committed
7497 7498
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
                                      NULL, NULL, 1, 1);
7499
  char *curr_host= thd->security_ctx->priv_host_name();
7500
  DBUG_ENTER("fill_schema_user_privileges");
7501

7502 7503
  if (!initialized)
    DBUG_RETURN(0);
Marc Alff's avatar
Marc Alff committed
7504
  mysql_mutex_lock(&acl_cache->lock);
7505

7506 7507 7508 7509 7510 7511 7512 7513
  for (counter=0 ; counter < acl_users.elements ; counter++)
  {
    const char *user,*host, *is_grantable="YES";
    acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
    if (!(user=acl_user->user))
      user= "";
    if (!(host=acl_user->host.hostname))
      host= "";
7514 7515

    if (no_global_access &&
7516
        (strcmp(thd->security_ctx->priv_user, user) ||
7517 7518 7519
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;
      
7520 7521 7522 7523 7524 7525
    want_access= acl_user->access;
    if (!(want_access & GRANT_ACL))
      is_grantable= "NO";

    strxmov(buff,"'",user,"'@'",host,"'",NullS);
    if (!(want_access & ~GRANT_ACL))
7526 7527 7528 7529 7530 7531 7532 7533
    {
      if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
                                  STRING_WITH_LEN("USAGE"), is_grantable))
      {
        error= 1;
        goto err;
      }
    }
7534 7535 7536 7537 7538 7539 7540
    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)
7541 7542 7543 7544 7545 7546 7547 7548 7549
        {
          if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0, 
                                      command_array[priv_id],
                                      command_lengths[priv_id], is_grantable))
          {
            error= 1;
            goto err;
          }
        }
7550 7551 7552
      }
    }
  }
7553
err:
Marc Alff's avatar
Marc Alff committed
7554
  mysql_mutex_unlock(&acl_cache->lock);
7555

7556
  DBUG_RETURN(error);
7557 7558 7559
#else
  return(0);
#endif
7560 7561 7562 7563 7564 7565
}


int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7566
  int error= 0;
7567 7568 7569 7570 7571
  uint counter;
  ACL_DB *acl_db;
  ulong want_access;
  char buff[100];
  TABLE *table= tables->table;
Marc Alff's avatar
Marc Alff committed
7572 7573
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
                                      NULL, NULL, 1, 1);
7574
  char *curr_host= thd->security_ctx->priv_host_name();
7575 7576
  DBUG_ENTER("fill_schema_schema_privileges");

7577 7578
  if (!initialized)
    DBUG_RETURN(0);
Marc Alff's avatar
Marc Alff committed
7579
  mysql_mutex_lock(&acl_cache->lock);
7580

7581 7582 7583 7584 7585 7586 7587 7588 7589 7590
  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= "";

7591
    if (no_global_access &&
7592
        (strcmp(thd->security_ctx->priv_user, user) ||
7593 7594 7595
         my_strcasecmp(system_charset_info, curr_host, host)))
      continue;

7596 7597 7598 7599 7600 7601 7602 7603 7604
    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))
7605 7606 7607 7608 7609 7610 7611 7612
      {
        if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0,
                                    0, STRING_WITH_LEN("USAGE"), is_grantable))
        {
          error= 1;
          goto err;
        }
      }
7613 7614 7615 7616 7617 7618
      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)
7619 7620 7621 7622 7623 7624 7625 7626 7627
          {
            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;
            }
          }
7628 7629 7630
      }
    }
  }
7631
err:
Marc Alff's avatar
Marc Alff committed
7632
  mysql_mutex_unlock(&acl_cache->lock);
7633

7634
  DBUG_RETURN(error);
7635 7636 7637
#else
  return (0);
#endif
7638 7639 7640 7641 7642 7643
}


int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7644
  int error= 0;
7645 7646 7647
  uint index;
  char buff[100];
  TABLE *table= tables->table;
Marc Alff's avatar
Marc Alff committed
7648 7649
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
                                      NULL, NULL, 1, 1);
7650
  char *curr_host= thd->security_ctx->priv_host_name();
7651 7652
  DBUG_ENTER("fill_schema_table_privileges");

Marc Alff's avatar
Marc Alff committed
7653
  mysql_rwlock_rdlock(&LOCK_grant);
7654

7655 7656
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
7657
    const char *user, *host, *is_grantable= "YES";
Konstantin Osipov's avatar
Konstantin Osipov committed
7658
    GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
7659 7660 7661
							  index);
    if (!(user=grant_table->user))
      user= "";
7662 7663
    if (!(host= grant_table->host.hostname))
      host= "";
7664 7665

    if (no_global_access &&
7666
        (strcmp(thd->security_ctx->priv_user, user) ||
7667
         my_strcasecmp(system_charset_info, curr_host, host)))
7668 7669
      continue;

7670
    ulong table_access= grant_table->privs;
7671
    if (table_access)
7672 7673
    {
      ulong test_access= table_access & ~GRANT_ACL;
unknown's avatar
unknown committed
7674 7675 7676 7677
      /*
        We should skip 'usage' privilege on table if
        we have any privileges on column(s) of this table
      */
7678 7679
      if (!test_access && grant_table->cols)
        continue;
7680 7681 7682
      if (!(table_access & GRANT_ACL))
        is_grantable= "NO";

7683
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
7684
      if (!test_access)
7685 7686 7687 7688 7689 7690 7691 7692 7693
      {
        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;
        }
      }
7694 7695 7696 7697 7698 7699 7700
      else
      {
        ulong j;
        int cnt;
        for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
        {
          if (test_access & j)
7701 7702 7703 7704 7705 7706 7707 7708 7709 7710
          {
            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;
            }
          }
7711 7712
        }
      }
7713
    }   
7714
  }
7715
err:
Marc Alff's avatar
Marc Alff committed
7716
  mysql_rwlock_unlock(&LOCK_grant);
7717

7718
  DBUG_RETURN(error);
7719 7720 7721
#else
  return (0);
#endif
7722 7723 7724 7725 7726 7727
}


int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7728
  int error= 0;
7729 7730 7731
  uint index;
  char buff[100];
  TABLE *table= tables->table;
Marc Alff's avatar
Marc Alff committed
7732 7733
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
                                      NULL, NULL, 1, 1);
7734
  char *curr_host= thd->security_ctx->priv_host_name();
7735 7736
  DBUG_ENTER("fill_schema_table_privileges");

Marc Alff's avatar
Marc Alff committed
7737
  mysql_rwlock_rdlock(&LOCK_grant);
7738

7739 7740
  for (index=0 ; index < column_priv_hash.records ; index++)
  {
7741
    const char *user, *host, *is_grantable= "YES";
Konstantin Osipov's avatar
Konstantin Osipov committed
7742
    GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
7743 7744 7745
							  index);
    if (!(user=grant_table->user))
      user= "";
7746 7747
    if (!(host= grant_table->host.hostname))
      host= "";
7748 7749

    if (no_global_access &&
7750
        (strcmp(thd->security_ctx->priv_user, user) ||
7751
         my_strcasecmp(system_charset_info, curr_host, host)))
7752 7753
      continue;

7754 7755 7756
    ulong table_access= grant_table->cols;
    if (table_access != 0)
    {
unknown's avatar
unknown committed
7757
      if (!(grant_table->privs & GRANT_ACL))
7758 7759
        is_grantable= "NO";

unknown's avatar
unknown committed
7760
      ulong test_access= table_access & ~GRANT_ACL;
7761
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776
      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
7777
                my_hash_element(&grant_table->hash_columns,col_index);
7778
              if ((grant_column->rights & j) && (table_access & j))
7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790
              {
                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;
                }
              }
7791 7792 7793 7794 7795 7796
            }
          }
        }
      }
    }
  }
7797
err:
Marc Alff's avatar
Marc Alff committed
7798
  mysql_rwlock_unlock(&LOCK_grant);
7799

7800
  DBUG_RETURN(error);
7801 7802 7803
#else
  return (0);
#endif
7804 7805 7806
}


unknown's avatar
VIEW  
unknown committed
7807 7808 7809 7810 7811
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
  fill effective privileges for table

  SYNOPSIS
7812 7813
    fill_effective_table_privileges()
    thd     thread handler
unknown's avatar
VIEW  
unknown committed
7814 7815 7816 7817 7818 7819 7820 7821
    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)
{
7822
  Security_context *sctx= thd->security_ctx;
7823 7824 7825 7826 7827
  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));
7828 7829 7830
  /* --skip-grants */
  if (!initialized)
  {
7831
    DBUG_PRINT("info", ("skip grants"));
7832
    grant->privilege= ~NO_ACCESS;             // everything is allowed
7833 7834
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;
7835 7836
  }

unknown's avatar
VIEW  
unknown committed
7837
  /* global privileges */
7838
  grant->privilege= sctx->master_access;
7839

7840
  if (!sctx->priv_user)
7841 7842 7843 7844
  {
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
    DBUG_VOID_RETURN;                         // it is slave
  }
7845

7846
  /* db privileges */
7847
  grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
7848

unknown's avatar
VIEW  
unknown committed
7849
  /* table privileges */
Marc Alff's avatar
Marc Alff committed
7850
  mysql_rwlock_rdlock(&LOCK_grant);
unknown's avatar
VIEW  
unknown committed
7851 7852 7853
  if (grant->version != grant_version)
  {
    grant->grant_table=
7854 7855
      table_hash_search(sctx->host, sctx->ip, db,
			sctx->priv_user,
unknown's avatar
VIEW  
unknown committed
7856 7857 7858 7859 7860 7861 7862
			table, 0);              /* purecov: inspected */
    grant->version= grant_version;              /* purecov: inspected */
  }
  if (grant->grant_table != 0)
  {
    grant->privilege|= grant->grant_table->privs;
  }
Marc Alff's avatar
Marc Alff committed
7863
  mysql_rwlock_unlock(&LOCK_grant);
7864

7865 7866
  DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
  DBUG_VOID_RETURN;
unknown's avatar
VIEW  
unknown committed
7867
}
7868 7869 7870 7871 7872 7873 7874

#else /* NO_EMBEDDED_ACCESS_CHECKS */

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

unknown's avatar
unknown committed
7875 7876
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
                             bool is_proc)
7877 7878 7879 7880
{
  return FALSE;
}

unknown's avatar
VIEW  
unknown committed
7881
#endif
Marc Alff's avatar
Marc Alff committed
7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962 7963 7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982

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


7983 7984 7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995
/****************************************************************************
   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
7996
#define get_or_create_user_conn(A,B,C,D) 0
7997 7998 7999 8000 8001 8002 8003 8004 8005 8006 8007
#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
*/
8008
struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
8009 8010 8011
{
  MYSQL_SERVER_AUTH_INFO auth_info;
  THD *thd;
Sergei Golubchik's avatar
Sergei Golubchik committed
8012
  ACL_USER *acl_user;       ///< a copy, independent from acl_users array
8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024 8025 8026
  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
8027
  bool make_it_fail;
8028 8029 8030 8031 8032 8033 8034
  /** 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
*/
8035
static void login_failed_error(THD *thd)
8036
{
Sergei Golubchik's avatar
Sergei Golubchik committed
8037
  my_error(access_denied_error_code(thd->password), MYF(0),
8038 8039
           thd->main_security_ctx.user,
           thd->main_security_ctx.host_or_ip,
8040
           thd->password ? ER(ER_YES) : ER(ER_NO));
Sergei Golubchik's avatar
Sergei Golubchik committed
8041 8042
  general_log_print(thd, COM_CONNECT,
                    ER(access_denied_error_code(thd->password)),
8043 8044
                    thd->main_security_ctx.user,
                    thd->main_security_ctx.host_or_ip,
8045
                    thd->password ? ER(ER_YES) : ER(ER_NO));
8046 8047 8048 8049 8050 8051 8052 8053
  status_var_increment(thd->status_var.access_denied_errors);
  /* 
    Log access denied messages to the error log when log-warnings = 2
    so that the overhead of the general query log is not required to track 
    failed connections.
  */
  if (global_system_variables.log_warnings > 1)
  {
Sergei Golubchik's avatar
Sergei Golubchik committed
8054
    sql_print_warning(ER(access_denied_error_code(thd->password)),
8055 8056
                      thd->main_security_ctx.user,
                      thd->main_security_ctx.host_or_ip,
8057
                      thd->password ? ER(ER_YES) : ER(ER_NO));      
8058 8059 8060 8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 8081 8082 8083 8084 8085 8086 8087 8088 8089 8090 8091 8092
  }
}

/**
  sends a server handshake initialization packet, the very first packet
  after the connection was established

  Packet format:
   
    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
8093
  char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + 1 + data_len + 64);
8094 8095
  char scramble_buf[SCRAMBLE_LENGTH];
  char *end= buff;
8096
  DBUG_ENTER("send_server_handshake_packet");
8097 8098 8099 8100 8101

  *end++= protocol_version;

  thd->client_capabilities= CLIENT_BASIC_FLAGS;

8102
  if (opt_using_transactions)
Sergei Golubchik's avatar
Sergei Golubchik committed
8103
    thd->client_capabilities|= CLIENT_TRANSACTIONS;
8104

Sergei Golubchik's avatar
Sergei Golubchik committed
8105
  thd->client_capabilities|= CAN_CLIENT_COMPRESS;
8106 8107 8108

  if (ssl_acceptor_fd)
  {
Sergei Golubchik's avatar
Sergei Golubchik committed
8109 8110
    thd->client_capabilities |= CLIENT_SSL;
    thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
8111 8112
  }

8113 8114 8115 8116 8117 8118 8119 8120 8121
  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)
8122 8123
    {
      /*
8124 8125 8126 8127
        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);
8128
      bzero(scramble_buf + data_len, SCRAMBLE_LENGTH - data_len);
8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156
      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;
  }

  end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
  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.
  */
8157
  end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
8158 8159 8160 8161 8162
  end+= SCRAMBLE_LENGTH_323;
  *end++= 0;
 
  int2store(end, thd->client_capabilities);
  /* write server characteristics: up to 16 bytes allowed */
8163
  end[2]= (char) default_charset_info->number;
8164 8165 8166
  int2store(end+3, mpvio->thd->server_status);
  int2store(end+5, thd->client_capabilities >> 16);
  end[7]= data_len;
8167
  DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
8168
  bzero(end + 8, 10);
8169 8170
  end+= 18;
  /* write scramble tail */
8171 8172
  end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
                      data_len - SCRAMBLE_LENGTH_323);
8173 8174 8175 8176
  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
8177 8178
  int res= my_net_write(&mpvio->thd->net, (uchar*) buff,
                        (size_t) (end - buff + 1)) ||
8179 8180
           net_flush(&mpvio->thd->net);
  my_afree(buff);
8181
  DBUG_RETURN (res);
8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237
}

static bool secure_auth(THD *thd)
{
  if (!opt_secure_auth)
    return 0;

  /*
    If the server is running in secure auth mode, short scrambles are 
    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:
   
    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 };
8238
  DBUG_ENTER("send_plugin_request_packet");
8239 8240 8241 8242

  mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART

  const char *client_auth_plugin=
8243
    ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
8244

8245
  DBUG_EXECUTE_IF("auth_disconnect", { vio_close(net->vio); DBUG_RETURN(1); });
8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261
  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
8262
    DBUG_RETURN (secure_auth(mpvio->thd) ||
8263 8264
                 my_net_write(net, switch_plugin_request_buf, 1) ||
                 net_flush(net));
8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 8277 8278

  /*
    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));
8279
    DBUG_RETURN (1);
8280 8281
  }

8282 8283 8284 8285 8286 8287
  DBUG_PRINT("info", ("requesting client to use the %s plugin", 
                      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));
8288 8289 8290 8291 8292 8293
}

#ifndef NO_EMBEDDED_ACCESS_CHECKS
/**
   Finds acl entry in user database for authentication purposes.
   
8294 8295
   Finds a user and copies it into mpvio. Creates a fake user
   if no matching user account is found.
8296 8297 8298 8299 8300

   @note find_acl_user is not the same, because it doesn't take into
   account the case when user is not empty, but acl_user->user is empty

   @retval 0    found
8301
   @retval 1    error
8302
*/
8303
static bool find_mpvio_user(MPVIO_EXT *mpvio)
8304
{
Sergei Golubchik's avatar
Sergei Golubchik committed
8305
  Security_context *sctx= mpvio->thd->security_ctx;
8306
  DBUG_ENTER("find_mpvio_user");
8307 8308
  DBUG_ASSERT(mpvio->acl_user == 0);

8309
  mysql_mutex_lock(&acl_cache->lock);
8310
  for (uint i=0; i < acl_users.elements; i++)
8311
  {
8312
    ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
8313 8314 8315 8316 8317 8318 8319
    if ((!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user)) &&
        compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
    {
      mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root);
      break;
    }
  }
8320
  mysql_mutex_unlock(&acl_cache->lock);
8321 8322 8323

  if (!mpvio->acl_user)
  {
8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337
    /*
      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
8338
    mysql_mutex_lock(&acl_cache->lock);
8339 8340
    if (!acl_users.elements)
    {
Sergei Golubchik's avatar
Sergei Golubchik committed
8341
      mysql_mutex_unlock(&acl_cache->lock);
8342
      login_failed_error(mpvio->thd);
Sergei Golubchik's avatar
Sergei Golubchik committed
8343
      DBUG_RETURN(1);
8344
    }
8345 8346 8347
    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
8348
    mysql_mutex_unlock(&acl_cache->lock);
8349 8350

    mpvio->make_it_fail= true;
8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363
  }

  /* 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));
8364
    DBUG_RETURN (1);
8365 8366 8367
  }

  mpvio->auth_info.user_name= sctx->user;
Sergei Golubchik's avatar
Sergei Golubchik committed
8368
  mpvio->auth_info.user_name_length= strlen(sctx->user);
8369
  mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
8370 8371 8372
  mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length;
  strmake_buf(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
              mpvio->acl_user->user : "");
8373

8374 8375 8376 8377 8378 8379 8380
  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);
8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393
}
#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 */
8394
  char *passwd= strend(user) + 1;
8395 8396
  uint user_len= passwd - user - 1;
  char *db= passwd;
Sergei Golubchik's avatar
Sergei Golubchik committed
8397
  char db_buff[SAFE_NAME_LEN + 1];            // buffer to store db in utf8
8398 8399
  char user_buff[USERNAME_LENGTH + 1];	      // buffer to store user in utf8
  uint dummy_errors;
8400
  DBUG_ENTER ("parse_com_change_user_packet");
8401 8402 8403 8404

  if (passwd >= end)
  {
    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
8405
    DBUG_RETURN (1);
8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418
  }

  /*
    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 ?
8419
                    (uchar) (*passwd++) : strlen(passwd));
8420 8421 8422 8423 8424 8425 8426 8427 8428

  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));
8429
    DBUG_RETURN (1);
8430 8431 8432 8433 8434 8435
  }

  uint db_len= strlen(db);

  char *ptr= db + db_len + 1;

8436
  if (ptr + 1 < end)
8437
  {
Sergei Golubchik's avatar
Sergei Golubchik committed
8438
    if (thd_init_client_charset(thd, uint2korr(ptr)))
8439
      DBUG_RETURN(1);
8440 8441 8442 8443
    thd->update_charset();
  }

  /* Convert database and user names to utf8 */
8444
  db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
8445 8446
                           db, db_len, thd->charset(), &dummy_errors);

8447
  user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
8448 8449 8450 8451
                             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
8452
    DBUG_RETURN(1);
8453 8454 8455

  /* Clear variables that are allocated */
  thd->user_connect= 0;
8456
  strmake_buf(sctx->priv_user, sctx->user);
8457 8458

  if (thd->make_lex_string(&mpvio->db, db_buff, db_len, 0) == 0)
8459
    DBUG_RETURN(1); /* The error is set by make_lex_string(). */
8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471

  /*
    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;
8472
    DBUG_RETURN(0);
8473 8474 8475
  }

#ifndef NO_EMBEDDED_ACCESS_CHECKS
8476
  thd->password= passwd_len > 0;
8477
  if (find_mpvio_user(mpvio))
8478
    DBUG_RETURN(1);
8479 8480 8481 8482 8483 8484 8485 8486

  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));
8487
      DBUG_RETURN(1);
8488
    }
8489
    client_plugin= fix_plugin_ptr(client_plugin);
8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 8505 8506 8507
  }
  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
        old_password_plugin, otherwise MySQL will think that server 
        and client plugins don't match.
      */
      if (mpvio->acl_user->auth_string.length == 0)
        mpvio->acl_user->plugin= old_password_plugin_name;
    }
  }
8508

8509
  DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
8510 8511 8512 8513
  /* 
    Remember the data part of the packet, to present it to plugin in 
    read_packet() 
  */
8514 8515 8516 8517 8518 8519
  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

8520
  DBUG_RETURN (0);
8521 8522 8523 8524 8525 8526 8527 8528 8529 8530 8531 8532 8533 8534 8535 8536
}


/* 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
8537 8538 8539 8540 8541 8542
  /*
    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);

8543
  if (mpvio->connect_errors)
Sergei Golubchik's avatar
Sergei Golubchik committed
8544
    reset_host_errors(thd->main_security_ctx.ip);
8545 8546 8547 8548

  ulong client_capabilities= uint2korr(net->read_pos);
  if (client_capabilities & CLIENT_PROTOCOL_41)
  {
8549
    if (pkt_len < 4)
Michael Widenius's avatar
Michael Widenius committed
8550
      return packet_error;
8551 8552 8553 8554 8555 8556 8557 8558 8559
    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
8560
    unsigned long errptr __attribute__((unused));
8561 8562 8563 8564 8565 8566

    /* Do the SSL layering. */
    if (!ssl_acceptor_fd)
      return packet_error;

    DBUG_PRINT("info", ("IO layer change in progress..."));
8567
    if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr))
8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 8579 8580 8581 8582
    {
      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;
    }
  }

8583 8584 8585 8586 8587 8588 8589 8590 8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 8601
  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;
  }

8602 8603 8604
  if (end >= (char*) net->read_pos+ pkt_len +2)
    return packet_error;

Sergei Golubchik's avatar
Sergei Golubchik committed
8605 8606 8607 8608 8609
  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;

8610 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620
  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
8621
  char db_buff[SAFE_NAME_LEN + 1];      // buffer to store db in utf8
8622 8623 8624 8625 8626 8627 8628 8629 8630 8631 8632 8633 8634 8635 8636 8637
  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
8638 8639
  db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
    db + passwd_len + 1 : 0;
8640

Sergei Golubchik's avatar
Sergei Golubchik committed
8641
  if (passwd + passwd_len + test(db) > (char *)net->read_pos + pkt_len)
8642 8643
    return packet_error;

Sergei Golubchik's avatar
Sergei Golubchik committed
8644 8645 8646
  /* strlen() can't be easily deleted without changing protocol */
  db_len= db ? strlen(db) : 0;

8647 8648 8649 8650 8651
  char *client_plugin= passwd + passwd_len + (db ? db_len + 1 : 0);

  /* Since 4.1 all database names are stored in utf8 */
  if (db)
  {
8652
    db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
8653 8654 8655 8656
                             db, db_len, thd->charset(), &dummy_errors);
    db= db_buff;
  }

8657
  user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
8658 8659 8660 8661 8662 8663 8664 8665 8666 8667 8668
                             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;
  }

8669 8670
  /*
    Clip username to allowed length in characters (not bytes).  This is
8671 8672
    mostly for backward compatibility (to truncate long usernames, as
    old 5.1 did)
8673 8674 8675 8676 8677 8678
  */
  {
    CHARSET_INFO *cs= system_charset_info;
    int           err;

    user_len= (uint) cs->cset->well_formed_len(cs, user, user + user_len,
8679
                                               username_char_length, &err);
8680 8681 8682
    user[user_len]= '\0';
  }

8683 8684 8685 8686
  Security_context *sctx= thd->security_ctx;

  if (thd->make_lex_string(&mpvio->db, db, db_len, 0) == 0)
    return packet_error; /* The error is set by make_lex_string(). */
Sergei Golubchik's avatar
Sergei Golubchik committed
8687
  my_free(sctx->user);
8688 8689 8690
  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
8691

8692 8693 8694 8695 8696 8697 8698 8699 8700 8701 8702 8703 8704 8705
  /*
    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;
  }

8706
  thd->password= passwd_len > 0;
8707
  if (find_mpvio_user(mpvio))
8708 8709
    return packet_error;

8710 8711
  if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) &&
      (client_plugin < (char *)net->read_pos + pkt_len))
8712
  {
8713
    client_plugin= fix_plugin_ptr(client_plugin);
8714 8715 8716
  }
  else
  {
8717 8718 8719
    /* Some clients lie. Sad, but true */
    thd->client_capabilities &= ~CLIENT_PLUGIN_AUTH;

8720 8721 8722 8723 8724 8725 8726 8727 8728 8729 8730 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761
    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
        old_password_plugin, otherwise MySQL will think that server 
        and client plugins don't match.
      */
      if (mpvio->acl_user->auth_string.length == 0)
        mpvio->acl_user->plugin= old_password_plugin_name;
    }
  }
  
  /*
    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=
8762
    ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
8763 8764 8765 8766 8767 8768

  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,
8769
                                   (uchar*) mpvio->cached_server_packet.pkt,
8770 8771 8772
                                   mpvio->cached_server_packet.pkt_len))
      return packet_error;

8773 8774
    passwd_len= my_net_read(&thd->net);
    passwd= (char*)thd->net.read_pos;
8775 8776
  }

8777
  *buff= (uchar*) passwd;
8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 8797
  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)
{
8798
  MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
8799
  int res;
8800
  DBUG_ENTER("server_mpvio_write_packet");
8801 8802 8803

  /* reset cached_client_reply */
  mpvio->cached_client_reply.pkt= 0;
Sergei Golubchik's avatar
Sergei Golubchik committed
8804

8805 8806
  /* for the 1st packet we wrap plugin data into the handshake packet */
  if (mpvio->packets_written == 0)
8807
    res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
8808 8809 8810 8811 8812 8813 8814 8815 8816 8817 8818 8819 8820 8821 8822 8823 8824 8825 8826
  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++;
8827
  DBUG_RETURN(res);
8828 8829 8830 8831 8832 8833 8834 8835 8836 8837 8838 8839 8840 8841
}

/**
  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)
{
8842
  MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
8843
  ulong pkt_len;
8844
  DBUG_ENTER("server_mpvio_read_packet");
8845 8846 8847 8848 8849 8850 8851 8852 8853 8854 8855
  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);
  }
8856
  else if (mpvio->cached_client_reply.pkt)
8857 8858 8859 8860 8861 8862 8863 8864 8865 8866
  {
    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=
8867
      ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
8868 8869 8870 8871 8872
    if (client_auth_plugin == 0 ||
        my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
                      client_auth_plugin) == 0)
    {
      mpvio->status= MPVIO_EXT::FAILURE;
8873
      *buf= (uchar*) mpvio->cached_client_reply.pkt;
8874 8875
      mpvio->cached_client_reply.pkt= 0;
      mpvio->packets_read++;
8876 8877 8878 8879

      if (mpvio->make_it_fail)
        goto err;

8880
      DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
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 8910 8911
    /*
      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
8912
    *buf= mpvio->thd->net.read_pos;
8913

8914 8915 8916
  if (mpvio->make_it_fail)
    goto err;

8917
  DBUG_RETURN((int)pkt_len);
8918 8919

err:
8920
  if (mpvio->status == MPVIO_EXT::FAILURE)
8921
  {
Sergei Golubchik's avatar
Sergei Golubchik committed
8922
    inc_host_errors(mpvio->thd->security_ctx->ip);
Sergei Golubchik's avatar
Sergei Golubchik committed
8923
    if (!mpvio->thd->is_error())
Sergei Golubchik's avatar
Sergei Golubchik committed
8924 8925 8926 8927 8928 8929
    {
      if (mpvio->make_it_fail)
        login_failed_error(mpvio->thd);
      else
        my_error(ER_HANDSHAKE_ERROR, MYF(0));
    }
8930
  }
8931
  DBUG_RETURN(-1);
8932 8933 8934 8935 8936 8937 8938 8939 8940
}

/**
  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)
{
8941
  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
8942 8943 8944
  mpvio_info(mpvio->thd->net.vio, info);
}

8945
static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
8946
{
Sergei Golubchik's avatar
Sergei Golubchik committed
8947
#ifdef HAVE_OPENSSL
8948 8949
  Vio *vio= thd->net.vio;
  SSL *ssl= (SSL *) vio->ssl_arg;
8950 8951 8952 8953 8954 8955 8956 8957 8958 8959 8960 8961 8962
  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
8963
#ifdef HAVE_OPENSSL
8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975 8976 8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988
  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)
    {
8989 8990 8991
      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)))
8992 8993 8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004
      {
        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)
    {
9005 9006
      char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
      DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 9022
                         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);
9023
      DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
9024
                         acl_user->x509_subject, ptr));
9025
      if (strcmp(acl_user->x509_subject, ptr))
9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049
      {
        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:
    /*
      If we don't have SSL but SSL is required for this user the 
      authentication should fail.
    */
    return 1;
#endif /* HAVE_OPENSSL */
  }
  return 1;
}

9050 9051

static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
9052 9053 9054 9055
                        MPVIO_EXT *mpvio)
{
  int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
  bool unlock_plugin= false;
Sergei Golubchik's avatar
Sergei Golubchik committed
9056
  plugin_ref plugin= NULL;
9057 9058 9059

  if (auth_plugin_name->str == native_password_plugin_name.str)
    plugin= native_password_plugin;
9060
#ifndef EMBEDDED_LIBRARY
Michael Widenius's avatar
Michael Widenius committed
9061
  else if (auth_plugin_name->str == old_password_plugin_name.str)
9062
    plugin= old_password_plugin;
Michael Widenius's avatar
Michael Widenius committed
9063 9064
  else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
                                           MYSQL_AUTHENTICATION_PLUGIN)))
9065 9066 9067 9068 9069 9070 9071 9072
    unlock_plugin= true;
#endif

  mpvio->plugin= plugin;
  old_status= mpvio->status;

  if (plugin)
  {
9073
    st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087
    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);
    }
9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108 9109 9110 9111 9112

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

9113

9114 9115 9116 9117 9118 9119 9120 9121 9122 9123 9124 9125 9126 9127
/**
  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
9128 9129
bool acl_authenticate(THD *thd, uint connect_errors,
                      uint com_change_user_pkt_len)
9130 9131 9132
{
  int res= CR_OK;
  MPVIO_EXT mpvio;
9133
  const LEX_STRING *auth_plugin_name= default_auth_plugin_name;
9134 9135 9136 9137 9138 9139 9140 9141 9142 9143 9144
  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;
9145
  mpvio.make_it_fail= false;
Sergei Golubchik's avatar
Sergei Golubchik committed
9146 9147 9148
  mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
  mpvio.auth_info.host_or_ip_length= 
    (unsigned int) strlen(thd->security_ctx->host_or_ip);
9149

9150
  DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
9151

9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 9162 9163 9164 9165 9166 9167 9168 9169 9170 9171 9172 9173
  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.
    */
9174 9175

    res= do_auth_once(thd, auth_plugin_name, &mpvio);  
9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190
  }

  /*
    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);
  }
9191 9192 9193 9194 9195 9196
  if (mpvio.make_it_fail)
  {
    mpvio.status= MPVIO_EXT::FAILURE;
    res= CR_ERROR;
  }
 
9197
  Security_context *sctx= thd->security_ctx;
9198
  const ACL_USER *acl_user= mpvio.acl_user;
9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227

  thd->password= mpvio.auth_info.password_used;  // remember for error messages 

  /*
    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())
9228
      login_failed_error(thd);
9229 9230 9231
    DBUG_RETURN(1);
  }

9232 9233
  sctx->proxy_user[0]= 0;

9234 9235
  if (initialized) // if not --skip-grant-tables
  {
9236 9237
#ifndef NO_EMBEDDED_ACCESS_CHECKS
    bool is_proxy_user= FALSE;
9238 9239
    const char *auth_user = acl_user->user ? acl_user->user : "";
    ACL_PROXY_USER *proxy_user;
9240
    /* check if the user is allowed to proxy as another user */
9241 9242 9243 9244
    proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
                                    mpvio.auth_info.authenticated_as,
                                          &is_proxy_user);
    if (is_proxy_user)
9245
    {
9246 9247 9248 9249 9250 9251
      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
9252
          login_failed_error(thd);
9253 9254
        DBUG_RETURN(1);
      }
9255

9256
      my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
9257 9258
                  "'%s'@'%s'", auth_user,
                  acl_user->host.hostname ? acl_user->host.hostname : "");
9259 9260 9261 9262 9263 9264 9265 9266

      /* we're proxying : find the proxy user definition */
      mysql_mutex_lock(&acl_cache->lock);
      acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ? 
                                    proxy_user->get_proxied_host() : "",
                                    mpvio.auth_info.authenticated_as, TRUE);
      if (!acl_proxy_user)
      {
9267
        mysql_mutex_unlock(&acl_cache->lock);
9268
        if (!thd->is_error())
Sergei Golubchik's avatar
Sergei Golubchik committed
9269
          login_failed_error(thd);
9270 9271 9272 9273 9274
        DBUG_RETURN(1);
      }
      acl_user= acl_proxy_user->copy(thd->mem_root);
      mysql_mutex_unlock(&acl_cache->lock);
    }
9275 9276
#endif

9277
    sctx->master_access= acl_user->access;
9278
    if (acl_user->user)
9279
      strmake_buf(sctx->priv_user, acl_user->user);
9280 9281 9282
    else
      *sctx->priv_user= 0;

9283
    if (acl_user->host.hostname)
9284
      strmake_buf(sctx->priv_host, acl_user->host.hostname);
9285 9286 9287 9288 9289 9290 9291 9292 9293 9294
    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))
    {
9295
      login_failed_error(thd);
9296 9297 9298
      DBUG_RETURN(1);
    }

9299 9300 9301 9302 9303 9304 9305 9306
    /*
      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 ||
9307
         acl_user->user_resource.conn_per_hour ||
9308
         acl_user->user_resource.user_conn || max_user_connections_checking) &&
9309 9310 9311 9312 9313 9314 9315 9316 9317 9318 9319 9320
        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))
      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 ||
9321
       max_user_connections_checking) &&
9322 9323
      check_for_max_user_connections(thd, thd->user_connect))
  {
9324 9325
    /* Ensure we don't decrement thd->user_connections->connections twice */
    thd->user_connect= 0;
9326 9327 9328 9329 9330 9331 9332 9333 9334 9335 9336 9337 9338 9339 9340 9341
    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))
  {
9342
    mysql_mutex_lock(&LOCK_connection_count);
9343 9344
    bool count_ok= (*thd->scheduler->connection_count <=
                    *thd->scheduler->max_connections);
9345
    mysql_mutex_unlock(&LOCK_connection_count);
9346 9347 9348 9349 9350 9351 9352 9353 9354 9355 9356 9357 9358 9359 9360 9361 9362 9363 9364 9365
    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. */
9366
      status_var_increment(thd->status_var.access_denied_errors);
9367 9368 9369 9370 9371 9372
      DBUG_RETURN(1);
    }
  }

  thd->net.net_skip_rest_factor= 2;  // skip at most 2*max_packet_size

9373 9374 9375
  if (mpvio.auth_info.external_user[0])
    sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));

9376
  if (res == CR_OK_HANDSHAKE_COMPLETE)
9377
    thd->stmt_da->disable_status();
9378 9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392
  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.
*/
9393
static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
9394 9395 9396 9397
                                        MYSQL_SERVER_AUTH_INFO *info)
{
  uchar *pkt;
  int pkt_len;
9398
  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
9399
  THD *thd=mpvio->thd;
9400
  DBUG_ENTER("native_password_authenticate");
9401 9402 9403

  /* generate the scramble, or reuse the old one */
  if (thd->scramble[SCRAMBLE_LENGTH])
9404
  {
9405
    create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
9406 9407
    /* and send it to the client */
    if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
Sergei Golubchik's avatar
Sergei Golubchik committed
9408
      DBUG_RETURN(CR_ERROR);
9409
  }
9410 9411 9412 9413 9414 9415 9416 9417 9418 9419 9420 9421 9422 9423 9424 9425 9426 9427 9428 9429 9430 9431 9432 9433 9434 9435 9436 9437 9438 9439 9440 9441 9442 9443 9444 9445 9446 9447 9448 9449

  /* 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)
9450 9451
    DBUG_RETURN(CR_ERROR);
  DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
9452 9453

#ifdef NO_EMBEDDED_ACCESS_CHECKS
9454
  DBUG_RETURN(CR_OK);
9455 9456 9457
#endif

  if (pkt_len == 0) /* no password */
Sergei Golubchik's avatar
Sergei Golubchik committed
9458
    DBUG_RETURN(info->auth_string[0] ? CR_ERROR : CR_OK);
9459

9460
  info->password_used= PASSWORD_USED_YES;
9461
  if (pkt_len == SCRAMBLE_LENGTH)
9462 9463 9464 9465
  {
    if (!mpvio->acl_user->salt_len)
      DBUG_RETURN(CR_ERROR);

Sergei Golubchik's avatar
Sergei Golubchik committed
9466 9467 9468 9469
    if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt))
      DBUG_RETURN(CR_ERROR);
    else
      DBUG_RETURN(CR_OK);
9470
  }
9471

Sergei Golubchik's avatar
Sergei Golubchik committed
9472
  inc_host_errors(mpvio->thd->security_ctx->ip);
Guilhem Bichot's avatar
Guilhem Bichot committed
9473
  my_error(ER_HANDSHAKE_ERROR, MYF(0));
9474
  DBUG_RETURN(CR_ERROR);
9475 9476 9477 9478 9479 9480 9481
}

static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, 
                                     MYSQL_SERVER_AUTH_INFO *info)
{
  uchar *pkt;
  int pkt_len;
9482
  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
9483 9484 9485 9486
  THD *thd=mpvio->thd;

  /* generate the scramble, or reuse the old one */
  if (thd->scramble[SCRAMBLE_LENGTH])
9487
  {
9488
    create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
9489 9490 9491 9492
    /* and send it to the client */
    if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
      return CR_ERROR;
  }
9493 9494 9495 9496 9497 9498 9499 9500 9501 9502 9503 9504 9505 9506

  /* 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.
  */
9507
  if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
9508 9509 9510 9511 9512 9513 9514 9515
    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;

9516
  info->password_used= PASSWORD_USED_YES;
9517 9518

  if (pkt_len == SCRAMBLE_LENGTH_323)
9519 9520 9521 9522
  {
    if (!mpvio->acl_user->salt_len)
      return CR_ERROR;

Sergei Golubchik's avatar
Sergei Golubchik committed
9523
    return check_scramble_323(pkt, thd->scramble,
9524 9525
                             (ulong *) mpvio->acl_user->salt) ? 
                             CR_ERROR : CR_OK;
9526
  }
9527

Sergei Golubchik's avatar
Sergei Golubchik committed
9528
  inc_host_errors(mpvio->thd->security_ctx->ip);
Guilhem Bichot's avatar
Guilhem Bichot committed
9529
  my_error(ER_HANDSHAKE_ERROR, MYF(0));
9530 9531 9532 9533 9534 9535 9536 9537 9538 9539 9540 9541 9542 9543 9544 9545 9546
  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
9547 9548 9549 9550 9551 9552 9553 9554 9555 9556 9557 9558 9559 9560 9561 9562 9563 9564 9565 9566 9567 9568 9569 9570 9571 9572 9573 9574 9575 9576 9577 9578
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;