~ubuntu-branches/ubuntu/quantal/mysql-5.5/quantal-updates

« back to all changes in this revision

Viewing changes to .pc/CVE-2012-5611.patch/sql/sql_acl.cc

  • Committer: Package Import Robot
  • Author(s): Marc Deslauriers
  • Date: 2013-01-16 10:11:37 UTC
  • mfrom: (1.1.11)
  • Revision ID: package-import@ubuntu.com-20130116101137-w2lant30o21mzi0b
Tags: 5.5.29-0ubuntu0.12.10.1
* SECURITY UPDATE: Update to 5.5.29 to fix security issues (LP: #1100264)
  - http://www.oracle.com/technetwork/topics/security/cpujan2013-1515902.html
* debian/patches/CVE-2012-5611.patch: removed, included upstream.
* debian/patches/38_scripts__mysqld_safe.sh__signals.patch: refreshed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
2
 
 
3
 
   This program is free software; you can redistribute it and/or modify
4
 
   it under the terms of the GNU General Public License as published by
5
 
   the Free Software Foundation; version 2 of the License.
6
 
 
7
 
   This program is distributed in the hope that it will be useful,
8
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 
   GNU General Public License for more details.
11
 
 
12
 
   You should have received a copy of the GNU General Public License
13
 
   along with this program; if not, write to the Free Software
14
 
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
 
 
16
 
 
17
 
/*
18
 
  The privileges are saved in the following tables:
19
 
  mysql/user     ; super user who are allowed to do almost anything
20
 
  mysql/host     ; host privileges. This is used if host is empty in mysql/db.
21
 
  mysql/db       ; database privileges / user
22
 
 
23
 
  data in tables is sorted according to how many not-wild-cards there is
24
 
  in the relevant fields. Empty strings comes last.
25
 
*/
26
 
 
27
 
#include "my_global.h"                          /* NO_EMBEDDED_ACCESS_CHECKS */
28
 
#include "sql_priv.h"
29
 
#include "sql_acl.h"         // MYSQL_DB_FIELD_COUNT, ACL_ACCESS
30
 
#include "sql_base.h"                           // close_mysql_tables
31
 
#include "key.h"             // key_copy, key_cmp_if_same, key_restore
32
 
#include "sql_show.h"        // append_identifier
33
 
#include "sql_table.h"                         // build_table_filename
34
 
#include "hash_filo.h"
35
 
#include "sql_parse.h"                          // check_access
36
 
#include "sql_view.h"                           // VIEW_ANY_ACL
37
 
#include "records.h"              // READ_RECORD, read_record_info,
38
 
                                  // init_read_record, end_read_record
39
 
#include "rpl_filter.h"           // rpl_filter
40
 
#include <m_ctype.h>
41
 
#include <stdarg.h>
42
 
#include "sp_head.h"
43
 
#include "sp.h"
44
 
#include "transaction.h"
45
 
#include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
46
 
#include "records.h"             // init_read_record, end_read_record
47
 
#include <sql_common.h>
48
 
#include <mysql/plugin_auth.h>
49
 
#include "sql_connect.h"
50
 
#include "hostname.h"
51
 
#include "sql_db.h"
52
 
 
53
 
bool mysql_user_table_is_in_short_password_format= false;
54
 
 
55
 
static const
56
 
TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
57
 
  {
58
 
    { C_STRING_WITH_LEN("Host") },            
59
 
    { C_STRING_WITH_LEN("char(60)") },
60
 
    {NULL, 0}
61
 
  }, 
62
 
  {
63
 
    { C_STRING_WITH_LEN("Db") },            
64
 
    { C_STRING_WITH_LEN("char(64)") },
65
 
    {NULL, 0}
66
 
  }, 
67
 
  {
68
 
    { C_STRING_WITH_LEN("User") },
69
 
    { C_STRING_WITH_LEN("char(16)") },
70
 
    {NULL, 0}
71
 
  },
72
 
  {
73
 
    { C_STRING_WITH_LEN("Select_priv") },
74
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
75
 
    { C_STRING_WITH_LEN("utf8") }
76
 
  },
77
 
  {
78
 
    { C_STRING_WITH_LEN("Insert_priv") },
79
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
80
 
    { C_STRING_WITH_LEN("utf8") }
81
 
  },
82
 
  {
83
 
    { C_STRING_WITH_LEN("Update_priv") },
84
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
85
 
    { C_STRING_WITH_LEN("utf8") }
86
 
  },
87
 
  {
88
 
    { C_STRING_WITH_LEN("Delete_priv") },
89
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
90
 
    { C_STRING_WITH_LEN("utf8") }
91
 
  },
92
 
  {
93
 
    { C_STRING_WITH_LEN("Create_priv") },
94
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
95
 
    { C_STRING_WITH_LEN("utf8") }
96
 
  },
97
 
  {
98
 
    { C_STRING_WITH_LEN("Drop_priv") },
99
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
100
 
    { C_STRING_WITH_LEN("utf8") }
101
 
  },
102
 
  {
103
 
    { C_STRING_WITH_LEN("Grant_priv") },
104
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
105
 
    { C_STRING_WITH_LEN("utf8") }
106
 
  },
107
 
  {
108
 
    { C_STRING_WITH_LEN("References_priv") },
109
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
110
 
    { C_STRING_WITH_LEN("utf8") }
111
 
  },
112
 
  {
113
 
    { C_STRING_WITH_LEN("Index_priv") },
114
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
115
 
    { C_STRING_WITH_LEN("utf8") }
116
 
  },
117
 
  {
118
 
    { C_STRING_WITH_LEN("Alter_priv") },
119
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
120
 
    { C_STRING_WITH_LEN("utf8") }
121
 
  },
122
 
  {
123
 
    { C_STRING_WITH_LEN("Create_tmp_table_priv") },
124
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
125
 
    { C_STRING_WITH_LEN("utf8") }
126
 
  },
127
 
  {
128
 
    { C_STRING_WITH_LEN("Lock_tables_priv") },
129
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
130
 
    { C_STRING_WITH_LEN("utf8") }
131
 
  },
132
 
  {
133
 
    { C_STRING_WITH_LEN("Create_view_priv") },
134
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
135
 
    { C_STRING_WITH_LEN("utf8") }
136
 
  },
137
 
  {
138
 
    { C_STRING_WITH_LEN("Show_view_priv") },
139
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
140
 
    { C_STRING_WITH_LEN("utf8") }
141
 
  },
142
 
  {
143
 
    { C_STRING_WITH_LEN("Create_routine_priv") },
144
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
145
 
    { C_STRING_WITH_LEN("utf8") }
146
 
  },
147
 
  {
148
 
    { C_STRING_WITH_LEN("Alter_routine_priv") },
149
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
150
 
    { C_STRING_WITH_LEN("utf8") }
151
 
  },
152
 
  {
153
 
    { C_STRING_WITH_LEN("Execute_priv") },
154
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
155
 
    { C_STRING_WITH_LEN("utf8") }
156
 
  },
157
 
  {
158
 
    { C_STRING_WITH_LEN("Event_priv") },
159
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
160
 
    { C_STRING_WITH_LEN("utf8") }
161
 
  },
162
 
  {
163
 
    { C_STRING_WITH_LEN("Trigger_priv") },
164
 
    { C_STRING_WITH_LEN("enum('N','Y')") },
165
 
    { C_STRING_WITH_LEN("utf8") }
166
 
  }
167
 
};
168
 
 
169
 
const TABLE_FIELD_DEF
170
 
  mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields};
171
 
 
172
 
static LEX_STRING native_password_plugin_name= {
173
 
  C_STRING_WITH_LEN("mysql_native_password")
174
 
};
175
 
  
176
 
static LEX_STRING old_password_plugin_name= {
177
 
  C_STRING_WITH_LEN("mysql_old_password")
178
 
};
179
 
  
180
 
/// @todo make it configurable
181
 
LEX_STRING *default_auth_plugin_name= &native_password_plugin_name;
182
 
 
183
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
184
 
static plugin_ref old_password_plugin;
185
 
#endif
186
 
static plugin_ref native_password_plugin;
187
 
 
188
 
/* Classes */
189
 
 
190
 
struct acl_host_and_ip
191
 
{
192
 
  char *hostname;
193
 
  long ip, ip_mask;                      // Used with masked ip:s
194
 
};
195
 
 
196
 
class ACL_ACCESS {
197
 
public:
198
 
  ulong sort;
199
 
  ulong access;
200
 
};
201
 
 
202
 
/* ACL_HOST is used if no host is specified */
203
 
 
204
 
class ACL_HOST :public ACL_ACCESS
205
 
{
206
 
public:
207
 
  acl_host_and_ip host;
208
 
  char *db;
209
 
};
210
 
 
211
 
class ACL_USER :public ACL_ACCESS
212
 
{
213
 
public:
214
 
  acl_host_and_ip host;
215
 
  uint hostname_length;
216
 
  USER_RESOURCES user_resource;
217
 
  char *user;
218
 
  uint8 salt[SCRAMBLE_LENGTH + 1];       // scrambled password in binary form
219
 
  uint8 salt_len;        // 0 - no password, 4 - 3.20, 8 - 4.0,  20 - 4.1.1 
220
 
  enum SSL_type ssl_type;
221
 
  const char *ssl_cipher, *x509_issuer, *x509_subject;
222
 
  LEX_STRING plugin;
223
 
  LEX_STRING auth_string;
224
 
 
225
 
  ACL_USER *copy(MEM_ROOT *root)
226
 
  {
227
 
    ACL_USER *dst= (ACL_USER *) alloc_root(root, sizeof(ACL_USER));
228
 
    if (!dst)
229
 
      return 0;
230
 
    *dst= *this;
231
 
    dst->user= safe_strdup_root(root, user);
232
 
    dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
233
 
    dst->x509_issuer= safe_strdup_root(root, x509_issuer);
234
 
    dst->x509_subject= safe_strdup_root(root, x509_subject);
235
 
    if (plugin.str == native_password_plugin_name.str ||
236
 
        plugin.str == old_password_plugin_name.str)
237
 
      dst->plugin= plugin;
238
 
    else
239
 
      dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
240
 
    dst->auth_string.str= safe_strdup_root(root, auth_string.str);
241
 
    dst->host.hostname= safe_strdup_root(root, host.hostname);
242
 
    return dst;
243
 
  }
244
 
};
245
 
 
246
 
class ACL_DB :public ACL_ACCESS
247
 
{
248
 
public:
249
 
  acl_host_and_ip host;
250
 
  char *user,*db;
251
 
};
252
 
 
253
 
 
254
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
255
 
static void update_hostname(acl_host_and_ip *host, const char *hostname);
256
 
static ulong get_sort(uint count,...);
257
 
static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
258
 
                             const char *ip);
259
 
static bool show_proxy_grants (THD *thd, LEX_USER *user,
260
 
                               char *buff, size_t buffsize);
261
 
 
262
 
class ACL_PROXY_USER :public ACL_ACCESS
263
 
{
264
 
  acl_host_and_ip host;
265
 
  const char *user;
266
 
  acl_host_and_ip proxied_host;
267
 
  const char *proxied_user;
268
 
  bool with_grant;
269
 
 
270
 
  typedef enum { 
271
 
    MYSQL_PROXIES_PRIV_HOST, 
272
 
    MYSQL_PROXIES_PRIV_USER, 
273
 
    MYSQL_PROXIES_PRIV_PROXIED_HOST,
274
 
    MYSQL_PROXIES_PRIV_PROXIED_USER, 
275
 
    MYSQL_PROXIES_PRIV_WITH_GRANT,
276
 
    MYSQL_PROXIES_PRIV_GRANTOR,
277
 
    MYSQL_PROXIES_PRIV_TIMESTAMP } old_acl_proxy_users;
278
 
public:
279
 
  ACL_PROXY_USER () {};
280
 
 
281
 
  void init(const char *host_arg, const char *user_arg,
282
 
       const char *proxied_host_arg, const char *proxied_user_arg,
283
 
       bool with_grant_arg)
284
 
  {
285
 
    user= (user_arg && *user_arg) ? user_arg : NULL;
286
 
    update_hostname (&host, 
287
 
                     (host_arg && *host_arg) ? host_arg : NULL);
288
 
    proxied_user= (proxied_user_arg && *proxied_user_arg) ? 
289
 
      proxied_user_arg : NULL;
290
 
    update_hostname (&proxied_host, 
291
 
                     (proxied_host_arg && *proxied_host_arg) ?
292
 
                     proxied_host_arg : NULL);
293
 
    with_grant= with_grant_arg;
294
 
    sort= get_sort(4, host.hostname, user,
295
 
                   proxied_host.hostname, proxied_user);
296
 
  }
297
 
 
298
 
  void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
299
 
       const char *proxied_host_arg, const char *proxied_user_arg,
300
 
       bool with_grant_arg)
301
 
  {
302
 
    init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
303
 
          (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
304
 
          (proxied_host_arg && *proxied_host_arg) ? 
305
 
            strdup_root (mem, proxied_host_arg) : NULL,
306
 
          (proxied_user_arg && *proxied_user_arg) ? 
307
 
            strdup_root (mem, proxied_user_arg) : NULL,
308
 
          with_grant_arg);
309
 
  }
310
 
 
311
 
  void init(TABLE *table, MEM_ROOT *mem)
312
 
  {
313
 
    init (get_field(mem, table->field[MYSQL_PROXIES_PRIV_HOST]),
314
 
          get_field(mem, table->field[MYSQL_PROXIES_PRIV_USER]),
315
 
          get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]),
316
 
          get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]),
317
 
          table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->val_int() != 0);
318
 
  }
319
 
 
320
 
  bool get_with_grant() { return with_grant; }
321
 
  const char *get_user() { return user; }
322
 
  const char *get_host() { return host.hostname; }
323
 
  const char *get_proxied_user() { return proxied_user; }
324
 
  const char *get_proxied_host() { return proxied_host.hostname; }
325
 
  void set_user(MEM_ROOT *mem, const char *user_arg) 
326
 
  { 
327
 
    user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
328
 
  }
329
 
  void set_host(MEM_ROOT *mem, const char *host_arg) 
330
 
  { 
331
 
    update_hostname(&host, 
332
 
                    (host_arg && *host_arg) ? 
333
 
                    strdup_root(mem, host_arg) : NULL);
334
 
  }
335
 
 
336
 
  bool check_validity(bool check_no_resolve)
337
 
  {
338
 
    if (check_no_resolve && 
339
 
        (hostname_requires_resolving(host.hostname) ||
340
 
         hostname_requires_resolving(proxied_host.hostname)))
341
 
    {
342
 
      sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
343
 
                        "ignored in --skip-name-resolve mode.",
344
 
                        proxied_user ? proxied_user : "",
345
 
                        proxied_host.hostname ? proxied_host.hostname : "",
346
 
                        user ? user : "",
347
 
                        host.hostname ? host.hostname : "");
348
 
      return TRUE;
349
 
    }
350
 
    return FALSE;
351
 
  }
352
 
 
353
 
  bool matches(const char *host_arg, const char *user_arg, const char *ip_arg,
354
 
                const char *proxied_user_arg)
355
 
  {
356
 
    DBUG_ENTER("ACL_PROXY_USER::matches");
357
 
    DBUG_PRINT("info", ("compare_hostname(%s,%s,%s) &&"
358
 
                        "compare_hostname(%s,%s,%s) &&"
359
 
                        "wild_compare (%s,%s) &&"
360
 
                        "wild_compare (%s,%s)",
361
 
                        host.hostname ? host.hostname : "<NULL>",
362
 
                        host_arg ? host_arg : "<NULL>",
363
 
                        ip_arg ? ip_arg : "<NULL>",
364
 
                        proxied_host.hostname ? proxied_host.hostname : "<NULL>",
365
 
                        host_arg ? host_arg : "<NULL>",
366
 
                        ip_arg ? ip_arg : "<NULL>",
367
 
                        user_arg ? user_arg : "<NULL>",
368
 
                        user ? user : "<NULL>",
369
 
                        proxied_user_arg ? proxied_user_arg : "<NULL>",
370
 
                        proxied_user ? proxied_user : "<NULL>"));
371
 
    DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
372
 
                compare_hostname(&proxied_host, host_arg, ip_arg) &&
373
 
                (!user ||
374
 
                 (user_arg && !wild_compare(user_arg, user, TRUE))) &&
375
 
                (!proxied_user || 
376
 
                 (proxied_user && !wild_compare(proxied_user_arg, 
377
 
                                                proxied_user, TRUE))));
378
 
  }
379
 
 
380
 
 
381
 
  inline static bool auth_element_equals(const char *a, const char *b)
382
 
  {
383
 
    return (a == b || (a != NULL && b != NULL && !strcmp(a,b)));
384
 
  }
385
 
 
386
 
 
387
 
  bool pk_equals(ACL_PROXY_USER *grant)
388
 
  {
389
 
    DBUG_ENTER("pk_equals");
390
 
    DBUG_PRINT("info", ("strcmp(%s,%s) &&"
391
 
                        "strcmp(%s,%s) &&"
392
 
                        "wild_compare (%s,%s) &&"
393
 
                        "wild_compare (%s,%s)",
394
 
                        user ? user : "<NULL>",
395
 
                        grant->user ? grant->user : "<NULL>",
396
 
                        proxied_user ? proxied_user : "<NULL>",
397
 
                        grant->proxied_user ? grant->proxied_user : "<NULL>",
398
 
                        host.hostname ? host.hostname : "<NULL>",
399
 
                        grant->host.hostname ? grant->host.hostname : "<NULL>",
400
 
                        proxied_host.hostname ? proxied_host.hostname : "<NULL>",
401
 
                        grant->proxied_host.hostname ? 
402
 
                        grant->proxied_host.hostname : "<NULL>"));
403
 
 
404
 
    DBUG_RETURN(auth_element_equals(user, grant->user) &&
405
 
                auth_element_equals(proxied_user, grant->proxied_user) &&
406
 
                auth_element_equals(host.hostname, grant->host.hostname) &&
407
 
                auth_element_equals(proxied_host.hostname, 
408
 
                                    grant->proxied_host.hostname));
409
 
  }
410
 
 
411
 
 
412
 
  bool granted_on(const char *host_arg, const char *user_arg)
413
 
  {
414
 
    return (((!user && (!user_arg || !user_arg[0])) ||
415
 
             (user && user_arg && !strcmp(user, user_arg))) &&
416
 
            ((!host.hostname && (!host_arg || !host_arg[0])) ||
417
 
             (host.hostname && host_arg && !strcmp(host.hostname, host_arg))));
418
 
  }
419
 
 
420
 
 
421
 
  void print_grant(String *str)
422
 
  {
423
 
    str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
424
 
    if (proxied_user)
425
 
      str->append(proxied_user, strlen(proxied_user));
426
 
    str->append(STRING_WITH_LEN("'@'"));
427
 
    if (proxied_host.hostname)
428
 
      str->append(proxied_host.hostname, strlen(proxied_host.hostname));
429
 
    str->append(STRING_WITH_LEN("' TO '"));
430
 
    if (user)
431
 
      str->append(user, strlen(user));
432
 
    str->append(STRING_WITH_LEN("'@'"));
433
 
    if (host.hostname)
434
 
      str->append(host.hostname, strlen(host.hostname));
435
 
    str->append(STRING_WITH_LEN("'"));
436
 
    if (with_grant)
437
 
      str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
438
 
  }
439
 
 
440
 
  void set_data(ACL_PROXY_USER *grant)
441
 
  {
442
 
    with_grant= grant->with_grant;
443
 
  }
444
 
 
445
 
  static int store_pk(TABLE *table, 
446
 
                      const LEX_STRING *host, 
447
 
                      const LEX_STRING *user,
448
 
                      const LEX_STRING *proxied_host, 
449
 
                      const LEX_STRING *proxied_user)
450
 
  {
451
 
    DBUG_ENTER("ACL_PROXY_USER::store_pk");
452
 
    DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
453
 
                        host->str ? host->str : "<NULL>",
454
 
                        user->str ? user->str : "<NULL>",
455
 
                        proxied_host->str ? proxied_host->str : "<NULL>",
456
 
                        proxied_user->str ? proxied_user->str : "<NULL>"));
457
 
    if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str, 
458
 
                                                   host->length,
459
 
                                                   system_charset_info))
460
 
      DBUG_RETURN(TRUE);
461
 
    if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str, 
462
 
                                                   user->length,
463
 
                                                   system_charset_info))
464
 
      DBUG_RETURN(TRUE);
465
 
    if (table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]->store(proxied_host->str,
466
 
                                                           proxied_host->length,
467
 
                                                           system_charset_info))
468
 
      DBUG_RETURN(TRUE);
469
 
    if (table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]->store(proxied_user->str,
470
 
                                                           proxied_user->length,
471
 
                                                           system_charset_info))
472
 
      DBUG_RETURN(TRUE);
473
 
 
474
 
    DBUG_RETURN(FALSE);
475
 
  }
476
 
 
477
 
  static int store_data_record(TABLE *table,
478
 
                               const LEX_STRING *host,
479
 
                               const LEX_STRING *user,
480
 
                               const LEX_STRING *proxied_host,
481
 
                               const LEX_STRING *proxied_user,
482
 
                               bool with_grant,
483
 
                               const char *grantor)
484
 
  {
485
 
    DBUG_ENTER("ACL_PROXY_USER::store_pk");
486
 
    if (store_pk(table,  host, user, proxied_host, proxied_user))
487
 
      DBUG_RETURN(TRUE);
488
 
    DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
489
 
    if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0, 
490
 
                                                         TRUE))
491
 
      DBUG_RETURN(TRUE);
492
 
    if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor, 
493
 
                                                        strlen(grantor),
494
 
                                                        system_charset_info))
495
 
      DBUG_RETURN(TRUE);
496
 
 
497
 
    DBUG_RETURN(FALSE);
498
 
  }
499
 
};
500
 
 
501
 
#define FIRST_NON_YN_FIELD 26
502
 
 
503
 
class acl_entry :public hash_filo_element
504
 
{
505
 
public:
506
 
  ulong access;
507
 
  uint16 length;
508
 
  char key[1];                                  // Key will be stored here
509
 
};
510
 
 
511
 
 
512
 
static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
513
 
                                my_bool not_used __attribute__((unused)))
514
 
{
515
 
  *length=(uint) entry->length;
516
 
  return (uchar*) entry->key;
517
 
}
518
 
 
519
 
#define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
520
 
#define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
521
 
                        1 + USERNAME_LENGTH + 1)
522
 
 
523
 
/** Size of the header fields of an authentication packet. */
524
 
#define AUTH_PACKET_HEADER_SIZE_PROTO_41    32
525
 
#define AUTH_PACKET_HEADER_SIZE_PROTO_40    5  
526
 
 
527
 
static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users;
528
 
static MEM_ROOT mem, memex;
529
 
static bool initialized=0;
530
 
static bool allow_all_hosts=1;
531
 
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
532
 
static DYNAMIC_ARRAY acl_wild_hosts;
533
 
static hash_filo *acl_cache;
534
 
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
535
 
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
536
 
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
537
 
static ulong get_sort(uint count,...);
538
 
static void init_check_host(void);
539
 
static void rebuild_check_host(void);
540
 
static ACL_USER *find_acl_user(const char *host, const char *user,
541
 
                               my_bool exact);
542
 
static bool update_user_table(THD *thd, TABLE *table,
543
 
                              const char *host, const char *user,
544
 
                              const char *new_password, uint new_password_len);
545
 
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
546
 
static my_bool grant_load(THD *thd, TABLE_LIST *tables);
547
 
static inline void get_grantor(THD *thd, char* grantor);
548
 
 
549
 
/*
550
 
  Convert scrambled password to binary form, according to scramble type, 
551
 
  Binary form is stored in user.salt.
552
 
*/
553
 
 
554
 
static
555
 
void
556
 
set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
557
 
{
558
 
  if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
559
 
  {
560
 
    get_salt_from_password(acl_user->salt, password);
561
 
    acl_user->salt_len= SCRAMBLE_LENGTH;
562
 
  }
563
 
  else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
564
 
  {
565
 
    get_salt_from_password_323((ulong *) acl_user->salt, password);
566
 
    acl_user->salt_len= SCRAMBLE_LENGTH_323;
567
 
  }
568
 
  else
569
 
    acl_user->salt_len= 0;
570
 
}
571
 
 
572
 
/*
573
 
  Initialize structures responsible for user/db-level privilege checking and
574
 
  load privilege information for them from tables in the 'mysql' database.
575
 
 
576
 
  SYNOPSIS
577
 
    acl_init()
578
 
      dont_read_acl_tables  TRUE if we want to skip loading data from
579
 
                            privilege tables and disable privilege checking.
580
 
 
581
 
  NOTES
582
 
    This function is mostly responsible for preparatory steps, main work
583
 
    on initialization and grants loading is done in acl_reload().
584
 
 
585
 
  RETURN VALUES
586
 
    0   ok
587
 
    1   Could not initialize grant's
588
 
*/
589
 
 
590
 
my_bool acl_init(bool dont_read_acl_tables)
591
 
{
592
 
  THD  *thd;
593
 
  my_bool return_val;
594
 
  DBUG_ENTER("acl_init");
595
 
 
596
 
  acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
597
 
                           (my_hash_get_key) acl_entry_get_key,
598
 
                           (my_hash_free_key) free,
599
 
                           &my_charset_utf8_bin);
600
 
 
601
 
  /*
602
 
    cache built-in native authentication plugins,
603
 
    to avoid hash searches and a global mutex lock on every connect
604
 
  */
605
 
  native_password_plugin= my_plugin_lock_by_name(0,
606
 
           &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
607
 
  old_password_plugin= my_plugin_lock_by_name(0,
608
 
           &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
609
 
 
610
 
  if (!native_password_plugin || !old_password_plugin)
611
 
    DBUG_RETURN(1);
612
 
 
613
 
  if (dont_read_acl_tables)
614
 
  {
615
 
    DBUG_RETURN(0); /* purecov: tested */
616
 
  }
617
 
 
618
 
  /*
619
 
    To be able to run this from boot, we allocate a temporary THD
620
 
  */
621
 
  if (!(thd=new THD))
622
 
    DBUG_RETURN(1); /* purecov: inspected */
623
 
  thd->thread_stack= (char*) &thd;
624
 
  thd->store_globals();
625
 
  /*
626
 
    It is safe to call acl_reload() since acl_* arrays and hashes which
627
 
    will be freed there are global static objects and thus are initialized
628
 
    by zeros at startup.
629
 
  */
630
 
  return_val= acl_reload(thd);
631
 
  delete thd;
632
 
  /* Remember that we don't have a THD */
633
 
  my_pthread_setspecific_ptr(THR_THD,  0);
634
 
  DBUG_RETURN(return_val);
635
 
}
636
 
 
637
 
/**
638
 
  Choose from either native or old password plugins when assigning a password
639
 
*/
640
 
 
641
 
static bool
642
 
set_user_plugin (ACL_USER *user, int password_len)
643
 
{
644
 
  switch (password_len) 
645
 
  {
646
 
  case 0: /* no password */
647
 
  case SCRAMBLED_PASSWORD_CHAR_LENGTH:
648
 
    user->plugin= native_password_plugin_name;
649
 
    return FALSE;
650
 
  case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
651
 
    user->plugin= old_password_plugin_name;
652
 
    return FALSE;
653
 
  case 45: /* 4.1: to be removed */
654
 
    sql_print_warning("Found 4.1.0 style password for user '%s@%s'. "
655
 
                      "Ignoring user. "
656
 
                      "You should change password for this user.",
657
 
                      user->user ? user->user : "",
658
 
                      user->host.hostname ? user->host.hostname : "");
659
 
    return TRUE;
660
 
  default:
661
 
    sql_print_warning("Found invalid password for user: '%s@%s'; "
662
 
                      "Ignoring user", user->user ? user->user : "",
663
 
                      user->host.hostname ? user->host.hostname : "");
664
 
    return TRUE;
665
 
  }
666
 
}
667
 
 
668
 
 
669
 
/*
670
 
  Initialize structures responsible for user/db-level privilege checking
671
 
  and load information about grants from open privilege tables.
672
 
 
673
 
  SYNOPSIS
674
 
    acl_load()
675
 
      thd     Current thread
676
 
      tables  List containing open "mysql.host", "mysql.user" and
677
 
              "mysql.db" tables.
678
 
 
679
 
  RETURN VALUES
680
 
    FALSE  Success
681
 
    TRUE   Error
682
 
*/
683
 
 
684
 
static my_bool acl_load(THD *thd, TABLE_LIST *tables)
685
 
{
686
 
  TABLE *table;
687
 
  READ_RECORD read_record_info;
688
 
  my_bool return_val= TRUE;
689
 
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
690
 
  char tmp_name[NAME_LEN+1];
691
 
  int password_length;
692
 
  ulong old_sql_mode= thd->variables.sql_mode;
693
 
  DBUG_ENTER("acl_load");
694
 
 
695
 
  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
696
 
 
697
 
  grant_version++; /* Privileges updated */
698
 
 
699
 
  acl_cache->clear(1);                          // Clear locked hostname cache
700
 
 
701
 
  init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
702
 
  init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0, 
703
 
                   FALSE);
704
 
  table->use_all_columns();
705
 
  (void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50);
706
 
  while (!(read_record_info.read_record(&read_record_info)))
707
 
  {
708
 
    ACL_HOST host;
709
 
    update_hostname(&host.host,get_field(&mem, table->field[0]));
710
 
    host.db=     get_field(&mem, table->field[1]);
711
 
    if (lower_case_table_names && host.db)
712
 
    {
713
 
      /*
714
 
        convert db to lower case and give a warning if the db wasn't
715
 
        already in lower case
716
 
      */
717
 
      (void) strmov(tmp_name, host.db);
718
 
      my_casedn_str(files_charset_info, host.db);
719
 
      if (strcmp(host.db, tmp_name) != 0)
720
 
        sql_print_warning("'host' entry '%s|%s' had database in mixed "
721
 
                          "case that has been forced to lowercase because "
722
 
                          "lower_case_table_names is set. It will not be "
723
 
                          "possible to remove this privilege using REVOKE.",
724
 
                          host.host.hostname ? host.host.hostname : "",
725
 
                          host.db ? host.db : "");
726
 
    }
727
 
    host.access= get_access(table,2);
728
 
    host.access= fix_rights_for_db(host.access);
729
 
    host.sort=   get_sort(2,host.host.hostname,host.db);
730
 
    if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
731
 
    {
732
 
      sql_print_warning("'host' entry '%s|%s' "
733
 
                      "ignored in --skip-name-resolve mode.",
734
 
                        host.host.hostname ? host.host.hostname : "",
735
 
                        host.db ? host.db : "");
736
 
      continue;
737
 
    }
738
 
#ifndef TO_BE_REMOVED
739
 
    if (table->s->fields == 8)
740
 
    {                                           // Without grant
741
 
      if (host.access & CREATE_ACL)
742
 
        host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
743
 
    }
744
 
#endif
745
 
    (void) push_dynamic(&acl_hosts,(uchar*) &host);
746
 
  }
747
 
  my_qsort((uchar*) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
748
 
           sizeof(ACL_HOST),(qsort_cmp) acl_compare);
749
 
  end_read_record(&read_record_info);
750
 
  freeze_size(&acl_hosts);
751
 
 
752
 
  init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0,FALSE);
753
 
  table->use_all_columns();
754
 
  (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100);
755
 
  password_length= table->field[2]->field_length /
756
 
    table->field[2]->charset()->mbmaxlen;
757
 
  if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
758
 
  {
759
 
    sql_print_error("Fatal error: mysql.user table is damaged or in "
760
 
                    "unsupported 3.20 format.");
761
 
    goto end;
762
 
  }
763
 
 
764
 
  DBUG_PRINT("info",("user table fields: %d, password length: %d",
765
 
                     table->s->fields, password_length));
766
 
 
767
 
  mysql_mutex_lock(&LOCK_global_system_variables);
768
 
  if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
769
 
  {
770
 
    if (opt_secure_auth)
771
 
    {
772
 
      mysql_mutex_unlock(&LOCK_global_system_variables);
773
 
      sql_print_error("Fatal error: mysql.user table is in old format, "
774
 
                      "but server started with --secure-auth option.");
775
 
      goto end;
776
 
    }
777
 
    mysql_user_table_is_in_short_password_format= true;
778
 
    if (global_system_variables.old_passwords)
779
 
      mysql_mutex_unlock(&LOCK_global_system_variables);
780
 
    else
781
 
    {
782
 
      global_system_variables.old_passwords= 1;
783
 
      mysql_mutex_unlock(&LOCK_global_system_variables);
784
 
      sql_print_warning("mysql.user table is not updated to new password format; "
785
 
                        "Disabling new password usage until "
786
 
                        "mysql_fix_privilege_tables is run");
787
 
    }
788
 
    thd->variables.old_passwords= 1;
789
 
  }
790
 
  else
791
 
  {
792
 
    mysql_user_table_is_in_short_password_format= false;
793
 
    mysql_mutex_unlock(&LOCK_global_system_variables);
794
 
  }
795
 
 
796
 
  allow_all_hosts=0;
797
 
  while (!(read_record_info.read_record(&read_record_info)))
798
 
  {
799
 
    ACL_USER user;
800
 
    bzero(&user, sizeof(user));
801
 
    update_hostname(&user.host, get_field(&mem, table->field[0]));
802
 
    user.user= get_field(&mem, table->field[1]);
803
 
    if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
804
 
    {
805
 
      sql_print_warning("'user' entry '%s@%s' "
806
 
                        "ignored in --skip-name-resolve mode.",
807
 
                        user.user ? user.user : "",
808
 
                        user.host.hostname ? user.host.hostname : "");
809
 
      continue;
810
 
    }
811
 
 
812
 
    char *password= get_field(&mem, table->field[2]);
813
 
    uint password_len= password ? strlen(password) : 0;
814
 
    set_user_salt(&user, password, password_len);
815
 
 
816
 
    if (set_user_plugin(&user, password_len))
817
 
      continue;
818
 
    
819
 
    {
820
 
      uint next_field;
821
 
      user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
822
 
      /*
823
 
        if it is pre 5.0.1 privilege table then map CREATE privilege on
824
 
        CREATE VIEW & SHOW VIEW privileges
825
 
      */
826
 
      if (table->s->fields <= 31 && (user.access & CREATE_ACL))
827
 
        user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
828
 
 
829
 
      /*
830
 
        if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
831
 
        CREATE PROCEDURE & ALTER PROCEDURE privileges
832
 
      */
833
 
      if (table->s->fields <= 33 && (user.access & CREATE_ACL))
834
 
        user.access|= CREATE_PROC_ACL;
835
 
      if (table->s->fields <= 33 && (user.access & ALTER_ACL))
836
 
        user.access|= ALTER_PROC_ACL;
837
 
 
838
 
      /*
839
 
        pre 5.0.3 did not have CREATE_USER_ACL
840
 
      */
841
 
      if (table->s->fields <= 36 && (user.access & GRANT_ACL))
842
 
        user.access|= CREATE_USER_ACL;
843
 
 
844
 
 
845
 
      /*
846
 
        if it is pre 5.1.6 privilege table then map CREATE privilege on
847
 
        CREATE|ALTER|DROP|EXECUTE EVENT
848
 
      */
849
 
      if (table->s->fields <= 37 && (user.access & SUPER_ACL))
850
 
        user.access|= EVENT_ACL;
851
 
 
852
 
      /*
853
 
        if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
854
 
      */
855
 
      if (table->s->fields <= 38 && (user.access & SUPER_ACL))
856
 
        user.access|= TRIGGER_ACL;
857
 
 
858
 
      user.sort= get_sort(2,user.host.hostname,user.user);
859
 
      user.hostname_length= (user.host.hostname ?
860
 
                             (uint) strlen(user.host.hostname) : 0);
861
 
 
862
 
      /* Starting from 4.0.2 we have more fields */
863
 
      if (table->s->fields >= 31)
864
 
      {
865
 
        char *ssl_type=get_field(thd->mem_root, table->field[next_field++]);
866
 
        if (!ssl_type)
867
 
          user.ssl_type=SSL_TYPE_NONE;
868
 
        else if (!strcmp(ssl_type, "ANY"))
869
 
          user.ssl_type=SSL_TYPE_ANY;
870
 
        else if (!strcmp(ssl_type, "X509"))
871
 
          user.ssl_type=SSL_TYPE_X509;
872
 
        else  /* !strcmp(ssl_type, "SPECIFIED") */
873
 
          user.ssl_type=SSL_TYPE_SPECIFIED;
874
 
 
875
 
        user.ssl_cipher=   get_field(&mem, table->field[next_field++]);
876
 
        user.x509_issuer=  get_field(&mem, table->field[next_field++]);
877
 
        user.x509_subject= get_field(&mem, table->field[next_field++]);
878
 
 
879
 
        char *ptr = get_field(thd->mem_root, table->field[next_field++]);
880
 
        user.user_resource.questions=ptr ? atoi(ptr) : 0;
881
 
        ptr = get_field(thd->mem_root, table->field[next_field++]);
882
 
        user.user_resource.updates=ptr ? atoi(ptr) : 0;
883
 
        ptr = get_field(thd->mem_root, table->field[next_field++]);
884
 
        user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
885
 
        if (user.user_resource.questions || user.user_resource.updates ||
886
 
            user.user_resource.conn_per_hour)
887
 
          mqh_used=1;
888
 
 
889
 
        if (table->s->fields >= 36)
890
 
        {
891
 
          /* Starting from 5.0.3 we have max_user_connections field */
892
 
          ptr= get_field(thd->mem_root, table->field[next_field++]);
893
 
          user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
894
 
        }
895
 
 
896
 
        if (table->s->fields >= 41)
897
 
        {
898
 
          /* We may have plugin & auth_String fields */
899
 
          char *tmpstr= get_field(&mem, table->field[next_field++]);
900
 
          if (tmpstr)
901
 
          {
902
 
            if (password_len)
903
 
            {
904
 
              sql_print_warning("'user' entry '%s@%s' has both a password "
905
 
                                "and an authentication plugin specified. The "
906
 
                                "password will be ignored.",
907
 
                                user.user ? user.user : "",
908
 
                                user.host.hostname ? user.host.hostname : "");
909
 
            }
910
 
            if (my_strcasecmp(system_charset_info, tmpstr,
911
 
                              native_password_plugin_name.str) == 0)
912
 
              user.plugin= native_password_plugin_name;
913
 
            else
914
 
              if (my_strcasecmp(system_charset_info, tmpstr,
915
 
                                old_password_plugin_name.str) == 0)
916
 
                user.plugin= old_password_plugin_name;
917
 
              else
918
 
              {
919
 
                user.plugin.str= tmpstr;
920
 
                user.plugin.length= strlen(tmpstr);
921
 
              }
922
 
            user.auth_string.str= get_field(&mem, table->field[next_field++]);
923
 
            if (!user.auth_string.str)
924
 
              user.auth_string.str= const_cast<char*>("");
925
 
            user.auth_string.length= strlen(user.auth_string.str);
926
 
          }
927
 
        }
928
 
      }
929
 
      else
930
 
      {
931
 
        user.ssl_type=SSL_TYPE_NONE;
932
 
#ifndef TO_BE_REMOVED
933
 
        if (table->s->fields <= 13)
934
 
        {                                               // Without grant
935
 
          if (user.access & CREATE_ACL)
936
 
            user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
937
 
        }
938
 
        /* Convert old privileges */
939
 
        user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
940
 
        if (user.access & FILE_ACL)
941
 
          user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
942
 
        if (user.access & PROCESS_ACL)
943
 
          user.access|= SUPER_ACL | EXECUTE_ACL;
944
 
#endif
945
 
      }
946
 
      (void) push_dynamic(&acl_users,(uchar*) &user);
947
 
      if (!user.host.hostname ||
948
 
          (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
949
 
        allow_all_hosts=1;                      // Anyone can connect
950
 
    }
951
 
  }
952
 
  my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
953
 
           sizeof(ACL_USER),(qsort_cmp) acl_compare);
954
 
  end_read_record(&read_record_info);
955
 
  freeze_size(&acl_users);
956
 
 
957
 
  init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0,FALSE);
958
 
  table->use_all_columns();
959
 
  (void) my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100);
960
 
  while (!(read_record_info.read_record(&read_record_info)))
961
 
  {
962
 
    ACL_DB db;
963
 
    update_hostname(&db.host,get_field(&mem, table->field[MYSQL_DB_FIELD_HOST]));
964
 
    db.db=get_field(&mem, table->field[MYSQL_DB_FIELD_DB]);
965
 
    if (!db.db)
966
 
    {
967
 
      sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
968
 
      continue;
969
 
    }
970
 
    db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]);
971
 
    if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
972
 
    {
973
 
      sql_print_warning("'db' entry '%s %s@%s' "
974
 
                        "ignored in --skip-name-resolve mode.",
975
 
                        db.db,
976
 
                        db.user ? db.user : "",
977
 
                        db.host.hostname ? db.host.hostname : "");
978
 
      continue;
979
 
    }
980
 
    db.access=get_access(table,3);
981
 
    db.access=fix_rights_for_db(db.access);
982
 
    if (lower_case_table_names)
983
 
    {
984
 
      /*
985
 
        convert db to lower case and give a warning if the db wasn't
986
 
        already in lower case
987
 
      */
988
 
      (void)strmov(tmp_name, db.db);
989
 
      my_casedn_str(files_charset_info, db.db);
990
 
      if (strcmp(db.db, tmp_name) != 0)
991
 
      {
992
 
        sql_print_warning("'db' entry '%s %s@%s' had database in mixed "
993
 
                          "case that has been forced to lowercase because "
994
 
                          "lower_case_table_names is set. It will not be "
995
 
                          "possible to remove this privilege using REVOKE.",
996
 
                          db.db,
997
 
                          db.user ? db.user : "",
998
 
                          db.host.hostname ? db.host.hostname : "");
999
 
      }
1000
 
    }
1001
 
    db.sort=get_sort(3,db.host.hostname,db.db,db.user);
1002
 
#ifndef TO_BE_REMOVED
1003
 
    if (table->s->fields <=  9)
1004
 
    {                                           // Without grant
1005
 
      if (db.access & CREATE_ACL)
1006
 
        db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
1007
 
    }
1008
 
#endif
1009
 
    (void) push_dynamic(&acl_dbs,(uchar*) &db);
1010
 
  }
1011
 
  my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
1012
 
           sizeof(ACL_DB),(qsort_cmp) acl_compare);
1013
 
  end_read_record(&read_record_info);
1014
 
  freeze_size(&acl_dbs);
1015
 
 
1016
 
  (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), 
1017
 
                               50, 100);
1018
 
  if (tables[3].table)
1019
 
  {
1020
 
    init_read_record(&read_record_info, thd, table= tables[3].table, NULL, 1, 
1021
 
                     0, FALSE);
1022
 
    table->use_all_columns();
1023
 
    while (!(read_record_info.read_record(&read_record_info)))
1024
 
    {
1025
 
      ACL_PROXY_USER proxy;
1026
 
      proxy.init(table, &mem);
1027
 
      if (proxy.check_validity(check_no_resolve))
1028
 
        continue;
1029
 
      if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
1030
 
      {
1031
 
        end_read_record(&read_record_info);
1032
 
        goto end;
1033
 
      }
1034
 
    }
1035
 
    my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*),
1036
 
             acl_proxy_users.elements,
1037
 
             sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
1038
 
    end_read_record(&read_record_info);
1039
 
  }
1040
 
  else
1041
 
  {
1042
 
    sql_print_error("Missing system table mysql.proxies_priv; "
1043
 
                    "please run mysql_upgrade to create it");
1044
 
  }
1045
 
  freeze_size(&acl_proxy_users);
1046
 
 
1047
 
  init_check_host();
1048
 
 
1049
 
  initialized=1;
1050
 
  return_val= FALSE;
1051
 
 
1052
 
end:
1053
 
  thd->variables.sql_mode= old_sql_mode;
1054
 
  DBUG_RETURN(return_val);
1055
 
}
1056
 
 
1057
 
 
1058
 
void acl_free(bool end)
1059
 
{
1060
 
  free_root(&mem,MYF(0));
1061
 
  delete_dynamic(&acl_hosts);
1062
 
  delete_dynamic(&acl_users);
1063
 
  delete_dynamic(&acl_dbs);
1064
 
  delete_dynamic(&acl_wild_hosts);
1065
 
  delete_dynamic(&acl_proxy_users);
1066
 
  my_hash_free(&acl_check_hosts);
1067
 
  plugin_unlock(0, native_password_plugin);
1068
 
  plugin_unlock(0, old_password_plugin);
1069
 
  if (!end)
1070
 
    acl_cache->clear(1); /* purecov: inspected */
1071
 
  else
1072
 
  {
1073
 
    delete acl_cache;
1074
 
    acl_cache=0;
1075
 
  }
1076
 
}
1077
 
 
1078
 
 
1079
 
/*
1080
 
  Forget current user/db-level privileges and read new privileges
1081
 
  from the privilege tables.
1082
 
 
1083
 
  SYNOPSIS
1084
 
    acl_reload()
1085
 
      thd  Current thread
1086
 
 
1087
 
  NOTE
1088
 
    All tables of calling thread which were open and locked by LOCK TABLES
1089
 
    statement will be unlocked and closed.
1090
 
    This function is also used for initialization of structures responsible
1091
 
    for user/db-level privilege checking.
1092
 
 
1093
 
  RETURN VALUE
1094
 
    FALSE  Success
1095
 
    TRUE   Failure
1096
 
*/
1097
 
 
1098
 
my_bool acl_reload(THD *thd)
1099
 
{
1100
 
  TABLE_LIST tables[4];
1101
 
  DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users;
1102
 
  MEM_ROOT old_mem;
1103
 
  bool old_initialized;
1104
 
  my_bool return_val= TRUE;
1105
 
  DBUG_ENTER("acl_reload");
1106
 
 
1107
 
  /*
1108
 
    To avoid deadlocks we should obtain table locks before
1109
 
    obtaining acl_cache->lock mutex.
1110
 
  */
1111
 
  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
1112
 
                           C_STRING_WITH_LEN("host"), "host", TL_READ);
1113
 
  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
1114
 
                           C_STRING_WITH_LEN("user"), "user", TL_READ);
1115
 
  tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
1116
 
                           C_STRING_WITH_LEN("db"), "db", TL_READ);
1117
 
  tables[3].init_one_table(C_STRING_WITH_LEN("mysql"),
1118
 
                           C_STRING_WITH_LEN("proxies_priv"), 
1119
 
                           "proxies_priv", TL_READ);
1120
 
  tables[0].next_local= tables[0].next_global= tables + 1;
1121
 
  tables[1].next_local= tables[1].next_global= tables + 2;
1122
 
  tables[2].next_local= tables[2].next_global= tables + 3;
1123
 
  tables[0].open_type= tables[1].open_type= tables[2].open_type= 
1124
 
  tables[3].open_type= OT_BASE_ONLY;
1125
 
  tables[3].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
1126
 
 
1127
 
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
1128
 
  {
1129
 
    /*
1130
 
      Execution might have been interrupted; only print the error message
1131
 
      if an error condition has been raised.
1132
 
    */
1133
 
    if (thd->stmt_da->is_error())
1134
 
      sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
1135
 
                      thd->stmt_da->message());
1136
 
    goto end;
1137
 
  }
1138
 
 
1139
 
  if ((old_initialized=initialized))
1140
 
    mysql_mutex_lock(&acl_cache->lock);
1141
 
 
1142
 
  old_acl_hosts= acl_hosts;
1143
 
  old_acl_users= acl_users;
1144
 
  old_acl_proxy_users= acl_proxy_users;
1145
 
  old_acl_dbs= acl_dbs;
1146
 
  old_mem= mem;
1147
 
  delete_dynamic(&acl_wild_hosts);
1148
 
  my_hash_free(&acl_check_hosts);
1149
 
 
1150
 
  if ((return_val= acl_load(thd, tables)))
1151
 
  {                                     // Error. Revert to old list
1152
 
    DBUG_PRINT("error",("Reverting to old privileges"));
1153
 
    acl_free();                         /* purecov: inspected */
1154
 
    acl_hosts= old_acl_hosts;
1155
 
    acl_users= old_acl_users;
1156
 
    acl_proxy_users= old_acl_proxy_users;
1157
 
    acl_dbs= old_acl_dbs;
1158
 
    mem= old_mem;
1159
 
    init_check_host();
1160
 
  }
1161
 
  else
1162
 
  {
1163
 
    free_root(&old_mem,MYF(0));
1164
 
    delete_dynamic(&old_acl_hosts);
1165
 
    delete_dynamic(&old_acl_users);
1166
 
    delete_dynamic(&old_acl_proxy_users);
1167
 
    delete_dynamic(&old_acl_dbs);
1168
 
  }
1169
 
  if (old_initialized)
1170
 
    mysql_mutex_unlock(&acl_cache->lock);
1171
 
end:
1172
 
  close_mysql_tables(thd);
1173
 
  DBUG_RETURN(return_val);
1174
 
}
1175
 
 
1176
 
 
1177
 
/*
1178
 
  Get all access bits from table after fieldnr
1179
 
 
1180
 
  IMPLEMENTATION
1181
 
  We know that the access privileges ends when there is no more fields
1182
 
  or the field is not an enum with two elements.
1183
 
 
1184
 
  SYNOPSIS
1185
 
    get_access()
1186
 
    form        an open table to read privileges from.
1187
 
                The record should be already read in table->record[0]
1188
 
    fieldnr     number of the first privilege (that is ENUM('N','Y') field
1189
 
    next_field  on return - number of the field next to the last ENUM
1190
 
                (unless next_field == 0)
1191
 
 
1192
 
  RETURN VALUE
1193
 
    privilege mask
1194
 
*/
1195
 
 
1196
 
static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
1197
 
{
1198
 
  ulong access_bits=0,bit;
1199
 
  char buff[2];
1200
 
  String res(buff,sizeof(buff),&my_charset_latin1);
1201
 
  Field **pos;
1202
 
 
1203
 
  for (pos=form->field+fieldnr, bit=1;
1204
 
       *pos && (*pos)->real_type() == MYSQL_TYPE_ENUM &&
1205
 
         ((Field_enum*) (*pos))->typelib->count == 2 ;
1206
 
       pos++, fieldnr++, bit<<=1)
1207
 
  {
1208
 
    (*pos)->val_str(&res);
1209
 
    if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
1210
 
      access_bits|= bit;
1211
 
  }
1212
 
  if (next_field)
1213
 
    *next_field=fieldnr;
1214
 
  return access_bits;
1215
 
}
1216
 
 
1217
 
 
1218
 
/*
1219
 
  Return a number which, if sorted 'desc', puts strings in this order:
1220
 
    no wildcards
1221
 
    wildcards
1222
 
    empty string
1223
 
*/
1224
 
 
1225
 
static ulong get_sort(uint count,...)
1226
 
{
1227
 
  va_list args;
1228
 
  va_start(args,count);
1229
 
  ulong sort=0;
1230
 
 
1231
 
  /* Should not use this function with more than 4 arguments for compare. */
1232
 
  DBUG_ASSERT(count <= 4);
1233
 
 
1234
 
  while (count--)
1235
 
  {
1236
 
    char *start, *str= va_arg(args,char*);
1237
 
    uint chars= 0;
1238
 
    uint wild_pos= 0;           /* first wildcard position */
1239
 
 
1240
 
    if ((start= str))
1241
 
    {
1242
 
      for (; *str ; str++)
1243
 
      {
1244
 
        if (*str == wild_prefix && str[1])
1245
 
          str++;
1246
 
        else if (*str == wild_many || *str == wild_one)
1247
 
        {
1248
 
          wild_pos= (uint) (str - start) + 1;
1249
 
          break;
1250
 
        }
1251
 
        chars= 128;                             // Marker that chars existed
1252
 
      }
1253
 
    }
1254
 
    sort= (sort << 8) + (wild_pos ? min(wild_pos, 127) : chars);
1255
 
  }
1256
 
  va_end(args);
1257
 
  return sort;
1258
 
}
1259
 
 
1260
 
 
1261
 
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
1262
 
{
1263
 
  if (a->sort > b->sort)
1264
 
    return -1;
1265
 
  if (a->sort < b->sort)
1266
 
    return 1;
1267
 
  return 0;
1268
 
}
1269
 
 
1270
 
 
1271
 
/*
1272
 
  Gets user credentials without authentication and resource limit checks.
1273
 
 
1274
 
  SYNOPSIS
1275
 
    acl_getroot()
1276
 
      sctx               Context which should be initialized
1277
 
      user               user name
1278
 
      host               host name
1279
 
      ip                 IP
1280
 
      db                 current data base name
1281
 
 
1282
 
  RETURN
1283
 
    FALSE  OK
1284
 
    TRUE   Error
1285
 
*/
1286
 
 
1287
 
bool acl_getroot(Security_context *sctx, char *user, char *host,
1288
 
                 char *ip, char *db)
1289
 
{
1290
 
  int res= 1;
1291
 
  uint i;
1292
 
  ACL_USER *acl_user= 0;
1293
 
  DBUG_ENTER("acl_getroot");
1294
 
 
1295
 
  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
1296
 
                       (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
1297
 
                       user, (db ? db : "(NULL)")));
1298
 
  sctx->user= user;
1299
 
  sctx->host= host;
1300
 
  sctx->ip= ip;
1301
 
  sctx->host_or_ip= host ? host : (ip ? ip : "");
1302
 
 
1303
 
  if (!initialized)
1304
 
  {
1305
 
    /*
1306
 
      here if mysqld's been started with --skip-grant-tables option.
1307
 
    */
1308
 
    sctx->skip_grants();
1309
 
    DBUG_RETURN(FALSE);
1310
 
  }
1311
 
 
1312
 
  mysql_mutex_lock(&acl_cache->lock);
1313
 
 
1314
 
  sctx->master_access= 0;
1315
 
  sctx->db_access= 0;
1316
 
  *sctx->priv_user= *sctx->priv_host= 0;
1317
 
 
1318
 
  /*
1319
 
     Find acl entry in user database.
1320
 
     This is specially tailored to suit the check we do for CALL of
1321
 
     a stored procedure; user is set to what is actually a
1322
 
     priv_user, which can be ''.
1323
 
  */
1324
 
  for (i=0 ; i < acl_users.elements ; i++)
1325
 
  {
1326
 
    ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
1327
 
    if ((!acl_user_tmp->user && !user[0]) ||
1328
 
        (acl_user_tmp->user && strcmp(user, acl_user_tmp->user) == 0))
1329
 
    {
1330
 
      if (compare_hostname(&acl_user_tmp->host, host, ip))
1331
 
      {
1332
 
        acl_user= acl_user_tmp;
1333
 
        res= 0;
1334
 
        break;
1335
 
      }
1336
 
    }
1337
 
  }
1338
 
 
1339
 
  if (acl_user)
1340
 
  {
1341
 
    for (i=0 ; i < acl_dbs.elements ; i++)
1342
 
    {
1343
 
      ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
1344
 
      if (!acl_db->user ||
1345
 
          (user && user[0] && !strcmp(user, acl_db->user)))
1346
 
      {
1347
 
        if (compare_hostname(&acl_db->host, host, ip))
1348
 
        {
1349
 
          if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
1350
 
          {
1351
 
            sctx->db_access= acl_db->access;
1352
 
            break;
1353
 
          }
1354
 
        }
1355
 
      }
1356
 
    }
1357
 
    sctx->master_access= acl_user->access;
1358
 
 
1359
 
    if (acl_user->user)
1360
 
      strmake(sctx->priv_user, user, USERNAME_LENGTH);
1361
 
    else
1362
 
      *sctx->priv_user= 0;
1363
 
 
1364
 
    if (acl_user->host.hostname)
1365
 
      strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
1366
 
    else
1367
 
      *sctx->priv_host= 0;
1368
 
  }
1369
 
  mysql_mutex_unlock(&acl_cache->lock);
1370
 
  DBUG_RETURN(res);
1371
 
}
1372
 
 
1373
 
static uchar* check_get_key(ACL_USER *buff, size_t *length,
1374
 
                            my_bool not_used __attribute__((unused)))
1375
 
{
1376
 
  *length=buff->hostname_length;
1377
 
  return (uchar*) buff->host.hostname;
1378
 
}
1379
 
 
1380
 
 
1381
 
static void acl_update_user(const char *user, const char *host,
1382
 
                            const char *password, uint password_len,
1383
 
                            enum SSL_type ssl_type,
1384
 
                            const char *ssl_cipher,
1385
 
                            const char *x509_issuer,
1386
 
                            const char *x509_subject,
1387
 
                            USER_RESOURCES  *mqh,
1388
 
                            ulong privileges,
1389
 
                            const LEX_STRING *plugin,
1390
 
                            const LEX_STRING *auth)
1391
 
{
1392
 
  mysql_mutex_assert_owner(&acl_cache->lock);
1393
 
 
1394
 
  for (uint i=0 ; i < acl_users.elements ; i++)
1395
 
  {
1396
 
    ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
1397
 
    if ((!acl_user->user && !user[0]) ||
1398
 
        (acl_user->user && !strcmp(user,acl_user->user)))
1399
 
    {
1400
 
      if ((!acl_user->host.hostname && !host[0]) ||
1401
 
          (acl_user->host.hostname &&
1402
 
          !my_strcasecmp(system_charset_info, host, acl_user->host.hostname)))
1403
 
      {
1404
 
        if (plugin->str[0])
1405
 
        {
1406
 
          acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
1407
 
          acl_user->plugin.length= plugin->length;
1408
 
          acl_user->auth_string.str= auth->str ?
1409
 
            strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
1410
 
          acl_user->auth_string.length= auth->length;
1411
 
        }
1412
 
        acl_user->access=privileges;
1413
 
        if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
1414
 
          acl_user->user_resource.questions=mqh->questions;
1415
 
        if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
1416
 
          acl_user->user_resource.updates=mqh->updates;
1417
 
        if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
1418
 
          acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
1419
 
        if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
1420
 
          acl_user->user_resource.user_conn= mqh->user_conn;
1421
 
        if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
1422
 
        {
1423
 
          acl_user->ssl_type= ssl_type;
1424
 
          acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&mem,ssl_cipher) :
1425
 
                                 0);
1426
 
          acl_user->x509_issuer= (x509_issuer ? strdup_root(&mem,x509_issuer) :
1427
 
                                  0);
1428
 
          acl_user->x509_subject= (x509_subject ?
1429
 
                                   strdup_root(&mem,x509_subject) : 0);
1430
 
        }
1431
 
        if (password)
1432
 
          set_user_salt(acl_user, password, password_len);
1433
 
        /* search complete: */
1434
 
        break;
1435
 
      }
1436
 
    }
1437
 
  }
1438
 
}
1439
 
 
1440
 
 
1441
 
static void acl_insert_user(const char *user, const char *host,
1442
 
                            const char *password, uint password_len,
1443
 
                            enum SSL_type ssl_type,
1444
 
                            const char *ssl_cipher,
1445
 
                            const char *x509_issuer,
1446
 
                            const char *x509_subject,
1447
 
                            USER_RESOURCES *mqh,
1448
 
                            ulong privileges,
1449
 
                            const LEX_STRING *plugin,
1450
 
                            const LEX_STRING *auth)
1451
 
{
1452
 
  ACL_USER acl_user;
1453
 
 
1454
 
  mysql_mutex_assert_owner(&acl_cache->lock);
1455
 
 
1456
 
  acl_user.user=*user ? strdup_root(&mem,user) : 0;
1457
 
  update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
1458
 
  if (plugin->str[0])
1459
 
  {
1460
 
    acl_user.plugin.str= strmake_root(&mem, plugin->str, plugin->length);
1461
 
    acl_user.plugin.length= plugin->length;
1462
 
    acl_user.auth_string.str= auth->str ?
1463
 
      strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
1464
 
    acl_user.auth_string.length= auth->length;
1465
 
  }
1466
 
  else
1467
 
  {
1468
 
    acl_user.plugin= password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 ?
1469
 
      old_password_plugin_name : native_password_plugin_name;
1470
 
    acl_user.auth_string.str= const_cast<char*>("");
1471
 
    acl_user.auth_string.length= 0;
1472
 
  }
1473
 
 
1474
 
  acl_user.access=privileges;
1475
 
  acl_user.user_resource = *mqh;
1476
 
  acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
1477
 
  acl_user.hostname_length=(uint) strlen(host);
1478
 
  acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ?
1479
 
                      ssl_type : SSL_TYPE_NONE);
1480
 
  acl_user.ssl_cipher=  ssl_cipher   ? strdup_root(&mem,ssl_cipher) : 0;
1481
 
  acl_user.x509_issuer= x509_issuer  ? strdup_root(&mem,x509_issuer) : 0;
1482
 
  acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
1483
 
 
1484
 
  set_user_salt(&acl_user, password, password_len);
1485
 
 
1486
 
  (void) push_dynamic(&acl_users,(uchar*) &acl_user);
1487
 
  if (!acl_user.host.hostname ||
1488
 
      (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
1489
 
    allow_all_hosts=1;          // Anyone can connect /* purecov: tested */
1490
 
  my_qsort((uchar*) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
1491
 
           sizeof(ACL_USER),(qsort_cmp) acl_compare);
1492
 
 
1493
 
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
1494
 
  rebuild_check_host();
1495
 
}
1496
 
 
1497
 
 
1498
 
static void acl_update_db(const char *user, const char *host, const char *db,
1499
 
                          ulong privileges)
1500
 
{
1501
 
  mysql_mutex_assert_owner(&acl_cache->lock);
1502
 
 
1503
 
  for (uint i=0 ; i < acl_dbs.elements ; i++)
1504
 
  {
1505
 
    ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
1506
 
    if ((!acl_db->user && !user[0]) ||
1507
 
        (acl_db->user &&
1508
 
        !strcmp(user,acl_db->user)))
1509
 
    {
1510
 
      if ((!acl_db->host.hostname && !host[0]) ||
1511
 
          (acl_db->host.hostname &&
1512
 
          !strcmp(host, acl_db->host.hostname)))
1513
 
      {
1514
 
        if ((!acl_db->db && !db[0]) ||
1515
 
            (acl_db->db && !strcmp(db,acl_db->db)))
1516
 
        {
1517
 
          if (privileges)
1518
 
            acl_db->access=privileges;
1519
 
          else
1520
 
            delete_dynamic_element(&acl_dbs,i);
1521
 
        }
1522
 
      }
1523
 
    }
1524
 
  }
1525
 
}
1526
 
 
1527
 
 
1528
 
/*
1529
 
  Insert a user/db/host combination into the global acl_cache
1530
 
 
1531
 
  SYNOPSIS
1532
 
    acl_insert_db()
1533
 
    user                User name
1534
 
    host                Host name
1535
 
    db                  Database name
1536
 
    privileges          Bitmap of privileges
1537
 
 
1538
 
  NOTES
1539
 
    acl_cache->lock must be locked when calling this
1540
 
*/
1541
 
 
1542
 
static void acl_insert_db(const char *user, const char *host, const char *db,
1543
 
                          ulong privileges)
1544
 
{
1545
 
  ACL_DB acl_db;
1546
 
  mysql_mutex_assert_owner(&acl_cache->lock);
1547
 
  acl_db.user=strdup_root(&mem,user);
1548
 
  update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
1549
 
  acl_db.db=strdup_root(&mem,db);
1550
 
  acl_db.access=privileges;
1551
 
  acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
1552
 
  (void) push_dynamic(&acl_dbs,(uchar*) &acl_db);
1553
 
  my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
1554
 
           sizeof(ACL_DB),(qsort_cmp) acl_compare);
1555
 
}
1556
 
 
1557
 
 
1558
 
 
1559
 
/*
1560
 
  Get privilege for a host, user and db combination
1561
 
 
1562
 
  as db_is_pattern changes the semantics of comparison,
1563
 
  acl_cache is not used if db_is_pattern is set.
1564
 
*/
1565
 
 
1566
 
ulong acl_get(const char *host, const char *ip,
1567
 
              const char *user, const char *db, my_bool db_is_pattern)
1568
 
{
1569
 
  ulong host_access= ~(ulong)0, db_access= 0;
1570
 
  uint i;
1571
 
  size_t key_length;
1572
 
  char key[ACL_KEY_LENGTH],*tmp_db,*end;
1573
 
  acl_entry *entry;
1574
 
  DBUG_ENTER("acl_get");
1575
 
 
1576
 
  mysql_mutex_lock(&acl_cache->lock);
1577
 
  end=strmov((tmp_db=strmov(strmov(key, ip ? ip : "")+1,user)+1),db);
1578
 
  if (lower_case_table_names)
1579
 
  {
1580
 
    my_casedn_str(files_charset_info, tmp_db);
1581
 
    db=tmp_db;
1582
 
  }
1583
 
  key_length= (size_t) (end-key);
1584
 
  if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key,
1585
 
                                                              key_length)))
1586
 
  {
1587
 
    db_access=entry->access;
1588
 
    mysql_mutex_unlock(&acl_cache->lock);
1589
 
    DBUG_PRINT("exit", ("access: 0x%lx", db_access));
1590
 
    DBUG_RETURN(db_access);
1591
 
  }
1592
 
 
1593
 
  /*
1594
 
    Check if there are some access rights for database and user
1595
 
  */
1596
 
  for (i=0 ; i < acl_dbs.elements ; i++)
1597
 
  {
1598
 
    ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
1599
 
    if (!acl_db->user || !strcmp(user,acl_db->user))
1600
 
    {
1601
 
      if (compare_hostname(&acl_db->host,host,ip))
1602
 
      {
1603
 
        if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
1604
 
        {
1605
 
          db_access=acl_db->access;
1606
 
          if (acl_db->host.hostname)
1607
 
            goto exit;                          // Fully specified. Take it
1608
 
          break; /* purecov: tested */
1609
 
        }
1610
 
      }
1611
 
    }
1612
 
  }
1613
 
  if (!db_access)
1614
 
    goto exit;                                  // Can't be better
1615
 
 
1616
 
  /*
1617
 
    No host specified for user. Get hostdata from host table
1618
 
  */
1619
 
  host_access=0;                                // Host must be found
1620
 
  for (i=0 ; i < acl_hosts.elements ; i++)
1621
 
  {
1622
 
    ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
1623
 
    if (compare_hostname(&acl_host->host,host,ip))
1624
 
    {
1625
 
      if (!acl_host->db || !wild_compare(db,acl_host->db,db_is_pattern))
1626
 
      {
1627
 
        host_access=acl_host->access;           // Fully specified. Take it
1628
 
        break;
1629
 
      }
1630
 
    }
1631
 
  }
1632
 
exit:
1633
 
  /* Save entry in cache for quick retrieval */
1634
 
  if (!db_is_pattern &&
1635
 
      (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
1636
 
  {
1637
 
    entry->access=(db_access & host_access);
1638
 
    entry->length=key_length;
1639
 
    memcpy((uchar*) entry->key,key,key_length);
1640
 
    acl_cache->add(entry);
1641
 
  }
1642
 
  mysql_mutex_unlock(&acl_cache->lock);
1643
 
  DBUG_PRINT("exit", ("access: 0x%lx", db_access & host_access));
1644
 
  DBUG_RETURN(db_access & host_access);
1645
 
}
1646
 
 
1647
 
/*
1648
 
  Check if there are any possible matching entries for this host
1649
 
 
1650
 
  NOTES
1651
 
    All host names without wild cards are stored in a hash table,
1652
 
    entries with wildcards are stored in a dynamic array
1653
 
*/
1654
 
 
1655
 
static void init_check_host(void)
1656
 
{
1657
 
  DBUG_ENTER("init_check_host");
1658
 
  (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
1659
 
                          acl_users.elements,1);
1660
 
  (void) my_hash_init(&acl_check_hosts,system_charset_info,
1661
 
                      acl_users.elements, 0, 0,
1662
 
                      (my_hash_get_key) check_get_key, 0, 0);
1663
 
  if (!allow_all_hosts)
1664
 
  {
1665
 
    for (uint i=0 ; i < acl_users.elements ; i++)
1666
 
    {
1667
 
      ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
1668
 
      if (strchr(acl_user->host.hostname,wild_many) ||
1669
 
          strchr(acl_user->host.hostname,wild_one) ||
1670
 
          acl_user->host.ip_mask)
1671
 
      {                                         // Has wildcard
1672
 
        uint j;
1673
 
        for (j=0 ; j < acl_wild_hosts.elements ; j++)
1674
 
        {                                       // Check if host already exists
1675
 
          acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j,
1676
 
                                               acl_host_and_ip *);
1677
 
          if (!my_strcasecmp(system_charset_info,
1678
 
                             acl_user->host.hostname, acl->hostname))
1679
 
            break;                              // already stored
1680
 
        }
1681
 
        if (j == acl_wild_hosts.elements)       // If new
1682
 
          (void) push_dynamic(&acl_wild_hosts,(uchar*) &acl_user->host);
1683
 
      }
1684
 
      else if (!my_hash_search(&acl_check_hosts,(uchar*)
1685
 
                               acl_user->host.hostname,
1686
 
                               strlen(acl_user->host.hostname)))
1687
 
      {
1688
 
        if (my_hash_insert(&acl_check_hosts,(uchar*) acl_user))
1689
 
        {                                       // End of memory
1690
 
          allow_all_hosts=1;                    // Should never happen
1691
 
          DBUG_VOID_RETURN;
1692
 
        }
1693
 
      }
1694
 
    }
1695
 
  }
1696
 
  freeze_size(&acl_wild_hosts);
1697
 
  freeze_size(&acl_check_hosts.array);
1698
 
  DBUG_VOID_RETURN;
1699
 
}
1700
 
 
1701
 
 
1702
 
/*
1703
 
  Rebuild lists used for checking of allowed hosts
1704
 
 
1705
 
  We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
1706
 
  dropping or renaming user, since they contain pointers to elements of
1707
 
  'acl_user' array, which are invalidated by drop operation, and use
1708
 
  ACL_USER::host::hostname as a key, which is changed by rename.
1709
 
*/
1710
 
void rebuild_check_host(void)
1711
 
{
1712
 
  delete_dynamic(&acl_wild_hosts);
1713
 
  my_hash_free(&acl_check_hosts);
1714
 
  init_check_host();
1715
 
}
1716
 
 
1717
 
 
1718
 
/* Return true if there is no users that can match the given host */
1719
 
 
1720
 
bool acl_check_host(const char *host, const char *ip)
1721
 
{
1722
 
  if (allow_all_hosts)
1723
 
    return 0;
1724
 
  mysql_mutex_lock(&acl_cache->lock);
1725
 
 
1726
 
  if ((host && my_hash_search(&acl_check_hosts,(uchar*) host,strlen(host))) ||
1727
 
      (ip && my_hash_search(&acl_check_hosts,(uchar*) ip, strlen(ip))))
1728
 
  {
1729
 
    mysql_mutex_unlock(&acl_cache->lock);
1730
 
    return 0;                                   // Found host
1731
 
  }
1732
 
  for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
1733
 
  {
1734
 
    acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
1735
 
    if (compare_hostname(acl, host, ip))
1736
 
    {
1737
 
      mysql_mutex_unlock(&acl_cache->lock);
1738
 
      return 0;                                 // Host ok
1739
 
    }
1740
 
  }
1741
 
  mysql_mutex_unlock(&acl_cache->lock);
1742
 
  return 1;                                     // Host is not allowed
1743
 
}
1744
 
 
1745
 
 
1746
 
/*
1747
 
  Check if the user is allowed to change password
1748
 
 
1749
 
  SYNOPSIS:
1750
 
    check_change_password()
1751
 
    thd         THD
1752
 
    host        hostname for the user
1753
 
    user        user name
1754
 
    new_password new password
1755
 
 
1756
 
  NOTE:
1757
 
    new_password cannot be NULL
1758
 
 
1759
 
    RETURN VALUE
1760
 
      0         OK
1761
 
      1         ERROR  ; In this case the error is sent to the client.
1762
 
*/
1763
 
 
1764
 
int check_change_password(THD *thd, const char *host, const char *user,
1765
 
                           char *new_password, uint new_password_len)
1766
 
{
1767
 
  if (!initialized)
1768
 
  {
1769
 
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
1770
 
    return(1);
1771
 
  }
1772
 
  if (!thd->slave_thread &&
1773
 
      (strcmp(thd->security_ctx->user, user) ||
1774
 
       my_strcasecmp(system_charset_info, host,
1775
 
                     thd->security_ctx->priv_host)))
1776
 
  {
1777
 
    if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
1778
 
      return(1);
1779
 
  }
1780
 
  if (!thd->slave_thread && !thd->security_ctx->user[0])
1781
 
  {
1782
 
    my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
1783
 
               MYF(0));
1784
 
    return(1);
1785
 
  }
1786
 
  size_t len= strlen(new_password);
1787
 
  if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
1788
 
      len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
1789
 
  {
1790
 
    my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
1791
 
    return -1;
1792
 
  }
1793
 
  return(0);
1794
 
}
1795
 
 
1796
 
 
1797
 
/*
1798
 
  Change a password for a user
1799
 
 
1800
 
  SYNOPSIS
1801
 
    change_password()
1802
 
    thd                 Thread handle
1803
 
    host                Hostname
1804
 
    user                User name
1805
 
    new_password        New password for host@user
1806
 
 
1807
 
  RETURN VALUES
1808
 
    0   ok
1809
 
    1   ERROR; In this case the error is sent to the client.
1810
 
*/
1811
 
 
1812
 
bool change_password(THD *thd, const char *host, const char *user,
1813
 
                     char *new_password)
1814
 
{
1815
 
  TABLE_LIST tables;
1816
 
  TABLE *table;
1817
 
  /* Buffer should be extended when password length is extended. */
1818
 
  char buff[512];
1819
 
  ulong query_length;
1820
 
  bool save_binlog_row_based;
1821
 
  uint new_password_len= (uint) strlen(new_password);
1822
 
  bool result= 1;
1823
 
  DBUG_ENTER("change_password");
1824
 
  DBUG_PRINT("enter",("host: '%s'  user: '%s'  new_password: '%s'",
1825
 
                      host,user,new_password));
1826
 
  DBUG_ASSERT(host != 0);                       // Ensured by parent
1827
 
 
1828
 
  if (check_change_password(thd, host, user, new_password, new_password_len))
1829
 
    DBUG_RETURN(1);
1830
 
 
1831
 
  tables.init_one_table("mysql", 5, "user", 4, "user", TL_WRITE);
1832
 
 
1833
 
#ifdef HAVE_REPLICATION
1834
 
  /*
1835
 
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
1836
 
    some kind of updates to the mysql.% tables.
1837
 
  */
1838
 
  if (thd->slave_thread && rpl_filter->is_on())
1839
 
  {
1840
 
    /*
1841
 
      The tables must be marked "updating" so that tables_ok() takes them into
1842
 
      account in tests.  It's ok to leave 'updating' set after tables_ok.
1843
 
    */
1844
 
    tables.updating= 1;
1845
 
    /* Thanks to bzero, tables.next==0 */
1846
 
    if (!(thd->spcont || rpl_filter->tables_ok(0, &tables)))
1847
 
      DBUG_RETURN(0);
1848
 
  }
1849
 
#endif
1850
 
  if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
1851
 
    DBUG_RETURN(1);
1852
 
 
1853
 
  /*
1854
 
    This statement will be replicated as a statement, even when using
1855
 
    row-based replication.  The flag will be reset at the end of the
1856
 
    statement.
1857
 
  */
1858
 
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
1859
 
    thd->clear_current_stmt_binlog_format_row();
1860
 
 
1861
 
  mysql_mutex_lock(&acl_cache->lock);
1862
 
  ACL_USER *acl_user;
1863
 
  if (!(acl_user= find_acl_user(host, user, TRUE)))
1864
 
  {
1865
 
    mysql_mutex_unlock(&acl_cache->lock);
1866
 
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
1867
 
    goto end;
1868
 
  }
1869
 
 
1870
 
  /* update loaded acl entry: */
1871
 
  set_user_salt(acl_user, new_password, new_password_len);
1872
 
 
1873
 
  if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
1874
 
                    native_password_plugin_name.str) &&
1875
 
      my_strcasecmp(system_charset_info, acl_user->plugin.str,
1876
 
                    old_password_plugin_name.str))
1877
 
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
1878
 
                 ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
1879
 
  else
1880
 
    set_user_plugin(acl_user, new_password_len);
1881
 
 
1882
 
  if (update_user_table(thd, table,
1883
 
                        acl_user->host.hostname ? acl_user->host.hostname : "",
1884
 
                        acl_user->user ? acl_user->user : "",
1885
 
                        new_password, new_password_len))
1886
 
  {
1887
 
    mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
1888
 
    goto end;
1889
 
  }
1890
 
 
1891
 
  acl_cache->clear(1);                          // Clear locked hostname cache
1892
 
  mysql_mutex_unlock(&acl_cache->lock);
1893
 
  result= 0;
1894
 
  if (mysql_bin_log.is_open())
1895
 
  {
1896
 
    query_length= sprintf(buff, "SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
1897
 
                          acl_user->user ? acl_user->user : "",
1898
 
                          acl_user->host.hostname ? acl_user->host.hostname : "",
1899
 
                          new_password);
1900
 
    thd->clear_error();
1901
 
    result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
1902
 
                              FALSE, FALSE, FALSE, 0);
1903
 
  }
1904
 
end:
1905
 
  close_mysql_tables(thd);
1906
 
 
1907
 
  /* Restore the state of binlog format */
1908
 
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
1909
 
  if (save_binlog_row_based)
1910
 
    thd->set_current_stmt_binlog_format_row();
1911
 
 
1912
 
  DBUG_RETURN(result);
1913
 
}
1914
 
 
1915
 
 
1916
 
/*
1917
 
  Find user in ACL
1918
 
 
1919
 
  SYNOPSIS
1920
 
    is_acl_user()
1921
 
    host                 host name
1922
 
    user                 user name
1923
 
 
1924
 
  RETURN
1925
 
   FALSE  user not fond
1926
 
   TRUE   there are such user
1927
 
*/
1928
 
 
1929
 
bool is_acl_user(const char *host, const char *user)
1930
 
{
1931
 
  bool res;
1932
 
 
1933
 
  /* --skip-grants */
1934
 
  if (!initialized)
1935
 
    return TRUE;
1936
 
 
1937
 
  mysql_mutex_lock(&acl_cache->lock);
1938
 
  res= find_acl_user(host, user, TRUE) != NULL;
1939
 
  mysql_mutex_unlock(&acl_cache->lock);
1940
 
  return res;
1941
 
}
1942
 
 
1943
 
 
1944
 
/*
1945
 
  Find first entry that matches the current user
1946
 
*/
1947
 
 
1948
 
static ACL_USER *
1949
 
find_acl_user(const char *host, const char *user, my_bool exact)
1950
 
{
1951
 
  DBUG_ENTER("find_acl_user");
1952
 
  DBUG_PRINT("enter",("host: '%s'  user: '%s'",host,user));
1953
 
 
1954
 
  mysql_mutex_assert_owner(&acl_cache->lock);
1955
 
 
1956
 
  for (uint i=0 ; i < acl_users.elements ; i++)
1957
 
  {
1958
 
    ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
1959
 
    DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
1960
 
                       user, acl_user->user ? acl_user->user : "",
1961
 
                       host,
1962
 
                       acl_user->host.hostname ? acl_user->host.hostname :
1963
 
                       ""));
1964
 
    if ((!acl_user->user && !user[0]) ||
1965
 
        (acl_user->user && !strcmp(user,acl_user->user)))
1966
 
    {
1967
 
      if (exact ? !my_strcasecmp(system_charset_info, host,
1968
 
                                 acl_user->host.hostname ?
1969
 
                                 acl_user->host.hostname : "") :
1970
 
          compare_hostname(&acl_user->host,host,host))
1971
 
      {
1972
 
        DBUG_RETURN(acl_user);
1973
 
      }
1974
 
    }
1975
 
  }
1976
 
  DBUG_RETURN(0);
1977
 
}
1978
 
 
1979
 
 
1980
 
/*
1981
 
  Comparing of hostnames
1982
 
 
1983
 
  NOTES
1984
 
  A hostname may be of type:
1985
 
  hostname   (May include wildcards);   monty.pp.sci.fi
1986
 
  ip       (May include wildcards);   192.168.0.0
1987
 
  ip/netmask                          192.168.0.0/255.255.255.0
1988
 
 
1989
 
  A net mask of 0.0.0.0 is not allowed.
1990
 
*/
1991
 
 
1992
 
static const char *calc_ip(const char *ip, long *val, char end)
1993
 
{
1994
 
  long ip_val,tmp;
1995
 
  if (!(ip=str2int(ip,10,0,255,&ip_val)) || *ip != '.')
1996
 
    return 0;
1997
 
  ip_val<<=24;
1998
 
  if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
1999
 
    return 0;
2000
 
  ip_val+=tmp<<16;
2001
 
  if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
2002
 
    return 0;
2003
 
  ip_val+=tmp<<8;
2004
 
  if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != end)
2005
 
    return 0;
2006
 
  *val=ip_val+tmp;
2007
 
  return ip;
2008
 
}
2009
 
 
2010
 
 
2011
 
static void update_hostname(acl_host_and_ip *host, const char *hostname)
2012
 
{
2013
 
  host->hostname=(char*) hostname;             // This will not be modified!
2014
 
  if (!hostname ||
2015
 
      (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
2016
 
       !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
2017
 
  {
2018
 
    host->ip= host->ip_mask=0;                  // Not a masked ip
2019
 
  }
2020
 
}
2021
 
 
2022
 
 
2023
 
static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
2024
 
                             const char *ip)
2025
 
{
2026
 
  long tmp;
2027
 
  if (host->ip_mask && ip && calc_ip(ip,&tmp,'\0'))
2028
 
  {
2029
 
    return (tmp & host->ip_mask) == host->ip;
2030
 
  }
2031
 
  return (!host->hostname ||
2032
 
          (hostname && !wild_case_compare(system_charset_info,
2033
 
                                          hostname, host->hostname)) ||
2034
 
          (ip && !wild_compare(ip, host->hostname, 0)));
2035
 
}
2036
 
 
2037
 
/**
2038
 
  Check if the given host name needs to be resolved or not.
2039
 
  Host name has to be resolved if it actually contains *name*.
2040
 
 
2041
 
  For example:
2042
 
    192.168.1.1               --> FALSE
2043
 
    192.168.1.0/255.255.255.0 --> FALSE
2044
 
    %                         --> FALSE
2045
 
    192.168.1.%               --> FALSE
2046
 
    AB%                       --> FALSE
2047
 
 
2048
 
    AAAAFFFF                  --> TRUE (Hostname)
2049
 
    AAAA:FFFF:1234:5678       --> FALSE
2050
 
    ::1                       --> FALSE
2051
 
 
2052
 
  This function does not check if the given string is a valid host name or
2053
 
  not. It assumes that the argument is a valid host name.
2054
 
 
2055
 
  @param hostname   the string to check.
2056
 
 
2057
 
  @return a flag telling if the argument needs to be resolved or not.
2058
 
  @retval TRUE the argument is a host name and needs to be resolved.
2059
 
  @retval FALSE the argument is either an IP address, or a patter and
2060
 
          should not be resolved.
2061
 
*/
2062
 
 
2063
 
bool hostname_requires_resolving(const char *hostname)
2064
 
{
2065
 
  if (!hostname)
2066
 
    return FALSE;
2067
 
 
2068
 
  /* Check if hostname is the localhost. */
2069
 
 
2070
 
  size_t hostname_len= strlen(hostname);
2071
 
  size_t localhost_len= strlen(my_localhost);
2072
 
 
2073
 
  if (hostname == my_localhost ||
2074
 
      (hostname_len == localhost_len &&
2075
 
       !my_strnncoll(system_charset_info,
2076
 
                     (const uchar *) hostname,  hostname_len,
2077
 
                     (const uchar *) my_localhost, strlen(my_localhost))))
2078
 
  {
2079
 
    return FALSE;
2080
 
  }
2081
 
 
2082
 
  /*
2083
 
    If the string contains any of {':', '%', '_', '/'}, it is definitely
2084
 
    not a host name:
2085
 
      - ':' means that the string is an IPv6 address;
2086
 
      - '%' or '_' means that the string is a pattern;
2087
 
      - '/' means that the string is an IPv4 network address;
2088
 
  */
2089
 
 
2090
 
  for (const char *p= hostname; *p; ++p)
2091
 
  {
2092
 
    switch (*p) {
2093
 
      case ':':
2094
 
      case '%':
2095
 
      case '_':
2096
 
      case '/':
2097
 
        return FALSE;
2098
 
    }
2099
 
  }
2100
 
 
2101
 
  /*
2102
 
    Now we have to tell a host name (ab.cd, 12.ab) from an IPv4 address
2103
 
    (12.34.56.78). The assumption is that if the string contains only
2104
 
    digits and dots, it is an IPv4 address. Otherwise -- a host name.
2105
 
  */
2106
 
 
2107
 
  for (const char *p= hostname; *p; ++p)
2108
 
  {
2109
 
    if (*p != '.' && !my_isdigit(&my_charset_latin1, *p))
2110
 
      return TRUE; /* a "letter" has been found. */
2111
 
  }
2112
 
 
2113
 
  return FALSE; /* all characters are either dots or digits. */
2114
 
}
2115
 
 
2116
 
 
2117
 
/*
2118
 
  Update record for user in mysql.user privilege table with new password.
2119
 
 
2120
 
  SYNOPSIS
2121
 
    update_user_table()
2122
 
      thd               Thread handle
2123
 
      table             Pointer to TABLE object for open mysql.user table
2124
 
      host/user         Hostname/username pair identifying user for which
2125
 
                        new password should be set
2126
 
      new_password      New password
2127
 
      new_password_len  Length of new password
2128
 
*/
2129
 
 
2130
 
static bool update_user_table(THD *thd, TABLE *table,
2131
 
                              const char *host, const char *user,
2132
 
                              const char *new_password, uint new_password_len)
2133
 
{
2134
 
  char user_key[MAX_KEY_LENGTH];
2135
 
  int error;
2136
 
  DBUG_ENTER("update_user_table");
2137
 
  DBUG_PRINT("enter",("user: %s  host: %s",user,host));
2138
 
 
2139
 
  table->use_all_columns();
2140
 
  table->field[0]->store(host,(uint) strlen(host), system_charset_info);
2141
 
  table->field[1]->store(user,(uint) strlen(user), system_charset_info);
2142
 
  key_copy((uchar *) user_key, table->record[0], table->key_info,
2143
 
           table->key_info->key_length);
2144
 
 
2145
 
  if (table->file->index_read_idx_map(table->record[0], 0,
2146
 
                                      (uchar *) user_key, HA_WHOLE_KEY,
2147
 
                                      HA_READ_KEY_EXACT))
2148
 
  {
2149
 
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
2150
 
               MYF(0)); /* purecov: deadcode */
2151
 
    DBUG_RETURN(1);                             /* purecov: deadcode */
2152
 
  }
2153
 
  store_record(table,record[1]);
2154
 
  table->field[2]->store(new_password, new_password_len, system_charset_info);
2155
 
  if ((error=table->file->ha_update_row(table->record[1],table->record[0])) &&
2156
 
      error != HA_ERR_RECORD_IS_THE_SAME)
2157
 
  {
2158
 
    table->file->print_error(error,MYF(0));     /* purecov: deadcode */
2159
 
    DBUG_RETURN(1);
2160
 
  }
2161
 
  DBUG_RETURN(0);
2162
 
}
2163
 
 
2164
 
 
2165
 
/*
2166
 
  Return 1 if we are allowed to create new users
2167
 
  the logic here is: INSERT_ACL is sufficient.
2168
 
  It's also a requirement in opt_safe_user_create,
2169
 
  otherwise CREATE_USER_ACL is enough.
2170
 
*/
2171
 
 
2172
 
static bool test_if_create_new_users(THD *thd)
2173
 
{
2174
 
  Security_context *sctx= thd->security_ctx;
2175
 
  bool create_new_users= test(sctx->master_access & INSERT_ACL) ||
2176
 
                         (!opt_safe_user_create &&
2177
 
                          test(sctx->master_access & CREATE_USER_ACL));
2178
 
  if (!create_new_users)
2179
 
  {
2180
 
    TABLE_LIST tl;
2181
 
    ulong db_access;
2182
 
    tl.init_one_table(C_STRING_WITH_LEN("mysql"),
2183
 
                      C_STRING_WITH_LEN("user"), "user", TL_WRITE);
2184
 
    create_new_users= 1;
2185
 
 
2186
 
    db_access=acl_get(sctx->host, sctx->ip,
2187
 
                      sctx->priv_user, tl.db, 0);
2188
 
    if (!(db_access & INSERT_ACL))
2189
 
    {
2190
 
      if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
2191
 
        create_new_users=0;
2192
 
    }
2193
 
  }
2194
 
  return create_new_users;
2195
 
}
2196
 
 
2197
 
 
2198
 
/****************************************************************************
2199
 
  Handle GRANT commands
2200
 
****************************************************************************/
2201
 
 
2202
 
static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
2203
 
                              ulong rights, bool revoke_grant,
2204
 
                              bool can_create_user, bool no_auto_create)
2205
 
{
2206
 
  int error = -1;
2207
 
  bool old_row_exists=0;
2208
 
  const char *password= "";
2209
 
  uint password_len= 0;
2210
 
  char what= (revoke_grant) ? 'N' : 'Y';
2211
 
  uchar user_key[MAX_KEY_LENGTH];
2212
 
  LEX *lex= thd->lex;
2213
 
  DBUG_ENTER("replace_user_table");
2214
 
 
2215
 
  mysql_mutex_assert_owner(&acl_cache->lock);
2216
 
 
2217
 
  if (combo.password.str && combo.password.str[0])
2218
 
  {
2219
 
    if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
2220
 
        combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
2221
 
    {
2222
 
      my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
2223
 
      DBUG_RETURN(-1);
2224
 
    }
2225
 
    password_len= combo.password.length;
2226
 
    password=combo.password.str;
2227
 
  }
2228
 
 
2229
 
  table->use_all_columns();
2230
 
  table->field[0]->store(combo.host.str,combo.host.length,
2231
 
                         system_charset_info);
2232
 
  table->field[1]->store(combo.user.str,combo.user.length,
2233
 
                         system_charset_info);
2234
 
  key_copy(user_key, table->record[0], table->key_info,
2235
 
           table->key_info->key_length);
2236
 
 
2237
 
  if (table->file->index_read_idx_map(table->record[0], 0, user_key,
2238
 
                                      HA_WHOLE_KEY,
2239
 
                                      HA_READ_KEY_EXACT))
2240
 
  {
2241
 
    /* what == 'N' means revoke */
2242
 
    if (what == 'N')
2243
 
    {
2244
 
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
2245
 
      goto end;
2246
 
    }
2247
 
    /*
2248
 
      There are four options which affect the process of creation of
2249
 
      a new user (mysqld option --safe-create-user, 'insert' privilege
2250
 
      on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
2251
 
      SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
2252
 
      how it should work.
2253
 
      if (safe-user-create && ! INSERT_priv) => reject
2254
 
      else if (identified_by) => create
2255
 
      else if (no_auto_create_user) => reject
2256
 
      else create
2257
 
 
2258
 
      see also test_if_create_new_users()
2259
 
    */
2260
 
    else if (!password_len && !combo.plugin.length && no_auto_create)
2261
 
    {
2262
 
      my_error(ER_PASSWORD_NO_MATCH, MYF(0));
2263
 
      goto end;
2264
 
    }
2265
 
    else if (!can_create_user)
2266
 
    {
2267
 
      my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0));
2268
 
      goto end;
2269
 
    }
2270
 
    else if (combo.plugin.str[0])
2271
 
    {
2272
 
      if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN))
2273
 
      {
2274
 
        my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str);
2275
 
        goto end;
2276
 
      }
2277
 
    }
2278
 
 
2279
 
    old_row_exists = 0;
2280
 
    restore_record(table,s->default_values);
2281
 
    table->field[0]->store(combo.host.str,combo.host.length,
2282
 
                           system_charset_info);
2283
 
    table->field[1]->store(combo.user.str,combo.user.length,
2284
 
                           system_charset_info);
2285
 
    table->field[2]->store(password, password_len,
2286
 
                           system_charset_info);
2287
 
  }
2288
 
  else
2289
 
  {
2290
 
    old_row_exists = 1;
2291
 
    store_record(table,record[1]);                      // Save copy for update
2292
 
    /* what == 'N' means revoke */
2293
 
    if (combo.plugin.length && what != 'N')
2294
 
    {
2295
 
        my_error(ER_GRANT_PLUGIN_USER_EXISTS, MYF(0),
2296
 
                 static_cast<int>(combo.user.length), combo.user.str);
2297
 
        goto end;
2298
 
    }
2299
 
    if (combo.password.str)                             // If password given
2300
 
      table->field[2]->store(password, password_len, system_charset_info);
2301
 
    else if (!rights && !revoke_grant &&
2302
 
             lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
2303
 
             !lex->mqh.specified_limits)
2304
 
    {
2305
 
      DBUG_RETURN(0);
2306
 
    }
2307
 
  }
2308
 
 
2309
 
  /* Update table columns with new privileges */
2310
 
 
2311
 
  Field **tmp_field;
2312
 
  ulong priv;
2313
 
  uint next_field;
2314
 
  for (tmp_field= table->field+3, priv = SELECT_ACL;
2315
 
       *tmp_field && (*tmp_field)->real_type() == MYSQL_TYPE_ENUM &&
2316
 
         ((Field_enum*) (*tmp_field))->typelib->count == 2 ;
2317
 
       tmp_field++, priv <<= 1)
2318
 
  {
2319
 
    if (priv & rights)                           // set requested privileges
2320
 
      (*tmp_field)->store(&what, 1, &my_charset_latin1);
2321
 
  }
2322
 
  rights= get_access(table, 3, &next_field);
2323
 
  DBUG_PRINT("info",("table fields: %d",table->s->fields));
2324
 
  if (table->s->fields >= 31)           /* From 4.0.0 we have more fields */
2325
 
  {
2326
 
    /* We write down SSL related ACL stuff */
2327
 
    switch (lex->ssl_type) {
2328
 
    case SSL_TYPE_ANY:
2329
 
      table->field[next_field]->store(STRING_WITH_LEN("ANY"),
2330
 
                                      &my_charset_latin1);
2331
 
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
2332
 
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
2333
 
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
2334
 
      break;
2335
 
    case SSL_TYPE_X509:
2336
 
      table->field[next_field]->store(STRING_WITH_LEN("X509"),
2337
 
                                      &my_charset_latin1);
2338
 
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
2339
 
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
2340
 
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
2341
 
      break;
2342
 
    case SSL_TYPE_SPECIFIED:
2343
 
      table->field[next_field]->store(STRING_WITH_LEN("SPECIFIED"),
2344
 
                                      &my_charset_latin1);
2345
 
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
2346
 
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
2347
 
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
2348
 
      if (lex->ssl_cipher)
2349
 
        table->field[next_field+1]->store(lex->ssl_cipher,
2350
 
                                strlen(lex->ssl_cipher), system_charset_info);
2351
 
      if (lex->x509_issuer)
2352
 
        table->field[next_field+2]->store(lex->x509_issuer,
2353
 
                                strlen(lex->x509_issuer), system_charset_info);
2354
 
      if (lex->x509_subject)
2355
 
        table->field[next_field+3]->store(lex->x509_subject,
2356
 
                                strlen(lex->x509_subject), system_charset_info);
2357
 
      break;
2358
 
    case SSL_TYPE_NOT_SPECIFIED:
2359
 
      break;
2360
 
    case SSL_TYPE_NONE:
2361
 
      table->field[next_field]->store("", 0, &my_charset_latin1);
2362
 
      table->field[next_field+1]->store("", 0, &my_charset_latin1);
2363
 
      table->field[next_field+2]->store("", 0, &my_charset_latin1);
2364
 
      table->field[next_field+3]->store("", 0, &my_charset_latin1);
2365
 
      break;
2366
 
    }
2367
 
    next_field+=4;
2368
 
 
2369
 
    USER_RESOURCES mqh= lex->mqh;
2370
 
    if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
2371
 
      table->field[next_field]->store((longlong) mqh.questions, TRUE);
2372
 
    if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
2373
 
      table->field[next_field+1]->store((longlong) mqh.updates, TRUE);
2374
 
    if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
2375
 
      table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE);
2376
 
    if (table->s->fields >= 36 &&
2377
 
        (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
2378
 
      table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE);
2379
 
    mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
2380
 
 
2381
 
    next_field+= 4;
2382
 
    if (combo.plugin.str[0])
2383
 
    {
2384
 
      if (table->s->fields >= 41 && combo.plugin.str[0])
2385
 
      {
2386
 
        table->field[next_field]->store(combo.plugin.str, combo.plugin.length,
2387
 
                                        system_charset_info);
2388
 
        table->field[next_field]->set_notnull();
2389
 
        table->field[next_field + 1]->store(combo.auth.str, combo.auth.length,
2390
 
                                            system_charset_info);
2391
 
        table->field[next_field + 1]->set_notnull();
2392
 
      }
2393
 
      else
2394
 
      {
2395
 
        my_error(ER_BAD_FIELD_ERROR, MYF(0), "plugin", "mysql.user");
2396
 
        goto end;
2397
 
      }
2398
 
    }
2399
 
  }
2400
 
 
2401
 
  if (old_row_exists)
2402
 
  {
2403
 
    /*
2404
 
      We should NEVER delete from the user table, as a uses can still
2405
 
      use mysqld even if he doesn't have any privileges in the user table!
2406
 
    */
2407
 
    if (cmp_record(table,record[1]))
2408
 
    {
2409
 
      if ((error=
2410
 
           table->file->ha_update_row(table->record[1],table->record[0])) &&
2411
 
          error != HA_ERR_RECORD_IS_THE_SAME)
2412
 
      {                                         // This should never happen
2413
 
        table->file->print_error(error,MYF(0)); /* purecov: deadcode */
2414
 
        error= -1;                              /* purecov: deadcode */
2415
 
        goto end;                               /* purecov: deadcode */
2416
 
      }
2417
 
      else
2418
 
        error= 0;
2419
 
    }
2420
 
  }
2421
 
  else if ((error=table->file->ha_write_row(table->record[0]))) // insert
2422
 
  {                                             // This should never happen
2423
 
    if (table->file->is_fatal_error(error, HA_CHECK_DUP))
2424
 
    {
2425
 
      table->file->print_error(error,MYF(0));   /* purecov: deadcode */
2426
 
      error= -1;                                /* purecov: deadcode */
2427
 
      goto end;                                 /* purecov: deadcode */
2428
 
    }
2429
 
  }
2430
 
  error=0;                                      // Privileges granted / revoked
2431
 
 
2432
 
end:
2433
 
  if (!error)
2434
 
  {
2435
 
    acl_cache->clear(1);                        // Clear privilege cache
2436
 
    if (old_row_exists)
2437
 
      acl_update_user(combo.user.str, combo.host.str,
2438
 
                      combo.password.str, password_len,
2439
 
                      lex->ssl_type,
2440
 
                      lex->ssl_cipher,
2441
 
                      lex->x509_issuer,
2442
 
                      lex->x509_subject,
2443
 
                      &lex->mqh,
2444
 
                      rights,
2445
 
                      &combo.plugin,
2446
 
                      &combo.auth);
2447
 
    else
2448
 
      acl_insert_user(combo.user.str, combo.host.str, password, password_len,
2449
 
                      lex->ssl_type,
2450
 
                      lex->ssl_cipher,
2451
 
                      lex->x509_issuer,
2452
 
                      lex->x509_subject,
2453
 
                      &lex->mqh,
2454
 
                      rights,
2455
 
                      &combo.plugin,
2456
 
                      &combo.auth);
2457
 
  }
2458
 
  DBUG_RETURN(error);
2459
 
}
2460
 
 
2461
 
 
2462
 
/*
2463
 
  change grants in the mysql.db table
2464
 
*/
2465
 
 
2466
 
static int replace_db_table(TABLE *table, const char *db,
2467
 
                            const LEX_USER &combo,
2468
 
                            ulong rights, bool revoke_grant)
2469
 
{
2470
 
  uint i;
2471
 
  ulong priv,store_rights;
2472
 
  bool old_row_exists=0;
2473
 
  int error;
2474
 
  char what= (revoke_grant) ? 'N' : 'Y';
2475
 
  uchar user_key[MAX_KEY_LENGTH];
2476
 
  DBUG_ENTER("replace_db_table");
2477
 
 
2478
 
  if (!initialized)
2479
 
  {
2480
 
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2481
 
    DBUG_RETURN(-1);
2482
 
  }
2483
 
 
2484
 
  /* Check if there is such a user in user table in memory? */
2485
 
  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
2486
 
  {
2487
 
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
2488
 
    DBUG_RETURN(-1);
2489
 
  }
2490
 
 
2491
 
  table->use_all_columns();
2492
 
  table->field[0]->store(combo.host.str,combo.host.length,
2493
 
                         system_charset_info);
2494
 
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
2495
 
  table->field[2]->store(combo.user.str,combo.user.length,
2496
 
                         system_charset_info);
2497
 
  key_copy(user_key, table->record[0], table->key_info,
2498
 
           table->key_info->key_length);
2499
 
 
2500
 
  if (table->file->index_read_idx_map(table->record[0],0, user_key,
2501
 
                                      HA_WHOLE_KEY,
2502
 
                                      HA_READ_KEY_EXACT))
2503
 
  {
2504
 
    if (what == 'N')
2505
 
    { // no row, no revoke
2506
 
      my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
2507
 
      goto abort;
2508
 
    }
2509
 
    old_row_exists = 0;
2510
 
    restore_record(table, s->default_values);
2511
 
    table->field[0]->store(combo.host.str,combo.host.length,
2512
 
                           system_charset_info);
2513
 
    table->field[1]->store(db,(uint) strlen(db), system_charset_info);
2514
 
    table->field[2]->store(combo.user.str,combo.user.length,
2515
 
                           system_charset_info);
2516
 
  }
2517
 
  else
2518
 
  {
2519
 
    old_row_exists = 1;
2520
 
    store_record(table,record[1]);
2521
 
  }
2522
 
 
2523
 
  store_rights=get_rights_for_db(rights);
2524
 
  for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
2525
 
  {
2526
 
    if (priv & store_rights)                    // do it if priv is chosen
2527
 
      table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
2528
 
  }
2529
 
  rights=get_access(table,3);
2530
 
  rights=fix_rights_for_db(rights);
2531
 
 
2532
 
  if (old_row_exists)
2533
 
  {
2534
 
    /* update old existing row */
2535
 
    if (rights)
2536
 
    {
2537
 
      if ((error= table->file->ha_update_row(table->record[1],
2538
 
                                             table->record[0])) &&
2539
 
          error != HA_ERR_RECORD_IS_THE_SAME)
2540
 
        goto table_error;                       /* purecov: deadcode */
2541
 
    }
2542
 
    else        /* must have been a revoke of all privileges */
2543
 
    {
2544
 
      if ((error= table->file->ha_delete_row(table->record[1])))
2545
 
        goto table_error;                       /* purecov: deadcode */
2546
 
    }
2547
 
  }
2548
 
  else if (rights && (error= table->file->ha_write_row(table->record[0])))
2549
 
  {
2550
 
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
2551
 
      goto table_error; /* purecov: deadcode */
2552
 
  }
2553
 
 
2554
 
  acl_cache->clear(1);                          // Clear privilege cache
2555
 
  if (old_row_exists)
2556
 
    acl_update_db(combo.user.str,combo.host.str,db,rights);
2557
 
  else
2558
 
  if (rights)
2559
 
    acl_insert_db(combo.user.str,combo.host.str,db,rights);
2560
 
  DBUG_RETURN(0);
2561
 
 
2562
 
  /* This could only happen if the grant tables got corrupted */
2563
 
table_error:
2564
 
  table->file->print_error(error,MYF(0));       /* purecov: deadcode */
2565
 
 
2566
 
abort:
2567
 
  DBUG_RETURN(-1);
2568
 
}
2569
 
 
2570
 
 
2571
 
static void  
2572
 
acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
2573
 
{
2574
 
  mysql_mutex_assert_owner(&acl_cache->lock);
2575
 
 
2576
 
  DBUG_ENTER("acl_update_proxy_user");
2577
 
  for (uint i= 0; i < acl_proxy_users.elements; i++)
2578
 
  {
2579
 
    ACL_PROXY_USER *acl_user= 
2580
 
      dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
2581
 
 
2582
 
    if (acl_user->pk_equals(new_value))
2583
 
    {
2584
 
      if (is_revoke)
2585
 
      {
2586
 
        DBUG_PRINT("info", ("delting ACL_PROXY_USER"));
2587
 
        delete_dynamic_element(&acl_proxy_users, i);
2588
 
      }
2589
 
      else
2590
 
      {
2591
 
        DBUG_PRINT("info", ("updating ACL_PROXY_USER"));
2592
 
        acl_user->set_data(new_value);
2593
 
      }
2594
 
      break;
2595
 
    }
2596
 
  }
2597
 
  DBUG_VOID_RETURN;
2598
 
}
2599
 
 
2600
 
 
2601
 
static void  
2602
 
acl_insert_proxy_user(ACL_PROXY_USER *new_value)
2603
 
{
2604
 
  DBUG_ENTER("acl_insert_proxy_user");
2605
 
  mysql_mutex_assert_owner(&acl_cache->lock);
2606
 
  (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
2607
 
  my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER *),
2608
 
           acl_proxy_users.elements,
2609
 
           sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
2610
 
  DBUG_VOID_RETURN;
2611
 
}
2612
 
 
2613
 
 
2614
 
static int 
2615
 
replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
2616
 
                         const LEX_USER *proxied_user, bool with_grant_arg, 
2617
 
                         bool revoke_grant)
2618
 
{
2619
 
  bool old_row_exists= 0;
2620
 
  int error;
2621
 
  uchar user_key[MAX_KEY_LENGTH];
2622
 
  ACL_PROXY_USER new_grant;
2623
 
  char grantor[USER_HOST_BUFF_SIZE];
2624
 
 
2625
 
  DBUG_ENTER("replace_proxies_priv_table");
2626
 
 
2627
 
  if (!initialized)
2628
 
  {
2629
 
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
2630
 
    DBUG_RETURN(-1);
2631
 
  }
2632
 
 
2633
 
  /* Check if there is such a user in user table in memory? */
2634
 
  if (!find_acl_user(user->host.str,user->user.str, FALSE))
2635
 
  {
2636
 
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
2637
 
    DBUG_RETURN(-1);
2638
 
  }
2639
 
 
2640
 
  table->use_all_columns();
2641
 
  ACL_PROXY_USER::store_pk (table, &user->host, &user->user, 
2642
 
                            &proxied_user->host, &proxied_user->user);
2643
 
 
2644
 
  key_copy(user_key, table->record[0], table->key_info,
2645
 
           table->key_info->key_length);
2646
 
 
2647
 
  get_grantor(thd, grantor);
2648
 
 
2649
 
  table->file->ha_index_init(0, 1);
2650
 
  if (table->file->index_read_map(table->record[0], user_key,
2651
 
                                      HA_WHOLE_KEY,
2652
 
                                      HA_READ_KEY_EXACT))
2653
 
  {
2654
 
    DBUG_PRINT ("info", ("Row not found"));
2655
 
    if (revoke_grant)
2656
 
    { // no row, no revoke
2657
 
      my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
2658
 
      goto abort;
2659
 
    }
2660
 
    old_row_exists= 0;
2661
 
    restore_record(table, s->default_values);
2662
 
    ACL_PROXY_USER::store_data_record(table, &user->host, &user->user,
2663
 
                                      &proxied_user->host,
2664
 
                                      &proxied_user->user,
2665
 
                                      with_grant_arg,
2666
 
                                      grantor);
2667
 
  }
2668
 
  else
2669
 
  {
2670
 
    DBUG_PRINT("info", ("Row found"));
2671
 
    old_row_exists= 1;
2672
 
    store_record(table, record[1]);
2673
 
  }
2674
 
 
2675
 
  if (old_row_exists)
2676
 
  {
2677
 
    /* update old existing row */
2678
 
    if (!revoke_grant)
2679
 
    {
2680
 
      if ((error= table->file->ha_update_row(table->record[1],
2681
 
                                             table->record[0])) &&
2682
 
          error != HA_ERR_RECORD_IS_THE_SAME)
2683
 
        goto table_error;                       /* purecov: inspected */
2684
 
    }
2685
 
    else
2686
 
    {
2687
 
      if ((error= table->file->ha_delete_row(table->record[1])))
2688
 
        goto table_error;                       /* purecov: inspected */
2689
 
    }
2690
 
  }
2691
 
  else if ((error= table->file->ha_write_row(table->record[0])))
2692
 
  {
2693
 
    DBUG_PRINT("info", ("error inserting the row"));
2694
 
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
2695
 
      goto table_error; /* purecov: inspected */
2696
 
  }
2697
 
 
2698
 
  acl_cache->clear(1);                          // Clear privilege cache
2699
 
  if (old_row_exists)
2700
 
  {
2701
 
    new_grant.init(user->host.str, user->user.str,
2702
 
                   proxied_user->host.str, proxied_user->user.str,
2703
 
                   with_grant_arg);
2704
 
    acl_update_proxy_user(&new_grant, revoke_grant);
2705
 
  }
2706
 
  else
2707
 
  {
2708
 
    new_grant.init(&mem, user->host.str, user->user.str,
2709
 
                   proxied_user->host.str, proxied_user->user.str,
2710
 
                   with_grant_arg);
2711
 
    acl_insert_proxy_user(&new_grant);
2712
 
  }
2713
 
 
2714
 
  table->file->ha_index_end();
2715
 
  DBUG_RETURN(0);
2716
 
 
2717
 
  /* This could only happen if the grant tables got corrupted */
2718
 
table_error:
2719
 
  DBUG_PRINT("info", ("table error"));
2720
 
  table->file->print_error(error, MYF(0));      /* purecov: inspected */
2721
 
 
2722
 
abort:
2723
 
  DBUG_PRINT("info", ("aborting replace_proxies_priv_table"));
2724
 
  table->file->ha_index_end();
2725
 
  DBUG_RETURN(-1);
2726
 
}
2727
 
 
2728
 
 
2729
 
class GRANT_COLUMN :public Sql_alloc
2730
 
{
2731
 
public:
2732
 
  char *column;
2733
 
  ulong rights;
2734
 
  uint key_length;
2735
 
  GRANT_COLUMN(String &c,  ulong y) :rights (y)
2736
 
  {
2737
 
    column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length());
2738
 
  }
2739
 
};
2740
 
 
2741
 
 
2742
 
static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
2743
 
                            my_bool not_used __attribute__((unused)))
2744
 
{
2745
 
  *length=buff->key_length;
2746
 
  return (uchar*) buff->column;
2747
 
}
2748
 
 
2749
 
 
2750
 
class GRANT_NAME :public Sql_alloc
2751
 
{
2752
 
public:
2753
 
  acl_host_and_ip host;
2754
 
  char *db, *user, *tname, *hash_key;
2755
 
  ulong privs;
2756
 
  ulong sort;
2757
 
  size_t key_length;
2758
 
  GRANT_NAME(const char *h, const char *d,const char *u,
2759
 
             const char *t, ulong p, bool is_routine);
2760
 
  GRANT_NAME (TABLE *form, bool is_routine);
2761
 
  virtual ~GRANT_NAME() {};
2762
 
  virtual bool ok() { return privs != 0; }
2763
 
  void set_user_details(const char *h, const char *d,
2764
 
                        const char *u, const char *t,
2765
 
                        bool is_routine);
2766
 
};
2767
 
 
2768
 
 
2769
 
class GRANT_TABLE :public GRANT_NAME
2770
 
{
2771
 
public:
2772
 
  ulong cols;
2773
 
  HASH hash_columns;
2774
 
 
2775
 
  GRANT_TABLE(const char *h, const char *d,const char *u,
2776
 
              const char *t, ulong p, ulong c);
2777
 
  GRANT_TABLE (TABLE *form, TABLE *col_privs);
2778
 
  ~GRANT_TABLE();
2779
 
  bool ok() { return privs != 0 || cols != 0; }
2780
 
};
2781
 
 
2782
 
 
2783
 
void GRANT_NAME::set_user_details(const char *h, const char *d,
2784
 
                                  const char *u, const char *t,
2785
 
                                  bool is_routine)
2786
 
{
2787
 
  /* Host given by user */
2788
 
  update_hostname(&host, strdup_root(&memex, h));
2789
 
  if (db != d)
2790
 
  {
2791
 
    db= strdup_root(&memex, d);
2792
 
    if (lower_case_table_names)
2793
 
      my_casedn_str(files_charset_info, db);
2794
 
  }
2795
 
  user = strdup_root(&memex,u);
2796
 
  sort=  get_sort(3,host.hostname,db,user);
2797
 
  if (tname != t)
2798
 
  {
2799
 
    tname= strdup_root(&memex, t);
2800
 
    if (lower_case_table_names || is_routine)
2801
 
      my_casedn_str(files_charset_info, tname);
2802
 
  }
2803
 
  key_length= strlen(d) + strlen(u)+ strlen(t)+3;
2804
 
  hash_key=   (char*) alloc_root(&memex,key_length);
2805
 
  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
2806
 
}
2807
 
 
2808
 
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
2809
 
                       const char *t, ulong p, bool is_routine)
2810
 
  :db(0), tname(0), privs(p)
2811
 
{
2812
 
  set_user_details(h, d, u, t, is_routine);
2813
 
}
2814
 
 
2815
 
GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
2816
 
                         const char *t, ulong p, ulong c)
2817
 
  :GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
2818
 
{
2819
 
  (void) my_hash_init2(&hash_columns,4,system_charset_info,
2820
 
                   0,0,0, (my_hash_get_key) get_key_column,0,0);
2821
 
}
2822
 
 
2823
 
 
2824
 
GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
2825
 
{
2826
 
  update_hostname(&host, get_field(&memex, form->field[0]));
2827
 
  db=    get_field(&memex,form->field[1]);
2828
 
  user=  get_field(&memex,form->field[2]);
2829
 
  if (!user)
2830
 
    user= (char*) "";
2831
 
  sort=  get_sort(3, host.hostname, db, user);
2832
 
  tname= get_field(&memex,form->field[3]);
2833
 
  if (!db || !tname)
2834
 
  {
2835
 
    /* Wrong table row; Ignore it */
2836
 
    privs= 0;
2837
 
    return;                                     /* purecov: inspected */
2838
 
  }
2839
 
  if (lower_case_table_names)
2840
 
  {
2841
 
    my_casedn_str(files_charset_info, db);
2842
 
  }
2843
 
  if (lower_case_table_names || is_routine)
2844
 
  {
2845
 
    my_casedn_str(files_charset_info, tname);
2846
 
  }
2847
 
  key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
2848
 
  hash_key=   (char*) alloc_root(&memex, key_length);
2849
 
  strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
2850
 
  privs = (ulong) form->field[6]->val_int();
2851
 
  privs = fix_rights_for_table(privs);
2852
 
}
2853
 
 
2854
 
 
2855
 
GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
2856
 
  :GRANT_NAME(form, FALSE)
2857
 
{
2858
 
  uchar key[MAX_KEY_LENGTH];
2859
 
 
2860
 
  if (!db || !tname)
2861
 
  {
2862
 
    /* Wrong table row; Ignore it */
2863
 
    my_hash_clear(&hash_columns);               /* allow for destruction */
2864
 
    cols= 0;
2865
 
    return;
2866
 
  }
2867
 
  cols= (ulong) form->field[7]->val_int();
2868
 
  cols =  fix_rights_for_column(cols);
2869
 
 
2870
 
  (void) my_hash_init2(&hash_columns,4,system_charset_info,
2871
 
                   0,0,0, (my_hash_get_key) get_key_column,0,0);
2872
 
  if (cols)
2873
 
  {
2874
 
    uint key_prefix_len;
2875
 
    KEY_PART_INFO *key_part= col_privs->key_info->key_part;
2876
 
    col_privs->field[0]->store(host.hostname,
2877
 
                               host.hostname ? (uint) strlen(host.hostname) :
2878
 
                               0,
2879
 
                               system_charset_info);
2880
 
    col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
2881
 
    col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
2882
 
    col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
2883
 
 
2884
 
    key_prefix_len= (key_part[0].store_length +
2885
 
                     key_part[1].store_length +
2886
 
                     key_part[2].store_length +
2887
 
                     key_part[3].store_length);
2888
 
    key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
2889
 
    col_privs->field[4]->store("",0, &my_charset_latin1);
2890
 
 
2891
 
    col_privs->file->ha_index_init(0, 1);
2892
 
    if (col_privs->file->index_read_map(col_privs->record[0], (uchar*) key,
2893
 
                                        (key_part_map)15, HA_READ_KEY_EXACT))
2894
 
    {
2895
 
      cols = 0; /* purecov: deadcode */
2896
 
      col_privs->file->ha_index_end();
2897
 
      return;
2898
 
    }
2899
 
    do
2900
 
    {
2901
 
      String *res,column_name;
2902
 
      GRANT_COLUMN *mem_check;
2903
 
      /* As column name is a string, we don't have to supply a buffer */
2904
 
      res=col_privs->field[4]->val_str(&column_name);
2905
 
      ulong priv= (ulong) col_privs->field[6]->val_int();
2906
 
      if (!(mem_check = new GRANT_COLUMN(*res,
2907
 
                                         fix_rights_for_column(priv))))
2908
 
      {
2909
 
        /* Don't use this entry */
2910
 
        privs = cols = 0;                       /* purecov: deadcode */
2911
 
        return;                         /* purecov: deadcode */
2912
 
      }
2913
 
      if (my_hash_insert(&hash_columns, (uchar *) mem_check))
2914
 
      {
2915
 
        /* Invalidate this entry */
2916
 
        privs= cols= 0;
2917
 
        return;
2918
 
      }
2919
 
    } while (!col_privs->file->index_next(col_privs->record[0]) &&
2920
 
             !key_cmp_if_same(col_privs,key,0,key_prefix_len));
2921
 
    col_privs->file->ha_index_end();
2922
 
  }
2923
 
}
2924
 
 
2925
 
 
2926
 
GRANT_TABLE::~GRANT_TABLE()
2927
 
{
2928
 
  my_hash_free(&hash_columns);
2929
 
}
2930
 
 
2931
 
 
2932
 
static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
2933
 
                             my_bool not_used __attribute__((unused)))
2934
 
{
2935
 
  *length=buff->key_length;
2936
 
  return (uchar*) buff->hash_key;
2937
 
}
2938
 
 
2939
 
 
2940
 
void free_grant_table(GRANT_TABLE *grant_table)
2941
 
{
2942
 
  my_hash_free(&grant_table->hash_columns);
2943
 
}
2944
 
 
2945
 
 
2946
 
/* Search after a matching grant. Prefer exact grants before not exact ones */
2947
 
 
2948
 
static GRANT_NAME *name_hash_search(HASH *name_hash,
2949
 
                                    const char *host,const char* ip,
2950
 
                                    const char *db,
2951
 
                                    const char *user, const char *tname,
2952
 
                                    bool exact, bool name_tolower)
2953
 
{
2954
 
  char helping [NAME_LEN*2+USERNAME_LENGTH+3], *name_ptr;
2955
 
  uint len;
2956
 
  GRANT_NAME *grant_name,*found=0;
2957
 
  HASH_SEARCH_STATE state;
2958
 
 
2959
 
  name_ptr= strmov(strmov(helping, user) + 1, db) + 1;
2960
 
  len  = (uint) (strmov(name_ptr, tname) - helping) + 1;
2961
 
  if (name_tolower)
2962
 
    my_casedn_str(files_charset_info, name_ptr);
2963
 
  for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping,
2964
 
                                               len, &state);
2965
 
       grant_name ;
2966
 
       grant_name= (GRANT_NAME*) my_hash_next(name_hash,(uchar*) helping,
2967
 
                                              len, &state))
2968
 
  {
2969
 
    if (exact)
2970
 
    {
2971
 
      if (!grant_name->host.hostname ||
2972
 
          (host &&
2973
 
           !my_strcasecmp(system_charset_info, host,
2974
 
                          grant_name->host.hostname)) ||
2975
 
          (ip && !strcmp(ip, grant_name->host.hostname)))
2976
 
        return grant_name;
2977
 
    }
2978
 
    else
2979
 
    {
2980
 
      if (compare_hostname(&grant_name->host, host, ip) &&
2981
 
          (!found || found->sort < grant_name->sort))
2982
 
        found=grant_name;                                       // Host ok
2983
 
    }
2984
 
  }
2985
 
  return found;
2986
 
}
2987
 
 
2988
 
 
2989
 
inline GRANT_NAME *
2990
 
routine_hash_search(const char *host, const char *ip, const char *db,
2991
 
                 const char *user, const char *tname, bool proc, bool exact)
2992
 
{
2993
 
  return (GRANT_TABLE*)
2994
 
    name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
2995
 
                     host, ip, db, user, tname, exact, TRUE);
2996
 
}
2997
 
 
2998
 
 
2999
 
inline GRANT_TABLE *
3000
 
table_hash_search(const char *host, const char *ip, const char *db,
3001
 
                  const char *user, const char *tname, bool exact)
3002
 
{
3003
 
  return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
3004
 
                                         user, tname, exact, FALSE);
3005
 
}
3006
 
 
3007
 
 
3008
 
inline GRANT_COLUMN *
3009
 
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
3010
 
{
3011
 
  return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
3012
 
                                        (uchar*) cname, length);
3013
 
}
3014
 
 
3015
 
 
3016
 
static int replace_column_table(GRANT_TABLE *g_t,
3017
 
                                TABLE *table, const LEX_USER &combo,
3018
 
                                List <LEX_COLUMN> &columns,
3019
 
                                const char *db, const char *table_name,
3020
 
                                ulong rights, bool revoke_grant)
3021
 
{
3022
 
  int error=0,result=0;
3023
 
  uchar key[MAX_KEY_LENGTH];
3024
 
  uint key_prefix_length;
3025
 
  KEY_PART_INFO *key_part= table->key_info->key_part;
3026
 
  DBUG_ENTER("replace_column_table");
3027
 
 
3028
 
  table->use_all_columns();
3029
 
  table->field[0]->store(combo.host.str,combo.host.length,
3030
 
                         system_charset_info);
3031
 
  table->field[1]->store(db,(uint) strlen(db),
3032
 
                         system_charset_info);
3033
 
  table->field[2]->store(combo.user.str,combo.user.length,
3034
 
                         system_charset_info);
3035
 
  table->field[3]->store(table_name,(uint) strlen(table_name),
3036
 
                         system_charset_info);
3037
 
 
3038
 
  /* Get length of 4 first key parts */
3039
 
  key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
3040
 
                      key_part[2].store_length + key_part[3].store_length);
3041
 
  key_copy(key, table->record[0], table->key_info, key_prefix_length);
3042
 
 
3043
 
  rights&= COL_ACLS;                            // Only ACL for columns
3044
 
 
3045
 
  /* first fix privileges for all columns in column list */
3046
 
 
3047
 
  List_iterator <LEX_COLUMN> iter(columns);
3048
 
  class LEX_COLUMN *column;
3049
 
  table->file->ha_index_init(0, 1);
3050
 
  while ((column= iter++))
3051
 
  {
3052
 
    ulong privileges= column->rights;
3053
 
    bool old_row_exists=0;
3054
 
    uchar user_key[MAX_KEY_LENGTH];
3055
 
 
3056
 
    key_restore(table->record[0],key,table->key_info,
3057
 
                key_prefix_length);
3058
 
    table->field[4]->store(column->column.ptr(), column->column.length(),
3059
 
                           system_charset_info);
3060
 
    /* Get key for the first 4 columns */
3061
 
    key_copy(user_key, table->record[0], table->key_info,
3062
 
             table->key_info->key_length);
3063
 
 
3064
 
    if (table->file->index_read_map(table->record[0], user_key, HA_WHOLE_KEY,
3065
 
                                    HA_READ_KEY_EXACT))
3066
 
    {
3067
 
      if (revoke_grant)
3068
 
      {
3069
 
        my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
3070
 
                 combo.user.str, combo.host.str,
3071
 
                 table_name);                   /* purecov: inspected */
3072
 
        result= -1;                             /* purecov: inspected */
3073
 
        continue;                               /* purecov: inspected */
3074
 
      }
3075
 
      old_row_exists = 0;
3076
 
      restore_record(table, s->default_values);         // Get empty record
3077
 
      key_restore(table->record[0],key,table->key_info,
3078
 
                  key_prefix_length);
3079
 
      table->field[4]->store(column->column.ptr(),column->column.length(),
3080
 
                             system_charset_info);
3081
 
    }
3082
 
    else
3083
 
    {
3084
 
      ulong tmp= (ulong) table->field[6]->val_int();
3085
 
      tmp=fix_rights_for_column(tmp);
3086
 
 
3087
 
      if (revoke_grant)
3088
 
        privileges = tmp & ~(privileges | rights);
3089
 
      else
3090
 
        privileges |= tmp;
3091
 
      old_row_exists = 1;
3092
 
      store_record(table,record[1]);                    // copy original row
3093
 
    }
3094
 
 
3095
 
    table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
3096
 
 
3097
 
    if (old_row_exists)
3098
 
    {
3099
 
      GRANT_COLUMN *grant_column;
3100
 
      if (privileges)
3101
 
        error=table->file->ha_update_row(table->record[1],table->record[0]);
3102
 
      else
3103
 
        error=table->file->ha_delete_row(table->record[1]);
3104
 
      if (error && error != HA_ERR_RECORD_IS_THE_SAME)
3105
 
      {
3106
 
        table->file->print_error(error,MYF(0)); /* purecov: inspected */
3107
 
        result= -1;                             /* purecov: inspected */
3108
 
        goto end;                               /* purecov: inspected */
3109
 
      }
3110
 
      else
3111
 
        error= 0;
3112
 
      grant_column= column_hash_search(g_t, column->column.ptr(),
3113
 
                                       column->column.length());
3114
 
      if (grant_column)                         // Should always be true
3115
 
        grant_column->rights= privileges;       // Update hash
3116
 
    }
3117
 
    else                                        // new grant
3118
 
    {
3119
 
      GRANT_COLUMN *grant_column;
3120
 
      if ((error=table->file->ha_write_row(table->record[0])))
3121
 
      {
3122
 
        table->file->print_error(error,MYF(0)); /* purecov: inspected */
3123
 
        result= -1;                             /* purecov: inspected */
3124
 
        goto end;                               /* purecov: inspected */
3125
 
      }
3126
 
      grant_column= new GRANT_COLUMN(column->column,privileges);
3127
 
      if (my_hash_insert(&g_t->hash_columns,(uchar*) grant_column))
3128
 
      {
3129
 
        result= -1;
3130
 
        goto end;
3131
 
      }
3132
 
    }
3133
 
  }
3134
 
 
3135
 
  /*
3136
 
    If revoke of privileges on the table level, remove all such privileges
3137
 
    for all columns
3138
 
  */
3139
 
 
3140
 
  if (revoke_grant)
3141
 
  {
3142
 
    uchar user_key[MAX_KEY_LENGTH];
3143
 
    key_copy(user_key, table->record[0], table->key_info,
3144
 
             key_prefix_length);
3145
 
 
3146
 
    if (table->file->index_read_map(table->record[0], user_key,
3147
 
                                    (key_part_map)15,
3148
 
                                    HA_READ_KEY_EXACT))
3149
 
      goto end;
3150
 
 
3151
 
    /* Scan through all rows with the same host,db,user and table */
3152
 
    do
3153
 
    {
3154
 
      ulong privileges = (ulong) table->field[6]->val_int();
3155
 
      privileges=fix_rights_for_column(privileges);
3156
 
      store_record(table,record[1]);
3157
 
 
3158
 
      if (privileges & rights)  // is in this record the priv to be revoked ??
3159
 
      {
3160
 
        GRANT_COLUMN *grant_column = NULL;
3161
 
        char  colum_name_buf[HOSTNAME_LENGTH+1];
3162
 
        String column_name(colum_name_buf,sizeof(colum_name_buf),
3163
 
                           system_charset_info);
3164
 
 
3165
 
        privileges&= ~rights;
3166
 
        table->field[6]->store((longlong)
3167
 
                               get_rights_for_column(privileges), TRUE);
3168
 
        table->field[4]->val_str(&column_name);
3169
 
        grant_column = column_hash_search(g_t,
3170
 
                                          column_name.ptr(),
3171
 
                                          column_name.length());
3172
 
        if (privileges)
3173
 
        {
3174
 
          int tmp_error;
3175
 
          if ((tmp_error=table->file->ha_update_row(table->record[1],
3176
 
                                                    table->record[0])) &&
3177
 
              tmp_error != HA_ERR_RECORD_IS_THE_SAME)
3178
 
          {                                     /* purecov: deadcode */
3179
 
            table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
3180
 
            result= -1;                         /* purecov: deadcode */
3181
 
            goto end;                           /* purecov: deadcode */
3182
 
          }
3183
 
          if (grant_column)
3184
 
            grant_column->rights  = privileges; // Update hash
3185
 
        }
3186
 
        else
3187
 
        {
3188
 
          int tmp_error;
3189
 
          if ((tmp_error = table->file->ha_delete_row(table->record[1])))
3190
 
          {                                     /* purecov: deadcode */
3191
 
            table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
3192
 
            result= -1;                         /* purecov: deadcode */
3193
 
            goto end;                           /* purecov: deadcode */
3194
 
          }
3195
 
          if (grant_column)
3196
 
            my_hash_delete(&g_t->hash_columns,(uchar*) grant_column);
3197
 
        }
3198
 
      }
3199
 
    } while (!table->file->index_next(table->record[0]) &&
3200
 
             !key_cmp_if_same(table, key, 0, key_prefix_length));
3201
 
  }
3202
 
 
3203
 
end:
3204
 
  table->file->ha_index_end();
3205
 
  DBUG_RETURN(result);
3206
 
}
3207
 
 
3208
 
static inline void get_grantor(THD *thd, char *grantor)
3209
 
{
3210
 
  const char *user= thd->security_ctx->user;
3211
 
  const char *host= thd->security_ctx->host_or_ip;
3212
 
 
3213
 
#if defined(HAVE_REPLICATION)
3214
 
  if (thd->slave_thread && thd->has_invoker())
3215
 
  {
3216
 
    user= thd->get_invoker_user().str;
3217
 
    host= thd->get_invoker_host().str;
3218
 
  }
3219
 
#endif
3220
 
  strxmov(grantor, user, "@", host, NullS);
3221
 
}
3222
 
 
3223
 
static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
3224
 
                               TABLE *table, const LEX_USER &combo,
3225
 
                               const char *db, const char *table_name,
3226
 
                               ulong rights, ulong col_rights,
3227
 
                               bool revoke_grant)
3228
 
{
3229
 
  char grantor[USER_HOST_BUFF_SIZE];
3230
 
  int old_row_exists = 1;
3231
 
  int error=0;
3232
 
  ulong store_table_rights, store_col_rights;
3233
 
  uchar user_key[MAX_KEY_LENGTH];
3234
 
  DBUG_ENTER("replace_table_table");
3235
 
 
3236
 
  get_grantor(thd, grantor);
3237
 
  /*
3238
 
    The following should always succeed as new users are created before
3239
 
    this function is called!
3240
 
  */
3241
 
  if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
3242
 
  {
3243
 
    my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
3244
 
               MYF(0)); /* purecov: deadcode */
3245
 
    DBUG_RETURN(-1);                            /* purecov: deadcode */
3246
 
  }
3247
 
 
3248
 
  table->use_all_columns();
3249
 
  restore_record(table, s->default_values);     // Get empty record
3250
 
  table->field[0]->store(combo.host.str,combo.host.length,
3251
 
                         system_charset_info);
3252
 
  table->field[1]->store(db,(uint) strlen(db), system_charset_info);
3253
 
  table->field[2]->store(combo.user.str,combo.user.length,
3254
 
                         system_charset_info);
3255
 
  table->field[3]->store(table_name,(uint) strlen(table_name),
3256
 
                         system_charset_info);
3257
 
  store_record(table,record[1]);                        // store at pos 1
3258
 
  key_copy(user_key, table->record[0], table->key_info,
3259
 
           table->key_info->key_length);
3260
 
 
3261
 
  if (table->file->index_read_idx_map(table->record[0], 0, user_key,
3262
 
                                      HA_WHOLE_KEY,
3263
 
                                      HA_READ_KEY_EXACT))
3264
 
  {
3265
 
    /*
3266
 
      The following should never happen as we first check the in memory
3267
 
      grant tables for the user.  There is however always a small change that
3268
 
      the user has modified the grant tables directly.
3269
 
    */
3270
 
    if (revoke_grant)
3271
 
    { // no row, no revoke
3272
 
      my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
3273
 
               combo.user.str, combo.host.str,
3274
 
               table_name);                     /* purecov: deadcode */
3275
 
      DBUG_RETURN(-1);                          /* purecov: deadcode */
3276
 
    }
3277
 
    old_row_exists = 0;
3278
 
    restore_record(table,record[1]);                    // Get saved record
3279
 
  }
3280
 
 
3281
 
  store_table_rights= get_rights_for_table(rights);
3282
 
  store_col_rights=   get_rights_for_column(col_rights);
3283
 
  if (old_row_exists)
3284
 
  {
3285
 
    ulong j,k;
3286
 
    store_record(table,record[1]);
3287
 
    j = (ulong) table->field[6]->val_int();
3288
 
    k = (ulong) table->field[7]->val_int();
3289
 
 
3290
 
    if (revoke_grant)
3291
 
    {
3292
 
      /* column rights are already fixed in mysql_table_grant */
3293
 
      store_table_rights=j & ~store_table_rights;
3294
 
    }
3295
 
    else
3296
 
    {
3297
 
      store_table_rights|= j;
3298
 
      store_col_rights|=   k;
3299
 
    }
3300
 
  }
3301
 
 
3302
 
  table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
3303
 
  table->field[6]->store((longlong) store_table_rights, TRUE);
3304
 
  table->field[7]->store((longlong) store_col_rights, TRUE);
3305
 
  rights=fix_rights_for_table(store_table_rights);
3306
 
  col_rights=fix_rights_for_column(store_col_rights);
3307
 
 
3308
 
  if (old_row_exists)
3309
 
  {
3310
 
    if (store_table_rights || store_col_rights)
3311
 
    {
3312
 
      if ((error=table->file->ha_update_row(table->record[1],
3313
 
                                            table->record[0])) &&
3314
 
          error != HA_ERR_RECORD_IS_THE_SAME)
3315
 
        goto table_error;                       /* purecov: deadcode */
3316
 
    }
3317
 
    else if ((error = table->file->ha_delete_row(table->record[1])))
3318
 
      goto table_error;                         /* purecov: deadcode */
3319
 
  }
3320
 
  else
3321
 
  {
3322
 
    error=table->file->ha_write_row(table->record[0]);
3323
 
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
3324
 
      goto table_error;                         /* purecov: deadcode */
3325
 
  }
3326
 
 
3327
 
  if (rights | col_rights)
3328
 
  {
3329
 
    grant_table->privs= rights;
3330
 
    grant_table->cols=  col_rights;
3331
 
  }
3332
 
  else
3333
 
  {
3334
 
    my_hash_delete(&column_priv_hash,(uchar*) grant_table);
3335
 
  }
3336
 
  DBUG_RETURN(0);
3337
 
 
3338
 
  /* This should never happen */
3339
 
table_error:
3340
 
  table->file->print_error(error,MYF(0)); /* purecov: deadcode */
3341
 
  DBUG_RETURN(-1); /* purecov: deadcode */
3342
 
}
3343
 
 
3344
 
 
3345
 
/**
3346
 
  @retval       0  success
3347
 
  @retval      -1  error
3348
 
*/
3349
 
static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
3350
 
                              TABLE *table, const LEX_USER &combo,
3351
 
                              const char *db, const char *routine_name,
3352
 
                              bool is_proc, ulong rights, bool revoke_grant)
3353
 
{
3354
 
  char grantor[USER_HOST_BUFF_SIZE];
3355
 
  int old_row_exists= 1;
3356
 
  int error=0;
3357
 
  ulong store_proc_rights;
3358
 
  DBUG_ENTER("replace_routine_table");
3359
 
 
3360
 
  if (!initialized)
3361
 
  {
3362
 
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
3363
 
    DBUG_RETURN(-1);
3364
 
  }
3365
 
 
3366
 
  get_grantor(thd, grantor);
3367
 
  /*
3368
 
    New users are created before this function is called.
3369
 
 
3370
 
    There may be some cases where a routine's definer is removed but the
3371
 
    routine remains.
3372
 
  */
3373
 
 
3374
 
  table->use_all_columns();
3375
 
  restore_record(table, s->default_values);             // Get empty record
3376
 
  table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
3377
 
  table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
3378
 
  table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
3379
 
  table->field[3]->store(routine_name,(uint) strlen(routine_name),
3380
 
                         &my_charset_latin1);
3381
 
  table->field[4]->store((longlong)(is_proc ?
3382
 
                                    TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION),
3383
 
                         TRUE);
3384
 
  store_record(table,record[1]);                        // store at pos 1
3385
 
 
3386
 
  if (table->file->index_read_idx_map(table->record[0], 0,
3387
 
                                      (uchar*) table->field[0]->ptr,
3388
 
                                      HA_WHOLE_KEY,
3389
 
                                      HA_READ_KEY_EXACT))
3390
 
  {
3391
 
    /*
3392
 
      The following should never happen as we first check the in memory
3393
 
      grant tables for the user.  There is however always a small change that
3394
 
      the user has modified the grant tables directly.
3395
 
    */
3396
 
    if (revoke_grant)
3397
 
    { // no row, no revoke
3398
 
      my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
3399
 
               combo.user.str, combo.host.str, routine_name);
3400
 
      DBUG_RETURN(-1);
3401
 
    }
3402
 
    old_row_exists= 0;
3403
 
    restore_record(table,record[1]);                    // Get saved record
3404
 
  }
3405
 
 
3406
 
  store_proc_rights= get_rights_for_procedure(rights);
3407
 
  if (old_row_exists)
3408
 
  {
3409
 
    ulong j;
3410
 
    store_record(table,record[1]);
3411
 
    j= (ulong) table->field[6]->val_int();
3412
 
 
3413
 
    if (revoke_grant)
3414
 
    {
3415
 
      /* column rights are already fixed in mysql_table_grant */
3416
 
      store_proc_rights=j & ~store_proc_rights;
3417
 
    }
3418
 
    else
3419
 
    {
3420
 
      store_proc_rights|= j;
3421
 
    }
3422
 
  }
3423
 
 
3424
 
  table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
3425
 
  table->field[6]->store((longlong) store_proc_rights, TRUE);
3426
 
  rights=fix_rights_for_procedure(store_proc_rights);
3427
 
 
3428
 
  if (old_row_exists)
3429
 
  {
3430
 
    if (store_proc_rights)
3431
 
    {
3432
 
      if ((error=table->file->ha_update_row(table->record[1],
3433
 
                                            table->record[0])) &&
3434
 
          error != HA_ERR_RECORD_IS_THE_SAME)
3435
 
        goto table_error;
3436
 
    }
3437
 
    else if ((error= table->file->ha_delete_row(table->record[1])))
3438
 
      goto table_error;
3439
 
  }
3440
 
  else
3441
 
  {
3442
 
    error=table->file->ha_write_row(table->record[0]);
3443
 
    if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
3444
 
      goto table_error;
3445
 
  }
3446
 
 
3447
 
  if (rights)
3448
 
  {
3449
 
    grant_name->privs= rights;
3450
 
  }
3451
 
  else
3452
 
  {
3453
 
    my_hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*)
3454
 
                   grant_name);
3455
 
  }
3456
 
  DBUG_RETURN(0);
3457
 
 
3458
 
  /* This should never happen */
3459
 
table_error:
3460
 
  table->file->print_error(error,MYF(0));
3461
 
  DBUG_RETURN(-1);
3462
 
}
3463
 
 
3464
 
 
3465
 
/*
3466
 
  Store table level and column level grants in the privilege tables
3467
 
 
3468
 
  SYNOPSIS
3469
 
    mysql_table_grant()
3470
 
    thd                 Thread handle
3471
 
    table_list          List of tables to give grant
3472
 
    user_list           List of users to give grant
3473
 
    columns             List of columns to give grant
3474
 
    rights              Table level grant
3475
 
    revoke_grant        Set to 1 if this is a REVOKE command
3476
 
 
3477
 
  RETURN
3478
 
    FALSE ok
3479
 
    TRUE  error
3480
 
*/
3481
 
 
3482
 
int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
3483
 
                      List <LEX_USER> &user_list,
3484
 
                      List <LEX_COLUMN> &columns, ulong rights,
3485
 
                      bool revoke_grant)
3486
 
{
3487
 
  ulong column_priv= 0;
3488
 
  List_iterator <LEX_USER> str_list (user_list);
3489
 
  LEX_USER *Str, *tmp_Str;
3490
 
  TABLE_LIST tables[3];
3491
 
  bool create_new_users=0;
3492
 
  char *db_name, *table_name;
3493
 
  bool save_binlog_row_based;
3494
 
  DBUG_ENTER("mysql_table_grant");
3495
 
 
3496
 
  if (!initialized)
3497
 
  {
3498
 
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
3499
 
             "--skip-grant-tables");    /* purecov: inspected */
3500
 
    DBUG_RETURN(TRUE);                          /* purecov: inspected */
3501
 
  }
3502
 
  if (rights & ~TABLE_ACLS)
3503
 
  {
3504
 
    my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
3505
 
               MYF(0));
3506
 
    DBUG_RETURN(TRUE);
3507
 
  }
3508
 
 
3509
 
  if (!revoke_grant)
3510
 
  {
3511
 
    if (columns.elements)
3512
 
    {
3513
 
      class LEX_COLUMN *column;
3514
 
      List_iterator <LEX_COLUMN> column_iter(columns);
3515
 
 
3516
 
      if (open_normal_and_derived_tables(thd, table_list, 0))
3517
 
        DBUG_RETURN(TRUE);
3518
 
 
3519
 
      while ((column = column_iter++))
3520
 
      {
3521
 
        uint unused_field_idx= NO_CACHED_FIELD_INDEX;
3522
 
        TABLE_LIST *dummy;
3523
 
        Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
3524
 
                                         column->column.length(),
3525
 
                                         column->column.ptr(), NULL, NULL,
3526
 
                                         NULL, TRUE, FALSE,
3527
 
                                         &unused_field_idx, FALSE, &dummy);
3528
 
        if (f == (Field*)0)
3529
 
        {
3530
 
          my_error(ER_BAD_FIELD_ERROR, MYF(0),
3531
 
                   column->column.c_ptr(), table_list->alias);
3532
 
          DBUG_RETURN(TRUE);
3533
 
        }
3534
 
        if (f == (Field *)-1)
3535
 
          DBUG_RETURN(TRUE);
3536
 
        column_priv|= column->rights;
3537
 
      }
3538
 
      close_mysql_tables(thd);
3539
 
    }
3540
 
    else
3541
 
    {
3542
 
      if (!(rights & CREATE_ACL))
3543
 
      {
3544
 
        char buf[FN_REFLEN + 1];
3545
 
        build_table_filename(buf, sizeof(buf) - 1, table_list->db,
3546
 
                             table_list->table_name, reg_ext, 0);
3547
 
        fn_format(buf, buf, "", "", MY_UNPACK_FILENAME  | MY_RESOLVE_SYMLINKS |
3548
 
                                    MY_RETURN_REAL_PATH | MY_APPEND_EXT);
3549
 
        if (access(buf,F_OK))
3550
 
        {
3551
 
          my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
3552
 
          DBUG_RETURN(TRUE);
3553
 
        }
3554
 
      }
3555
 
      if (table_list->grant.want_privilege)
3556
 
      {
3557
 
        char command[128];
3558
 
        get_privilege_desc(command, sizeof(command),
3559
 
                           table_list->grant.want_privilege);
3560
 
        my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
3561
 
                 command, thd->security_ctx->priv_user,
3562
 
                 thd->security_ctx->host_or_ip, table_list->alias);
3563
 
        DBUG_RETURN(-1);
3564
 
      }
3565
 
    }
3566
 
  }
3567
 
 
3568
 
  /* open the mysql.tables_priv and mysql.columns_priv tables */
3569
 
 
3570
 
  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
3571
 
                           C_STRING_WITH_LEN("user"), "user", TL_WRITE);
3572
 
  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
3573
 
                           C_STRING_WITH_LEN("tables_priv"),
3574
 
                           "tables_priv", TL_WRITE);
3575
 
  tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
3576
 
                           C_STRING_WITH_LEN("columns_priv"),
3577
 
                           "columns_priv", TL_WRITE);
3578
 
  tables[0].next_local= tables[0].next_global= tables+1;
3579
 
  /* Don't open column table if we don't need it ! */
3580
 
  if (column_priv || (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
3581
 
    tables[1].next_local= tables[1].next_global= tables+2;
3582
 
 
3583
 
  /*
3584
 
    This statement will be replicated as a statement, even when using
3585
 
    row-based replication.  The flag will be reset at the end of the
3586
 
    statement.
3587
 
  */
3588
 
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
3589
 
    thd->clear_current_stmt_binlog_format_row();
3590
 
 
3591
 
#ifdef HAVE_REPLICATION
3592
 
  /*
3593
 
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
3594
 
    some kind of updates to the mysql.% tables.
3595
 
  */
3596
 
  if (thd->slave_thread && rpl_filter->is_on())
3597
 
  {
3598
 
    /*
3599
 
      The tables must be marked "updating" so that tables_ok() takes them into
3600
 
      account in tests.
3601
 
    */
3602
 
    tables[0].updating= tables[1].updating= tables[2].updating= 1;
3603
 
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
3604
 
    {
3605
 
      /* Restore the state of binlog format */
3606
 
      DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3607
 
      if (save_binlog_row_based)
3608
 
        thd->set_current_stmt_binlog_format_row();
3609
 
      DBUG_RETURN(FALSE);
3610
 
    }
3611
 
  }
3612
 
#endif
3613
 
 
3614
 
  /* 
3615
 
    The lock api is depending on the thd->lex variable which needs to be
3616
 
    re-initialized.
3617
 
  */
3618
 
  Query_tables_list backup;
3619
 
  thd->lex->reset_n_backup_query_tables_list(&backup);
3620
 
  /*
3621
 
    Restore Query_tables_list::sql_command value, which was reset
3622
 
    above, as the code writing query to the binary log assumes that
3623
 
    this value corresponds to the statement being executed.
3624
 
  */
3625
 
  thd->lex->sql_command= backup.sql_command;
3626
 
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
3627
 
  {                                             // Should never happen
3628
 
    /* Restore the state of binlog format */
3629
 
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3630
 
    thd->lex->restore_backup_query_tables_list(&backup);
3631
 
    if (save_binlog_row_based)
3632
 
      thd->set_current_stmt_binlog_format_row();
3633
 
    DBUG_RETURN(TRUE);                          /* purecov: deadcode */
3634
 
  }
3635
 
 
3636
 
  if (!revoke_grant)
3637
 
    create_new_users= test_if_create_new_users(thd);
3638
 
  bool result= FALSE;
3639
 
  mysql_rwlock_wrlock(&LOCK_grant);
3640
 
  mysql_mutex_lock(&acl_cache->lock);
3641
 
  MEM_ROOT *old_root= thd->mem_root;
3642
 
  thd->mem_root= &memex;
3643
 
  grant_version++;
3644
 
 
3645
 
  while ((tmp_Str = str_list++))
3646
 
  {
3647
 
    int error;
3648
 
    GRANT_TABLE *grant_table;
3649
 
    if (!(Str= get_current_user(thd, tmp_Str)))
3650
 
    {
3651
 
      result= TRUE;
3652
 
      continue;
3653
 
    }  
3654
 
    /* Create user if needed */
3655
 
    error=replace_user_table(thd, tables[0].table, *Str,
3656
 
                             0, revoke_grant, create_new_users,
3657
 
                             test(thd->variables.sql_mode &
3658
 
                                  MODE_NO_AUTO_CREATE_USER));
3659
 
    if (error)
3660
 
    {
3661
 
      result= TRUE;                             // Remember error
3662
 
      continue;                                 // Add next user
3663
 
    }
3664
 
 
3665
 
    db_name= table_list->get_db_name();
3666
 
    table_name= table_list->get_table_name();
3667
 
 
3668
 
    /* Find/create cached table grant */
3669
 
    grant_table= table_hash_search(Str->host.str, NullS, db_name,
3670
 
                                   Str->user.str, table_name, 1);
3671
 
    if (!grant_table)
3672
 
    {
3673
 
      if (revoke_grant)
3674
 
      {
3675
 
        my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
3676
 
                 Str->user.str, Str->host.str, table_list->table_name);
3677
 
        result= TRUE;
3678
 
        continue;
3679
 
      }
3680
 
      grant_table = new GRANT_TABLE (Str->host.str, db_name,
3681
 
                                     Str->user.str, table_name,
3682
 
                                     rights,
3683
 
                                     column_priv);
3684
 
      if (!grant_table ||
3685
 
        my_hash_insert(&column_priv_hash,(uchar*) grant_table))
3686
 
      {
3687
 
        result= TRUE;                           /* purecov: deadcode */
3688
 
        continue;                               /* purecov: deadcode */
3689
 
      }
3690
 
    }
3691
 
 
3692
 
    /* If revoke_grant, calculate the new column privilege for tables_priv */
3693
 
    if (revoke_grant)
3694
 
    {
3695
 
      class LEX_COLUMN *column;
3696
 
      List_iterator <LEX_COLUMN> column_iter(columns);
3697
 
      GRANT_COLUMN *grant_column;
3698
 
 
3699
 
      /* Fix old grants */
3700
 
      while ((column = column_iter++))
3701
 
      {
3702
 
        grant_column = column_hash_search(grant_table,
3703
 
                                          column->column.ptr(),
3704
 
                                          column->column.length());
3705
 
        if (grant_column)
3706
 
          grant_column->rights&= ~(column->rights | rights);
3707
 
      }
3708
 
      /* scan trough all columns to get new column grant */
3709
 
      column_priv= 0;
3710
 
      for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
3711
 
      {
3712
 
        grant_column= (GRANT_COLUMN*)
3713
 
          my_hash_element(&grant_table->hash_columns, idx);
3714
 
        grant_column->rights&= ~rights;         // Fix other columns
3715
 
        column_priv|= grant_column->rights;
3716
 
      }
3717
 
    }
3718
 
    else
3719
 
    {
3720
 
      column_priv|= grant_table->cols;
3721
 
    }
3722
 
 
3723
 
 
3724
 
    /* update table and columns */
3725
 
 
3726
 
    if (replace_table_table(thd, grant_table, tables[1].table, *Str,
3727
 
                            db_name, table_name,
3728
 
                            rights, column_priv, revoke_grant))
3729
 
    {
3730
 
      /* Should only happen if table is crashed */
3731
 
      result= TRUE;                            /* purecov: deadcode */
3732
 
    }
3733
 
    else if (tables[2].table)
3734
 
    {
3735
 
      if ((replace_column_table(grant_table, tables[2].table, *Str,
3736
 
                                columns,
3737
 
                                db_name, table_name,
3738
 
                                rights, revoke_grant)))
3739
 
      {
3740
 
        result= TRUE;
3741
 
      }
3742
 
    }
3743
 
  }
3744
 
  thd->mem_root= old_root;
3745
 
  mysql_mutex_unlock(&acl_cache->lock);
3746
 
 
3747
 
  if (!result) /* success */
3748
 
  {
3749
 
    result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
3750
 
  }
3751
 
 
3752
 
  mysql_rwlock_unlock(&LOCK_grant);
3753
 
 
3754
 
  if (!result) /* success */
3755
 
    my_ok(thd);
3756
 
 
3757
 
  /* Tables are automatically closed */
3758
 
  thd->lex->restore_backup_query_tables_list(&backup);
3759
 
  /* Restore the state of binlog format */
3760
 
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3761
 
  if (save_binlog_row_based)
3762
 
    thd->set_current_stmt_binlog_format_row();
3763
 
  DBUG_RETURN(result);
3764
 
}
3765
 
 
3766
 
 
3767
 
/**
3768
 
  Store routine level grants in the privilege tables
3769
 
 
3770
 
  @param thd Thread handle
3771
 
  @param table_list List of routines to give grant
3772
 
  @param is_proc Is this a list of procedures?
3773
 
  @param user_list List of users to give grant
3774
 
  @param rights Table level grant
3775
 
  @param revoke_grant Is this is a REVOKE command?
3776
 
 
3777
 
  @return
3778
 
    @retval FALSE Success.
3779
 
    @retval TRUE An error occurred.
3780
 
*/
3781
 
 
3782
 
bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
3783
 
                         List <LEX_USER> &user_list, ulong rights,
3784
 
                         bool revoke_grant, bool write_to_binlog)
3785
 
{
3786
 
  List_iterator <LEX_USER> str_list (user_list);
3787
 
  LEX_USER *Str, *tmp_Str;
3788
 
  TABLE_LIST tables[2];
3789
 
  bool create_new_users=0, result=0;
3790
 
  char *db_name, *table_name;
3791
 
  bool save_binlog_row_based;
3792
 
  DBUG_ENTER("mysql_routine_grant");
3793
 
 
3794
 
  if (!initialized)
3795
 
  {
3796
 
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
3797
 
             "--skip-grant-tables");
3798
 
    DBUG_RETURN(TRUE);
3799
 
  }
3800
 
  if (rights & ~PROC_ACLS)
3801
 
  {
3802
 
    my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
3803
 
               MYF(0));
3804
 
    DBUG_RETURN(TRUE);
3805
 
  }
3806
 
 
3807
 
  if (!revoke_grant)
3808
 
  {
3809
 
    if (sp_exist_routines(thd, table_list, is_proc))
3810
 
      DBUG_RETURN(TRUE);
3811
 
  }
3812
 
 
3813
 
  /* open the mysql.user and mysql.procs_priv tables */
3814
 
 
3815
 
  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
3816
 
                           C_STRING_WITH_LEN("user"), "user", TL_WRITE);
3817
 
  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
3818
 
                           C_STRING_WITH_LEN("procs_priv"), "procs_priv", TL_WRITE);
3819
 
  tables[0].next_local= tables[0].next_global= tables+1;
3820
 
 
3821
 
  /*
3822
 
    This statement will be replicated as a statement, even when using
3823
 
    row-based replication.  The flag will be reset at the end of the
3824
 
    statement.
3825
 
  */
3826
 
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
3827
 
    thd->clear_current_stmt_binlog_format_row();
3828
 
 
3829
 
#ifdef HAVE_REPLICATION
3830
 
  /*
3831
 
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
3832
 
    some kind of updates to the mysql.% tables.
3833
 
  */
3834
 
  if (thd->slave_thread && rpl_filter->is_on())
3835
 
  {
3836
 
    /*
3837
 
      The tables must be marked "updating" so that tables_ok() takes them into
3838
 
      account in tests.
3839
 
    */
3840
 
    tables[0].updating= tables[1].updating= 1;
3841
 
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
3842
 
    {
3843
 
      /* Restore the state of binlog format */
3844
 
      DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3845
 
      if (save_binlog_row_based)
3846
 
        thd->set_current_stmt_binlog_format_row();
3847
 
      DBUG_RETURN(FALSE);
3848
 
    }
3849
 
  }
3850
 
#endif
3851
 
 
3852
 
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
3853
 
  {                                             // Should never happen
3854
 
    /* Restore the state of binlog format */
3855
 
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3856
 
    if (save_binlog_row_based)
3857
 
      thd->set_current_stmt_binlog_format_row();
3858
 
    DBUG_RETURN(TRUE);
3859
 
  }
3860
 
 
3861
 
  if (!revoke_grant)
3862
 
    create_new_users= test_if_create_new_users(thd);
3863
 
  mysql_rwlock_wrlock(&LOCK_grant);
3864
 
  mysql_mutex_lock(&acl_cache->lock);
3865
 
  MEM_ROOT *old_root= thd->mem_root;
3866
 
  thd->mem_root= &memex;
3867
 
 
3868
 
  DBUG_PRINT("info",("now time to iterate and add users"));
3869
 
 
3870
 
  while ((tmp_Str= str_list++))
3871
 
  {
3872
 
    int error;
3873
 
    GRANT_NAME *grant_name;
3874
 
    if (!(Str= get_current_user(thd, tmp_Str)))
3875
 
    {
3876
 
      result= TRUE;
3877
 
      continue;
3878
 
    }  
3879
 
    /* Create user if needed */
3880
 
    error=replace_user_table(thd, tables[0].table, *Str,
3881
 
                             0, revoke_grant, create_new_users,
3882
 
                             test(thd->variables.sql_mode &
3883
 
                                  MODE_NO_AUTO_CREATE_USER));
3884
 
    if (error)
3885
 
    {
3886
 
      result= TRUE;                             // Remember error
3887
 
      continue;                                 // Add next user
3888
 
    }
3889
 
 
3890
 
    db_name= table_list->db;
3891
 
    table_name= table_list->table_name;
3892
 
 
3893
 
    grant_name= routine_hash_search(Str->host.str, NullS, db_name,
3894
 
                                    Str->user.str, table_name, is_proc, 1);
3895
 
    if (!grant_name)
3896
 
    {
3897
 
      if (revoke_grant)
3898
 
      {
3899
 
        my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
3900
 
                 Str->user.str, Str->host.str, table_name);
3901
 
        result= TRUE;
3902
 
        continue;
3903
 
      }
3904
 
      grant_name= new GRANT_NAME(Str->host.str, db_name,
3905
 
                                 Str->user.str, table_name,
3906
 
                                 rights, TRUE);
3907
 
      if (!grant_name ||
3908
 
        my_hash_insert(is_proc ?
3909
 
                       &proc_priv_hash : &func_priv_hash,(uchar*) grant_name))
3910
 
      {
3911
 
        result= TRUE;
3912
 
        continue;
3913
 
      }
3914
 
    }
3915
 
 
3916
 
    if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
3917
 
                              db_name, table_name, is_proc, rights, 
3918
 
                              revoke_grant) != 0)
3919
 
    {
3920
 
      result= TRUE;
3921
 
      continue;
3922
 
    }
3923
 
  }
3924
 
  thd->mem_root= old_root;
3925
 
  mysql_mutex_unlock(&acl_cache->lock);
3926
 
 
3927
 
  if (write_to_binlog)
3928
 
  {
3929
 
    if (write_bin_log(thd, FALSE, thd->query(), thd->query_length()))
3930
 
      result= TRUE;
3931
 
  }
3932
 
 
3933
 
  mysql_rwlock_unlock(&LOCK_grant);
3934
 
  /* Restore the state of binlog format */
3935
 
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
3936
 
  if (save_binlog_row_based)
3937
 
    thd->set_current_stmt_binlog_format_row();
3938
 
 
3939
 
  /* Tables are automatically closed */
3940
 
  DBUG_RETURN(result);
3941
 
}
3942
 
 
3943
 
 
3944
 
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
3945
 
                 ulong rights, bool revoke_grant, bool is_proxy)
3946
 
{
3947
 
  List_iterator <LEX_USER> str_list (list);
3948
 
  LEX_USER *Str, *tmp_Str, *proxied_user= NULL;
3949
 
  char tmp_db[NAME_LEN+1];
3950
 
  bool create_new_users=0;
3951
 
  TABLE_LIST tables[2];
3952
 
  bool save_binlog_row_based;
3953
 
  DBUG_ENTER("mysql_grant");
3954
 
  if (!initialized)
3955
 
  {
3956
 
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
3957
 
             "--skip-grant-tables");    /* purecov: tested */
3958
 
    DBUG_RETURN(TRUE);                          /* purecov: tested */
3959
 
  }
3960
 
 
3961
 
  if (lower_case_table_names && db)
3962
 
  {
3963
 
    strmov(tmp_db,db);
3964
 
    my_casedn_str(files_charset_info, tmp_db);
3965
 
    db=tmp_db;
3966
 
  }
3967
 
 
3968
 
  if (is_proxy)
3969
 
  {
3970
 
    DBUG_ASSERT(!db);
3971
 
    proxied_user= str_list++;
3972
 
  }
3973
 
 
3974
 
  /* open the mysql.user and mysql.db or mysql.proxies_priv tables */
3975
 
  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
3976
 
                           C_STRING_WITH_LEN("user"), "user", TL_WRITE);
3977
 
  if (is_proxy)
3978
 
 
3979
 
    tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
3980
 
                             C_STRING_WITH_LEN("proxies_priv"),
3981
 
                             "proxies_priv", 
3982
 
                             TL_WRITE);
3983
 
  else
3984
 
    tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
3985
 
                             C_STRING_WITH_LEN("db"), 
3986
 
                             "db", 
3987
 
                             TL_WRITE);
3988
 
  tables[0].next_local= tables[0].next_global= tables+1;
3989
 
 
3990
 
  /*
3991
 
    This statement will be replicated as a statement, even when using
3992
 
    row-based replication.  The flag will be reset at the end of the
3993
 
    statement.
3994
 
  */
3995
 
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
3996
 
    thd->clear_current_stmt_binlog_format_row();
3997
 
 
3998
 
#ifdef HAVE_REPLICATION
3999
 
  /*
4000
 
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
4001
 
    some kind of updates to the mysql.% tables.
4002
 
  */
4003
 
  if (thd->slave_thread && rpl_filter->is_on())
4004
 
  {
4005
 
    /*
4006
 
      The tables must be marked "updating" so that tables_ok() takes them into
4007
 
      account in tests.
4008
 
    */
4009
 
    tables[0].updating= tables[1].updating= 1;
4010
 
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
4011
 
    {
4012
 
      /* Restore the state of binlog format */
4013
 
      DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4014
 
      if (save_binlog_row_based)
4015
 
        thd->set_current_stmt_binlog_format_row();
4016
 
      DBUG_RETURN(FALSE);
4017
 
    }
4018
 
  }
4019
 
#endif
4020
 
 
4021
 
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
4022
 
  {                                             // This should never happen
4023
 
    /* Restore the state of binlog format */
4024
 
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4025
 
    if (save_binlog_row_based)
4026
 
      thd->set_current_stmt_binlog_format_row();
4027
 
    DBUG_RETURN(TRUE);                          /* purecov: deadcode */
4028
 
  }
4029
 
 
4030
 
  if (!revoke_grant)
4031
 
    create_new_users= test_if_create_new_users(thd);
4032
 
 
4033
 
  /* go through users in user_list */
4034
 
  mysql_rwlock_wrlock(&LOCK_grant);
4035
 
  mysql_mutex_lock(&acl_cache->lock);
4036
 
  grant_version++;
4037
 
 
4038
 
  int result=0;
4039
 
  while ((tmp_Str = str_list++))
4040
 
  {
4041
 
    if (!(Str= get_current_user(thd, tmp_Str)))
4042
 
    {
4043
 
      result= TRUE;
4044
 
      continue;
4045
 
    }
4046
 
    /*
4047
 
      No User, but a password?
4048
 
      They did GRANT ... TO CURRENT_USER() IDENTIFIED BY ... !
4049
 
      Get the current user, and shallow-copy the new password to them!
4050
 
    */
4051
 
    if (!tmp_Str->user.str && tmp_Str->password.str)
4052
 
      Str->password= tmp_Str->password;
4053
 
    if (replace_user_table(thd, tables[0].table, *Str,
4054
 
                           (!db ? rights : 0), revoke_grant, create_new_users,
4055
 
                           test(thd->variables.sql_mode &
4056
 
                                MODE_NO_AUTO_CREATE_USER)))
4057
 
      result= -1;
4058
 
    else if (db)
4059
 
    {
4060
 
      ulong db_rights= rights & DB_ACLS;
4061
 
      if (db_rights  == rights)
4062
 
      {
4063
 
        if (replace_db_table(tables[1].table, db, *Str, db_rights,
4064
 
                             revoke_grant))
4065
 
          result= -1;
4066
 
      }
4067
 
      else
4068
 
      {
4069
 
        my_error(ER_WRONG_USAGE, MYF(0), "DB GRANT", "GLOBAL PRIVILEGES");
4070
 
        result= -1;
4071
 
      }
4072
 
    }
4073
 
    else if (is_proxy)
4074
 
    {
4075
 
      if (replace_proxies_priv_table (thd, tables[1].table, Str, proxied_user,
4076
 
                                    rights & GRANT_ACL ? TRUE : FALSE, 
4077
 
                                    revoke_grant))
4078
 
        result= -1;
4079
 
    }
4080
 
  }
4081
 
  mysql_mutex_unlock(&acl_cache->lock);
4082
 
 
4083
 
  if (!result)
4084
 
  {
4085
 
    result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
4086
 
  }
4087
 
 
4088
 
  mysql_rwlock_unlock(&LOCK_grant);
4089
 
 
4090
 
  if (!result)
4091
 
    my_ok(thd);
4092
 
  /* Restore the state of binlog format */
4093
 
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
4094
 
  if (save_binlog_row_based)
4095
 
    thd->set_current_stmt_binlog_format_row();
4096
 
 
4097
 
  DBUG_RETURN(result);
4098
 
}
4099
 
 
4100
 
 
4101
 
/* Free grant array if possible */
4102
 
 
4103
 
void  grant_free(void)
4104
 
{
4105
 
  DBUG_ENTER("grant_free");
4106
 
  my_hash_free(&column_priv_hash);
4107
 
  my_hash_free(&proc_priv_hash);
4108
 
  my_hash_free(&func_priv_hash);
4109
 
  free_root(&memex,MYF(0));
4110
 
  DBUG_VOID_RETURN;
4111
 
}
4112
 
 
4113
 
 
4114
 
/**
4115
 
  @brief Initialize structures responsible for table/column-level privilege
4116
 
   checking and load information for them from tables in the 'mysql' database.
4117
 
 
4118
 
  @return Error status
4119
 
    @retval 0 OK
4120
 
    @retval 1 Could not initialize grant subsystem.
4121
 
*/
4122
 
 
4123
 
my_bool grant_init()
4124
 
{
4125
 
  THD  *thd;
4126
 
  my_bool return_val;
4127
 
  DBUG_ENTER("grant_init");
4128
 
 
4129
 
  if (!(thd= new THD))
4130
 
    DBUG_RETURN(1);                             /* purecov: deadcode */
4131
 
  thd->thread_stack= (char*) &thd;
4132
 
  thd->store_globals();
4133
 
  return_val=  grant_reload(thd);
4134
 
  delete thd;
4135
 
  /* Remember that we don't have a THD */
4136
 
  my_pthread_setspecific_ptr(THR_THD,  0);
4137
 
  DBUG_RETURN(return_val);
4138
 
}
4139
 
 
4140
 
 
4141
 
/**
4142
 
  @brief Helper function to grant_reload_procs_priv
4143
 
 
4144
 
  Reads the procs_priv table into memory hash.
4145
 
 
4146
 
  @param table A pointer to the procs_priv table structure.
4147
 
 
4148
 
  @see grant_reload
4149
 
  @see grant_reload_procs_priv
4150
 
 
4151
 
  @return Error state
4152
 
    @retval TRUE An error occurred
4153
 
    @retval FALSE Success
4154
 
*/
4155
 
 
4156
 
static my_bool grant_load_procs_priv(TABLE *p_table)
4157
 
{
4158
 
  MEM_ROOT *memex_ptr;
4159
 
  my_bool return_val= 1;
4160
 
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
4161
 
  MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
4162
 
                                                           THR_MALLOC);
4163
 
  DBUG_ENTER("grant_load_procs_priv");
4164
 
  (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
4165
 
                      0,0,0, (my_hash_get_key) get_grant_table,
4166
 
                      0,0);
4167
 
  (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
4168
 
                      0,0,0, (my_hash_get_key) get_grant_table,
4169
 
                      0,0);
4170
 
  p_table->file->ha_index_init(0, 1);
4171
 
  p_table->use_all_columns();
4172
 
 
4173
 
  if (!p_table->file->index_first(p_table->record[0]))
4174
 
  {
4175
 
    memex_ptr= &memex;
4176
 
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
4177
 
    do
4178
 
    {
4179
 
      GRANT_NAME *mem_check;
4180
 
      HASH *hash;
4181
 
      if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE)))
4182
 
      {
4183
 
        /* This could only happen if we are out memory */
4184
 
        goto end_unlock;
4185
 
      }
4186
 
 
4187
 
      if (check_no_resolve)
4188
 
      {
4189
 
        if (hostname_requires_resolving(mem_check->host.hostname))
4190
 
        {
4191
 
          sql_print_warning("'procs_priv' entry '%s %s@%s' "
4192
 
                            "ignored in --skip-name-resolve mode.",
4193
 
                            mem_check->tname, mem_check->user,
4194
 
                            mem_check->host.hostname ?
4195
 
                            mem_check->host.hostname : "");
4196
 
          continue;
4197
 
        }
4198
 
      }
4199
 
      if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
4200
 
      {
4201
 
        hash= &proc_priv_hash;
4202
 
      }
4203
 
      else
4204
 
      if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
4205
 
      {
4206
 
        hash= &func_priv_hash;
4207
 
      }
4208
 
      else
4209
 
      {
4210
 
        sql_print_warning("'procs_priv' entry '%s' "
4211
 
                          "ignored, bad routine type",
4212
 
                          mem_check->tname);
4213
 
        continue;
4214
 
      }
4215
 
 
4216
 
      mem_check->privs= fix_rights_for_procedure(mem_check->privs);
4217
 
      if (! mem_check->ok())
4218
 
        delete mem_check;
4219
 
      else if (my_hash_insert(hash, (uchar*) mem_check))
4220
 
      {
4221
 
        delete mem_check;
4222
 
        goto end_unlock;
4223
 
      }
4224
 
    }
4225
 
    while (!p_table->file->index_next(p_table->record[0]));
4226
 
  }
4227
 
  /* Return ok */
4228
 
  return_val= 0;
4229
 
 
4230
 
end_unlock:
4231
 
  p_table->file->ha_index_end();
4232
 
  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
4233
 
  DBUG_RETURN(return_val);
4234
 
}
4235
 
 
4236
 
 
4237
 
/**
4238
 
  @brief Initialize structures responsible for table/column-level privilege
4239
 
    checking and load information about grants from open privilege tables.
4240
 
 
4241
 
  @param thd Current thread
4242
 
  @param tables List containing open "mysql.tables_priv" and
4243
 
    "mysql.columns_priv" tables.
4244
 
 
4245
 
  @see grant_reload
4246
 
 
4247
 
  @return Error state
4248
 
    @retval FALSE Success
4249
 
    @retval TRUE Error
4250
 
*/
4251
 
 
4252
 
static my_bool grant_load(THD *thd, TABLE_LIST *tables)
4253
 
{
4254
 
  MEM_ROOT *memex_ptr;
4255
 
  my_bool return_val= 1;
4256
 
  TABLE *t_table= 0, *c_table= 0;
4257
 
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
4258
 
  MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
4259
 
                                                           THR_MALLOC);
4260
 
  ulong old_sql_mode= thd->variables.sql_mode;
4261
 
  DBUG_ENTER("grant_load");
4262
 
 
4263
 
  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
4264
 
 
4265
 
  (void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
4266
 
                      0,0,0, (my_hash_get_key) get_grant_table,
4267
 
                      (my_hash_free_key) free_grant_table,0);
4268
 
 
4269
 
  t_table = tables[0].table;
4270
 
  c_table = tables[1].table;
4271
 
  t_table->file->ha_index_init(0, 1);
4272
 
  t_table->use_all_columns();
4273
 
  c_table->use_all_columns();
4274
 
 
4275
 
  if (!t_table->file->index_first(t_table->record[0]))
4276
 
  {
4277
 
    memex_ptr= &memex;
4278
 
    my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
4279
 
    do
4280
 
    {
4281
 
      GRANT_TABLE *mem_check;
4282
 
      if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table)))
4283
 
      {
4284
 
        /* This could only happen if we are out memory */
4285
 
        goto end_unlock;
4286
 
      }
4287
 
 
4288
 
      if (check_no_resolve)
4289
 
      {
4290
 
        if (hostname_requires_resolving(mem_check->host.hostname))
4291
 
        {
4292
 
          sql_print_warning("'tables_priv' entry '%s %s@%s' "
4293
 
                            "ignored in --skip-name-resolve mode.",
4294
 
                            mem_check->tname,
4295
 
                            mem_check->user ? mem_check->user : "",
4296
 
                            mem_check->host.hostname ?
4297
 
                            mem_check->host.hostname : "");
4298
 
          continue;
4299
 
        }
4300
 
      }
4301
 
 
4302
 
      if (! mem_check->ok())
4303
 
        delete mem_check;
4304
 
      else if (my_hash_insert(&column_priv_hash,(uchar*) mem_check))
4305
 
      {
4306
 
        delete mem_check;
4307
 
        goto end_unlock;
4308
 
      }
4309
 
    }
4310
 
    while (!t_table->file->index_next(t_table->record[0]));
4311
 
  }
4312
 
 
4313
 
  return_val=0;                                 // Return ok
4314
 
 
4315
 
end_unlock:
4316
 
  thd->variables.sql_mode= old_sql_mode;
4317
 
  t_table->file->ha_index_end();
4318
 
  my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
4319
 
  DBUG_RETURN(return_val);
4320
 
}
4321
 
 
4322
 
 
4323
 
/**
4324
 
  @brief Helper function to grant_reload. Reloads procs_priv table is it
4325
 
    exists.
4326
 
 
4327
 
  @param thd A pointer to the thread handler object.
4328
 
 
4329
 
  @see grant_reload
4330
 
 
4331
 
  @return Error state
4332
 
    @retval FALSE Success
4333
 
    @retval TRUE An error has occurred.
4334
 
*/
4335
 
 
4336
 
static my_bool grant_reload_procs_priv(THD *thd)
4337
 
{
4338
 
  HASH old_proc_priv_hash, old_func_priv_hash;
4339
 
  TABLE_LIST table;
4340
 
  my_bool return_val= FALSE;
4341
 
  DBUG_ENTER("grant_reload_procs_priv");
4342
 
 
4343
 
  table.init_one_table("mysql", 5, "procs_priv",
4344
 
                       strlen("procs_priv"), "procs_priv",
4345
 
                       TL_READ);
4346
 
  table.open_type= OT_BASE_ONLY;
4347
 
 
4348
 
  if (open_and_lock_tables(thd, &table, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
4349
 
    DBUG_RETURN(TRUE);
4350
 
 
4351
 
  mysql_rwlock_wrlock(&LOCK_grant);
4352
 
  /* Save a copy of the current hash if we need to undo the grant load */
4353
 
  old_proc_priv_hash= proc_priv_hash;
4354
 
  old_func_priv_hash= func_priv_hash;
4355
 
 
4356
 
  if ((return_val= grant_load_procs_priv(table.table)))
4357
 
  {
4358
 
    /* Error; Reverting to old hash */
4359
 
    DBUG_PRINT("error",("Reverting to old privileges"));
4360
 
    grant_free();
4361
 
    proc_priv_hash= old_proc_priv_hash;
4362
 
    func_priv_hash= old_func_priv_hash;
4363
 
  }
4364
 
  else
4365
 
  {
4366
 
    my_hash_free(&old_proc_priv_hash);
4367
 
    my_hash_free(&old_func_priv_hash);
4368
 
  }
4369
 
  mysql_rwlock_unlock(&LOCK_grant);
4370
 
 
4371
 
  close_mysql_tables(thd);
4372
 
  DBUG_RETURN(return_val);
4373
 
}
4374
 
 
4375
 
 
4376
 
/**
4377
 
  @brief Reload information about table and column level privileges if possible
4378
 
 
4379
 
  @param thd Current thread
4380
 
 
4381
 
  Locked tables are checked by acl_reload() and doesn't have to be checked
4382
 
  in this call.
4383
 
  This function is also used for initialization of structures responsible
4384
 
  for table/column-level privilege checking.
4385
 
 
4386
 
  @return Error state
4387
 
    @retval FALSE Success
4388
 
    @retval TRUE  Error
4389
 
*/
4390
 
 
4391
 
my_bool grant_reload(THD *thd)
4392
 
{
4393
 
  TABLE_LIST tables[2];
4394
 
  HASH old_column_priv_hash;
4395
 
  MEM_ROOT old_mem;
4396
 
  my_bool return_val= 1;
4397
 
  DBUG_ENTER("grant_reload");
4398
 
 
4399
 
  /* Don't do anything if running with --skip-grant-tables */
4400
 
  if (!initialized)
4401
 
    DBUG_RETURN(0);
4402
 
 
4403
 
  tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
4404
 
                           C_STRING_WITH_LEN("tables_priv"),
4405
 
                           "tables_priv", TL_READ);
4406
 
  tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
4407
 
                           C_STRING_WITH_LEN("columns_priv"),
4408
 
                           "columns_priv", TL_READ);
4409
 
  tables[0].next_local= tables[0].next_global= tables+1;
4410
 
  tables[0].open_type= tables[1].open_type= OT_BASE_ONLY;
4411
 
 
4412
 
  /*
4413
 
    To avoid deadlocks we should obtain table locks before
4414
 
    obtaining LOCK_grant rwlock.
4415
 
  */
4416
 
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
4417
 
    goto end;
4418
 
 
4419
 
  mysql_rwlock_wrlock(&LOCK_grant);
4420
 
  old_column_priv_hash= column_priv_hash;
4421
 
 
4422
 
  /*
4423
 
    Create a new memory pool but save the current memory pool to make an undo
4424
 
    opertion possible in case of failure.
4425
 
  */
4426
 
  old_mem= memex;
4427
 
  init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
4428
 
 
4429
 
  if ((return_val= grant_load(thd, tables)))
4430
 
  {                                             // Error. Revert to old hash
4431
 
    DBUG_PRINT("error",("Reverting to old privileges"));
4432
 
    grant_free();                               /* purecov: deadcode */
4433
 
    column_priv_hash= old_column_priv_hash;     /* purecov: deadcode */
4434
 
    memex= old_mem;                             /* purecov: deadcode */
4435
 
  }
4436
 
  else
4437
 
  {
4438
 
    my_hash_free(&old_column_priv_hash);
4439
 
    free_root(&old_mem,MYF(0));
4440
 
  }
4441
 
  mysql_rwlock_unlock(&LOCK_grant);
4442
 
  close_mysql_tables(thd);
4443
 
 
4444
 
  /*
4445
 
    It is OK failing to load procs_priv table because we may be
4446
 
    working with 4.1 privilege tables.
4447
 
  */
4448
 
  if (grant_reload_procs_priv(thd))
4449
 
    return_val= 1;
4450
 
 
4451
 
  mysql_rwlock_wrlock(&LOCK_grant);
4452
 
  grant_version++;
4453
 
  mysql_rwlock_unlock(&LOCK_grant);
4454
 
 
4455
 
end:
4456
 
  DBUG_RETURN(return_val);
4457
 
}
4458
 
 
4459
 
 
4460
 
/**
4461
 
  @brief Check table level grants
4462
 
 
4463
 
  @param thd          Thread handler
4464
 
  @param want_access  Bits of privileges user needs to have.
4465
 
  @param tables       List of tables to check. The user should have
4466
 
                      'want_access' to all tables in list.
4467
 
  @param any_combination_will_do TRUE if it's enough to have any privilege for
4468
 
    any combination of the table columns.
4469
 
  @param number       Check at most this number of tables.
4470
 
  @param no_errors    TRUE if no error should be sent directly to the client.
4471
 
 
4472
 
  If table->grant.want_privilege != 0 then the requested privileges where
4473
 
  in the set of COL_ACLS but access was not granted on the table level. As
4474
 
  a consequence an extra check of column privileges is required.
4475
 
 
4476
 
  Specifically if this function returns FALSE the user has some kind of
4477
 
  privilege on a combination of columns in each table.
4478
 
 
4479
 
  This function is usually preceeded by check_access which establish the
4480
 
  User-, Db- and Host access rights.
4481
 
 
4482
 
  @see check_access
4483
 
  @see check_table_access
4484
 
 
4485
 
  @note This functions assumes that either number of tables to be inspected
4486
 
     by it is limited explicitly (i.e. is is not UINT_MAX) or table list
4487
 
     used and thd->lex->query_tables_own_last value correspond to each
4488
 
     other (the latter should be either 0 or point to next_global member
4489
 
     of one of elements of this table list).
4490
 
 
4491
 
   @return Access status
4492
 
     @retval FALSE Access granted; But column privileges might need to be
4493
 
      checked.
4494
 
     @retval TRUE The user did not have the requested privileges on any of the
4495
 
      tables.
4496
 
 
4497
 
*/
4498
 
 
4499
 
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
4500
 
                 bool any_combination_will_do, uint number, bool no_errors)
4501
 
{
4502
 
  TABLE_LIST *tl;
4503
 
  TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
4504
 
  Security_context *sctx= thd->security_ctx;
4505
 
  uint i;
4506
 
  ulong orig_want_access= want_access;
4507
 
  DBUG_ENTER("check_grant");
4508
 
  DBUG_ASSERT(number > 0);
4509
 
 
4510
 
  /*
4511
 
    Walk through the list of tables that belong to the query and save the
4512
 
    requested access (orig_want_privilege) to be able to use it when
4513
 
    checking access rights to the underlying tables of a view. Our grant
4514
 
    system gradually eliminates checked bits from want_privilege and thus
4515
 
    after all checks are done we can no longer use it.
4516
 
    The check that first_not_own_table is not reached is for the case when
4517
 
    the given table list refers to the list for prelocking (contains tables
4518
 
    of other queries). For simple queries first_not_own_table is 0.
4519
 
  */
4520
 
  for (i= 0, tl= tables;
4521
 
       i < number  && tl != first_not_own_table;
4522
 
       tl= tl->next_global, i++)
4523
 
  {
4524
 
    /*
4525
 
      Save a copy of the privileges without the SHOW_VIEW_ACL attribute.
4526
 
      It will be checked during making view.
4527
 
    */
4528
 
    tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
4529
 
  }
4530
 
 
4531
 
  mysql_rwlock_rdlock(&LOCK_grant);
4532
 
  for (tl= tables;
4533
 
       tl && number-- && tl != first_not_own_table;
4534
 
       tl= tl->next_global)
4535
 
  {
4536
 
    sctx = test(tl->security_ctx) ? tl->security_ctx : thd->security_ctx;
4537
 
 
4538
 
    const ACL_internal_table_access *access=
4539
 
      get_cached_table_access(&tl->grant.m_internal,
4540
 
                              tl->get_db_name(),
4541
 
                              tl->get_table_name());
4542
 
 
4543
 
    if (access)
4544
 
    {
4545
 
      switch(access->check(orig_want_access, &tl->grant.privilege))
4546
 
      {
4547
 
      case ACL_INTERNAL_ACCESS_GRANTED:
4548
 
        /*
4549
 
          Currently,
4550
 
          -  the information_schema does not subclass ACL_internal_table_access,
4551
 
          there are no per table privilege checks for I_S,
4552
 
          - the performance schema does use per tables checks, but at most
4553
 
          returns 'CHECK_GRANT', and never 'ACCESS_GRANTED'.
4554
 
          so this branch is not used.
4555
 
        */
4556
 
        DBUG_ASSERT(0);
4557
 
      case ACL_INTERNAL_ACCESS_DENIED:
4558
 
        goto err;
4559
 
      case ACL_INTERNAL_ACCESS_CHECK_GRANT:
4560
 
        break;
4561
 
      }
4562
 
    }
4563
 
 
4564
 
    want_access= orig_want_access;
4565
 
    want_access&= ~sctx->master_access;
4566
 
    if (!want_access)
4567
 
      continue;                                 // ok
4568
 
 
4569
 
    if (!(~tl->grant.privilege & want_access) ||
4570
 
        tl->is_anonymous_derived_table() || tl->schema_table)
4571
 
    {
4572
 
      /*
4573
 
        It is subquery in the FROM clause. VIEW set tl->derived after
4574
 
        table opening, but this function always called before table opening.
4575
 
      */
4576
 
      if (!tl->referencing_view)
4577
 
      {
4578
 
        /*
4579
 
          If it's a temporary table created for a subquery in the FROM
4580
 
          clause, or an INFORMATION_SCHEMA table, drop the request for
4581
 
          a privilege.
4582
 
        */
4583
 
        tl->grant.want_privilege= 0;
4584
 
      }
4585
 
      continue;
4586
 
    }
4587
 
    GRANT_TABLE *grant_table= table_hash_search(sctx->host, sctx->ip,
4588
 
                                                tl->get_db_name(),
4589
 
                                                sctx->priv_user,
4590
 
                                                tl->get_table_name(),
4591
 
                                                FALSE);
4592
 
 
4593
 
    if (!grant_table)
4594
 
    {
4595
 
      want_access &= ~tl->grant.privilege;
4596
 
      goto err;                                 // No grants
4597
 
    }
4598
 
 
4599
 
    /*
4600
 
      For SHOW COLUMNS, SHOW INDEX it is enough to have some
4601
 
      privileges on any column combination on the table.
4602
 
    */
4603
 
    if (any_combination_will_do)
4604
 
      continue;
4605
 
 
4606
 
    tl->grant.grant_table= grant_table; // Remember for column test
4607
 
    tl->grant.version= grant_version;
4608
 
    tl->grant.privilege|= grant_table->privs;
4609
 
    tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege);
4610
 
 
4611
 
    if (!(~tl->grant.privilege & want_access))
4612
 
      continue;
4613
 
 
4614
 
    if (want_access & ~(grant_table->cols | tl->grant.privilege))
4615
 
    {
4616
 
      want_access &= ~(grant_table->cols | tl->grant.privilege);
4617
 
      goto err;                                 // impossible
4618
 
    }
4619
 
  }
4620
 
  mysql_rwlock_unlock(&LOCK_grant);
4621
 
  DBUG_RETURN(FALSE);
4622
 
 
4623
 
err:
4624
 
  mysql_rwlock_unlock(&LOCK_grant);
4625
 
  if (!no_errors)                               // Not a silent skip of table
4626
 
  {
4627
 
    char command[128];
4628
 
    get_privilege_desc(command, sizeof(command), want_access);
4629
 
    my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
4630
 
             command,
4631
 
             sctx->priv_user,
4632
 
             sctx->host_or_ip,
4633
 
             tl ? tl->get_table_name() : "unknown");
4634
 
  }
4635
 
  DBUG_RETURN(TRUE);
4636
 
}
4637
 
 
4638
 
 
4639
 
/*
4640
 
  Check column rights in given security context
4641
 
 
4642
 
  SYNOPSIS
4643
 
    check_grant_column()
4644
 
    thd                  thread handler
4645
 
    grant                grant information structure
4646
 
    db_name              db name
4647
 
    table_name           table  name
4648
 
    name                 column name
4649
 
    length               column name length
4650
 
    sctx                 security context
4651
 
 
4652
 
  RETURN
4653
 
    FALSE OK
4654
 
    TRUE  access denied
4655
 
*/
4656
 
 
4657
 
bool check_grant_column(THD *thd, GRANT_INFO *grant,
4658
 
                        const char *db_name, const char *table_name,
4659
 
                        const char *name, uint length,  Security_context *sctx)
4660
 
{
4661
 
  GRANT_TABLE *grant_table;
4662
 
  GRANT_COLUMN *grant_column;
4663
 
  ulong want_access= grant->want_privilege & ~grant->privilege;
4664
 
  DBUG_ENTER("check_grant_column");
4665
 
  DBUG_PRINT("enter", ("table: %s  want_access: %lu", table_name, want_access));
4666
 
 
4667
 
  if (!want_access)
4668
 
    DBUG_RETURN(0);                             // Already checked
4669
 
 
4670
 
  mysql_rwlock_rdlock(&LOCK_grant);
4671
 
 
4672
 
  /* reload table if someone has modified any grants */
4673
 
 
4674
 
  if (grant->version != grant_version)
4675
 
  {
4676
 
    grant->grant_table=
4677
 
      table_hash_search(sctx->host, sctx->ip, db_name,
4678
 
                        sctx->priv_user,
4679
 
                        table_name, 0);         /* purecov: inspected */
4680
 
    grant->version= grant_version;              /* purecov: inspected */
4681
 
  }
4682
 
  if (!(grant_table= grant->grant_table))
4683
 
    goto err;                                   /* purecov: deadcode */
4684
 
 
4685
 
  grant_column=column_hash_search(grant_table, name, length);
4686
 
  if (grant_column && !(~grant_column->rights & want_access))
4687
 
  {
4688
 
    mysql_rwlock_unlock(&LOCK_grant);
4689
 
    DBUG_RETURN(0);
4690
 
  }
4691
 
 
4692
 
err:
4693
 
  mysql_rwlock_unlock(&LOCK_grant);
4694
 
  char command[128];
4695
 
  get_privilege_desc(command, sizeof(command), want_access);
4696
 
  my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
4697
 
           command,
4698
 
           sctx->priv_user,
4699
 
           sctx->host_or_ip,
4700
 
           name,
4701
 
           table_name);
4702
 
  DBUG_RETURN(1);
4703
 
}
4704
 
 
4705
 
 
4706
 
/*
4707
 
  Check the access right to a column depending on the type of table.
4708
 
 
4709
 
  SYNOPSIS
4710
 
    check_column_grant_in_table_ref()
4711
 
    thd              thread handler
4712
 
    table_ref        table reference where to check the field
4713
 
    name             name of field to check
4714
 
    length           length of name
4715
 
 
4716
 
  DESCRIPTION
4717
 
    Check the access rights to a column depending on the type of table
4718
 
    reference where the column is checked. The function provides a
4719
 
    generic interface to check column access rights that hides the
4720
 
    heterogeneity of the column representation - whether it is a view
4721
 
    or a stored table colum.
4722
 
 
4723
 
  RETURN
4724
 
    FALSE OK
4725
 
    TRUE  access denied
4726
 
*/
4727
 
 
4728
 
bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
4729
 
                                     const char *name, uint length)
4730
 
{
4731
 
  GRANT_INFO *grant;
4732
 
  const char *db_name;
4733
 
  const char *table_name;
4734
 
  Security_context *sctx= test(table_ref->security_ctx) ?
4735
 
                          table_ref->security_ctx : thd->security_ctx;
4736
 
 
4737
 
  if (table_ref->view || table_ref->field_translation)
4738
 
  {
4739
 
    /* View or derived information schema table. */
4740
 
    ulong view_privs;
4741
 
    grant= &(table_ref->grant);
4742
 
    db_name= table_ref->view_db.str;
4743
 
    table_name= table_ref->view_name.str;
4744
 
    if (table_ref->belong_to_view && 
4745
 
        thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
4746
 
    {
4747
 
      view_privs= get_column_grant(thd, grant, db_name, table_name, name);
4748
 
      if (view_privs & VIEW_ANY_ACL)
4749
 
      {
4750
 
        table_ref->belong_to_view->allowed_show= TRUE;
4751
 
        return FALSE;
4752
 
      }
4753
 
      table_ref->belong_to_view->allowed_show= FALSE;
4754
 
      my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
4755
 
      return TRUE;
4756
 
    }
4757
 
  }
4758
 
  else
4759
 
  {
4760
 
    /* Normal or temporary table. */
4761
 
    TABLE *table= table_ref->table;
4762
 
    grant= &(table->grant);
4763
 
    db_name= table->s->db.str;
4764
 
    table_name= table->s->table_name.str;
4765
 
  }
4766
 
 
4767
 
  if (grant->want_privilege)
4768
 
    return check_grant_column(thd, grant, db_name, table_name, name,
4769
 
                              length, sctx);
4770
 
  else
4771
 
    return FALSE;
4772
 
 
4773
 
}
4774
 
 
4775
 
 
4776
 
/** 
4777
 
  @brief check if a query can access a set of columns
4778
 
 
4779
 
  @param  thd  the current thread
4780
 
  @param  want_access_arg  the privileges requested
4781
 
  @param  fields an iterator over the fields of a table reference.
4782
 
  @return Operation status
4783
 
    @retval 0 Success
4784
 
    @retval 1 Falure
4785
 
  @details This function walks over the columns of a table reference 
4786
 
   The columns may originate from different tables, depending on the kind of
4787
 
   table reference, e.g. join, view.
4788
 
   For each table it will retrieve the grant information and will use it
4789
 
   to check the required access privileges for the fields requested from it.
4790
 
*/    
4791
 
bool check_grant_all_columns(THD *thd, ulong want_access_arg, 
4792
 
                             Field_iterator_table_ref *fields)
4793
 
{
4794
 
  Security_context *sctx= thd->security_ctx;
4795
 
  ulong want_access= want_access_arg;
4796
 
  const char *table_name= NULL;
4797
 
 
4798
 
  const char* db_name; 
4799
 
  GRANT_INFO *grant;
4800
 
  /* Initialized only to make gcc happy */
4801
 
  GRANT_TABLE *grant_table= NULL;
4802
 
  /* 
4803
 
     Flag that gets set if privilege checking has to be performed on column
4804
 
     level.
4805
 
  */
4806
 
  bool using_column_privileges= FALSE;
4807
 
 
4808
 
  mysql_rwlock_rdlock(&LOCK_grant);
4809
 
 
4810
 
  for (; !fields->end_of_fields(); fields->next())
4811
 
  {
4812
 
    const char *field_name= fields->name();
4813
 
 
4814
 
    if (table_name != fields->get_table_name())
4815
 
    {
4816
 
      table_name= fields->get_table_name();
4817
 
      db_name= fields->get_db_name();
4818
 
      grant= fields->grant();
4819
 
      /* get a fresh one for each table */
4820
 
      want_access= want_access_arg & ~grant->privilege;
4821
 
      if (want_access)
4822
 
      {
4823
 
        /* reload table if someone has modified any grants */
4824
 
        if (grant->version != grant_version)
4825
 
        {
4826
 
          grant->grant_table=
4827
 
            table_hash_search(sctx->host, sctx->ip, db_name,
4828
 
                              sctx->priv_user,
4829
 
                              table_name, 0);   /* purecov: inspected */
4830
 
          grant->version= grant_version;        /* purecov: inspected */
4831
 
        }
4832
 
 
4833
 
        grant_table= grant->grant_table;
4834
 
        DBUG_ASSERT (grant_table);
4835
 
      }
4836
 
    }
4837
 
 
4838
 
    if (want_access)
4839
 
    {
4840
 
      GRANT_COLUMN *grant_column= 
4841
 
        column_hash_search(grant_table, field_name,
4842
 
                           (uint) strlen(field_name));
4843
 
      if (grant_column)
4844
 
        using_column_privileges= TRUE;
4845
 
      if (!grant_column || (~grant_column->rights & want_access))
4846
 
        goto err;
4847
 
    }
4848
 
  }
4849
 
  mysql_rwlock_unlock(&LOCK_grant);
4850
 
  return 0;
4851
 
 
4852
 
err:
4853
 
  mysql_rwlock_unlock(&LOCK_grant);
4854
 
 
4855
 
  char command[128];
4856
 
  get_privilege_desc(command, sizeof(command), want_access);
4857
 
  /*
4858
 
    Do not give an error message listing a column name unless the user has
4859
 
    privilege to see all columns.
4860
 
  */
4861
 
  if (using_column_privileges)
4862
 
    my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
4863
 
             command, sctx->priv_user,
4864
 
             sctx->host_or_ip, table_name); 
4865
 
  else
4866
 
    my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
4867
 
             command,
4868
 
             sctx->priv_user,
4869
 
             sctx->host_or_ip,
4870
 
             fields->name(),
4871
 
             table_name);
4872
 
  return 1;
4873
 
}
4874
 
 
4875
 
 
4876
 
static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
4877
 
{
4878
 
  Security_context *sctx= thd->security_ctx;
4879
 
 
4880
 
  for (uint idx= 0; idx < hash->records; ++idx)
4881
 
  {
4882
 
    GRANT_NAME *item= (GRANT_NAME*) my_hash_element(hash, idx);
4883
 
 
4884
 
    if (strcmp(item->user, sctx->priv_user) == 0 &&
4885
 
        strcmp(item->db, db) == 0 &&
4886
 
        compare_hostname(&item->host, sctx->host, sctx->ip))
4887
 
    {
4888
 
      return FALSE;
4889
 
    }
4890
 
  }
4891
 
 
4892
 
  return TRUE;
4893
 
}
4894
 
 
4895
 
 
4896
 
/*
4897
 
  Check if a user has the right to access a database
4898
 
  Access is accepted if he has a grant for any table/routine in the database
4899
 
  Return 1 if access is denied
4900
 
*/
4901
 
 
4902
 
bool check_grant_db(THD *thd,const char *db)
4903
 
{
4904
 
  Security_context *sctx= thd->security_ctx;
4905
 
  char helping [NAME_LEN+USERNAME_LENGTH+2];
4906
 
  uint len;
4907
 
  bool error= TRUE;
4908
 
 
4909
 
  len= (uint) (strmov(strmov(helping, sctx->priv_user) + 1, db) - helping) + 1;
4910
 
 
4911
 
  mysql_rwlock_rdlock(&LOCK_grant);
4912
 
 
4913
 
  for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
4914
 
  {
4915
 
    GRANT_TABLE *grant_table= (GRANT_TABLE*)
4916
 
      my_hash_element(&column_priv_hash,
4917
 
                      idx);
4918
 
    if (len < grant_table->key_length &&
4919
 
        !memcmp(grant_table->hash_key,helping,len) &&
4920
 
        compare_hostname(&grant_table->host, sctx->host, sctx->ip))
4921
 
    {
4922
 
      error= FALSE; /* Found match. */
4923
 
      break;
4924
 
    }
4925
 
  }
4926
 
 
4927
 
  if (error)
4928
 
    error= check_grant_db_routine(thd, db, &proc_priv_hash) &&
4929
 
           check_grant_db_routine(thd, db, &func_priv_hash);
4930
 
 
4931
 
  mysql_rwlock_unlock(&LOCK_grant);
4932
 
 
4933
 
  return error;
4934
 
}
4935
 
 
4936
 
 
4937
 
/****************************************************************************
4938
 
  Check routine level grants
4939
 
 
4940
 
  SYNPOSIS
4941
 
   bool check_grant_routine()
4942
 
   thd          Thread handler
4943
 
   want_access  Bits of privileges user needs to have
4944
 
   procs        List of routines to check. The user should have 'want_access'
4945
 
   is_proc      True if the list is all procedures, else functions
4946
 
   no_errors    If 0 then we write an error. The error is sent directly to
4947
 
                the client
4948
 
 
4949
 
   RETURN
4950
 
     0  ok
4951
 
     1  Error: User did not have the requested privielges
4952
 
****************************************************************************/
4953
 
 
4954
 
bool check_grant_routine(THD *thd, ulong want_access,
4955
 
                         TABLE_LIST *procs, bool is_proc, bool no_errors)
4956
 
{
4957
 
  TABLE_LIST *table;
4958
 
  Security_context *sctx= thd->security_ctx;
4959
 
  char *user= sctx->priv_user;
4960
 
  char *host= sctx->priv_host;
4961
 
  DBUG_ENTER("check_grant_routine");
4962
 
 
4963
 
  want_access&= ~sctx->master_access;
4964
 
  if (!want_access)
4965
 
    DBUG_RETURN(0);                             // ok
4966
 
 
4967
 
  mysql_rwlock_rdlock(&LOCK_grant);
4968
 
  for (table= procs; table; table= table->next_global)
4969
 
  {
4970
 
    GRANT_NAME *grant_proc;
4971
 
    if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
4972
 
                                         table->table_name, is_proc, 0)))
4973
 
      table->grant.privilege|= grant_proc->privs;
4974
 
 
4975
 
    if (want_access & ~table->grant.privilege)
4976
 
    {
4977
 
      want_access &= ~table->grant.privilege;
4978
 
      goto err;
4979
 
    }
4980
 
  }
4981
 
  mysql_rwlock_unlock(&LOCK_grant);
4982
 
  DBUG_RETURN(0);
4983
 
err:
4984
 
  mysql_rwlock_unlock(&LOCK_grant);
4985
 
  if (!no_errors)
4986
 
  {
4987
 
    char buff[1024];
4988
 
    const char *command="";
4989
 
    if (table)
4990
 
      strxmov(buff, table->db, ".", table->table_name, NullS);
4991
 
    if (want_access & EXECUTE_ACL)
4992
 
      command= "execute";
4993
 
    else if (want_access & ALTER_PROC_ACL)
4994
 
      command= "alter routine";
4995
 
    else if (want_access & GRANT_ACL)
4996
 
      command= "grant";
4997
 
    my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
4998
 
             command, user, host, table ? buff : "unknown");
4999
 
  }
5000
 
  DBUG_RETURN(1);
5001
 
}
5002
 
 
5003
 
 
5004
 
/*
5005
 
  Check if routine has any of the 
5006
 
  routine level grants
5007
 
  
5008
 
  SYNPOSIS
5009
 
   bool    check_routine_level_acl()
5010
 
   thd          Thread handler
5011
 
   db           Database name
5012
 
   name         Routine name
5013
 
 
5014
 
  RETURN
5015
 
   0            Ok 
5016
 
   1            error
5017
 
*/
5018
 
 
5019
 
bool check_routine_level_acl(THD *thd, const char *db, const char *name, 
5020
 
                             bool is_proc)
5021
 
{
5022
 
  bool no_routine_acl= 1;
5023
 
  GRANT_NAME *grant_proc;
5024
 
  Security_context *sctx= thd->security_ctx;
5025
 
  mysql_rwlock_rdlock(&LOCK_grant);
5026
 
  if ((grant_proc= routine_hash_search(sctx->priv_host,
5027
 
                                       sctx->ip, db,
5028
 
                                       sctx->priv_user,
5029
 
                                       name, is_proc, 0)))
5030
 
    no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
5031
 
  mysql_rwlock_unlock(&LOCK_grant);
5032
 
  return no_routine_acl;
5033
 
}
5034
 
 
5035
 
 
5036
 
/*****************************************************************************
5037
 
  Functions to retrieve the grant for a table/column  (for SHOW functions)
5038
 
*****************************************************************************/
5039
 
 
5040
 
ulong get_table_grant(THD *thd, TABLE_LIST *table)
5041
 
{
5042
 
  ulong privilege;
5043
 
  Security_context *sctx= thd->security_ctx;
5044
 
  const char *db = table->db ? table->db : thd->db;
5045
 
  GRANT_TABLE *grant_table;
5046
 
 
5047
 
  mysql_rwlock_rdlock(&LOCK_grant);
5048
 
#ifdef EMBEDDED_LIBRARY
5049
 
  grant_table= NULL;
5050
 
#else
5051
 
  grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
5052
 
                                 table->table_name, 0);
5053
 
#endif
5054
 
  table->grant.grant_table=grant_table; // Remember for column test
5055
 
  table->grant.version=grant_version;
5056
 
  if (grant_table)
5057
 
    table->grant.privilege|= grant_table->privs;
5058
 
  privilege= table->grant.privilege;
5059
 
  mysql_rwlock_unlock(&LOCK_grant);
5060
 
  return privilege;
5061
 
}
5062
 
 
5063
 
 
5064
 
/*
5065
 
  Determine the access priviliges for a field.
5066
 
 
5067
 
  SYNOPSIS
5068
 
    get_column_grant()
5069
 
    thd         thread handler
5070
 
    grant       grants table descriptor
5071
 
    db_name     name of database that the field belongs to
5072
 
    table_name  name of table that the field belongs to
5073
 
    field_name  name of field
5074
 
 
5075
 
  DESCRIPTION
5076
 
    The procedure may also modify: grant->grant_table and grant->version.
5077
 
 
5078
 
  RETURN
5079
 
    The access priviliges for the field db_name.table_name.field_name
5080
 
*/
5081
 
 
5082
 
ulong get_column_grant(THD *thd, GRANT_INFO *grant,
5083
 
                       const char *db_name, const char *table_name,
5084
 
                       const char *field_name)
5085
 
{
5086
 
  GRANT_TABLE *grant_table;
5087
 
  GRANT_COLUMN *grant_column;
5088
 
  ulong priv;
5089
 
 
5090
 
  mysql_rwlock_rdlock(&LOCK_grant);
5091
 
  /* reload table if someone has modified any grants */
5092
 
  if (grant->version != grant_version)
5093
 
  {
5094
 
    Security_context *sctx= thd->security_ctx;
5095
 
    grant->grant_table=
5096
 
      table_hash_search(sctx->host, sctx->ip,
5097
 
                        db_name, sctx->priv_user,
5098
 
                        table_name, 0);         /* purecov: inspected */
5099
 
    grant->version= grant_version;              /* purecov: inspected */
5100
 
  }
5101
 
 
5102
 
  if (!(grant_table= grant->grant_table))
5103
 
    priv= grant->privilege;
5104
 
  else
5105
 
  {
5106
 
    grant_column= column_hash_search(grant_table, field_name,
5107
 
                                     (uint) strlen(field_name));
5108
 
    if (!grant_column)
5109
 
      priv= (grant->privilege | grant_table->privs);
5110
 
    else
5111
 
      priv= (grant->privilege | grant_table->privs | grant_column->rights);
5112
 
  }
5113
 
  mysql_rwlock_unlock(&LOCK_grant);
5114
 
  return priv;
5115
 
}
5116
 
 
5117
 
 
5118
 
/* Help function for mysql_show_grants */
5119
 
 
5120
 
static void add_user_option(String *grant, ulong value, const char *name)
5121
 
{
5122
 
  if (value)
5123
 
  {
5124
 
    char buff[22], *p; // just as in int2str
5125
 
    grant->append(' ');
5126
 
    grant->append(name, strlen(name));
5127
 
    grant->append(' ');
5128
 
    p=int10_to_str(value, buff, 10);
5129
 
    grant->append(buff,p-buff);
5130
 
  }
5131
 
}
5132
 
 
5133
 
static const char *command_array[]=
5134
 
{
5135
 
  "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD",
5136
 
  "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX",
5137
 
  "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES",
5138
 
  "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT",
5139
 
  "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
5140
 
  "CREATE USER", "EVENT", "TRIGGER", "CREATE TABLESPACE"
5141
 
};
5142
 
 
5143
 
static uint command_lengths[]=
5144
 
{
5145
 
  6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
5146
 
  14, 13, 11, 5, 7, 17
5147
 
};
5148
 
 
5149
 
 
5150
 
static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
5151
 
                               const char *type, int typelen,
5152
 
                               char *buff, int buffsize);
5153
 
 
5154
 
 
5155
 
/*
5156
 
  SHOW GRANTS;  Send grants for a user to the client
5157
 
 
5158
 
  IMPLEMENTATION
5159
 
   Send to client grant-like strings depicting user@host privileges
5160
 
*/
5161
 
 
5162
 
bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
5163
 
{
5164
 
  ulong want_access;
5165
 
  uint counter,index;
5166
 
  int  error = 0;
5167
 
  ACL_USER *acl_user;
5168
 
  ACL_DB *acl_db;
5169
 
  char buff[1024];
5170
 
  Protocol *protocol= thd->protocol;
5171
 
  DBUG_ENTER("mysql_show_grants");
5172
 
 
5173
 
  LINT_INIT(acl_user);
5174
 
  if (!initialized)
5175
 
  {
5176
 
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
5177
 
    DBUG_RETURN(TRUE);
5178
 
  }
5179
 
 
5180
 
  mysql_rwlock_rdlock(&LOCK_grant);
5181
 
  mysql_mutex_lock(&acl_cache->lock);
5182
 
 
5183
 
  acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE);
5184
 
  if (!acl_user)
5185
 
  {
5186
 
    mysql_mutex_unlock(&acl_cache->lock);
5187
 
    mysql_rwlock_unlock(&LOCK_grant);
5188
 
 
5189
 
    my_error(ER_NONEXISTING_GRANT, MYF(0),
5190
 
             lex_user->user.str, lex_user->host.str);
5191
 
    DBUG_RETURN(TRUE);
5192
 
  }
5193
 
 
5194
 
  Item_string *field=new Item_string("",0,&my_charset_latin1);
5195
 
  List<Item> field_list;
5196
 
  field->name=buff;
5197
 
  field->max_length=1024;
5198
 
  strxmov(buff,"Grants for ",lex_user->user.str,"@",
5199
 
          lex_user->host.str,NullS);
5200
 
  field_list.push_back(field);
5201
 
  if (protocol->send_result_set_metadata(&field_list,
5202
 
                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
5203
 
  {
5204
 
    mysql_mutex_unlock(&acl_cache->lock);
5205
 
    mysql_rwlock_unlock(&LOCK_grant);
5206
 
 
5207
 
    DBUG_RETURN(TRUE);
5208
 
  }
5209
 
 
5210
 
  /* Add first global access grants */
5211
 
  {
5212
 
    String global(buff,sizeof(buff),system_charset_info);
5213
 
    global.length(0);
5214
 
    global.append(STRING_WITH_LEN("GRANT "));
5215
 
 
5216
 
    want_access= acl_user->access;
5217
 
    if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
5218
 
      global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
5219
 
    else if (!(want_access & ~GRANT_ACL))
5220
 
      global.append(STRING_WITH_LEN("USAGE"));
5221
 
    else
5222
 
    {
5223
 
      bool found=0;
5224
 
      ulong j,test_access= want_access & ~GRANT_ACL;
5225
 
      for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
5226
 
      {
5227
 
        if (test_access & j)
5228
 
        {
5229
 
          if (found)
5230
 
            global.append(STRING_WITH_LEN(", "));
5231
 
          found=1;
5232
 
          global.append(command_array[counter],command_lengths[counter]);
5233
 
        }
5234
 
      }
5235
 
    }
5236
 
    global.append (STRING_WITH_LEN(" ON *.* TO '"));
5237
 
    global.append(lex_user->user.str, lex_user->user.length,
5238
 
                  system_charset_info);
5239
 
    global.append (STRING_WITH_LEN("'@'"));
5240
 
    global.append(lex_user->host.str,lex_user->host.length,
5241
 
                  system_charset_info);
5242
 
    global.append ('\'');
5243
 
    if (acl_user->salt_len)
5244
 
    {
5245
 
      char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
5246
 
      if (acl_user->salt_len == SCRAMBLE_LENGTH)
5247
 
        make_password_from_salt(passwd_buff, acl_user->salt);
5248
 
      else
5249
 
        make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt);
5250
 
      global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
5251
 
      global.append(passwd_buff);
5252
 
      global.append('\'');
5253
 
    }
5254
 
    /* "show grants" SSL related stuff */
5255
 
    if (acl_user->ssl_type == SSL_TYPE_ANY)
5256
 
      global.append(STRING_WITH_LEN(" REQUIRE SSL"));
5257
 
    else if (acl_user->ssl_type == SSL_TYPE_X509)
5258
 
      global.append(STRING_WITH_LEN(" REQUIRE X509"));
5259
 
    else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
5260
 
    {
5261
 
      int ssl_options = 0;
5262
 
      global.append(STRING_WITH_LEN(" REQUIRE "));
5263
 
      if (acl_user->x509_issuer)
5264
 
      {
5265
 
        ssl_options++;
5266
 
        global.append(STRING_WITH_LEN("ISSUER \'"));
5267
 
        global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
5268
 
        global.append('\'');
5269
 
      }
5270
 
      if (acl_user->x509_subject)
5271
 
      {
5272
 
        if (ssl_options++)
5273
 
          global.append(' ');
5274
 
        global.append(STRING_WITH_LEN("SUBJECT \'"));
5275
 
        global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
5276
 
                      system_charset_info);
5277
 
        global.append('\'');
5278
 
      }
5279
 
      if (acl_user->ssl_cipher)
5280
 
      {
5281
 
        if (ssl_options++)
5282
 
          global.append(' ');
5283
 
        global.append(STRING_WITH_LEN("CIPHER '"));
5284
 
        global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
5285
 
                      system_charset_info);
5286
 
        global.append('\'');
5287
 
      }
5288
 
    }
5289
 
    if ((want_access & GRANT_ACL) ||
5290
 
        (acl_user->user_resource.questions ||
5291
 
         acl_user->user_resource.updates ||
5292
 
         acl_user->user_resource.conn_per_hour ||
5293
 
         acl_user->user_resource.user_conn))
5294
 
    {
5295
 
      global.append(STRING_WITH_LEN(" WITH"));
5296
 
      if (want_access & GRANT_ACL)
5297
 
        global.append(STRING_WITH_LEN(" GRANT OPTION"));
5298
 
      add_user_option(&global, acl_user->user_resource.questions,
5299
 
                      "MAX_QUERIES_PER_HOUR");
5300
 
      add_user_option(&global, acl_user->user_resource.updates,
5301
 
                      "MAX_UPDATES_PER_HOUR");
5302
 
      add_user_option(&global, acl_user->user_resource.conn_per_hour,
5303
 
                      "MAX_CONNECTIONS_PER_HOUR");
5304
 
      add_user_option(&global, acl_user->user_resource.user_conn,
5305
 
                      "MAX_USER_CONNECTIONS");
5306
 
    }
5307
 
    protocol->prepare_for_resend();
5308
 
    protocol->store(global.ptr(),global.length(),global.charset());
5309
 
    if (protocol->write())
5310
 
    {
5311
 
      error= -1;
5312
 
      goto end;
5313
 
    }
5314
 
  }
5315
 
 
5316
 
  /* Add database access */
5317
 
  for (counter=0 ; counter < acl_dbs.elements ; counter++)
5318
 
  {
5319
 
    const char *user, *host;
5320
 
 
5321
 
    acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
5322
 
    if (!(user=acl_db->user))
5323
 
      user= "";
5324
 
    if (!(host=acl_db->host.hostname))
5325
 
      host= "";
5326
 
 
5327
 
    /*
5328
 
      We do not make SHOW GRANTS case-sensitive here (like REVOKE),
5329
 
      but make it case-insensitive because that's the way they are
5330
 
      actually applied, and showing fewer privileges than are applied
5331
 
      would be wrong from a security point of view.
5332
 
    */
5333
 
 
5334
 
    if (!strcmp(lex_user->user.str,user) &&
5335
 
        !my_strcasecmp(system_charset_info, lex_user->host.str, host))
5336
 
    {
5337
 
      want_access=acl_db->access;
5338
 
      if (want_access)
5339
 
      {
5340
 
        String db(buff,sizeof(buff),system_charset_info);
5341
 
        db.length(0);
5342
 
        db.append(STRING_WITH_LEN("GRANT "));
5343
 
 
5344
 
        if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
5345
 
          db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
5346
 
        else if (!(want_access & ~GRANT_ACL))
5347
 
          db.append(STRING_WITH_LEN("USAGE"));
5348
 
        else
5349
 
        {
5350
 
          int found=0, cnt;
5351
 
          ulong j,test_access= want_access & ~GRANT_ACL;
5352
 
          for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
5353
 
          {
5354
 
            if (test_access & j)
5355
 
            {
5356
 
              if (found)
5357
 
                db.append(STRING_WITH_LEN(", "));
5358
 
              found = 1;
5359
 
              db.append(command_array[cnt],command_lengths[cnt]);
5360
 
            }
5361
 
          }
5362
 
        }
5363
 
        db.append (STRING_WITH_LEN(" ON "));
5364
 
        append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
5365
 
        db.append (STRING_WITH_LEN(".* TO '"));
5366
 
        db.append(lex_user->user.str, lex_user->user.length,
5367
 
                  system_charset_info);
5368
 
        db.append (STRING_WITH_LEN("'@'"));
5369
 
        // host and lex_user->host are equal except for case
5370
 
        db.append(host, strlen(host), system_charset_info);
5371
 
        db.append ('\'');
5372
 
        if (want_access & GRANT_ACL)
5373
 
          db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
5374
 
        protocol->prepare_for_resend();
5375
 
        protocol->store(db.ptr(),db.length(),db.charset());
5376
 
        if (protocol->write())
5377
 
        {
5378
 
          error= -1;
5379
 
          goto end;
5380
 
        }
5381
 
      }
5382
 
    }
5383
 
  }
5384
 
 
5385
 
  /* Add table & column access */
5386
 
  for (index=0 ; index < column_priv_hash.records ; index++)
5387
 
  {
5388
 
    const char *user, *host;
5389
 
    GRANT_TABLE *grant_table= (GRANT_TABLE*)
5390
 
      my_hash_element(&column_priv_hash, index);
5391
 
 
5392
 
    if (!(user=grant_table->user))
5393
 
      user= "";
5394
 
    if (!(host= grant_table->host.hostname))
5395
 
      host= "";
5396
 
 
5397
 
    /*
5398
 
      We do not make SHOW GRANTS case-sensitive here (like REVOKE),
5399
 
      but make it case-insensitive because that's the way they are
5400
 
      actually applied, and showing fewer privileges than are applied
5401
 
      would be wrong from a security point of view.
5402
 
    */
5403
 
 
5404
 
    if (!strcmp(lex_user->user.str,user) &&
5405
 
        !my_strcasecmp(system_charset_info, lex_user->host.str, host))
5406
 
    {
5407
 
      ulong table_access= grant_table->privs;
5408
 
      if ((table_access | grant_table->cols) != 0)
5409
 
      {
5410
 
        String global(buff, sizeof(buff), system_charset_info);
5411
 
        ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;
5412
 
 
5413
 
        global.length(0);
5414
 
        global.append(STRING_WITH_LEN("GRANT "));
5415
 
 
5416
 
        if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
5417
 
          global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
5418
 
        else if (!test_access)
5419
 
          global.append(STRING_WITH_LEN("USAGE"));
5420
 
        else
5421
 
        {
5422
 
          /* Add specific column access */
5423
 
          int found= 0;
5424
 
          ulong j;
5425
 
 
5426
 
          for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
5427
 
          {
5428
 
            if (test_access & j)
5429
 
            {
5430
 
              if (found)
5431
 
                global.append(STRING_WITH_LEN(", "));
5432
 
              found= 1;
5433
 
              global.append(command_array[counter],command_lengths[counter]);
5434
 
 
5435
 
              if (grant_table->cols)
5436
 
              {
5437
 
                uint found_col= 0;
5438
 
                for (uint col_index=0 ;
5439
 
                     col_index < grant_table->hash_columns.records ;
5440
 
                     col_index++)
5441
 
                {
5442
 
                  GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
5443
 
                    my_hash_element(&grant_table->hash_columns,col_index);
5444
 
                  if (grant_column->rights & j)
5445
 
                  {
5446
 
                    if (!found_col)
5447
 
                    {
5448
 
                      found_col= 1;
5449
 
                      /*
5450
 
                        If we have a duplicated table level privilege, we
5451
 
                        must write the access privilege name again.
5452
 
                      */
5453
 
                      if (table_access & j)
5454
 
                      {
5455
 
                        global.append(STRING_WITH_LEN(", "));
5456
 
                        global.append(command_array[counter],
5457
 
                                      command_lengths[counter]);
5458
 
                      }
5459
 
                      global.append(STRING_WITH_LEN(" ("));
5460
 
                    }
5461
 
                    else
5462
 
                      global.append(STRING_WITH_LEN(", "));
5463
 
                    global.append(grant_column->column,
5464
 
                                  grant_column->key_length,
5465
 
                                  system_charset_info);
5466
 
                  }
5467
 
                }
5468
 
                if (found_col)
5469
 
                  global.append(')');
5470
 
              }
5471
 
            }
5472
 
          }
5473
 
        }
5474
 
        global.append(STRING_WITH_LEN(" ON "));
5475
 
        append_identifier(thd, &global, grant_table->db,
5476
 
                          strlen(grant_table->db));
5477
 
        global.append('.');
5478
 
        append_identifier(thd, &global, grant_table->tname,
5479
 
                          strlen(grant_table->tname));
5480
 
        global.append(STRING_WITH_LEN(" TO '"));
5481
 
        global.append(lex_user->user.str, lex_user->user.length,
5482
 
                      system_charset_info);
5483
 
        global.append(STRING_WITH_LEN("'@'"));
5484
 
        // host and lex_user->host are equal except for case
5485
 
        global.append(host, strlen(host), system_charset_info);
5486
 
        global.append('\'');
5487
 
        if (table_access & GRANT_ACL)
5488
 
          global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
5489
 
        protocol->prepare_for_resend();
5490
 
        protocol->store(global.ptr(),global.length(),global.charset());
5491
 
        if (protocol->write())
5492
 
        {
5493
 
          error= -1;
5494
 
          break;
5495
 
        }
5496
 
      }
5497
 
    }
5498
 
  }
5499
 
 
5500
 
  if (show_routine_grants(thd, lex_user, &proc_priv_hash, 
5501
 
                          STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
5502
 
  {
5503
 
    error= -1;
5504
 
    goto end;
5505
 
  }
5506
 
 
5507
 
  if (show_routine_grants(thd, lex_user, &func_priv_hash,
5508
 
                          STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
5509
 
  {
5510
 
    error= -1;
5511
 
    goto end;
5512
 
  }
5513
 
 
5514
 
  if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
5515
 
  {
5516
 
    error= -1;
5517
 
    goto end;
5518
 
  }
5519
 
 
5520
 
end:
5521
 
  mysql_mutex_unlock(&acl_cache->lock);
5522
 
  mysql_rwlock_unlock(&LOCK_grant);
5523
 
 
5524
 
  my_eof(thd);
5525
 
  DBUG_RETURN(error);
5526
 
}
5527
 
 
5528
 
static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
5529
 
                               const char *type, int typelen,
5530
 
                               char *buff, int buffsize)
5531
 
{
5532
 
  uint counter, index;
5533
 
  int error= 0;
5534
 
  Protocol *protocol= thd->protocol;
5535
 
  /* Add routine access */
5536
 
  for (index=0 ; index < hash->records ; index++)
5537
 
  {
5538
 
    const char *user, *host;
5539
 
    GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
5540
 
 
5541
 
    if (!(user=grant_proc->user))
5542
 
      user= "";
5543
 
    if (!(host= grant_proc->host.hostname))
5544
 
      host= "";
5545
 
 
5546
 
    /*
5547
 
      We do not make SHOW GRANTS case-sensitive here (like REVOKE),
5548
 
      but make it case-insensitive because that's the way they are
5549
 
      actually applied, and showing fewer privileges than are applied
5550
 
      would be wrong from a security point of view.
5551
 
    */
5552
 
 
5553
 
    if (!strcmp(lex_user->user.str,user) &&
5554
 
        !my_strcasecmp(system_charset_info, lex_user->host.str, host))
5555
 
    {
5556
 
      ulong proc_access= grant_proc->privs;
5557
 
      if (proc_access != 0)
5558
 
      {
5559
 
        String global(buff, buffsize, system_charset_info);
5560
 
        ulong test_access= proc_access & ~GRANT_ACL;
5561
 
 
5562
 
        global.length(0);
5563
 
        global.append(STRING_WITH_LEN("GRANT "));
5564
 
 
5565
 
        if (!test_access)
5566
 
          global.append(STRING_WITH_LEN("USAGE"));
5567
 
        else
5568
 
        {
5569
 
          /* Add specific procedure access */
5570
 
          int found= 0;
5571
 
          ulong j;
5572
 
 
5573
 
          for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
5574
 
          {
5575
 
            if (test_access & j)
5576
 
            {
5577
 
              if (found)
5578
 
                global.append(STRING_WITH_LEN(", "));
5579
 
              found= 1;
5580
 
              global.append(command_array[counter],command_lengths[counter]);
5581
 
            }
5582
 
          }
5583
 
        }
5584
 
        global.append(STRING_WITH_LEN(" ON "));
5585
 
        global.append(type,typelen);
5586
 
        global.append(' ');
5587
 
        append_identifier(thd, &global, grant_proc->db,
5588
 
                          strlen(grant_proc->db));
5589
 
        global.append('.');
5590
 
        append_identifier(thd, &global, grant_proc->tname,
5591
 
                          strlen(grant_proc->tname));
5592
 
        global.append(STRING_WITH_LEN(" TO '"));
5593
 
        global.append(lex_user->user.str, lex_user->user.length,
5594
 
                      system_charset_info);
5595
 
        global.append(STRING_WITH_LEN("'@'"));
5596
 
        // host and lex_user->host are equal except for case
5597
 
        global.append(host, strlen(host), system_charset_info);
5598
 
        global.append('\'');
5599
 
        if (proc_access & GRANT_ACL)
5600
 
          global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
5601
 
        protocol->prepare_for_resend();
5602
 
        protocol->store(global.ptr(),global.length(),global.charset());
5603
 
        if (protocol->write())
5604
 
        {
5605
 
          error= -1;
5606
 
          break;
5607
 
        }
5608
 
      }
5609
 
    }
5610
 
  }
5611
 
  return error;
5612
 
}
5613
 
 
5614
 
/*
5615
 
  Make a clear-text version of the requested privilege.
5616
 
*/
5617
 
 
5618
 
void get_privilege_desc(char *to, uint max_length, ulong access)
5619
 
{
5620
 
  uint pos;
5621
 
  char *start=to;
5622
 
  DBUG_ASSERT(max_length >= 30);                // For end ',' removal
5623
 
 
5624
 
  if (access)
5625
 
  {
5626
 
    max_length--;                               // Reserve place for end-zero
5627
 
    for (pos=0 ; access ; pos++, access>>=1)
5628
 
    {
5629
 
      if ((access & 1) &&
5630
 
          command_lengths[pos] + (uint) (to-start) < max_length)
5631
 
      {
5632
 
        to= strmov(to, command_array[pos]);
5633
 
        *to++=',';
5634
 
      }
5635
 
    }
5636
 
    to--;                                       // Remove end ','
5637
 
  }
5638
 
  *to=0;
5639
 
}
5640
 
 
5641
 
 
5642
 
void get_mqh(const char *user, const char *host, USER_CONN *uc)
5643
 
{
5644
 
  ACL_USER *acl_user;
5645
 
 
5646
 
  mysql_mutex_lock(&acl_cache->lock);
5647
 
 
5648
 
  if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
5649
 
    uc->user_resources= acl_user->user_resource;
5650
 
  else
5651
 
    bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
5652
 
 
5653
 
  mysql_mutex_unlock(&acl_cache->lock);
5654
 
}
5655
 
 
5656
 
/*
5657
 
  Open the grant tables.
5658
 
 
5659
 
  SYNOPSIS
5660
 
    open_grant_tables()
5661
 
    thd                         The current thread.
5662
 
    tables (out)                The 4 elements array for the opened tables.
5663
 
 
5664
 
  DESCRIPTION
5665
 
    Tables are numbered as follows:
5666
 
    0 user
5667
 
    1 db
5668
 
    2 tables_priv
5669
 
    3 columns_priv
5670
 
 
5671
 
  RETURN
5672
 
    1           Skip GRANT handling during replication.
5673
 
    0           OK.
5674
 
    < 0         Error.
5675
 
*/
5676
 
 
5677
 
#define GRANT_TABLES 6
5678
 
int open_grant_tables(THD *thd, TABLE_LIST *tables)
5679
 
{
5680
 
  DBUG_ENTER("open_grant_tables");
5681
 
 
5682
 
  if (!initialized)
5683
 
  {
5684
 
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
5685
 
    DBUG_RETURN(-1);
5686
 
  }
5687
 
 
5688
 
  tables->init_one_table(C_STRING_WITH_LEN("mysql"),
5689
 
                         C_STRING_WITH_LEN("user"), "user", TL_WRITE);
5690
 
  (tables+1)->init_one_table(C_STRING_WITH_LEN("mysql"),
5691
 
                             C_STRING_WITH_LEN("db"), "db", TL_WRITE);
5692
 
  (tables+2)->init_one_table(C_STRING_WITH_LEN("mysql"),
5693
 
                             C_STRING_WITH_LEN("tables_priv"),
5694
 
                             "tables_priv", TL_WRITE);
5695
 
  (tables+3)->init_one_table(C_STRING_WITH_LEN("mysql"),
5696
 
                             C_STRING_WITH_LEN("columns_priv"),
5697
 
                             "columns_priv", TL_WRITE);
5698
 
  (tables+4)->init_one_table(C_STRING_WITH_LEN("mysql"),
5699
 
                             C_STRING_WITH_LEN("procs_priv"),
5700
 
                             "procs_priv", TL_WRITE);
5701
 
  (tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"),
5702
 
                             C_STRING_WITH_LEN("proxies_priv"),
5703
 
                             "proxies_priv", TL_WRITE);
5704
 
  tables[5].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
5705
 
 
5706
 
  tables->next_local= tables->next_global= tables + 1;
5707
 
  (tables+1)->next_local= (tables+1)->next_global= tables + 2;
5708
 
  (tables+2)->next_local= (tables+2)->next_global= tables + 3;
5709
 
  (tables+3)->next_local= (tables+3)->next_global= tables + 4;
5710
 
  (tables+4)->next_local= (tables+4)->next_global= tables + 5;
5711
 
 
5712
 
#ifdef HAVE_REPLICATION
5713
 
  /*
5714
 
    GRANT and REVOKE are applied the slave in/exclusion rules as they are
5715
 
    some kind of updates to the mysql.% tables.
5716
 
  */
5717
 
  if (thd->slave_thread && rpl_filter->is_on())
5718
 
  {
5719
 
    /*
5720
 
      The tables must be marked "updating" so that tables_ok() takes them into
5721
 
      account in tests.
5722
 
    */
5723
 
    tables[0].updating= tables[1].updating= tables[2].updating=
5724
 
      tables[3].updating= tables[4].updating= tables[5].updating= 1;
5725
 
    if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
5726
 
      DBUG_RETURN(1);
5727
 
    tables[0].updating= tables[1].updating= tables[2].updating=
5728
 
      tables[3].updating= tables[4].updating= tables[5].updating= 0;
5729
 
  }
5730
 
#endif
5731
 
 
5732
 
  if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
5733
 
  {                                             // This should never happen
5734
 
    DBUG_RETURN(-1);
5735
 
  }
5736
 
 
5737
 
  DBUG_RETURN(0);
5738
 
}
5739
 
 
5740
 
ACL_USER *check_acl_user(LEX_USER *user_name,
5741
 
                         uint *acl_acl_userdx)
5742
 
{
5743
 
  ACL_USER *acl_user= 0;
5744
 
  uint counter;
5745
 
 
5746
 
  mysql_mutex_assert_owner(&acl_cache->lock);
5747
 
 
5748
 
  for (counter= 0 ; counter < acl_users.elements ; counter++)
5749
 
  {
5750
 
    const char *user,*host;
5751
 
    acl_user= dynamic_element(&acl_users, counter, ACL_USER*);
5752
 
    if (!(user=acl_user->user))
5753
 
      user= "";
5754
 
    if (!(host=acl_user->host.hostname))
5755
 
      host= "";
5756
 
    if (!strcmp(user_name->user.str,user) &&
5757
 
        !my_strcasecmp(system_charset_info, user_name->host.str, host))
5758
 
      break;
5759
 
  }
5760
 
  if (counter == acl_users.elements)
5761
 
    return 0;
5762
 
 
5763
 
  *acl_acl_userdx= counter;
5764
 
  return acl_user;
5765
 
}
5766
 
 
5767
 
/*
5768
 
  Modify a privilege table.
5769
 
 
5770
 
  SYNOPSIS
5771
 
    modify_grant_table()
5772
 
    table                       The table to modify.
5773
 
    host_field                  The host name field.
5774
 
    user_field                  The user name field.
5775
 
    user_to                     The new name for the user if to be renamed,
5776
 
                                NULL otherwise.
5777
 
 
5778
 
  DESCRIPTION
5779
 
  Update user/host in the current record if user_to is not NULL.
5780
 
  Delete the current record if user_to is NULL.
5781
 
 
5782
 
  RETURN
5783
 
    0           OK.
5784
 
    != 0        Error.
5785
 
*/
5786
 
 
5787
 
static int modify_grant_table(TABLE *table, Field *host_field,
5788
 
                              Field *user_field, LEX_USER *user_to)
5789
 
{
5790
 
  int error;
5791
 
  DBUG_ENTER("modify_grant_table");
5792
 
 
5793
 
  if (user_to)
5794
 
  {
5795
 
    /* rename */
5796
 
    store_record(table, record[1]);
5797
 
    host_field->store(user_to->host.str, user_to->host.length,
5798
 
                      system_charset_info);
5799
 
    user_field->store(user_to->user.str, user_to->user.length,
5800
 
                      system_charset_info);
5801
 
    if ((error= table->file->ha_update_row(table->record[1], 
5802
 
                                           table->record[0])) &&
5803
 
        error != HA_ERR_RECORD_IS_THE_SAME)
5804
 
      table->file->print_error(error, MYF(0));
5805
 
    else
5806
 
      error= 0;
5807
 
  }
5808
 
  else
5809
 
  {
5810
 
    /* delete */
5811
 
    if ((error=table->file->ha_delete_row(table->record[0])))
5812
 
      table->file->print_error(error, MYF(0));
5813
 
  }
5814
 
 
5815
 
  DBUG_RETURN(error);
5816
 
}
5817
 
 
5818
 
/*
5819
 
  Handle a privilege table.
5820
 
 
5821
 
  SYNOPSIS
5822
 
    handle_grant_table()
5823
 
    tables                      The array with the four open tables.
5824
 
    table_no                    The number of the table to handle (0..4).
5825
 
    drop                        If user_from is to be dropped.
5826
 
    user_from                   The the user to be searched/dropped/renamed.
5827
 
    user_to                     The new name for the user if to be renamed,
5828
 
                                NULL otherwise.
5829
 
 
5830
 
  DESCRIPTION
5831
 
    Scan through all records in a grant table and apply the requested
5832
 
    operation. For the "user" table, a single index access is sufficient,
5833
 
    since there is an unique index on (host, user).
5834
 
    Delete from grant table if drop is true.
5835
 
    Update in grant table if drop is false and user_to is not NULL.
5836
 
    Search in grant table if drop is false and user_to is NULL.
5837
 
    Tables are numbered as follows:
5838
 
    0 user
5839
 
    1 db
5840
 
    2 tables_priv
5841
 
    3 columns_priv
5842
 
    4 procs_priv
5843
 
 
5844
 
  RETURN
5845
 
    > 0         At least one record matched.
5846
 
    0           OK, but no record matched.
5847
 
    < 0         Error.
5848
 
*/
5849
 
 
5850
 
static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
5851
 
                              LEX_USER *user_from, LEX_USER *user_to)
5852
 
{
5853
 
  int result= 0;
5854
 
  int error;
5855
 
  TABLE *table= tables[table_no].table;
5856
 
  Field *host_field= table->field[0];
5857
 
  Field *user_field= table->field[table_no && table_no != 5 ? 2 : 1];
5858
 
  char *host_str= user_from->host.str;
5859
 
  char *user_str= user_from->user.str;
5860
 
  const char *host;
5861
 
  const char *user;
5862
 
  uchar user_key[MAX_KEY_LENGTH];
5863
 
  uint key_prefix_length;
5864
 
  DBUG_ENTER("handle_grant_table");
5865
 
  THD *thd= current_thd;
5866
 
 
5867
 
  table->use_all_columns();
5868
 
  if (! table_no) // mysql.user table
5869
 
  {
5870
 
    /*
5871
 
      The 'user' table has an unique index on (host, user).
5872
 
      Thus, we can handle everything with a single index access.
5873
 
      The host- and user fields are consecutive in the user table records.
5874
 
      So we set host- and user fields of table->record[0] and use the
5875
 
      pointer to the host field as key.
5876
 
      index_read_idx() will replace table->record[0] (its first argument)
5877
 
      by the searched record, if it exists.
5878
 
    */
5879
 
    DBUG_PRINT("info",("read table: '%s'  search: '%s'@'%s'",
5880
 
                       table->s->table_name.str, user_str, host_str));
5881
 
    host_field->store(host_str, user_from->host.length, system_charset_info);
5882
 
    user_field->store(user_str, user_from->user.length, system_charset_info);
5883
 
 
5884
 
    key_prefix_length= (table->key_info->key_part[0].store_length +
5885
 
                        table->key_info->key_part[1].store_length);
5886
 
    key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
5887
 
 
5888
 
    if ((error= table->file->index_read_idx_map(table->record[0], 0,
5889
 
                                                user_key, (key_part_map)3,
5890
 
                                                HA_READ_KEY_EXACT)))
5891
 
    {
5892
 
      if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
5893
 
      {
5894
 
        table->file->print_error(error, MYF(0));
5895
 
        result= -1;
5896
 
      }
5897
 
    }
5898
 
    else
5899
 
    {
5900
 
      /* If requested, delete or update the record. */
5901
 
      result= ((drop || user_to) &&
5902
 
               modify_grant_table(table, host_field, user_field, user_to)) ?
5903
 
        -1 : 1; /* Error or found. */
5904
 
    }
5905
 
    DBUG_PRINT("info",("read result: %d", result));
5906
 
  }
5907
 
  else
5908
 
  {
5909
 
    /*
5910
 
      The non-'user' table do not have indexes on (host, user).
5911
 
      And their host- and user fields are not consecutive.
5912
 
      Thus, we need to do a table scan to find all matching records.
5913
 
    */
5914
 
    if ((error= table->file->ha_rnd_init(1)))
5915
 
    {
5916
 
      table->file->print_error(error, MYF(0));
5917
 
      result= -1;
5918
 
    }
5919
 
    else
5920
 
    {
5921
 
#ifdef EXTRA_DEBUG
5922
 
      DBUG_PRINT("info",("scan table: '%s'  search: '%s'@'%s'",
5923
 
                         table->s->table_name.str, user_str, host_str));
5924
 
#endif
5925
 
      while ((error= table->file->rnd_next(table->record[0])) != 
5926
 
             HA_ERR_END_OF_FILE)
5927
 
      {
5928
 
        if (error)
5929
 
        {
5930
 
          /* Most probable 'deleted record'. */
5931
 
          DBUG_PRINT("info",("scan error: %d", error));
5932
 
          continue;
5933
 
        }
5934
 
        if (! (host= get_field(thd->mem_root, host_field)))
5935
 
          host= "";
5936
 
        if (! (user= get_field(thd->mem_root, user_field)))
5937
 
          user= "";
5938
 
 
5939
 
#ifdef EXTRA_DEBUG
5940
 
        if (table_no != 5)
5941
 
        {
5942
 
          DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
5943
 
                             user, host,
5944
 
                             get_field(thd->mem_root, table->field[1]) /*db*/,
5945
 
                             get_field(thd->mem_root, table->field[3]) /*table*/,
5946
 
                             get_field(thd->mem_root,
5947
 
                                       table->field[4]) /*column*/));
5948
 
        }
5949
 
#endif
5950
 
        if (strcmp(user_str, user) ||
5951
 
            my_strcasecmp(system_charset_info, host_str, host))
5952
 
          continue;
5953
 
 
5954
 
        /* If requested, delete or update the record. */
5955
 
        result= ((drop || user_to) &&
5956
 
                 modify_grant_table(table, host_field, user_field, user_to)) ?
5957
 
          -1 : result ? result : 1; /* Error or keep result or found. */
5958
 
        /* If search is requested, we do not need to search further. */
5959
 
        if (! drop && ! user_to)
5960
 
          break ;
5961
 
      }
5962
 
      (void) table->file->ha_rnd_end();
5963
 
      DBUG_PRINT("info",("scan result: %d", result));
5964
 
    }
5965
 
  }
5966
 
 
5967
 
  DBUG_RETURN(result);
5968
 
}
5969
 
 
5970
 
 
5971
 
/**
5972
 
  Handle an in-memory privilege structure.
5973
 
 
5974
 
  @param struct_no  The number of the structure to handle (0..5).
5975
 
  @param drop       If user_from is to be dropped.
5976
 
  @param user_from  The the user to be searched/dropped/renamed.
5977
 
  @param user_to    The new name for the user if to be renamed, NULL otherwise.
5978
 
 
5979
 
  @note
5980
 
    Scan through all elements in an in-memory grant structure and apply
5981
 
    the requested operation.
5982
 
    Delete from grant structure if drop is true.
5983
 
    Update in grant structure if drop is false and user_to is not NULL.
5984
 
    Search in grant structure if drop is false and user_to is NULL.
5985
 
    Structures are numbered as follows:
5986
 
    0 acl_users
5987
 
    1 acl_dbs
5988
 
    2 column_priv_hash
5989
 
    3 proc_priv_hash
5990
 
    4 func_priv_hash
5991
 
    5 acl_proxy_users
5992
 
 
5993
 
  @retval > 0  At least one element matched.
5994
 
  @retval 0    OK, but no element matched.
5995
 
  @retval -1   Wrong arguments to function.
5996
 
*/
5997
 
 
5998
 
static int handle_grant_struct(uint struct_no, bool drop,
5999
 
                               LEX_USER *user_from, LEX_USER *user_to)
6000
 
{
6001
 
  int result= 0;
6002
 
  uint idx;
6003
 
  uint elements;
6004
 
  const char *user;
6005
 
  const char *host;
6006
 
  ACL_USER *acl_user= NULL;
6007
 
  ACL_DB *acl_db= NULL;
6008
 
  ACL_PROXY_USER *acl_proxy_user= NULL;
6009
 
  GRANT_NAME *grant_name= NULL;
6010
 
  HASH *grant_name_hash= NULL;
6011
 
  DBUG_ENTER("handle_grant_struct");
6012
 
  DBUG_PRINT("info",("scan struct: %u  search: '%s'@'%s'",
6013
 
                     struct_no, user_from->user.str, user_from->host.str));
6014
 
 
6015
 
  LINT_INIT(user);
6016
 
  LINT_INIT(host);
6017
 
 
6018
 
  mysql_mutex_assert_owner(&acl_cache->lock);
6019
 
 
6020
 
  /* Get the number of elements in the in-memory structure. */
6021
 
  switch (struct_no) {
6022
 
  case 0:
6023
 
    elements= acl_users.elements;
6024
 
    break;
6025
 
  case 1:
6026
 
    elements= acl_dbs.elements;
6027
 
    break;
6028
 
  case 2:
6029
 
    elements= column_priv_hash.records;
6030
 
    grant_name_hash= &column_priv_hash;
6031
 
    break;
6032
 
  case 3:
6033
 
    elements= proc_priv_hash.records;
6034
 
    grant_name_hash= &proc_priv_hash;
6035
 
    break;
6036
 
  case 4:
6037
 
    elements= func_priv_hash.records;
6038
 
    grant_name_hash= &func_priv_hash;
6039
 
    break;
6040
 
  case 5:
6041
 
    elements= acl_proxy_users.elements;
6042
 
    break;
6043
 
  default:
6044
 
    return -1;
6045
 
  }
6046
 
 
6047
 
#ifdef EXTRA_DEBUG
6048
 
    DBUG_PRINT("loop",("scan struct: %u  search    user: '%s'  host: '%s'",
6049
 
                       struct_no, user_from->user.str, user_from->host.str));
6050
 
#endif
6051
 
  /* Loop over all elements. */
6052
 
  for (idx= 0; idx < elements; idx++)
6053
 
  {
6054
 
    /*
6055
 
      Get a pointer to the element.
6056
 
    */
6057
 
    switch (struct_no) {
6058
 
    case 0:
6059
 
      acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
6060
 
      user= acl_user->user;
6061
 
      host= acl_user->host.hostname;
6062
 
    break;
6063
 
 
6064
 
    case 1:
6065
 
      acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
6066
 
      user= acl_db->user;
6067
 
      host= acl_db->host.hostname;
6068
 
      break;
6069
 
 
6070
 
    case 2:
6071
 
    case 3:
6072
 
    case 4:
6073
 
      grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx);
6074
 
      user= grant_name->user;
6075
 
      host= grant_name->host.hostname;
6076
 
      break;
6077
 
 
6078
 
    case 5:
6079
 
      acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);
6080
 
      user= acl_proxy_user->get_user();
6081
 
      host= acl_proxy_user->get_host();
6082
 
      break;
6083
 
 
6084
 
    default:
6085
 
      MY_ASSERT_UNREACHABLE();
6086
 
    }
6087
 
    if (! user)
6088
 
      user= "";
6089
 
    if (! host)
6090
 
      host= "";
6091
 
 
6092
 
#ifdef EXTRA_DEBUG
6093
 
    DBUG_PRINT("loop",("scan struct: %u  index: %u  user: '%s'  host: '%s'",
6094
 
                       struct_no, idx, user, host));
6095
 
#endif
6096
 
    if (strcmp(user_from->user.str, user) ||
6097
 
        my_strcasecmp(system_charset_info, user_from->host.str, host))
6098
 
      continue;
6099
 
 
6100
 
    result= 1; /* At least one element found. */
6101
 
    if ( drop )
6102
 
    {
6103
 
      switch ( struct_no ) {
6104
 
      case 0:
6105
 
        delete_dynamic_element(&acl_users, idx);
6106
 
        break;
6107
 
 
6108
 
      case 1:
6109
 
        delete_dynamic_element(&acl_dbs, idx);
6110
 
        break;
6111
 
 
6112
 
      case 2:
6113
 
      case 3:
6114
 
      case 4:
6115
 
        my_hash_delete(grant_name_hash, (uchar*) grant_name);
6116
 
        break;
6117
 
 
6118
 
      case 5:
6119
 
        delete_dynamic_element(&acl_proxy_users, idx);
6120
 
        break;
6121
 
 
6122
 
      }
6123
 
      elements--;
6124
 
      /*
6125
 
        - If we are iterating through an array then we just have moved all
6126
 
          elements after the current element one position closer to its head.
6127
 
          This means that we have to take another look at the element at
6128
 
          current position as it is a new element from the array's tail.
6129
 
        - If we are iterating through a hash the current element was replaced
6130
 
          with one of elements from the tail. So we also have to take a look
6131
 
          at the new element in current position.
6132
 
          Note that in our HASH implementation hash_delete() won't move any
6133
 
          elements with position after current one to position before the
6134
 
          current (i.e. from the tail to the head), so it is safe to continue
6135
 
          iteration without re-starting.
6136
 
      */
6137
 
      idx--;
6138
 
    }
6139
 
    else if ( user_to )
6140
 
    {
6141
 
      switch ( struct_no ) {
6142
 
      case 0:
6143
 
        acl_user->user= strdup_root(&mem, user_to->user.str);
6144
 
        acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
6145
 
        break;
6146
 
 
6147
 
      case 1:
6148
 
        acl_db->user= strdup_root(&mem, user_to->user.str);
6149
 
        acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
6150
 
        break;
6151
 
 
6152
 
      case 2:
6153
 
      case 3:
6154
 
      case 4:
6155
 
        {
6156
 
          /*
6157
 
            Save old hash key and its length to be able properly update
6158
 
            element position in hash.
6159
 
          */
6160
 
          char *old_key= grant_name->hash_key;
6161
 
          size_t old_key_length= grant_name->key_length;
6162
 
 
6163
 
          /*
6164
 
            Update the grant structure with the new user name and host name.
6165
 
          */
6166
 
          grant_name->set_user_details(user_to->host.str, grant_name->db,
6167
 
                                       user_to->user.str, grant_name->tname,
6168
 
                                       TRUE);
6169
 
 
6170
 
          /*
6171
 
            Since username is part of the hash key, when the user name
6172
 
            is renamed, the hash key is changed. Update the hash to
6173
 
            ensure that the position matches the new hash key value
6174
 
          */
6175
 
          my_hash_update(grant_name_hash, (uchar*) grant_name, (uchar*) old_key,
6176
 
                         old_key_length);
6177
 
          /*
6178
 
            hash_update() operation could have moved element from the tail
6179
 
            of the hash to the current position. So we need to take a look
6180
 
            at the element in current position once again.
6181
 
            Thanks to the fact that hash_update() for our HASH implementation
6182
 
            won't move any elements from the tail of the hash to the positions
6183
 
            before the current one (a.k.a. head) it is safe to continue
6184
 
            iteration without restarting.
6185
 
          */
6186
 
          idx--;
6187
 
          break;
6188
 
        }
6189
 
 
6190
 
      case 5:
6191
 
        acl_proxy_user->set_user (&mem, user_to->user.str);
6192
 
        acl_proxy_user->set_host (&mem, user_to->host.str);
6193
 
        break;
6194
 
 
6195
 
      }
6196
 
    }
6197
 
    else
6198
 
    {
6199
 
      /* If search is requested, we do not need to search further. */
6200
 
      break;
6201
 
    }
6202
 
  }
6203
 
#ifdef EXTRA_DEBUG
6204
 
  DBUG_PRINT("loop",("scan struct: %u  result %d", struct_no, result));
6205
 
#endif
6206
 
 
6207
 
  DBUG_RETURN(result);
6208
 
}
6209
 
 
6210
 
 
6211
 
/*
6212
 
  Handle all privilege tables and in-memory privilege structures.
6213
 
 
6214
 
  SYNOPSIS
6215
 
    handle_grant_data()
6216
 
    tables                      The array with the four open tables.
6217
 
    drop                        If user_from is to be dropped.
6218
 
    user_from                   The the user to be searched/dropped/renamed.
6219
 
    user_to                     The new name for the user if to be renamed,
6220
 
                                NULL otherwise.
6221
 
 
6222
 
  DESCRIPTION
6223
 
    Go through all grant tables and in-memory grant structures and apply
6224
 
    the requested operation.
6225
 
    Delete from grant data if drop is true.
6226
 
    Update in grant data if drop is false and user_to is not NULL.
6227
 
    Search in grant data if drop is false and user_to is NULL.
6228
 
 
6229
 
  RETURN
6230
 
    > 0         At least one element matched.
6231
 
    0           OK, but no element matched.
6232
 
    < 0         Error.
6233
 
*/
6234
 
 
6235
 
static int handle_grant_data(TABLE_LIST *tables, bool drop,
6236
 
                             LEX_USER *user_from, LEX_USER *user_to)
6237
 
{
6238
 
  int result= 0;
6239
 
  int found;
6240
 
  DBUG_ENTER("handle_grant_data");
6241
 
 
6242
 
  /* Handle user table. */
6243
 
  if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
6244
 
  {
6245
 
    /* Handle of table failed, don't touch the in-memory array. */
6246
 
    result= -1;
6247
 
  }
6248
 
  else
6249
 
  {
6250
 
    /* Handle user array. */
6251
 
    if ((handle_grant_struct(0, drop, user_from, user_to) && ! result) ||
6252
 
        found)
6253
 
    {
6254
 
      result= 1; /* At least one record/element found. */
6255
 
      /* If search is requested, we do not need to search further. */
6256
 
      if (! drop && ! user_to)
6257
 
        goto end;
6258
 
    }
6259
 
  }
6260
 
 
6261
 
  /* Handle db table. */
6262
 
  if ((found= handle_grant_table(tables, 1, drop, user_from, user_to)) < 0)
6263
 
  {
6264
 
    /* Handle of table failed, don't touch the in-memory array. */
6265
 
    result= -1;
6266
 
  }
6267
 
  else
6268
 
  {
6269
 
    /* Handle db array. */
6270
 
    if (((handle_grant_struct(1, drop, user_from, user_to) && ! result) ||
6271
 
         found) && ! result)
6272
 
    {
6273
 
      result= 1; /* At least one record/element found. */
6274
 
      /* If search is requested, we do not need to search further. */
6275
 
      if (! drop && ! user_to)
6276
 
        goto end;
6277
 
    }
6278
 
  }
6279
 
 
6280
 
  /* Handle stored routines table. */
6281
 
  if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0)
6282
 
  {
6283
 
    /* Handle of table failed, don't touch in-memory array. */
6284
 
    result= -1;
6285
 
  }
6286
 
  else
6287
 
  {
6288
 
    /* Handle procs array. */
6289
 
    if (((handle_grant_struct(3, drop, user_from, user_to) && ! result) ||
6290
 
         found) && ! result)
6291
 
    {
6292
 
      result= 1; /* At least one record/element found. */
6293
 
      /* If search is requested, we do not need to search further. */
6294
 
      if (! drop && ! user_to)
6295
 
        goto end;
6296
 
    }
6297
 
    /* Handle funcs array. */
6298
 
    if (((handle_grant_struct(4, drop, user_from, user_to) && ! result) ||
6299
 
         found) && ! result)
6300
 
    {
6301
 
      result= 1; /* At least one record/element found. */
6302
 
      /* If search is requested, we do not need to search further. */
6303
 
      if (! drop && ! user_to)
6304
 
        goto end;
6305
 
    }
6306
 
  }
6307
 
 
6308
 
  /* Handle tables table. */
6309
 
  if ((found= handle_grant_table(tables, 2, drop, user_from, user_to)) < 0)
6310
 
  {
6311
 
    /* Handle of table failed, don't touch columns and in-memory array. */
6312
 
    result= -1;
6313
 
  }
6314
 
  else
6315
 
  {
6316
 
    if (found && ! result)
6317
 
    {
6318
 
      result= 1; /* At least one record found. */
6319
 
      /* If search is requested, we do not need to search further. */
6320
 
      if (! drop && ! user_to)
6321
 
        goto end;
6322
 
    }
6323
 
 
6324
 
    /* Handle columns table. */
6325
 
    if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
6326
 
    {
6327
 
      /* Handle of table failed, don't touch the in-memory array. */
6328
 
      result= -1;
6329
 
    }
6330
 
    else
6331
 
    {
6332
 
      /* Handle columns hash. */
6333
 
      if (((handle_grant_struct(2, drop, user_from, user_to) && ! result) ||
6334
 
           found) && ! result)
6335
 
        result= 1; /* At least one record/element found. */
6336
 
    }
6337
 
  }
6338
 
 
6339
 
  /* Handle proxies_priv table. */
6340
 
  if (tables[5].table)
6341
 
  {
6342
 
    if ((found= handle_grant_table(tables, 5, drop, user_from, user_to)) < 0)
6343
 
    {
6344
 
      /* Handle of table failed, don't touch the in-memory array. */
6345
 
      result= -1;
6346
 
    }
6347
 
    else
6348
 
    {
6349
 
      /* Handle proxies_priv array. */
6350
 
      if ((handle_grant_struct(5, drop, user_from, user_to) && !result) ||
6351
 
          found)
6352
 
        result= 1; /* At least one record/element found. */
6353
 
    }
6354
 
  }
6355
 
 end:
6356
 
  DBUG_RETURN(result);
6357
 
}
6358
 
 
6359
 
 
6360
 
static void append_user(String *str, LEX_USER *user)
6361
 
{
6362
 
  if (str->length())
6363
 
    str->append(',');
6364
 
  str->append('\'');
6365
 
  str->append(user->user.str);
6366
 
  str->append(STRING_WITH_LEN("'@'"));
6367
 
  str->append(user->host.str);
6368
 
  str->append('\'');
6369
 
}
6370
 
 
6371
 
 
6372
 
/*
6373
 
  Create a list of users.
6374
 
 
6375
 
  SYNOPSIS
6376
 
    mysql_create_user()
6377
 
    thd                         The current thread.
6378
 
    list                        The users to create.
6379
 
 
6380
 
  RETURN
6381
 
    FALSE       OK.
6382
 
    TRUE        Error.
6383
 
*/
6384
 
 
6385
 
bool mysql_create_user(THD *thd, List <LEX_USER> &list)
6386
 
{
6387
 
  int result;
6388
 
  String wrong_users;
6389
 
  LEX_USER *user_name, *tmp_user_name;
6390
 
  List_iterator <LEX_USER> user_list(list);
6391
 
  TABLE_LIST tables[GRANT_TABLES];
6392
 
  bool some_users_created= FALSE;
6393
 
  bool save_binlog_row_based;
6394
 
  DBUG_ENTER("mysql_create_user");
6395
 
 
6396
 
  /*
6397
 
    This statement will be replicated as a statement, even when using
6398
 
    row-based replication.  The flag will be reset at the end of the
6399
 
    statement.
6400
 
  */
6401
 
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
6402
 
    thd->clear_current_stmt_binlog_format_row();
6403
 
 
6404
 
  /* CREATE USER may be skipped on replication client. */
6405
 
  if ((result= open_grant_tables(thd, tables)))
6406
 
  {
6407
 
    /* Restore the state of binlog format */
6408
 
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6409
 
    if (save_binlog_row_based)
6410
 
      thd->set_current_stmt_binlog_format_row();
6411
 
    DBUG_RETURN(result != 1);
6412
 
  }
6413
 
 
6414
 
  mysql_rwlock_wrlock(&LOCK_grant);
6415
 
  mysql_mutex_lock(&acl_cache->lock);
6416
 
 
6417
 
  while ((tmp_user_name= user_list++))
6418
 
  {
6419
 
    if (!(user_name= get_current_user(thd, tmp_user_name)))
6420
 
    {
6421
 
      result= TRUE;
6422
 
      continue;
6423
 
    }
6424
 
 
6425
 
    /*
6426
 
      Search all in-memory structures and grant tables
6427
 
      for a mention of the new user name.
6428
 
    */
6429
 
    if (handle_grant_data(tables, 0, user_name, NULL))
6430
 
    {
6431
 
      append_user(&wrong_users, user_name);
6432
 
      result= TRUE;
6433
 
      continue;
6434
 
    }
6435
 
 
6436
 
    some_users_created= TRUE;
6437
 
    if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
6438
 
    {
6439
 
      append_user(&wrong_users, user_name);
6440
 
      result= TRUE;
6441
 
    }
6442
 
  }
6443
 
 
6444
 
  mysql_mutex_unlock(&acl_cache->lock);
6445
 
 
6446
 
  if (result)
6447
 
    my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
6448
 
 
6449
 
  if (some_users_created)
6450
 
    result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
6451
 
 
6452
 
  mysql_rwlock_unlock(&LOCK_grant);
6453
 
  /* Restore the state of binlog format */
6454
 
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6455
 
  if (save_binlog_row_based)
6456
 
    thd->set_current_stmt_binlog_format_row();
6457
 
  DBUG_RETURN(result);
6458
 
}
6459
 
 
6460
 
 
6461
 
/*
6462
 
  Drop a list of users and all their privileges.
6463
 
 
6464
 
  SYNOPSIS
6465
 
    mysql_drop_user()
6466
 
    thd                         The current thread.
6467
 
    list                        The users to drop.
6468
 
 
6469
 
  RETURN
6470
 
    FALSE       OK.
6471
 
    TRUE        Error.
6472
 
*/
6473
 
 
6474
 
bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
6475
 
{
6476
 
  int result;
6477
 
  String wrong_users;
6478
 
  LEX_USER *user_name, *tmp_user_name;
6479
 
  List_iterator <LEX_USER> user_list(list);
6480
 
  TABLE_LIST tables[GRANT_TABLES];
6481
 
  bool some_users_deleted= FALSE;
6482
 
  ulong old_sql_mode= thd->variables.sql_mode;
6483
 
  bool save_binlog_row_based;
6484
 
  DBUG_ENTER("mysql_drop_user");
6485
 
 
6486
 
  /*
6487
 
    This statement will be replicated as a statement, even when using
6488
 
    row-based replication.  The flag will be reset at the end of the
6489
 
    statement.
6490
 
  */
6491
 
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
6492
 
    thd->clear_current_stmt_binlog_format_row();
6493
 
 
6494
 
  /* DROP USER may be skipped on replication client. */
6495
 
  if ((result= open_grant_tables(thd, tables)))
6496
 
  {
6497
 
    /* Restore the state of binlog format */
6498
 
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6499
 
    if (save_binlog_row_based)
6500
 
      thd->set_current_stmt_binlog_format_row();
6501
 
    DBUG_RETURN(result != 1);
6502
 
  }
6503
 
 
6504
 
  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
6505
 
 
6506
 
  mysql_rwlock_wrlock(&LOCK_grant);
6507
 
  mysql_mutex_lock(&acl_cache->lock);
6508
 
 
6509
 
  while ((tmp_user_name= user_list++))
6510
 
  {
6511
 
    if (!(user_name= get_current_user(thd, tmp_user_name)))
6512
 
    {
6513
 
      result= TRUE;
6514
 
      continue;
6515
 
    }  
6516
 
    if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
6517
 
    {
6518
 
      append_user(&wrong_users, user_name);
6519
 
      result= TRUE;
6520
 
      continue;
6521
 
    }
6522
 
    some_users_deleted= TRUE;
6523
 
  }
6524
 
 
6525
 
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
6526
 
  rebuild_check_host();
6527
 
 
6528
 
  mysql_mutex_unlock(&acl_cache->lock);
6529
 
 
6530
 
  if (result)
6531
 
    my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
6532
 
 
6533
 
  if (some_users_deleted)
6534
 
    result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
6535
 
 
6536
 
  mysql_rwlock_unlock(&LOCK_grant);
6537
 
  thd->variables.sql_mode= old_sql_mode;
6538
 
  /* Restore the state of binlog format */
6539
 
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6540
 
  if (save_binlog_row_based)
6541
 
    thd->set_current_stmt_binlog_format_row();
6542
 
  DBUG_RETURN(result);
6543
 
}
6544
 
 
6545
 
 
6546
 
/*
6547
 
  Rename a user.
6548
 
 
6549
 
  SYNOPSIS
6550
 
    mysql_rename_user()
6551
 
    thd                         The current thread.
6552
 
    list                        The user name pairs: (from, to).
6553
 
 
6554
 
  RETURN
6555
 
    FALSE       OK.
6556
 
    TRUE        Error.
6557
 
*/
6558
 
 
6559
 
bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
6560
 
{
6561
 
  int result;
6562
 
  String wrong_users;
6563
 
  LEX_USER *user_from, *tmp_user_from;
6564
 
  LEX_USER *user_to, *tmp_user_to;
6565
 
  List_iterator <LEX_USER> user_list(list);
6566
 
  TABLE_LIST tables[GRANT_TABLES];
6567
 
  bool some_users_renamed= FALSE;
6568
 
  bool save_binlog_row_based;
6569
 
  DBUG_ENTER("mysql_rename_user");
6570
 
 
6571
 
  /*
6572
 
    This statement will be replicated as a statement, even when using
6573
 
    row-based replication.  The flag will be reset at the end of the
6574
 
    statement.
6575
 
  */
6576
 
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
6577
 
    thd->clear_current_stmt_binlog_format_row();
6578
 
 
6579
 
  /* RENAME USER may be skipped on replication client. */
6580
 
  if ((result= open_grant_tables(thd, tables)))
6581
 
  {
6582
 
    /* Restore the state of binlog format */
6583
 
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6584
 
    if (save_binlog_row_based)
6585
 
      thd->set_current_stmt_binlog_format_row();
6586
 
    DBUG_RETURN(result != 1);
6587
 
  }
6588
 
 
6589
 
  mysql_rwlock_wrlock(&LOCK_grant);
6590
 
  mysql_mutex_lock(&acl_cache->lock);
6591
 
 
6592
 
  while ((tmp_user_from= user_list++))
6593
 
  {
6594
 
    if (!(user_from= get_current_user(thd, tmp_user_from)))
6595
 
    {
6596
 
      result= TRUE;
6597
 
      continue;
6598
 
    }  
6599
 
    tmp_user_to= user_list++;
6600
 
    if (!(user_to= get_current_user(thd, tmp_user_to)))
6601
 
    {
6602
 
      result= TRUE;
6603
 
      continue;
6604
 
    }  
6605
 
    DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
6606
 
 
6607
 
    /*
6608
 
      Search all in-memory structures and grant tables
6609
 
      for a mention of the new user name.
6610
 
    */
6611
 
    if (handle_grant_data(tables, 0, user_to, NULL) ||
6612
 
        handle_grant_data(tables, 0, user_from, user_to) <= 0)
6613
 
    {
6614
 
      append_user(&wrong_users, user_from);
6615
 
      result= TRUE;
6616
 
      continue;
6617
 
    }
6618
 
    some_users_renamed= TRUE;
6619
 
  }
6620
 
  
6621
 
  /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
6622
 
  rebuild_check_host();
6623
 
 
6624
 
  mysql_mutex_unlock(&acl_cache->lock);
6625
 
 
6626
 
  if (result)
6627
 
    my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
6628
 
  
6629
 
  if (some_users_renamed && mysql_bin_log.is_open())
6630
 
    result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
6631
 
 
6632
 
  mysql_rwlock_unlock(&LOCK_grant);
6633
 
  /* Restore the state of binlog format */
6634
 
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6635
 
  if (save_binlog_row_based)
6636
 
    thd->set_current_stmt_binlog_format_row();
6637
 
  DBUG_RETURN(result);
6638
 
}
6639
 
 
6640
 
 
6641
 
/*
6642
 
  Revoke all privileges from a list of users.
6643
 
 
6644
 
  SYNOPSIS
6645
 
    mysql_revoke_all()
6646
 
    thd                         The current thread.
6647
 
    list                        The users to revoke all privileges from.
6648
 
 
6649
 
  RETURN
6650
 
    > 0         Error. Error message already sent.
6651
 
    0           OK.
6652
 
    < 0         Error. Error message not yet sent.
6653
 
*/
6654
 
 
6655
 
bool mysql_revoke_all(THD *thd,  List <LEX_USER> &list)
6656
 
{
6657
 
  uint counter, revoked, is_proc;
6658
 
  int result;
6659
 
  ACL_DB *acl_db;
6660
 
  TABLE_LIST tables[GRANT_TABLES];
6661
 
  bool save_binlog_row_based;
6662
 
  DBUG_ENTER("mysql_revoke_all");
6663
 
 
6664
 
  /*
6665
 
    This statement will be replicated as a statement, even when using
6666
 
    row-based replication.  The flag will be reset at the end of the
6667
 
    statement.
6668
 
  */
6669
 
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
6670
 
    thd->clear_current_stmt_binlog_format_row();
6671
 
 
6672
 
  if ((result= open_grant_tables(thd, tables)))
6673
 
  {
6674
 
    /* Restore the state of binlog format */
6675
 
    DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6676
 
    if (save_binlog_row_based)
6677
 
      thd->set_current_stmt_binlog_format_row();
6678
 
    DBUG_RETURN(result != 1);
6679
 
  }
6680
 
 
6681
 
  mysql_rwlock_wrlock(&LOCK_grant);
6682
 
  mysql_mutex_lock(&acl_cache->lock);
6683
 
 
6684
 
  LEX_USER *lex_user, *tmp_lex_user;
6685
 
  List_iterator <LEX_USER> user_list(list);
6686
 
  while ((tmp_lex_user= user_list++))
6687
 
  {
6688
 
    if (!(lex_user= get_current_user(thd, tmp_lex_user)))
6689
 
    {
6690
 
      result= -1;
6691
 
      continue;
6692
 
    }  
6693
 
    if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
6694
 
    {
6695
 
      result= -1;
6696
 
      continue;
6697
 
    }
6698
 
 
6699
 
    if (replace_user_table(thd, tables[0].table,
6700
 
                           *lex_user, ~(ulong)0, 1, 0, 0))
6701
 
    {
6702
 
      result= -1;
6703
 
      continue;
6704
 
    }
6705
 
 
6706
 
    /* Remove db access privileges */
6707
 
    /*
6708
 
      Because acl_dbs and column_priv_hash shrink and may re-order
6709
 
      as privileges are removed, removal occurs in a repeated loop
6710
 
      until no more privileges are revoked.
6711
 
     */
6712
 
    do
6713
 
    {
6714
 
      for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
6715
 
      {
6716
 
        const char *user,*host;
6717
 
 
6718
 
        acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
6719
 
        if (!(user=acl_db->user))
6720
 
          user= "";
6721
 
        if (!(host=acl_db->host.hostname))
6722
 
          host= "";
6723
 
 
6724
 
        if (!strcmp(lex_user->user.str,user) &&
6725
 
            !strcmp(lex_user->host.str, host))
6726
 
        {
6727
 
          if (!replace_db_table(tables[1].table, acl_db->db, *lex_user,
6728
 
                                ~(ulong)0, 1))
6729
 
          {
6730
 
            /*
6731
 
              Don't increment counter as replace_db_table deleted the
6732
 
              current element in acl_dbs.
6733
 
             */
6734
 
            revoked= 1;
6735
 
            continue;
6736
 
          }
6737
 
          result= -1; // Something went wrong
6738
 
        }
6739
 
        counter++;
6740
 
      }
6741
 
    } while (revoked);
6742
 
 
6743
 
    /* Remove column access */
6744
 
    do
6745
 
    {
6746
 
      for (counter= 0, revoked= 0 ; counter < column_priv_hash.records ; )
6747
 
      {
6748
 
        const char *user,*host;
6749
 
        GRANT_TABLE *grant_table=
6750
 
          (GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
6751
 
        if (!(user=grant_table->user))
6752
 
          user= "";
6753
 
        if (!(host=grant_table->host.hostname))
6754
 
          host= "";
6755
 
 
6756
 
        if (!strcmp(lex_user->user.str,user) &&
6757
 
            !strcmp(lex_user->host.str, host))
6758
 
        {
6759
 
          if (replace_table_table(thd,grant_table,tables[2].table,*lex_user,
6760
 
                                  grant_table->db,
6761
 
                                  grant_table->tname,
6762
 
                                  ~(ulong)0, 0, 1))
6763
 
          {
6764
 
            result= -1;
6765
 
          }
6766
 
          else
6767
 
          {
6768
 
            if (!grant_table->cols)
6769
 
            {
6770
 
              revoked= 1;
6771
 
              continue;
6772
 
            }
6773
 
            List<LEX_COLUMN> columns;
6774
 
            if (!replace_column_table(grant_table,tables[3].table, *lex_user,
6775
 
                                      columns,
6776
 
                                      grant_table->db,
6777
 
                                      grant_table->tname,
6778
 
                                      ~(ulong)0, 1))
6779
 
            {
6780
 
              revoked= 1;
6781
 
              continue;
6782
 
            }
6783
 
            result= -1;
6784
 
          }
6785
 
        }
6786
 
        counter++;
6787
 
      }
6788
 
    } while (revoked);
6789
 
 
6790
 
    /* Remove procedure access */
6791
 
    for (is_proc=0; is_proc<2; is_proc++) do {
6792
 
      HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
6793
 
      for (counter= 0, revoked= 0 ; counter < hash->records ; )
6794
 
      {
6795
 
        const char *user,*host;
6796
 
        GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
6797
 
        if (!(user=grant_proc->user))
6798
 
          user= "";
6799
 
        if (!(host=grant_proc->host.hostname))
6800
 
          host= "";
6801
 
 
6802
 
        if (!strcmp(lex_user->user.str,user) &&
6803
 
            !strcmp(lex_user->host.str, host))
6804
 
        {
6805
 
          if (replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
6806
 
                                  grant_proc->db,
6807
 
                                  grant_proc->tname,
6808
 
                                  is_proc,
6809
 
                                  ~(ulong)0, 1) == 0)
6810
 
          {
6811
 
            revoked= 1;
6812
 
            continue;
6813
 
          }
6814
 
          result= -1;   // Something went wrong
6815
 
        }
6816
 
        counter++;
6817
 
      }
6818
 
    } while (revoked);
6819
 
  }
6820
 
 
6821
 
  mysql_mutex_unlock(&acl_cache->lock);
6822
 
 
6823
 
  if (result)
6824
 
    my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
6825
 
 
6826
 
  result= result |
6827
 
    write_bin_log(thd, FALSE, thd->query(), thd->query_length());
6828
 
 
6829
 
  mysql_rwlock_unlock(&LOCK_grant);
6830
 
  /* Restore the state of binlog format */
6831
 
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6832
 
  if (save_binlog_row_based)
6833
 
    thd->set_current_stmt_binlog_format_row();
6834
 
 
6835
 
  DBUG_RETURN(result);
6836
 
}
6837
 
 
6838
 
 
6839
 
 
6840
 
 
6841
 
/**
6842
 
  If the defining user for a routine does not exist, then the ACL lookup
6843
 
  code should raise two errors which we should intercept.  We convert the more
6844
 
  descriptive error into a warning, and consume the other.
6845
 
 
6846
 
  If any other errors are raised, then we set a flag that should indicate
6847
 
  that there was some failure we should complain at a higher level.
6848
 
*/
6849
 
class Silence_routine_definer_errors : public Internal_error_handler
6850
 
{
6851
 
public:
6852
 
  Silence_routine_definer_errors()
6853
 
    : is_grave(FALSE)
6854
 
  {}
6855
 
 
6856
 
  virtual ~Silence_routine_definer_errors()
6857
 
  {}
6858
 
 
6859
 
  virtual bool handle_condition(THD *thd,
6860
 
                                uint sql_errno,
6861
 
                                const char* sqlstate,
6862
 
                                MYSQL_ERROR::enum_warning_level level,
6863
 
                                const char* msg,
6864
 
                                MYSQL_ERROR ** cond_hdl);
6865
 
 
6866
 
  bool has_errors() { return is_grave; }
6867
 
 
6868
 
private:
6869
 
  bool is_grave;
6870
 
};
6871
 
 
6872
 
bool
6873
 
Silence_routine_definer_errors::handle_condition(
6874
 
  THD *thd,
6875
 
  uint sql_errno,
6876
 
  const char*,
6877
 
  MYSQL_ERROR::enum_warning_level level,
6878
 
  const char* msg,
6879
 
  MYSQL_ERROR ** cond_hdl)
6880
 
{
6881
 
  *cond_hdl= NULL;
6882
 
  if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
6883
 
  {
6884
 
    switch (sql_errno)
6885
 
    {
6886
 
      case ER_NONEXISTING_PROC_GRANT:
6887
 
        /* Convert the error into a warning. */
6888
 
        push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
6889
 
                     sql_errno, msg);
6890
 
        return TRUE;
6891
 
      default:
6892
 
        is_grave= TRUE;
6893
 
    }
6894
 
  }
6895
 
 
6896
 
  return FALSE;
6897
 
}
6898
 
 
6899
 
 
6900
 
/**
6901
 
  Revoke privileges for all users on a stored procedure.  Use an error handler
6902
 
  that converts errors about missing grants into warnings.
6903
 
 
6904
 
  @param
6905
 
    thd                         The current thread.
6906
 
  @param
6907
 
    db                          DB of the stored procedure
6908
 
  @param
6909
 
    name                        Name of the stored procedure
6910
 
 
6911
 
  @retval
6912
 
    0           OK.
6913
 
  @retval
6914
 
    < 0         Error. Error message not yet sent.
6915
 
*/
6916
 
 
6917
 
bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
6918
 
                          bool is_proc)
6919
 
{
6920
 
  uint counter, revoked;
6921
 
  int result;
6922
 
  TABLE_LIST tables[GRANT_TABLES];
6923
 
  HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
6924
 
  Silence_routine_definer_errors error_handler;
6925
 
  bool save_binlog_row_based;
6926
 
  DBUG_ENTER("sp_revoke_privileges");
6927
 
 
6928
 
  if ((result= open_grant_tables(thd, tables)))
6929
 
    DBUG_RETURN(result != 1);
6930
 
 
6931
 
  /* Be sure to pop this before exiting this scope! */
6932
 
  thd->push_internal_handler(&error_handler);
6933
 
 
6934
 
  mysql_rwlock_wrlock(&LOCK_grant);
6935
 
  mysql_mutex_lock(&acl_cache->lock);
6936
 
 
6937
 
  /*
6938
 
    This statement will be replicated as a statement, even when using
6939
 
    row-based replication.  The flag will be reset at the end of the
6940
 
    statement.
6941
 
  */
6942
 
  if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
6943
 
    thd->clear_current_stmt_binlog_format_row();
6944
 
 
6945
 
  /* Remove procedure access */
6946
 
  do
6947
 
  {
6948
 
    for (counter= 0, revoked= 0 ; counter < hash->records ; )
6949
 
    {
6950
 
      GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
6951
 
      if (!my_strcasecmp(&my_charset_utf8_bin, grant_proc->db, sp_db) &&
6952
 
          !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
6953
 
      {
6954
 
        LEX_USER lex_user;
6955
 
        lex_user.user.str= grant_proc->user;
6956
 
        lex_user.user.length= strlen(grant_proc->user);
6957
 
        lex_user.host.str= grant_proc->host.hostname ?
6958
 
          grant_proc->host.hostname : (char*)"";
6959
 
        lex_user.host.length= grant_proc->host.hostname ?
6960
 
          strlen(grant_proc->host.hostname) : 0;
6961
 
 
6962
 
        if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
6963
 
                                  grant_proc->db, grant_proc->tname,
6964
 
                                  is_proc, ~(ulong)0, 1) == 0)
6965
 
        {
6966
 
          revoked= 1;
6967
 
          continue;
6968
 
        }
6969
 
      }
6970
 
      counter++;
6971
 
    }
6972
 
  } while (revoked);
6973
 
 
6974
 
  mysql_mutex_unlock(&acl_cache->lock);
6975
 
  mysql_rwlock_unlock(&LOCK_grant);
6976
 
 
6977
 
  thd->pop_internal_handler();
6978
 
  /* Restore the state of binlog format */
6979
 
  DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
6980
 
  if (save_binlog_row_based)
6981
 
    thd->set_current_stmt_binlog_format_row();
6982
 
 
6983
 
  DBUG_RETURN(error_handler.has_errors());
6984
 
}
6985
 
 
6986
 
 
6987
 
/**
6988
 
  Grant EXECUTE,ALTER privilege for a stored procedure
6989
 
 
6990
 
  @param thd The current thread.
6991
 
  @param sp_db
6992
 
  @param sp_name
6993
 
  @param is_proc
6994
 
 
6995
 
  @return
6996
 
    @retval FALSE Success
6997
 
    @retval TRUE An error occured. Error message not yet sent.
6998
 
*/
6999
 
 
7000
 
bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
7001
 
                         bool is_proc)
7002
 
{
7003
 
  Security_context *sctx= thd->security_ctx;
7004
 
  LEX_USER *combo;
7005
 
  TABLE_LIST tables[1];
7006
 
  List<LEX_USER> user_list;
7007
 
  bool result;
7008
 
  ACL_USER *au;
7009
 
  char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
7010
 
  Dummy_error_handler error_handler;
7011
 
  DBUG_ENTER("sp_grant_privileges");
7012
 
 
7013
 
  if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
7014
 
    DBUG_RETURN(TRUE);
7015
 
 
7016
 
  combo->user.str= sctx->user;
7017
 
 
7018
 
  mysql_mutex_lock(&acl_cache->lock);
7019
 
 
7020
 
  if ((au= find_acl_user(combo->host.str=(char*)sctx->host_or_ip,combo->user.str,FALSE)))
7021
 
    goto found_acl;
7022
 
  if ((au= find_acl_user(combo->host.str=(char*)sctx->host, combo->user.str,FALSE)))
7023
 
    goto found_acl;
7024
 
  if ((au= find_acl_user(combo->host.str=(char*)sctx->ip, combo->user.str,FALSE)))
7025
 
    goto found_acl;
7026
 
  if((au= find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE)))
7027
 
    goto found_acl;
7028
 
 
7029
 
  mysql_mutex_unlock(&acl_cache->lock);
7030
 
  DBUG_RETURN(TRUE);
7031
 
 
7032
 
 found_acl:
7033
 
  mysql_mutex_unlock(&acl_cache->lock);
7034
 
 
7035
 
  bzero((char*)tables, sizeof(TABLE_LIST));
7036
 
  user_list.empty();
7037
 
 
7038
 
  tables->db= (char*)sp_db;
7039
 
  tables->table_name= tables->alias= (char*)sp_name;
7040
 
 
7041
 
  thd->make_lex_string(&combo->user,
7042
 
                       combo->user.str, strlen(combo->user.str), 0);
7043
 
  thd->make_lex_string(&combo->host,
7044
 
                       combo->host.str, strlen(combo->host.str), 0);
7045
 
 
7046
 
  combo->password= empty_lex_str;
7047
 
  combo->plugin= empty_lex_str;
7048
 
  combo->auth= empty_lex_str;
7049
 
 
7050
 
  if(au)
7051
 
  {
7052
 
    if (au->salt_len)
7053
 
    {
7054
 
      if (au->salt_len == SCRAMBLE_LENGTH)
7055
 
      {
7056
 
        make_password_from_salt(passwd_buff, au->salt);
7057
 
        combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
7058
 
      }
7059
 
      else if (au->salt_len == SCRAMBLE_LENGTH_323)
7060
 
      {
7061
 
        make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
7062
 
        combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
7063
 
      }
7064
 
      else
7065
 
      {
7066
 
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PASSWD_LENGTH,
7067
 
                            ER(ER_PASSWD_LENGTH), SCRAMBLED_PASSWORD_CHAR_LENGTH);
7068
 
        return TRUE;
7069
 
      }
7070
 
      combo->password.str= passwd_buff;
7071
 
    }
7072
 
 
7073
 
    if (au->plugin.str != native_password_plugin_name.str &&
7074
 
        au->plugin.str != old_password_plugin_name.str)
7075
 
    {
7076
 
      combo->plugin= au->plugin;
7077
 
      combo->auth= au->auth_string;
7078
 
    }
7079
 
  }
7080
 
 
7081
 
  if (user_list.push_back(combo))
7082
 
    DBUG_RETURN(TRUE);
7083
 
 
7084
 
  thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
7085
 
  thd->lex->ssl_cipher= thd->lex->x509_subject= thd->lex->x509_issuer= 0;
7086
 
  bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
7087
 
 
7088
 
  /*
7089
 
    Only care about whether the operation failed or succeeded
7090
 
    as all errors will be handled later.
7091
 
  */
7092
 
  thd->push_internal_handler(&error_handler);
7093
 
  result= mysql_routine_grant(thd, tables, is_proc, user_list,
7094
 
                              DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE);
7095
 
  thd->pop_internal_handler();
7096
 
  DBUG_RETURN(result);
7097
 
}
7098
 
 
7099
 
 
7100
 
/*****************************************************************************
7101
 
  Instantiate used templates
7102
 
*****************************************************************************/
7103
 
 
7104
 
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
7105
 
template class List_iterator<LEX_COLUMN>;
7106
 
template class List_iterator<LEX_USER>;
7107
 
template class List<LEX_COLUMN>;
7108
 
template class List<LEX_USER>;
7109
 
#endif
7110
 
 
7111
 
/**
7112
 
  Validate if a user can proxy as another user
7113
 
 
7114
 
  @thd                     current thread
7115
 
  @param user              the logged in user (proxy user)
7116
 
  @param authenticated_as  the effective user a plugin is trying to 
7117
 
                           impersonate as (proxied user)
7118
 
  @return                  proxy user definition
7119
 
    @retval NULL           proxy user definition not found or not applicable
7120
 
    @retval non-null       the proxy user data
7121
 
*/
7122
 
 
7123
 
static ACL_PROXY_USER *
7124
 
acl_find_proxy_user(const char *user, const char *host, const char *ip, 
7125
 
                    const char *authenticated_as, bool *proxy_used)
7126
 
{
7127
 
  uint i;
7128
 
  /* if the proxied and proxy user are the same return OK */
7129
 
  DBUG_ENTER("acl_find_proxy_user");
7130
 
  DBUG_PRINT("info", ("user=%s host=%s ip=%s authenticated_as=%s",
7131
 
                      user, host, ip, authenticated_as));
7132
 
 
7133
 
  if (!strcmp(authenticated_as, user))
7134
 
  {
7135
 
    DBUG_PRINT ("info", ("user is the same as authenticated_as"));
7136
 
    DBUG_RETURN (NULL);
7137
 
  }
7138
 
 
7139
 
  *proxy_used= TRUE; 
7140
 
  for (i=0; i < acl_proxy_users.elements; i++)
7141
 
  {
7142
 
    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i, 
7143
 
                                           ACL_PROXY_USER *);
7144
 
    if (proxy->matches(host, user, ip, authenticated_as))
7145
 
      DBUG_RETURN(proxy);
7146
 
  }
7147
 
 
7148
 
  DBUG_RETURN(NULL);
7149
 
}
7150
 
 
7151
 
 
7152
 
bool
7153
 
acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
7154
 
                             bool with_grant)
7155
 
{
7156
 
  DBUG_ENTER("acl_check_proxy_grant_access");
7157
 
  DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host, 
7158
 
                      (int) with_grant));
7159
 
  if (!initialized)
7160
 
  {
7161
 
    my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
7162
 
    DBUG_RETURN(1);
7163
 
  }
7164
 
 
7165
 
  /* replication slave thread can do anything */
7166
 
  if (thd->slave_thread)
7167
 
  {
7168
 
    DBUG_PRINT("info", ("replication slave"));
7169
 
    DBUG_RETURN(FALSE);
7170
 
  }
7171
 
 
7172
 
  /* one can grant proxy to himself to others */
7173
 
  if (!strcmp(thd->security_ctx->user, user) &&
7174
 
      !my_strcasecmp(system_charset_info, host,
7175
 
                     thd->security_ctx->host))
7176
 
  {
7177
 
    DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal", 
7178
 
                        thd->security_ctx->user, user,
7179
 
                        host, thd->security_ctx->host));
7180
 
    DBUG_RETURN(FALSE);
7181
 
  }
7182
 
 
7183
 
  /* check for matching WITH PROXY rights */
7184
 
  for (uint i=0; i < acl_proxy_users.elements; i++)
7185
 
  {
7186
 
    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i, 
7187
 
                                           ACL_PROXY_USER *);
7188
 
    if (proxy->matches(thd->security_ctx->host,
7189
 
                       thd->security_ctx->user,
7190
 
                       thd->security_ctx->ip,
7191
 
                       user) &&
7192
 
        proxy->get_with_grant())
7193
 
    {
7194
 
      DBUG_PRINT("info", ("found"));
7195
 
      DBUG_RETURN(FALSE);
7196
 
    }
7197
 
  }
7198
 
 
7199
 
  my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
7200
 
           thd->security_ctx->user,
7201
 
           thd->security_ctx->host_or_ip);
7202
 
  DBUG_RETURN(TRUE);
7203
 
}
7204
 
 
7205
 
 
7206
 
static bool
7207
 
show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
7208
 
{
7209
 
  Protocol *protocol= thd->protocol;
7210
 
  int error= 0;
7211
 
 
7212
 
  for (uint i=0; i < acl_proxy_users.elements; i++)
7213
 
  {
7214
 
    ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
7215
 
                                           ACL_PROXY_USER *);
7216
 
    if (proxy->granted_on(user->host.str, user->user.str))
7217
 
    {
7218
 
      String global(buff, buffsize, system_charset_info);
7219
 
      global.length(0);
7220
 
      proxy->print_grant(&global);
7221
 
      protocol->prepare_for_resend();
7222
 
      protocol->store(global.ptr(), global.length(), global.charset());
7223
 
      if (protocol->write())
7224
 
      {
7225
 
        error= -1;
7226
 
        break;
7227
 
      }
7228
 
    }
7229
 
  }
7230
 
  return error;
7231
 
}
7232
 
 
7233
 
 
7234
 
#endif /*NO_EMBEDDED_ACCESS_CHECKS */
7235
 
 
7236
 
 
7237
 
int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
7238
 
{
7239
 
  reg3 int flag;
7240
 
  DBUG_ENTER("wild_case_compare");
7241
 
  DBUG_PRINT("enter",("str: '%s'  wildstr: '%s'",str,wildstr));
7242
 
  while (*wildstr)
7243
 
  {
7244
 
    while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
7245
 
    {
7246
 
      if (*wildstr == wild_prefix && wildstr[1])
7247
 
        wildstr++;
7248
 
      if (my_toupper(cs, *wildstr++) !=
7249
 
          my_toupper(cs, *str++)) DBUG_RETURN(1);
7250
 
    }
7251
 
    if (! *wildstr ) DBUG_RETURN (*str != 0);
7252
 
    if (*wildstr++ == wild_one)
7253
 
    {
7254
 
      if (! *str++) DBUG_RETURN (1);    /* One char; skip */
7255
 
    }
7256
 
    else
7257
 
    {                                           /* Found '*' */
7258
 
      if (!*wildstr) DBUG_RETURN(0);            /* '*' as last char: OK */
7259
 
      flag=(*wildstr != wild_many && *wildstr != wild_one);
7260
 
      do
7261
 
      {
7262
 
        if (flag)
7263
 
        {
7264
 
          char cmp;
7265
 
          if ((cmp= *wildstr) == wild_prefix && wildstr[1])
7266
 
            cmp=wildstr[1];
7267
 
          cmp=my_toupper(cs, cmp);
7268
 
          while (*str && my_toupper(cs, *str) != cmp)
7269
 
            str++;
7270
 
          if (!*str) DBUG_RETURN (1);
7271
 
        }
7272
 
        if (wild_case_compare(cs, str,wildstr) == 0) DBUG_RETURN (0);
7273
 
      } while (*str++);
7274
 
      DBUG_RETURN(1);
7275
 
    }
7276
 
  }
7277
 
  DBUG_RETURN (*str != '\0');
7278
 
}
7279
 
 
7280
 
 
7281
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7282
 
static bool update_schema_privilege(THD *thd, TABLE *table, char *buff,
7283
 
                                    const char* db, const char* t_name,
7284
 
                                    const char* column, uint col_length,
7285
 
                                    const char *priv, uint priv_length,
7286
 
                                    const char* is_grantable)
7287
 
{
7288
 
  int i= 2;
7289
 
  CHARSET_INFO *cs= system_charset_info;
7290
 
  restore_record(table, s->default_values);
7291
 
  table->field[0]->store(buff, (uint) strlen(buff), cs);
7292
 
  table->field[1]->store(STRING_WITH_LEN("def"), cs);
7293
 
  if (db)
7294
 
    table->field[i++]->store(db, (uint) strlen(db), cs);
7295
 
  if (t_name)
7296
 
    table->field[i++]->store(t_name, (uint) strlen(t_name), cs);
7297
 
  if (column)
7298
 
    table->field[i++]->store(column, col_length, cs);
7299
 
  table->field[i++]->store(priv, priv_length, cs);
7300
 
  table->field[i]->store(is_grantable, strlen(is_grantable), cs);
7301
 
  return schema_table_store_record(thd, table);
7302
 
}
7303
 
#endif
7304
 
 
7305
 
 
7306
 
int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
7307
 
{
7308
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7309
 
  int error= 0;
7310
 
  uint counter;
7311
 
  ACL_USER *acl_user;
7312
 
  ulong want_access;
7313
 
  char buff[100];
7314
 
  TABLE *table= tables->table;
7315
 
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
7316
 
                                      NULL, NULL, 1, 1);
7317
 
  char *curr_host= thd->security_ctx->priv_host_name();
7318
 
  DBUG_ENTER("fill_schema_user_privileges");
7319
 
 
7320
 
  if (!initialized)
7321
 
    DBUG_RETURN(0);
7322
 
  mysql_mutex_lock(&acl_cache->lock);
7323
 
 
7324
 
  for (counter=0 ; counter < acl_users.elements ; counter++)
7325
 
  {
7326
 
    const char *user,*host, *is_grantable="YES";
7327
 
    acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
7328
 
    if (!(user=acl_user->user))
7329
 
      user= "";
7330
 
    if (!(host=acl_user->host.hostname))
7331
 
      host= "";
7332
 
 
7333
 
    if (no_global_access &&
7334
 
        (strcmp(thd->security_ctx->priv_user, user) ||
7335
 
         my_strcasecmp(system_charset_info, curr_host, host)))
7336
 
      continue;
7337
 
      
7338
 
    want_access= acl_user->access;
7339
 
    if (!(want_access & GRANT_ACL))
7340
 
      is_grantable= "NO";
7341
 
 
7342
 
    strxmov(buff,"'",user,"'@'",host,"'",NullS);
7343
 
    if (!(want_access & ~GRANT_ACL))
7344
 
    {
7345
 
      if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
7346
 
                                  STRING_WITH_LEN("USAGE"), is_grantable))
7347
 
      {
7348
 
        error= 1;
7349
 
        goto err;
7350
 
      }
7351
 
    }
7352
 
    else
7353
 
    {
7354
 
      uint priv_id;
7355
 
      ulong j,test_access= want_access & ~GRANT_ACL;
7356
 
      for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
7357
 
      {
7358
 
        if (test_access & j)
7359
 
        {
7360
 
          if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0, 
7361
 
                                      command_array[priv_id],
7362
 
                                      command_lengths[priv_id], is_grantable))
7363
 
          {
7364
 
            error= 1;
7365
 
            goto err;
7366
 
          }
7367
 
        }
7368
 
      }
7369
 
    }
7370
 
  }
7371
 
err:
7372
 
  mysql_mutex_unlock(&acl_cache->lock);
7373
 
 
7374
 
  DBUG_RETURN(error);
7375
 
#else
7376
 
  return(0);
7377
 
#endif
7378
 
}
7379
 
 
7380
 
 
7381
 
int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
7382
 
{
7383
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7384
 
  int error= 0;
7385
 
  uint counter;
7386
 
  ACL_DB *acl_db;
7387
 
  ulong want_access;
7388
 
  char buff[100];
7389
 
  TABLE *table= tables->table;
7390
 
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
7391
 
                                      NULL, NULL, 1, 1);
7392
 
  char *curr_host= thd->security_ctx->priv_host_name();
7393
 
  DBUG_ENTER("fill_schema_schema_privileges");
7394
 
 
7395
 
  if (!initialized)
7396
 
    DBUG_RETURN(0);
7397
 
  mysql_mutex_lock(&acl_cache->lock);
7398
 
 
7399
 
  for (counter=0 ; counter < acl_dbs.elements ; counter++)
7400
 
  {
7401
 
    const char *user, *host, *is_grantable="YES";
7402
 
 
7403
 
    acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
7404
 
    if (!(user=acl_db->user))
7405
 
      user= "";
7406
 
    if (!(host=acl_db->host.hostname))
7407
 
      host= "";
7408
 
 
7409
 
    if (no_global_access &&
7410
 
        (strcmp(thd->security_ctx->priv_user, user) ||
7411
 
         my_strcasecmp(system_charset_info, curr_host, host)))
7412
 
      continue;
7413
 
 
7414
 
    want_access=acl_db->access;
7415
 
    if (want_access)
7416
 
    {
7417
 
      if (!(want_access & GRANT_ACL))
7418
 
      {
7419
 
        is_grantable= "NO";
7420
 
      }
7421
 
      strxmov(buff,"'",user,"'@'",host,"'",NullS);
7422
 
      if (!(want_access & ~GRANT_ACL))
7423
 
      {
7424
 
        if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0,
7425
 
                                    0, STRING_WITH_LEN("USAGE"), is_grantable))
7426
 
        {
7427
 
          error= 1;
7428
 
          goto err;
7429
 
        }
7430
 
      }
7431
 
      else
7432
 
      {
7433
 
        int cnt;
7434
 
        ulong j,test_access= want_access & ~GRANT_ACL;
7435
 
        for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
7436
 
          if (test_access & j)
7437
 
          {
7438
 
            if (update_schema_privilege(thd, table, buff, acl_db->db, 0, 0, 0,
7439
 
                                        command_array[cnt], command_lengths[cnt],
7440
 
                                        is_grantable))
7441
 
            {
7442
 
              error= 1;
7443
 
              goto err;
7444
 
            }
7445
 
          }
7446
 
      }
7447
 
    }
7448
 
  }
7449
 
err:
7450
 
  mysql_mutex_unlock(&acl_cache->lock);
7451
 
 
7452
 
  DBUG_RETURN(error);
7453
 
#else
7454
 
  return (0);
7455
 
#endif
7456
 
}
7457
 
 
7458
 
 
7459
 
int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
7460
 
{
7461
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7462
 
  int error= 0;
7463
 
  uint index;
7464
 
  char buff[100];
7465
 
  TABLE *table= tables->table;
7466
 
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
7467
 
                                      NULL, NULL, 1, 1);
7468
 
  char *curr_host= thd->security_ctx->priv_host_name();
7469
 
  DBUG_ENTER("fill_schema_table_privileges");
7470
 
 
7471
 
  mysql_rwlock_rdlock(&LOCK_grant);
7472
 
 
7473
 
  for (index=0 ; index < column_priv_hash.records ; index++)
7474
 
  {
7475
 
    const char *user, *host, *is_grantable= "YES";
7476
 
    GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
7477
 
                                                          index);
7478
 
    if (!(user=grant_table->user))
7479
 
      user= "";
7480
 
    if (!(host= grant_table->host.hostname))
7481
 
      host= "";
7482
 
 
7483
 
    if (no_global_access &&
7484
 
        (strcmp(thd->security_ctx->priv_user, user) ||
7485
 
         my_strcasecmp(system_charset_info, curr_host, host)))
7486
 
      continue;
7487
 
 
7488
 
    ulong table_access= grant_table->privs;
7489
 
    if (table_access)
7490
 
    {
7491
 
      ulong test_access= table_access & ~GRANT_ACL;
7492
 
      /*
7493
 
        We should skip 'usage' privilege on table if
7494
 
        we have any privileges on column(s) of this table
7495
 
      */
7496
 
      if (!test_access && grant_table->cols)
7497
 
        continue;
7498
 
      if (!(table_access & GRANT_ACL))
7499
 
        is_grantable= "NO";
7500
 
 
7501
 
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
7502
 
      if (!test_access)
7503
 
      {
7504
 
        if (update_schema_privilege(thd, table, buff, grant_table->db,
7505
 
                                    grant_table->tname, 0, 0,
7506
 
                                    STRING_WITH_LEN("USAGE"), is_grantable))
7507
 
        {
7508
 
          error= 1;
7509
 
          goto err;
7510
 
        }
7511
 
      }
7512
 
      else
7513
 
      {
7514
 
        ulong j;
7515
 
        int cnt;
7516
 
        for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
7517
 
        {
7518
 
          if (test_access & j)
7519
 
          {
7520
 
            if (update_schema_privilege(thd, table, buff, grant_table->db,
7521
 
                                        grant_table->tname, 0, 0,
7522
 
                                        command_array[cnt],
7523
 
                                        command_lengths[cnt], is_grantable))
7524
 
            {
7525
 
              error= 1;
7526
 
              goto err;
7527
 
            }
7528
 
          }
7529
 
        }
7530
 
      }
7531
 
    }   
7532
 
  }
7533
 
err:
7534
 
  mysql_rwlock_unlock(&LOCK_grant);
7535
 
 
7536
 
  DBUG_RETURN(error);
7537
 
#else
7538
 
  return (0);
7539
 
#endif
7540
 
}
7541
 
 
7542
 
 
7543
 
int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
7544
 
{
7545
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7546
 
  int error= 0;
7547
 
  uint index;
7548
 
  char buff[100];
7549
 
  TABLE *table= tables->table;
7550
 
  bool no_global_access= check_access(thd, SELECT_ACL, "mysql",
7551
 
                                      NULL, NULL, 1, 1);
7552
 
  char *curr_host= thd->security_ctx->priv_host_name();
7553
 
  DBUG_ENTER("fill_schema_table_privileges");
7554
 
 
7555
 
  mysql_rwlock_rdlock(&LOCK_grant);
7556
 
 
7557
 
  for (index=0 ; index < column_priv_hash.records ; index++)
7558
 
  {
7559
 
    const char *user, *host, *is_grantable= "YES";
7560
 
    GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
7561
 
                                                          index);
7562
 
    if (!(user=grant_table->user))
7563
 
      user= "";
7564
 
    if (!(host= grant_table->host.hostname))
7565
 
      host= "";
7566
 
 
7567
 
    if (no_global_access &&
7568
 
        (strcmp(thd->security_ctx->priv_user, user) ||
7569
 
         my_strcasecmp(system_charset_info, curr_host, host)))
7570
 
      continue;
7571
 
 
7572
 
    ulong table_access= grant_table->cols;
7573
 
    if (table_access != 0)
7574
 
    {
7575
 
      if (!(grant_table->privs & GRANT_ACL))
7576
 
        is_grantable= "NO";
7577
 
 
7578
 
      ulong test_access= table_access & ~GRANT_ACL;
7579
 
      strxmov(buff, "'", user, "'@'", host, "'", NullS);
7580
 
      if (!test_access)
7581
 
        continue;
7582
 
      else
7583
 
      {
7584
 
        ulong j;
7585
 
        int cnt;
7586
 
        for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
7587
 
        {
7588
 
          if (test_access & j)
7589
 
          {
7590
 
            for (uint col_index=0 ;
7591
 
                 col_index < grant_table->hash_columns.records ;
7592
 
                 col_index++)
7593
 
            {
7594
 
              GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
7595
 
                my_hash_element(&grant_table->hash_columns,col_index);
7596
 
              if ((grant_column->rights & j) && (table_access & j))
7597
 
              {
7598
 
                if (update_schema_privilege(thd, table, buff, grant_table->db,
7599
 
                                            grant_table->tname,
7600
 
                                            grant_column->column,
7601
 
                                            grant_column->key_length,
7602
 
                                            command_array[cnt],
7603
 
                                            command_lengths[cnt], is_grantable))
7604
 
                {
7605
 
                  error= 1;
7606
 
                  goto err;
7607
 
                }
7608
 
              }
7609
 
            }
7610
 
          }
7611
 
        }
7612
 
      }
7613
 
    }
7614
 
  }
7615
 
err:
7616
 
  mysql_rwlock_unlock(&LOCK_grant);
7617
 
 
7618
 
  DBUG_RETURN(error);
7619
 
#else
7620
 
  return (0);
7621
 
#endif
7622
 
}
7623
 
 
7624
 
 
7625
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
7626
 
/*
7627
 
  fill effective privileges for table
7628
 
 
7629
 
  SYNOPSIS
7630
 
    fill_effective_table_privileges()
7631
 
    thd     thread handler
7632
 
    grant   grants table descriptor
7633
 
    db      db name
7634
 
    table   table name
7635
 
*/
7636
 
 
7637
 
void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
7638
 
                                     const char *db, const char *table)
7639
 
{
7640
 
  Security_context *sctx= thd->security_ctx;
7641
 
  DBUG_ENTER("fill_effective_table_privileges");
7642
 
  DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
7643
 
                       sctx->priv_host, (sctx->ip ? sctx->ip : "(NULL)"),
7644
 
                       (sctx->priv_user ? sctx->priv_user : "(NULL)"),
7645
 
                       db, table));
7646
 
  /* --skip-grants */
7647
 
  if (!initialized)
7648
 
  {
7649
 
    DBUG_PRINT("info", ("skip grants"));
7650
 
    grant->privilege= ~NO_ACCESS;             // everything is allowed
7651
 
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
7652
 
    DBUG_VOID_RETURN;
7653
 
  }
7654
 
 
7655
 
  /* global privileges */
7656
 
  grant->privilege= sctx->master_access;
7657
 
 
7658
 
  if (!sctx->priv_user)
7659
 
  {
7660
 
    DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
7661
 
    DBUG_VOID_RETURN;                         // it is slave
7662
 
  }
7663
 
 
7664
 
  /* db privileges */
7665
 
  grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
7666
 
 
7667
 
  /* table privileges */
7668
 
  mysql_rwlock_rdlock(&LOCK_grant);
7669
 
  if (grant->version != grant_version)
7670
 
  {
7671
 
    grant->grant_table=
7672
 
      table_hash_search(sctx->host, sctx->ip, db,
7673
 
                        sctx->priv_user,
7674
 
                        table, 0);              /* purecov: inspected */
7675
 
    grant->version= grant_version;              /* purecov: inspected */
7676
 
  }
7677
 
  if (grant->grant_table != 0)
7678
 
  {
7679
 
    grant->privilege|= grant->grant_table->privs;
7680
 
  }
7681
 
  mysql_rwlock_unlock(&LOCK_grant);
7682
 
 
7683
 
  DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
7684
 
  DBUG_VOID_RETURN;
7685
 
}
7686
 
 
7687
 
#else /* NO_EMBEDDED_ACCESS_CHECKS */
7688
 
 
7689
 
/****************************************************************************
7690
 
 Dummy wrappers when we don't have any access checks
7691
 
****************************************************************************/
7692
 
 
7693
 
bool check_routine_level_acl(THD *thd, const char *db, const char *name,
7694
 
                             bool is_proc)
7695
 
{
7696
 
  return FALSE;
7697
 
}
7698
 
 
7699
 
#endif
7700
 
 
7701
 
struct ACL_internal_schema_registry_entry
7702
 
{
7703
 
  const LEX_STRING *m_name;
7704
 
  const ACL_internal_schema_access *m_access;
7705
 
};
7706
 
 
7707
 
/**
7708
 
  Internal schema registered.
7709
 
  Currently, this is only:
7710
 
  - performance_schema
7711
 
  - information_schema,
7712
 
  This can be reused later for:
7713
 
  - mysql
7714
 
*/
7715
 
static ACL_internal_schema_registry_entry registry_array[2];
7716
 
static uint m_registry_array_size= 0;
7717
 
 
7718
 
/**
7719
 
  Add an internal schema to the registry.
7720
 
  @param name the schema name
7721
 
  @param access the schema ACL specific rules
7722
 
*/
7723
 
void ACL_internal_schema_registry::register_schema
7724
 
  (const LEX_STRING *name, const ACL_internal_schema_access *access)
7725
 
{
7726
 
  DBUG_ASSERT(m_registry_array_size < array_elements(registry_array));
7727
 
 
7728
 
  /* Not thread safe, and does not need to be. */
7729
 
  registry_array[m_registry_array_size].m_name= name;
7730
 
  registry_array[m_registry_array_size].m_access= access;
7731
 
  m_registry_array_size++;
7732
 
}
7733
 
 
7734
 
/**
7735
 
  Search per internal schema ACL by name.
7736
 
  @param name a schema name
7737
 
  @return per schema rules, or NULL
7738
 
*/
7739
 
const ACL_internal_schema_access *
7740
 
ACL_internal_schema_registry::lookup(const char *name)
7741
 
{
7742
 
  DBUG_ASSERT(name != NULL);
7743
 
 
7744
 
  uint i;
7745
 
 
7746
 
  for (i= 0; i<m_registry_array_size; i++)
7747
 
  {
7748
 
    if (my_strcasecmp(system_charset_info, registry_array[i].m_name->str,
7749
 
                      name) == 0)
7750
 
      return registry_array[i].m_access;
7751
 
  }
7752
 
  return NULL;
7753
 
}
7754
 
 
7755
 
/**
7756
 
  Get a cached internal schema access.
7757
 
  @param grant_internal_info the cache
7758
 
  @param schema_name the name of the internal schema
7759
 
*/
7760
 
const ACL_internal_schema_access *
7761
 
get_cached_schema_access(GRANT_INTERNAL_INFO *grant_internal_info,
7762
 
                         const char *schema_name)
7763
 
{
7764
 
  if (grant_internal_info)
7765
 
  {
7766
 
    if (! grant_internal_info->m_schema_lookup_done)
7767
 
    {
7768
 
      grant_internal_info->m_schema_access=
7769
 
        ACL_internal_schema_registry::lookup(schema_name);
7770
 
      grant_internal_info->m_schema_lookup_done= TRUE;
7771
 
    }
7772
 
    return grant_internal_info->m_schema_access;
7773
 
  }
7774
 
  return ACL_internal_schema_registry::lookup(schema_name);
7775
 
}
7776
 
 
7777
 
/**
7778
 
  Get a cached internal table access.
7779
 
  @param grant_internal_info the cache
7780
 
  @param schema_name the name of the internal schema
7781
 
  @param table_name the name of the internal table
7782
 
*/
7783
 
const ACL_internal_table_access *
7784
 
get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
7785
 
                        const char *schema_name,
7786
 
                        const char *table_name)
7787
 
{
7788
 
  DBUG_ASSERT(grant_internal_info);
7789
 
  if (! grant_internal_info->m_table_lookup_done)
7790
 
  {
7791
 
    const ACL_internal_schema_access *schema_access;
7792
 
    schema_access= get_cached_schema_access(grant_internal_info, schema_name);
7793
 
    if (schema_access)
7794
 
      grant_internal_info->m_table_access= schema_access->lookup(table_name);
7795
 
    grant_internal_info->m_table_lookup_done= TRUE;
7796
 
  }
7797
 
  return grant_internal_info->m_table_access;
7798
 
}
7799
 
 
7800
 
 
7801
 
/****************************************************************************
7802
 
   AUTHENTICATION CODE
7803
 
   including initial connect handshake, invoking appropriate plugins,
7804
 
   client-server plugin negotiation, COM_CHANGE_USER, and native
7805
 
   MySQL authentication plugins.
7806
 
****************************************************************************/
7807
 
 
7808
 
/* few defines to have less ifdef's in the code below */
7809
 
#ifdef EMBEDDED_LIBRARY
7810
 
#undef HAVE_OPENSSL
7811
 
#ifdef NO_EMBEDDED_ACCESS_CHECKS
7812
 
#define initialized 0
7813
 
#endif
7814
 
#endif
7815
 
#ifndef HAVE_OPENSSL
7816
 
#define ssl_acceptor_fd 0
7817
 
#define sslaccept(A,B,C) 1
7818
 
#endif
7819
 
 
7820
 
 
7821
 
class Thd_charset_adapter
7822
 
{
7823
 
  THD *thd;
7824
 
public:
7825
 
  Thd_charset_adapter(THD *thd_arg) : thd (thd_arg) {} 
7826
 
  bool init_client_charset(uint cs_number)
7827
 
  {
7828
 
    if (thd_init_client_charset(thd, cs_number))
7829
 
      return true;
7830
 
    thd->update_charset();
7831
 
    return thd->is_error();
7832
 
  }
7833
 
 
7834
 
  CHARSET_INFO *charset() { return thd->charset(); }
7835
 
};
7836
 
 
7837
 
 
7838
 
/**
7839
 
  The internal version of what plugins know as MYSQL_PLUGIN_VIO,
7840
 
  basically the context of the authentication session
7841
 
*/
7842
 
struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
7843
 
{
7844
 
  MYSQL_SERVER_AUTH_INFO auth_info;
7845
 
  const ACL_USER *acl_user;
7846
 
  plugin_ref plugin;        ///< what plugin we're under
7847
 
  LEX_STRING db;            ///< db name from the handshake packet
7848
 
  /** when restarting a plugin this caches the last client reply */
7849
 
  struct {
7850
 
    char *plugin, *pkt;     ///< pointers into NET::buff
7851
 
    uint pkt_len;
7852
 
  } cached_client_reply;
7853
 
  /** this caches the first plugin packet for restart request on the client */
7854
 
  struct {
7855
 
    char *pkt;
7856
 
    uint pkt_len;
7857
 
  } cached_server_packet;
7858
 
  int packets_read, packets_written; ///< counters for send/received packets
7859
 
  uint connect_errors;      ///< if there were connect errors for this host
7860
 
  /** when plugin returns a failure this tells us what really happened */
7861
 
  enum { SUCCESS, FAILURE, RESTART } status;
7862
 
 
7863
 
  /* encapsulation members */
7864
 
  ulong client_capabilities;
7865
 
  char *scramble;
7866
 
  MEM_ROOT *mem_root;
7867
 
  struct  rand_struct *rand;
7868
 
  my_thread_id  thread_id;
7869
 
  uint      *server_status;
7870
 
  NET *net;
7871
 
  ulong max_client_packet_length;
7872
 
  char *ip;
7873
 
  char *host;
7874
 
  Thd_charset_adapter *charset_adapter;
7875
 
  LEX_STRING acl_user_plugin;
7876
 
};
7877
 
 
7878
 
/**
7879
 
  a helper function to report an access denied error in all the proper places
7880
 
*/
7881
 
static void login_failed_error(MPVIO_EXT *mpvio, int passwd_used)
7882
 
{
7883
 
  THD *thd= current_thd;
7884
 
  if (passwd_used == 2)
7885
 
  {
7886
 
    my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
7887
 
             mpvio->auth_info.user_name,
7888
 
             mpvio->auth_info.host_or_ip);
7889
 
    general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
7890
 
                      mpvio->auth_info.user_name,
7891
 
                      mpvio->auth_info.host_or_ip);
7892
 
    /* 
7893
 
      Log access denied messages to the error log when log-warnings = 2
7894
 
      so that the overhead of the general query log is not required to track 
7895
 
      failed connections.
7896
 
    */
7897
 
    if (global_system_variables.log_warnings > 1)
7898
 
    {
7899
 
      sql_print_warning(ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
7900
 
                        mpvio->auth_info.user_name,
7901
 
                        mpvio->auth_info.host_or_ip);      
7902
 
    }
7903
 
  }
7904
 
  else
7905
 
  {
7906
 
    my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
7907
 
             mpvio->auth_info.user_name,
7908
 
             mpvio->auth_info.host_or_ip,
7909
 
             passwd_used ? ER(ER_YES) : ER(ER_NO));
7910
 
    general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
7911
 
                      mpvio->auth_info.user_name,
7912
 
                      mpvio->auth_info.host_or_ip,
7913
 
                      passwd_used ? ER(ER_YES) : ER(ER_NO));
7914
 
    /* 
7915
 
      Log access denied messages to the error log when log-warnings = 2
7916
 
      so that the overhead of the general query log is not required to track 
7917
 
      failed connections.
7918
 
    */
7919
 
    if (global_system_variables.log_warnings > 1)
7920
 
    {
7921
 
      sql_print_warning(ER(ER_ACCESS_DENIED_ERROR),
7922
 
                        mpvio->auth_info.user_name,
7923
 
                        mpvio->auth_info.host_or_ip,
7924
 
                        passwd_used ? ER(ER_YES) : ER(ER_NO));      
7925
 
    }
7926
 
  }
7927
 
}
7928
 
 
7929
 
/**
7930
 
  sends a server handshake initialization packet, the very first packet
7931
 
  after the connection was established
7932
 
 
7933
 
  Packet format:
7934
 
   
7935
 
    Bytes       Content
7936
 
    -----       ----
7937
 
    1           protocol version (always 10)
7938
 
    n           server version string, \0-terminated
7939
 
    4           thread id
7940
 
    8           first 8 bytes of the plugin provided data (scramble)
7941
 
    1           \0 byte, terminating the first part of a scramble
7942
 
    2           server capabilities (two lower bytes)
7943
 
    1           server character set
7944
 
    2           server status
7945
 
    2           server capabilities (two upper bytes)
7946
 
    1           length of the scramble
7947
 
    10          reserved, always 0
7948
 
    n           rest of the plugin provided data (at least 12 bytes)
7949
 
    1           \0 byte, terminating the second part of a scramble
7950
 
 
7951
 
  @retval 0 ok
7952
 
  @retval 1 error
7953
 
*/
7954
 
static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
7955
 
                                         const char *data, uint data_len)
7956
 
{
7957
 
  DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
7958
 
  DBUG_ASSERT(data_len <= 255);
7959
 
 
7960
 
  char *buff= (char *) my_alloca(1 + SERVER_VERSION_LENGTH + data_len + 64);
7961
 
  char scramble_buf[SCRAMBLE_LENGTH];
7962
 
  char *end= buff;
7963
 
 
7964
 
  DBUG_ENTER("send_server_handshake_packet");
7965
 
  *end++= protocol_version;
7966
 
 
7967
 
  mpvio->client_capabilities= CLIENT_BASIC_FLAGS;
7968
 
 
7969
 
  if (opt_using_transactions)
7970
 
    mpvio->client_capabilities|= CLIENT_TRANSACTIONS;
7971
 
 
7972
 
  mpvio->client_capabilities|= CAN_CLIENT_COMPRESS;
7973
 
 
7974
 
  if (ssl_acceptor_fd)
7975
 
  {
7976
 
    mpvio->client_capabilities|= CLIENT_SSL;
7977
 
    mpvio->client_capabilities|= CLIENT_SSL_VERIFY_SERVER_CERT;
7978
 
  }
7979
 
 
7980
 
  if (data_len)
7981
 
  {
7982
 
    mpvio->cached_server_packet.pkt= (char*) memdup_root(mpvio->mem_root, 
7983
 
                                                         data, data_len);
7984
 
    mpvio->cached_server_packet.pkt_len= data_len;
7985
 
  }
7986
 
 
7987
 
  if (data_len < SCRAMBLE_LENGTH)
7988
 
  {
7989
 
    if (data_len)
7990
 
    {
7991
 
      /*
7992
 
        the first packet *must* have at least 20 bytes of a scramble.
7993
 
        if a plugin provided less, we pad it to 20 with zeros
7994
 
      */
7995
 
      memcpy(scramble_buf, data, data_len);
7996
 
      bzero(scramble_buf + data_len, SCRAMBLE_LENGTH - data_len);
7997
 
      data= scramble_buf;
7998
 
    }
7999
 
    else
8000
 
    {
8001
 
      /*
8002
 
        if the default plugin does not provide the data for the scramble at
8003
 
        all, we generate a scramble internally anyway, just in case the
8004
 
        user account (that will be known only later) uses a
8005
 
        native_password_plugin (which needs a scramble). If we don't send a
8006
 
        scramble now - wasting 20 bytes in the packet -
8007
 
        native_password_plugin will have to send it in a separate packet,
8008
 
        adding one more round trip.
8009
 
      */
8010
 
      create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
8011
 
      data= mpvio->scramble;
8012
 
    }
8013
 
    data_len= SCRAMBLE_LENGTH;
8014
 
  }
8015
 
 
8016
 
  end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
8017
 
  int4store((uchar*) end, mpvio->thread_id);
8018
 
  end+= 4;
8019
 
 
8020
 
  /*
8021
 
    Old clients does not understand long scrambles, but can ignore packet
8022
 
    tail: that's why first part of the scramble is placed here, and second
8023
 
    part at the end of packet.
8024
 
  */
8025
 
  end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
8026
 
  end+= SCRAMBLE_LENGTH_323;
8027
 
  *end++= 0;
8028
 
 
8029
 
  int2store(end, mpvio->client_capabilities);
8030
 
  /* write server characteristics: up to 16 bytes allowed */
8031
 
  end[2]= (char) default_charset_info->number;
8032
 
  int2store(end + 3, mpvio->server_status[0]);
8033
 
  int2store(end + 5, mpvio->client_capabilities >> 16);
8034
 
  end[7]= data_len;
8035
 
  DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;);
8036
 
  bzero(end + 8, 10);
8037
 
  end+= 18;
8038
 
  /* write scramble tail */
8039
 
  end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323,
8040
 
                      data_len - SCRAMBLE_LENGTH_323);
8041
 
  end+= data_len - SCRAMBLE_LENGTH_323;
8042
 
  end= strmake(end, plugin_name(mpvio->plugin)->str,
8043
 
                    plugin_name(mpvio->plugin)->length);
8044
 
 
8045
 
  int res= my_net_write(mpvio->net, (uchar*) buff, (size_t) (end - buff + 1)) ||
8046
 
           net_flush(mpvio->net);
8047
 
  my_afree(buff);
8048
 
  DBUG_RETURN (res);
8049
 
}
8050
 
 
8051
 
static bool secure_auth(MPVIO_EXT *mpvio)
8052
 
{
8053
 
  THD *thd;
8054
 
  if (!opt_secure_auth)
8055
 
    return 0;
8056
 
  /*
8057
 
    If the server is running in secure auth mode, short scrambles are 
8058
 
    forbidden. Extra juggling to report the same error as the old code.
8059
 
  */
8060
 
 
8061
 
  thd= current_thd;
8062
 
  if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
8063
 
  {
8064
 
    my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
8065
 
             mpvio->auth_info.user_name,
8066
 
             mpvio->auth_info.host_or_ip);
8067
 
    general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
8068
 
                      mpvio->auth_info.user_name,
8069
 
                      mpvio->auth_info.host_or_ip);
8070
 
  }
8071
 
  else
8072
 
  {
8073
 
    my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
8074
 
    general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
8075
 
  }
8076
 
  return 1;
8077
 
}
8078
 
 
8079
 
/**
8080
 
  sends a "change plugin" packet, requesting a client to restart authentication
8081
 
  using a different authentication plugin
8082
 
 
8083
 
  Packet format:
8084
 
   
8085
 
    Bytes       Content
8086
 
    -----       ----
8087
 
    1           byte with the value 254
8088
 
    n           client plugin to use, \0-terminated
8089
 
    n           plugin provided data
8090
 
 
8091
 
  In a special case of switching from native_password_plugin to
8092
 
  old_password_plugin, the packet contains only one - the first - byte,
8093
 
  plugin name is omitted, plugin data aren't needed as the scramble was
8094
 
  already sent. This one-byte packet is identical to the "use the short
8095
 
  scramble" packet in the protocol before plugins were introduced.
8096
 
 
8097
 
  @retval 0 ok
8098
 
  @retval 1 error
8099
 
*/
8100
 
static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
8101
 
                                       const uchar *data, uint data_len)
8102
 
{
8103
 
  DBUG_ASSERT(mpvio->packets_written == 1);
8104
 
  DBUG_ASSERT(mpvio->packets_read == 1);
8105
 
  NET *net= mpvio->net;
8106
 
  static uchar switch_plugin_request_buf[]= { 254 };
8107
 
 
8108
 
  DBUG_ENTER("send_plugin_request_packet");
8109
 
  mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
8110
 
 
8111
 
  const char *client_auth_plugin=
8112
 
    ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
8113
 
 
8114
 
  DBUG_ASSERT(client_auth_plugin);
8115
 
 
8116
 
  /*
8117
 
    we send an old "short 4.0 scramble request", if we need to request a
8118
 
    client to use 4.0 auth plugin (short scramble) and the scramble was
8119
 
    already sent to the client
8120
 
 
8121
 
    below, cached_client_reply.plugin is the plugin name that client has used,
8122
 
    client_auth_plugin is derived from mysql.user table, for the given
8123
 
    user account, it's the plugin that the client need to use to login.
8124
 
  */
8125
 
  bool switch_from_long_to_short_scramble=
8126
 
    native_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
8127
 
    client_auth_plugin == old_password_plugin_name.str;
8128
 
 
8129
 
  if (switch_from_long_to_short_scramble)
8130
 
    DBUG_RETURN (secure_auth(mpvio) ||
8131
 
                 my_net_write(net, switch_plugin_request_buf, 1) ||
8132
 
                 net_flush(net));
8133
 
 
8134
 
  /*
8135
 
    We never request a client to switch from a short to long scramble.
8136
 
    Plugin-aware clients can do that, but traditionally it meant to
8137
 
    ask an old 4.0 client to use the new 4.1 authentication protocol.
8138
 
  */
8139
 
  bool switch_from_short_to_long_scramble=
8140
 
    old_password_plugin_name.str == mpvio->cached_client_reply.plugin && 
8141
 
    client_auth_plugin == native_password_plugin_name.str;
8142
 
 
8143
 
  if (switch_from_short_to_long_scramble)
8144
 
  {
8145
 
    my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
8146
 
    general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
8147
 
    DBUG_RETURN (1);
8148
 
  }
8149
 
 
8150
 
  /*
8151
 
    If we're dealing with an older client we can't just send a change plugin
8152
 
    packet to re-initiate the authentication handshake, because the client 
8153
 
    won't understand it. The good thing is that we don't need to : the old client
8154
 
    expects us to just check the user credentials here, which we can do by just reading
8155
 
    the cached data that are placed there by parse_com_change_user_packet() 
8156
 
    In this case we just do nothing and behave as if normal authentication
8157
 
    should continue.
8158
 
  */
8159
 
  if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
8160
 
  {
8161
 
    DBUG_PRINT("info", ("old client sent a COM_CHANGE_USER"));
8162
 
    DBUG_ASSERT(mpvio->cached_client_reply.pkt);
8163
 
    /* get the status back so the read can process the cached result */
8164
 
    mpvio->status= MPVIO_EXT::RESTART; 
8165
 
    DBUG_RETURN(0);
8166
 
  }
8167
 
 
8168
 
  DBUG_PRINT("info", ("requesting client to use the %s plugin", 
8169
 
                      client_auth_plugin));
8170
 
  DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
8171
 
                                (uchar*) client_auth_plugin,
8172
 
                                strlen(client_auth_plugin) + 1,
8173
 
                                (uchar*) data, data_len));
8174
 
}
8175
 
 
8176
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
8177
 
/**
8178
 
   Finds acl entry in user database for authentication purposes.
8179
 
   
8180
 
   Finds a user and copies it into mpvio. Reports an authentication
8181
 
   failure if a user is not found.
8182
 
 
8183
 
   @note find_acl_user is not the same, because it doesn't take into
8184
 
   account the case when user is not empty, but acl_user->user is empty
8185
 
 
8186
 
   @retval 0    found
8187
 
   @retval 1    not found
8188
 
*/
8189
 
static bool find_mpvio_user(MPVIO_EXT *mpvio)
8190
 
{
8191
 
  DBUG_ENTER("find_mpvio_user");
8192
 
  DBUG_PRINT("info", ("entry: %s", mpvio->auth_info.user_name));
8193
 
  DBUG_ASSERT(mpvio->acl_user == 0);
8194
 
  mysql_mutex_lock(&acl_cache->lock);
8195
 
  for (uint i=0; i < acl_users.elements; i++)
8196
 
  {
8197
 
    ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
8198
 
    if ((!acl_user_tmp->user || 
8199
 
         !strcmp(mpvio->auth_info.user_name, acl_user_tmp->user)) &&
8200
 
        compare_hostname(&acl_user_tmp->host, mpvio->host, mpvio->ip))
8201
 
    {
8202
 
      mpvio->acl_user= acl_user_tmp->copy(mpvio->mem_root);
8203
 
      if (acl_user_tmp->plugin.str == native_password_plugin_name.str ||
8204
 
          acl_user_tmp->plugin.str == old_password_plugin_name.str)
8205
 
        mpvio->acl_user_plugin= acl_user_tmp->plugin;
8206
 
      else
8207
 
        make_lex_string_root(mpvio->mem_root, 
8208
 
                             &mpvio->acl_user_plugin, 
8209
 
                             acl_user_tmp->plugin.str, 
8210
 
                             acl_user_tmp->plugin.length, 0);
8211
 
      break;
8212
 
    }
8213
 
  }
8214
 
  mysql_mutex_unlock(&acl_cache->lock);
8215
 
 
8216
 
  if (!mpvio->acl_user)
8217
 
  {
8218
 
    login_failed_error(mpvio, mpvio->auth_info.password_used);
8219
 
    DBUG_RETURN (1);
8220
 
  }
8221
 
 
8222
 
  /* user account requires non-default plugin and the client is too old */
8223
 
  if (mpvio->acl_user->plugin.str != native_password_plugin_name.str &&
8224
 
      mpvio->acl_user->plugin.str != old_password_plugin_name.str &&
8225
 
      !(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
8226
 
  {
8227
 
    DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
8228
 
                              native_password_plugin_name.str));
8229
 
    DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
8230
 
                              old_password_plugin_name.str));
8231
 
    my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
8232
 
    general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
8233
 
    DBUG_RETURN (1);
8234
 
  }
8235
 
 
8236
 
  mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
8237
 
  mpvio->auth_info.auth_string_length= 
8238
 
    (unsigned long) mpvio->acl_user->auth_string.length;
8239
 
  strmake(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
8240
 
          mpvio->acl_user->user : "", USERNAME_LENGTH);
8241
 
  DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
8242
 
                      "plugin=%s",
8243
 
                      mpvio->auth_info.user_name,
8244
 
                      mpvio->auth_info.auth_string,
8245
 
                      mpvio->auth_info.authenticated_as,
8246
 
                      mpvio->acl_user->plugin.str));
8247
 
  DBUG_RETURN(0);
8248
 
}
8249
 
#endif
8250
 
 
8251
 
/* the packet format is described in send_change_user_packet() */
8252
 
static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
8253
 
{
8254
 
  NET *net= mpvio->net;
8255
 
 
8256
 
  char *user= (char*) net->read_pos;
8257
 
  char *end= user + packet_length;
8258
 
  /* Safe because there is always a trailing \0 at the end of the packet */
8259
 
  char *passwd= strend(user) + 1;
8260
 
  uint user_len= passwd - user - 1;
8261
 
  char *db= passwd;
8262
 
  char db_buff[NAME_LEN + 1];                 // buffer to store db in utf8
8263
 
  char user_buff[USERNAME_LENGTH + 1];        // buffer to store user in utf8
8264
 
  uint dummy_errors;
8265
 
 
8266
 
  DBUG_ENTER ("parse_com_change_user_packet");
8267
 
  if (passwd >= end)
8268
 
  {
8269
 
    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
8270
 
    DBUG_RETURN (1);
8271
 
  }
8272
 
 
8273
 
  /*
8274
 
    Old clients send null-terminated string as password; new clients send
8275
 
    the size (1 byte) + string (not null-terminated). Hence in case of empty
8276
 
    password both send '\0'.
8277
 
 
8278
 
    This strlen() can't be easily deleted without changing protocol.
8279
 
 
8280
 
    Cast *passwd to an unsigned char, so that it doesn't extend the sign for
8281
 
    *passwd > 127 and become 2**32-127+ after casting to uint.
8282
 
  */
8283
 
  uint passwd_len= (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION ?
8284
 
                    (uchar) (*passwd++) : strlen(passwd));
8285
 
 
8286
 
  db+= passwd_len + 1;
8287
 
  /*
8288
 
    Database name is always NUL-terminated, so in case of empty database
8289
 
    the packet must contain at least the trailing '\0'.
8290
 
  */
8291
 
  if (db >= end)
8292
 
  {
8293
 
    my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
8294
 
    DBUG_RETURN (1);
8295
 
  }
8296
 
 
8297
 
  uint db_len= strlen(db);
8298
 
 
8299
 
  char *ptr= db + db_len + 1;
8300
 
 
8301
 
  if (ptr + 1 < end)
8302
 
  {
8303
 
    if (mpvio->charset_adapter->init_client_charset(uint2korr(ptr)))
8304
 
      DBUG_RETURN(1);
8305
 
  }
8306
 
 
8307
 
 
8308
 
  /* Convert database and user names to utf8 */
8309
 
  db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
8310
 
                           db, db_len, mpvio->charset_adapter->charset(),
8311
 
                           &dummy_errors);
8312
 
  db_buff[db_len]= 0;
8313
 
 
8314
 
  user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
8315
 
                                  system_charset_info, user, user_len,
8316
 
                                  mpvio->charset_adapter->charset(),
8317
 
                                  &dummy_errors);
8318
 
  user_buff[user_len]= 0;
8319
 
 
8320
 
  /* we should not free mpvio->user here: it's saved by dispatch_command() */
8321
 
  if (!(mpvio->auth_info.user_name= my_strndup(user_buff, user_len, MYF(MY_WME))))
8322
 
    return 1;
8323
 
  mpvio->auth_info.user_name_length= user_len;
8324
 
 
8325
 
  if (make_lex_string_root(mpvio->mem_root, 
8326
 
                           &mpvio->db, db_buff, db_len, 0) == 0)
8327
 
    DBUG_RETURN(1); /* The error is set by make_lex_string(). */
8328
 
 
8329
 
  if (!initialized)
8330
 
  {
8331
 
    // if mysqld's been started with --skip-grant-tables option
8332
 
    strmake(mpvio->auth_info.authenticated_as, 
8333
 
            mpvio->auth_info.user_name, USERNAME_LENGTH);
8334
 
 
8335
 
    mpvio->status= MPVIO_EXT::SUCCESS;
8336
 
    DBUG_RETURN(0);
8337
 
  }
8338
 
 
8339
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
8340
 
  if (find_mpvio_user(mpvio))
8341
 
    DBUG_RETURN(1);
8342
 
 
8343
 
  char *client_plugin;
8344
 
  if (mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)
8345
 
  {
8346
 
    client_plugin= ptr + 2;
8347
 
    if (client_plugin >= end)
8348
 
    {
8349
 
      my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
8350
 
      DBUG_RETURN(1);
8351
 
    }
8352
 
  }
8353
 
  else
8354
 
  {
8355
 
    if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
8356
 
      client_plugin= native_password_plugin_name.str;
8357
 
    else
8358
 
    {
8359
 
      client_plugin=  old_password_plugin_name.str;
8360
 
      /*
8361
 
        For a passwordless accounts we use native_password_plugin.
8362
 
        But when an old 4.0 client connects to it, we change it to
8363
 
        old_password_plugin, otherwise MySQL will think that server 
8364
 
        and client plugins don't match.
8365
 
      */
8366
 
      if (mpvio->acl_user->salt_len == 0)
8367
 
        mpvio->acl_user_plugin= old_password_plugin_name;
8368
 
    }
8369
 
  }
8370
 
 
8371
 
  DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
8372
 
  /* 
8373
 
    Remember the data part of the packet, to present it to plugin in 
8374
 
    read_packet() 
8375
 
  */
8376
 
  mpvio->cached_client_reply.pkt= passwd;
8377
 
  mpvio->cached_client_reply.pkt_len= passwd_len;
8378
 
  mpvio->cached_client_reply.plugin= client_plugin;
8379
 
  mpvio->status= MPVIO_EXT::RESTART;
8380
 
#endif
8381
 
 
8382
 
  DBUG_RETURN (0);
8383
 
}
8384
 
 
8385
 
#ifndef EMBEDDED_LIBRARY
8386
 
 
8387
 
/** Get a string according to the protocol of the underlying buffer. */
8388
 
typedef char * (*get_proto_string_func_t) (char **, size_t *, size_t *);
8389
 
 
8390
 
/**
8391
 
  Get a string formatted according to the 4.1 version of the MySQL protocol.
8392
 
 
8393
 
  @param buffer[in, out]    Pointer to the user-supplied buffer to be scanned.
8394
 
  @param max_bytes_available[in, out]  Limit the bytes to scan.
8395
 
  @param string_length[out] The number of characters scanned not including
8396
 
                            the null character.
8397
 
 
8398
 
  @remark Strings are always null character terminated in this version of the
8399
 
          protocol.
8400
 
 
8401
 
  @remark The string_length does not include the terminating null character.
8402
 
          However, after the call, the buffer is increased by string_length+1
8403
 
          bytes, beyond the null character if there still available bytes to
8404
 
          scan.
8405
 
 
8406
 
  @return pointer to beginning of the string scanned.
8407
 
    @retval NULL The buffer content is malformed
8408
 
*/
8409
 
 
8410
 
static
8411
 
char *get_41_protocol_string(char **buffer,
8412
 
                             size_t *max_bytes_available,
8413
 
                             size_t *string_length)
8414
 
{
8415
 
  char *str= (char *)memchr(*buffer, '\0', *max_bytes_available);
8416
 
 
8417
 
  if (str == NULL)
8418
 
    return NULL;
8419
 
 
8420
 
  *string_length= (size_t)(str - *buffer);
8421
 
  *max_bytes_available-= *string_length + 1;
8422
 
  str= *buffer;
8423
 
  *buffer += *string_length + 1;
8424
 
 
8425
 
  return str;
8426
 
}
8427
 
 
8428
 
 
8429
 
/**
8430
 
  Get a string formatted according to the 4.0 version of the MySQL protocol.
8431
 
 
8432
 
  @param buffer[in, out]    Pointer to the user-supplied buffer to be scanned.
8433
 
  @param max_bytes_available[in, out]  Limit the bytes to scan.
8434
 
  @param string_length[out] The number of characters scanned not including
8435
 
                            the null character.
8436
 
 
8437
 
  @remark If there are not enough bytes left after the current position of
8438
 
          the buffer to satisfy the current string, the string is considered
8439
 
          to be empty and a pointer to empty_c_string is returned.
8440
 
 
8441
 
  @remark A string at the end of the packet is not null terminated.
8442
 
 
8443
 
  @return Pointer to beginning of the string scanned, or a pointer to a empty
8444
 
          string.
8445
 
*/
8446
 
static
8447
 
char *get_40_protocol_string(char **buffer,
8448
 
                             size_t *max_bytes_available,
8449
 
                             size_t *string_length)
8450
 
{
8451
 
  char *str;
8452
 
  size_t len;
8453
 
 
8454
 
  /* No bytes to scan left, treat string as empty. */
8455
 
  if ((*max_bytes_available) == 0)
8456
 
  {
8457
 
    *string_length= 0;
8458
 
    return empty_c_string;
8459
 
  }
8460
 
 
8461
 
  str= (char *) memchr(*buffer, '\0', *max_bytes_available);
8462
 
 
8463
 
  /*
8464
 
    If the string was not null terminated by the client,
8465
 
    the remainder of the packet is the string. Otherwise,
8466
 
    advance the buffer past the end of the null terminated
8467
 
    string.
8468
 
  */
8469
 
  if (str == NULL)
8470
 
    len= *string_length= *max_bytes_available;
8471
 
  else
8472
 
    len= (*string_length= (size_t)(str - *buffer)) + 1;
8473
 
 
8474
 
  str= *buffer;
8475
 
  *buffer+= len;
8476
 
  *max_bytes_available-= len;
8477
 
 
8478
 
  return str;
8479
 
}
8480
 
 
8481
 
/**
8482
 
  Get a length encoded string from a user-supplied buffer.
8483
 
 
8484
 
  @param buffer[in, out] The buffer to scan; updates position after scan.
8485
 
  @param max_bytes_available[in, out] Limit the number of bytes to scan
8486
 
  @param string_length[out] Number of characters scanned
8487
 
 
8488
 
  @remark In case the length is zero, then the total size of the string is
8489
 
    considered to be 1 byte; the size byte.
8490
 
 
8491
 
  @return pointer to first byte after the header in buffer.
8492
 
    @retval NULL The buffer content is malformed
8493
 
*/
8494
 
 
8495
 
static
8496
 
char *get_length_encoded_string(char **buffer,
8497
 
                                size_t *max_bytes_available,
8498
 
                                size_t *string_length)
8499
 
{
8500
 
  if (*max_bytes_available == 0)
8501
 
    return NULL;
8502
 
 
8503
 
  /* Do double cast to prevent overflow from signed / unsigned conversion */
8504
 
  size_t str_len= (size_t)(unsigned char)**buffer;
8505
 
 
8506
 
  /*
8507
 
    If the length encoded string has the length 0
8508
 
    the total size of the string is only one byte long (the size byte)
8509
 
  */
8510
 
  if (str_len == 0)
8511
 
  {
8512
 
    ++*buffer;
8513
 
    *string_length= 0;
8514
 
    /*
8515
 
      Return a pointer to the 0 character so the return value will be
8516
 
      an empty string.
8517
 
    */
8518
 
    return *buffer-1;
8519
 
  }
8520
 
 
8521
 
  if (str_len >= *max_bytes_available)
8522
 
    return NULL;
8523
 
 
8524
 
  char *str= *buffer+1;
8525
 
  *string_length= str_len;
8526
 
  *max_bytes_available-= *string_length + 1;
8527
 
  *buffer+= *string_length + 1;
8528
 
  return str;
8529
 
}
8530
 
#endif
8531
 
 
8532
 
 
8533
 
/* the packet format is described in send_client_reply_packet() */
8534
 
static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
8535
 
                                           uchar **buff, ulong pkt_len)
8536
 
{
8537
 
#ifndef EMBEDDED_LIBRARY
8538
 
  NET *net= mpvio->net;
8539
 
  char *end;
8540
 
  bool packet_has_required_size= false;
8541
 
  DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
8542
 
 
8543
 
  if (mpvio->connect_errors)
8544
 
    reset_host_errors(mpvio->ip);
8545
 
 
8546
 
  uint charset_code= 0;
8547
 
  end= (char *)net->read_pos;
8548
 
  /*
8549
 
    In order to safely scan a head for '\0' string terminators
8550
 
    we must keep track of how many bytes remain in the allocated
8551
 
    buffer or we might read past the end of the buffer.
8552
 
  */
8553
 
  size_t bytes_remaining_in_packet= pkt_len;
8554
 
  
8555
 
  /*
8556
 
    Peek ahead on the client capability packet and determine which version of
8557
 
    the protocol should be used.
8558
 
  */
8559
 
  if (bytes_remaining_in_packet < 2)
8560
 
    return packet_error;
8561
 
    
8562
 
  mpvio->client_capabilities= uint2korr(end);
8563
 
 
8564
 
  /*
8565
 
    JConnector only sends server capabilities before starting SSL
8566
 
    negotiation.  The below code is patch for this.
8567
 
  */
8568
 
  if (bytes_remaining_in_packet == 4 &&
8569
 
      mpvio->client_capabilities & CLIENT_SSL)
8570
 
  {
8571
 
    mpvio->client_capabilities= uint4korr(end);
8572
 
    mpvio->max_client_packet_length= 0xfffff;
8573
 
    charset_code= default_charset_info->number;
8574
 
    if (mpvio->charset_adapter->init_client_charset(charset_code))
8575
 
      return packet_error;
8576
 
    goto skip_to_ssl;
8577
 
  }
8578
 
  
8579
 
  if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
8580
 
    packet_has_required_size= bytes_remaining_in_packet >= 
8581
 
      AUTH_PACKET_HEADER_SIZE_PROTO_41;
8582
 
  else
8583
 
    packet_has_required_size= bytes_remaining_in_packet >=
8584
 
      AUTH_PACKET_HEADER_SIZE_PROTO_40;
8585
 
  
8586
 
  if (!packet_has_required_size)
8587
 
    return packet_error;
8588
 
  
8589
 
  if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
8590
 
  {
8591
 
    mpvio->client_capabilities= uint4korr(end);
8592
 
    mpvio->max_client_packet_length= uint4korr(end + 4);
8593
 
    charset_code= (uint)(uchar)*(end + 8);
8594
 
    /*
8595
 
      Skip 23 remaining filler bytes which have no particular meaning.
8596
 
    */
8597
 
    end+= AUTH_PACKET_HEADER_SIZE_PROTO_41;
8598
 
    bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_41;
8599
 
  }
8600
 
  else
8601
 
  {
8602
 
    mpvio->client_capabilities= uint2korr(end);
8603
 
    mpvio->max_client_packet_length= uint3korr(end + 2);
8604
 
    end+= AUTH_PACKET_HEADER_SIZE_PROTO_40;
8605
 
    bytes_remaining_in_packet-= AUTH_PACKET_HEADER_SIZE_PROTO_40;
8606
 
    /**
8607
 
      Old clients didn't have their own charset. Instead the assumption
8608
 
      was that they used what ever the server used.
8609
 
    */
8610
 
    charset_code= default_charset_info->number;
8611
 
  }
8612
 
 
8613
 
  DBUG_PRINT("info", ("client_character_set: %u", charset_code));
8614
 
  if (mpvio->charset_adapter->init_client_charset(charset_code))
8615
 
    return packet_error;
8616
 
 
8617
 
skip_to_ssl:
8618
 
#if defined(HAVE_OPENSSL)
8619
 
  DBUG_PRINT("info", ("client capabilities: %lu", mpvio->client_capabilities));
8620
 
  
8621
 
  /*
8622
 
    If client requested SSL then we must stop parsing, try to switch to SSL,
8623
 
    and wait for the client to send a new handshake packet.
8624
 
    The client isn't expected to send any more bytes until SSL is initialized.
8625
 
  */
8626
 
  if (mpvio->client_capabilities & CLIENT_SSL)
8627
 
  {
8628
 
    unsigned long errptr;
8629
 
 
8630
 
    /* Do the SSL layering. */
8631
 
    if (!ssl_acceptor_fd)
8632
 
      return packet_error;
8633
 
 
8634
 
    DBUG_PRINT("info", ("IO layer change in progress..."));
8635
 
    if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, &errptr))
8636
 
    {
8637
 
      DBUG_PRINT("error", ("Failed to accept new SSL connection"));
8638
 
      return packet_error;
8639
 
    }
8640
 
 
8641
 
    DBUG_PRINT("info", ("Reading user information over SSL layer"));
8642
 
    if ((pkt_len= my_net_read(net)) == packet_error)
8643
 
    {
8644
 
      DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
8645
 
                           pkt_len));
8646
 
      return packet_error;
8647
 
    }
8648
 
    /*
8649
 
      A new packet was read and the statistics reflecting the remaining bytes
8650
 
      in the packet must be updated.
8651
 
    */
8652
 
    bytes_remaining_in_packet= pkt_len;
8653
 
 
8654
 
    /*
8655
 
      After the SSL handshake is performed the client resends the handshake
8656
 
      packet but because of legacy reasons we chose not to parse the packet
8657
 
      fields a second time and instead only assert the length of the packet.
8658
 
    */
8659
 
    if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
8660
 
    {
8661
 
      packet_has_required_size= bytes_remaining_in_packet >= 
8662
 
        AUTH_PACKET_HEADER_SIZE_PROTO_41;
8663
 
      end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_41;
8664
 
      bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_41;
8665
 
    }
8666
 
    else
8667
 
    {
8668
 
      packet_has_required_size= bytes_remaining_in_packet >= 
8669
 
        AUTH_PACKET_HEADER_SIZE_PROTO_40;
8670
 
      end= (char *)net->read_pos + AUTH_PACKET_HEADER_SIZE_PROTO_40;
8671
 
      bytes_remaining_in_packet -= AUTH_PACKET_HEADER_SIZE_PROTO_40;
8672
 
    }
8673
 
    
8674
 
    if (!packet_has_required_size)
8675
 
      return packet_error;
8676
 
  }
8677
 
#endif /* HAVE_OPENSSL */
8678
 
 
8679
 
  if ((mpvio->client_capabilities & CLIENT_TRANSACTIONS) &&
8680
 
      opt_using_transactions)
8681
 
    net->return_status= mpvio->server_status;
8682
 
 
8683
 
  /*
8684
 
    The 4.0 and 4.1 versions of the protocol differ on how strings
8685
 
    are terminated. In the 4.0 version, if a string is at the end
8686
 
    of the packet, the string is not null terminated. Do not assume
8687
 
    that the returned string is always null terminated.
8688
 
  */
8689
 
  get_proto_string_func_t get_string;
8690
 
 
8691
 
  if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
8692
 
    get_string= get_41_protocol_string;
8693
 
  else
8694
 
    get_string= get_40_protocol_string;
8695
 
 
8696
 
  /*
8697
 
    In order to safely scan a head for '\0' string terminators
8698
 
    we must keep track of how many bytes remain in the allocated
8699
 
    buffer or we might read past the end of the buffer.
8700
 
  */
8701
 
  bytes_remaining_in_packet= pkt_len - (end - (char *)net->read_pos);
8702
 
 
8703
 
  size_t user_len;
8704
 
  char *user= get_string(&end, &bytes_remaining_in_packet, &user_len);
8705
 
  if (user == NULL)
8706
 
    return packet_error;
8707
 
 
8708
 
  /*
8709
 
    Old clients send a null-terminated string as password; new clients send
8710
 
    the size (1 byte) + string (not null-terminated). Hence in case of empty
8711
 
    password both send '\0'.
8712
 
  */
8713
 
  size_t passwd_len= 0;
8714
 
  char *passwd= NULL;
8715
 
 
8716
 
  if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
8717
 
  {
8718
 
    /*
8719
 
      4.1+ password. First byte is password length.
8720
 
    */
8721
 
    passwd= get_length_encoded_string(&end, &bytes_remaining_in_packet,
8722
 
                                      &passwd_len);
8723
 
  }
8724
 
  else
8725
 
  {
8726
 
    /*
8727
 
      Old passwords are zero terminated strings.
8728
 
    */
8729
 
    passwd= get_string(&end, &bytes_remaining_in_packet, &passwd_len);
8730
 
  }
8731
 
 
8732
 
  if (passwd == NULL)
8733
 
    return packet_error;
8734
 
 
8735
 
  size_t db_len= 0;
8736
 
  char *db= NULL;
8737
 
 
8738
 
  if (mpvio->client_capabilities & CLIENT_CONNECT_WITH_DB)
8739
 
  {
8740
 
    db= get_string(&end, &bytes_remaining_in_packet, &db_len);
8741
 
    if (db == NULL)
8742
 
      return packet_error;
8743
 
  }
8744
 
 
8745
 
  /*
8746
 
    Set the default for the password supplied flag for non-existing users
8747
 
    as the default plugin (native passsword authentication) would do it
8748
 
    for compatibility reasons.
8749
 
  */
8750
 
  if (passwd_len)
8751
 
    mpvio->auth_info.password_used= PASSWORD_USED_YES;
8752
 
 
8753
 
  size_t client_plugin_len= 0;
8754
 
  char *client_plugin= get_string(&end, &bytes_remaining_in_packet,
8755
 
                                  &client_plugin_len);
8756
 
  if (client_plugin == NULL)
8757
 
    client_plugin= &empty_c_string[0];
8758
 
 
8759
 
  char db_buff[NAME_LEN + 1];           // buffer to store db in utf8
8760
 
  char user_buff[USERNAME_LENGTH + 1];  // buffer to store user in utf8
8761
 
  uint dummy_errors;
8762
 
 
8763
 
 
8764
 
  /*
8765
 
    Copy and convert the user and database names to the character set used
8766
 
    by the server. Since 4.1 all database names are stored in UTF-8. Also,
8767
 
    ensure that the names are properly null-terminated as this is relied
8768
 
    upon later.
8769
 
  */
8770
 
  if (db)
8771
 
  {
8772
 
    db_len= copy_and_convert(db_buff, sizeof(db_buff) - 1, system_charset_info,
8773
 
                             db, db_len, mpvio->charset_adapter->charset(),
8774
 
                             &dummy_errors);
8775
 
    db_buff[db_len]= '\0';
8776
 
    db= db_buff;
8777
 
  }
8778
 
 
8779
 
  user_len= copy_and_convert(user_buff, sizeof(user_buff) - 1,
8780
 
                             system_charset_info, user, user_len,
8781
 
                             mpvio->charset_adapter->charset(),
8782
 
                             &dummy_errors);
8783
 
  user_buff[user_len]= '\0';
8784
 
  user= user_buff;
8785
 
 
8786
 
  /* If username starts and ends in "'", chop them off */
8787
 
  if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
8788
 
  {
8789
 
    user[user_len - 1]= 0;
8790
 
    user++;
8791
 
    user_len-= 2;
8792
 
  }
8793
 
 
8794
 
  if (make_lex_string_root(mpvio->mem_root, 
8795
 
                           &mpvio->db, db, db_len, 0) == 0)
8796
 
    return packet_error; /* The error is set by make_lex_string(). */
8797
 
  if (mpvio->auth_info.user_name)
8798
 
    my_free(mpvio->auth_info.user_name);
8799
 
  if (!(mpvio->auth_info.user_name= my_strndup(user, user_len, MYF(MY_WME))))
8800
 
    return packet_error; /* The error is set by my_strdup(). */
8801
 
  mpvio->auth_info.user_name_length= user_len;
8802
 
 
8803
 
  if (!initialized)
8804
 
  {
8805
 
    // if mysqld's been started with --skip-grant-tables option
8806
 
    mpvio->status= MPVIO_EXT::SUCCESS;
8807
 
    return packet_error;
8808
 
  }
8809
 
 
8810
 
  if (find_mpvio_user(mpvio))
8811
 
    return packet_error;
8812
 
 
8813
 
  if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
8814
 
  {
8815
 
    /*
8816
 
      An old client is connecting
8817
 
    */
8818
 
    if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
8819
 
      client_plugin= native_password_plugin_name.str;
8820
 
    else
8821
 
    {
8822
 
      /*
8823
 
        A really old client is connecting
8824
 
      */
8825
 
      client_plugin= old_password_plugin_name.str;
8826
 
      /*
8827
 
        For a passwordless accounts we use native_password_plugin.
8828
 
        But when an old 4.0 client connects to it, we change it to
8829
 
        old_password_plugin, otherwise MySQL will think that server 
8830
 
        and client plugins don't match.
8831
 
      */
8832
 
      if (mpvio->acl_user->salt_len == 0)
8833
 
        mpvio->acl_user_plugin= old_password_plugin_name;
8834
 
    }
8835
 
  }
8836
 
  
8837
 
  /*
8838
 
    if the acl_user needs a different plugin to authenticate
8839
 
    (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
8840
 
    we need to restart the authentication in the server.
8841
 
    But perhaps the client has already used the correct plugin -
8842
 
    in that case the authentication on the client may not need to be
8843
 
    restarted and a server auth plugin will read the data that the client
8844
 
    has just send. Cache them to return in the next server_mpvio_read_packet().
8845
 
  */
8846
 
  if (my_strcasecmp(system_charset_info, mpvio->acl_user_plugin.str,
8847
 
                    plugin_name(mpvio->plugin)->str) != 0)
8848
 
  {
8849
 
    mpvio->cached_client_reply.pkt= passwd;
8850
 
    mpvio->cached_client_reply.pkt_len= passwd_len;
8851
 
    mpvio->cached_client_reply.plugin= client_plugin;
8852
 
    mpvio->status= MPVIO_EXT::RESTART;
8853
 
    return packet_error;
8854
 
  }
8855
 
 
8856
 
  /*
8857
 
    ok, we don't need to restart the authentication on the server.
8858
 
    but if the client used the wrong plugin, we need to restart
8859
 
    the authentication on the client. Do it here, the server plugin
8860
 
    doesn't need to know.
8861
 
  */
8862
 
  const char *client_auth_plugin=
8863
 
    ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
8864
 
 
8865
 
  if (client_auth_plugin &&
8866
 
      my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
8867
 
  {
8868
 
    mpvio->cached_client_reply.plugin= client_plugin;
8869
 
    if (send_plugin_request_packet(mpvio,
8870
 
                                   (uchar*) mpvio->cached_server_packet.pkt,
8871
 
                                   mpvio->cached_server_packet.pkt_len))
8872
 
      return packet_error;
8873
 
 
8874
 
    passwd_len= my_net_read(mpvio->net);
8875
 
    passwd = (char*) mpvio->net->read_pos;
8876
 
  }
8877
 
 
8878
 
  *buff= (uchar*) passwd;
8879
 
  return passwd_len;
8880
 
#else
8881
 
  return 0;
8882
 
#endif
8883
 
}
8884
 
 
8885
 
 
8886
 
/**
8887
 
  Make sure that when sending plugin supplied data to the client they
8888
 
  are not considered a special out-of-band command, like e.g. 
8889
 
  \255 (error) or \254 (change user request packet) or \0 (OK).
8890
 
  To avoid this the server will send all plugin data packets "wrapped" 
8891
 
  in a command \1.
8892
 
  Note that the client will continue sending its replies unrwapped.
8893
 
*/
8894
 
 
8895
 
static inline int 
8896
 
wrap_plguin_data_into_proper_command(NET *net, 
8897
 
                                     const uchar *packet, int packet_len)
8898
 
{
8899
 
  return net_write_command(net, 1, (uchar *) "", 0, packet, packet_len);
8900
 
}
8901
 
 
8902
 
 
8903
 
/**
8904
 
  vio->write_packet() callback method for server authentication plugins
8905
 
 
8906
 
  This function is called by a server authentication plugin, when it wants
8907
 
  to send data to the client.
8908
 
 
8909
 
  It transparently wraps the data into a handshake packet,
8910
 
  and handles plugin negotiation with the client. If necessary,
8911
 
  it escapes the plugin data, if it starts with a mysql protocol packet byte.
8912
 
*/
8913
 
static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
8914
 
                                   const uchar *packet, int packet_len)
8915
 
{
8916
 
  MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
8917
 
  int res;
8918
 
 
8919
 
  DBUG_ENTER("server_mpvio_write_packet");
8920
 
  /* 
8921
 
    Reset cached_client_reply if not an old client doing mysql_change_user, 
8922
 
    as this is where the password from COM_CHANGE_USER is stored.
8923
 
  */
8924
 
  if (!((!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)) && 
8925
 
        mpvio->status == MPVIO_EXT::RESTART &&
8926
 
        mpvio->cached_client_reply.plugin == 
8927
 
        ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin
8928
 
        ))
8929
 
    mpvio->cached_client_reply.pkt= 0;
8930
 
  /* for the 1st packet we wrap plugin data into the handshake packet */
8931
 
  if (mpvio->packets_written == 0)
8932
 
    res= send_server_handshake_packet(mpvio, (char*) packet, packet_len);
8933
 
  else if (mpvio->status == MPVIO_EXT::RESTART)
8934
 
    res= send_plugin_request_packet(mpvio, packet, packet_len);
8935
 
  else
8936
 
    res= wrap_plguin_data_into_proper_command(mpvio->net, packet, packet_len);
8937
 
  mpvio->packets_written++;
8938
 
  DBUG_RETURN(res);
8939
 
}
8940
 
 
8941
 
/**
8942
 
  vio->read_packet() callback method for server authentication plugins
8943
 
 
8944
 
  This function is called by a server authentication plugin, when it wants
8945
 
  to read data from the client.
8946
 
 
8947
 
  It transparently extracts the client plugin data, if embedded into
8948
 
  a client authentication handshake packet, and handles plugin negotiation
8949
 
  with the client, if necessary.
8950
 
*/
8951
 
static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
8952
 
{
8953
 
  MPVIO_EXT *mpvio= (MPVIO_EXT *) param;
8954
 
  ulong pkt_len;
8955
 
 
8956
 
  DBUG_ENTER("server_mpvio_read_packet");
8957
 
  if (mpvio->packets_written == 0)
8958
 
  {
8959
 
    /*
8960
 
      plugin wants to read the data without sending anything first.
8961
 
      send an empty packet to force a server handshake packet to be sent
8962
 
    */
8963
 
    if (mpvio->write_packet(mpvio, 0, 0))
8964
 
      pkt_len= packet_error;
8965
 
    else
8966
 
      pkt_len= my_net_read(mpvio->net);
8967
 
  }
8968
 
  else if (mpvio->cached_client_reply.pkt)
8969
 
  {
8970
 
    DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
8971
 
    DBUG_ASSERT(mpvio->packets_read > 0);
8972
 
    /*
8973
 
      if the have the data cached from the last server_mpvio_read_packet
8974
 
      (which can be the case if it's a restarted authentication)
8975
 
      and a client has used the correct plugin, then we can return the
8976
 
      cached data straight away and avoid one round trip.
8977
 
    */
8978
 
    const char *client_auth_plugin=
8979
 
      ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
8980
 
    if (client_auth_plugin == 0 ||
8981
 
        my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
8982
 
                      client_auth_plugin) == 0)
8983
 
    {
8984
 
      mpvio->status= MPVIO_EXT::FAILURE;
8985
 
      *buf= (uchar*) mpvio->cached_client_reply.pkt;
8986
 
      mpvio->cached_client_reply.pkt= 0;
8987
 
      mpvio->packets_read++;
8988
 
      DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
8989
 
    }
8990
 
 
8991
 
    /* older clients don't support change of client plugin request */
8992
 
    if (!(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
8993
 
    {
8994
 
      mpvio->status= MPVIO_EXT::FAILURE;
8995
 
      pkt_len= packet_error;
8996
 
      goto err;
8997
 
    }
8998
 
 
8999
 
    /*
9000
 
      But if the client has used the wrong plugin, the cached data are
9001
 
      useless. Furthermore, we have to send a "change plugin" request
9002
 
      to the client.
9003
 
    */
9004
 
    if (mpvio->write_packet(mpvio, 0, 0))
9005
 
      pkt_len= packet_error;
9006
 
    else
9007
 
      pkt_len= my_net_read(mpvio->net);
9008
 
  }
9009
 
  else
9010
 
    pkt_len= my_net_read(mpvio->net);
9011
 
 
9012
 
  if (pkt_len == packet_error)
9013
 
    goto err;
9014
 
 
9015
 
  mpvio->packets_read++;
9016
 
 
9017
 
  /*
9018
 
    the 1st packet has the plugin data wrapped into the client authentication
9019
 
    handshake packet
9020
 
  */
9021
 
  if (mpvio->packets_read == 1)
9022
 
  {
9023
 
    pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
9024
 
    if (pkt_len == packet_error)
9025
 
      goto err;
9026
 
  }
9027
 
  else
9028
 
    *buf= mpvio->net->read_pos;
9029
 
 
9030
 
  DBUG_RETURN((int)pkt_len);
9031
 
 
9032
 
err:
9033
 
  if (mpvio->status == MPVIO_EXT::FAILURE)
9034
 
  {
9035
 
    inc_host_errors(mpvio->ip);
9036
 
    my_error(ER_HANDSHAKE_ERROR, MYF(0));
9037
 
  }
9038
 
  DBUG_RETURN(-1);
9039
 
}
9040
 
 
9041
 
/**
9042
 
  fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
9043
 
  connection
9044
 
*/
9045
 
static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
9046
 
                              MYSQL_PLUGIN_VIO_INFO *info)
9047
 
{
9048
 
  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
9049
 
  mpvio_info(mpvio->net->vio, info);
9050
 
}
9051
 
 
9052
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
9053
 
static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
9054
 
{
9055
 
#if defined(HAVE_OPENSSL)
9056
 
  Vio *vio= thd->net.vio;
9057
 
  SSL *ssl= (SSL *) vio->ssl_arg;
9058
 
  X509 *cert;
9059
 
#endif
9060
 
 
9061
 
  /*
9062
 
    At this point we know that user is allowed to connect
9063
 
    from given host by given username/password pair. Now
9064
 
    we check if SSL is required, if user is using SSL and
9065
 
    if X509 certificate attributes are OK
9066
 
  */
9067
 
  switch (acl_user->ssl_type) {
9068
 
  case SSL_TYPE_NOT_SPECIFIED:                  // Impossible
9069
 
  case SSL_TYPE_NONE:                           // SSL is not required
9070
 
    return 0;
9071
 
#if defined(HAVE_OPENSSL)
9072
 
  case SSL_TYPE_ANY:                            // Any kind of SSL is ok
9073
 
    return vio_type(vio) != VIO_TYPE_SSL;
9074
 
  case SSL_TYPE_X509: /* Client should have any valid certificate. */
9075
 
    /*
9076
 
      Connections with non-valid certificates are dropped already
9077
 
      in sslaccept() anyway, so we do not check validity here.
9078
 
 
9079
 
      We need to check for absence of SSL because without SSL
9080
 
      we should reject connection.
9081
 
    */
9082
 
    if (vio_type(vio) == VIO_TYPE_SSL &&
9083
 
        SSL_get_verify_result(ssl) == X509_V_OK &&
9084
 
        (cert= SSL_get_peer_certificate(ssl)))
9085
 
    {
9086
 
      X509_free(cert);
9087
 
      return 0;
9088
 
    }
9089
 
    return 1;
9090
 
  case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
9091
 
    /* If a cipher name is specified, we compare it to actual cipher in use. */
9092
 
    if (vio_type(vio) != VIO_TYPE_SSL ||
9093
 
        SSL_get_verify_result(ssl) != X509_V_OK)
9094
 
      return 1;
9095
 
    if (acl_user->ssl_cipher)
9096
 
    {
9097
 
      DBUG_PRINT("info", ("comparing ciphers: '%s' and '%s'",
9098
 
                         acl_user->ssl_cipher, SSL_get_cipher(ssl)));
9099
 
      if (strcmp(acl_user->ssl_cipher, SSL_get_cipher(ssl)))
9100
 
      {
9101
 
        if (global_system_variables.log_warnings)
9102
 
          sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
9103
 
                            acl_user->ssl_cipher, SSL_get_cipher(ssl));
9104
 
        return 1;
9105
 
      }
9106
 
    }
9107
 
    /* Prepare certificate (if exists) */
9108
 
    if (!(cert= SSL_get_peer_certificate(ssl)))
9109
 
      return 1;
9110
 
    /* If X509 issuer is specified, we check it... */
9111
 
    if (acl_user->x509_issuer)
9112
 
    {
9113
 
      char *ptr= X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
9114
 
      DBUG_PRINT("info", ("comparing issuers: '%s' and '%s'",
9115
 
                         acl_user->x509_issuer, ptr));
9116
 
      if (strcmp(acl_user->x509_issuer, ptr))
9117
 
      {
9118
 
        if (global_system_variables.log_warnings)
9119
 
          sql_print_information("X509 issuer mismatch: should be '%s' "
9120
 
                            "but is '%s'", acl_user->x509_issuer, ptr);
9121
 
        free(ptr);
9122
 
        X509_free(cert);
9123
 
        return 1;
9124
 
      }
9125
 
      free(ptr);
9126
 
    }
9127
 
    /* X509 subject is specified, we check it .. */
9128
 
    if (acl_user->x509_subject)
9129
 
    {
9130
 
      char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
9131
 
      DBUG_PRINT("info", ("comparing subjects: '%s' and '%s'",
9132
 
                         acl_user->x509_subject, ptr));
9133
 
      if (strcmp(acl_user->x509_subject, ptr))
9134
 
      {
9135
 
        if (global_system_variables.log_warnings)
9136
 
          sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
9137
 
                          acl_user->x509_subject, ptr);
9138
 
        free(ptr);
9139
 
        X509_free(cert);
9140
 
        return 1;
9141
 
      }
9142
 
      free(ptr);
9143
 
    }
9144
 
    X509_free(cert);
9145
 
    return 0;
9146
 
#else  /* HAVE_OPENSSL */
9147
 
  default:
9148
 
    /*
9149
 
      If we don't have SSL but SSL is required for this user the 
9150
 
      authentication should fail.
9151
 
    */
9152
 
    return 1;
9153
 
#endif /* HAVE_OPENSSL */
9154
 
  }
9155
 
  return 1;
9156
 
}
9157
 
#endif
9158
 
 
9159
 
 
9160
 
static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
9161
 
                        MPVIO_EXT *mpvio)
9162
 
{
9163
 
  int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
9164
 
  bool unlock_plugin= false;
9165
 
  plugin_ref plugin;
9166
 
 
9167
 
  if (auth_plugin_name->str == native_password_plugin_name.str)
9168
 
    plugin= native_password_plugin;
9169
 
  else
9170
 
#ifndef EMBEDDED_LIBRARY
9171
 
  if (auth_plugin_name->str == old_password_plugin_name.str)
9172
 
    plugin= old_password_plugin;
9173
 
  else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
9174
 
                                           MYSQL_AUTHENTICATION_PLUGIN)))
9175
 
    unlock_plugin= true;
9176
 
  else
9177
 
#endif
9178
 
    plugin= NULL;
9179
 
    
9180
 
  mpvio->plugin= plugin;
9181
 
  old_status= mpvio->status;
9182
 
  
9183
 
  if (plugin)
9184
 
  {
9185
 
    st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
9186
 
    res= auth->authenticate_user(mpvio, &mpvio->auth_info);
9187
 
 
9188
 
    if (unlock_plugin)
9189
 
      plugin_unlock(thd, plugin);
9190
 
  }
9191
 
  else
9192
 
  {
9193
 
    /* Server cannot load the required plugin. */
9194
 
    my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
9195
 
    res= CR_ERROR;
9196
 
  }
9197
 
 
9198
 
  /*
9199
 
    If the status was MPVIO_EXT::RESTART before the authenticate_user() call
9200
 
    it can never be MPVIO_EXT::RESTART after the call, because any call
9201
 
    to write_packet() or read_packet() will reset the status.
9202
 
 
9203
 
    But (!) if a plugin never called a read_packet() or write_packet(), the
9204
 
    status will stay unchanged. We'll fix it, by resetting the status here.
9205
 
  */
9206
 
  if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART)
9207
 
    mpvio->status= MPVIO_EXT::FAILURE; // reset to the default
9208
 
 
9209
 
  return res;
9210
 
}
9211
 
 
9212
 
 
9213
 
static void
9214
 
server_mpvio_initialize(THD *thd, MPVIO_EXT *mpvio, uint connect_errors,
9215
 
                        Thd_charset_adapter *charset_adapter)
9216
 
{
9217
 
  memset(mpvio, 0, sizeof(MPVIO_EXT));
9218
 
  mpvio->read_packet= server_mpvio_read_packet;
9219
 
  mpvio->write_packet= server_mpvio_write_packet;
9220
 
  mpvio->info= server_mpvio_info;
9221
 
  mpvio->auth_info.host_or_ip= thd->security_ctx->host_or_ip;
9222
 
  mpvio->auth_info.host_or_ip_length= 
9223
 
    (unsigned int) strlen(thd->security_ctx->host_or_ip);
9224
 
  mpvio->auth_info.user_name= NULL;
9225
 
  mpvio->auth_info.user_name_length= 0;
9226
 
  mpvio->connect_errors= connect_errors;
9227
 
  mpvio->status= MPVIO_EXT::FAILURE;
9228
 
 
9229
 
  mpvio->client_capabilities= thd->client_capabilities;
9230
 
  mpvio->mem_root= thd->mem_root;
9231
 
  mpvio->scramble= thd->scramble;
9232
 
  mpvio->rand= &thd->rand;
9233
 
  mpvio->thread_id= thd->thread_id;
9234
 
  mpvio->server_status= &thd->server_status;
9235
 
  mpvio->net= &thd->net;
9236
 
  mpvio->ip= thd->security_ctx->ip;
9237
 
  mpvio->host= thd->security_ctx->host;
9238
 
  mpvio->charset_adapter= charset_adapter;
9239
 
}
9240
 
 
9241
 
 
9242
 
static void
9243
 
server_mpvio_update_thd(THD *thd, MPVIO_EXT *mpvio)
9244
 
{
9245
 
  thd->client_capabilities= mpvio->client_capabilities;
9246
 
  thd->max_client_packet_length= mpvio->max_client_packet_length;
9247
 
  if (mpvio->client_capabilities & CLIENT_INTERACTIVE)
9248
 
    thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
9249
 
  thd->security_ctx->user= mpvio->auth_info.user_name;
9250
 
  if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
9251
 
    thd->variables.sql_mode|= MODE_IGNORE_SPACE;
9252
 
}
9253
 
 
9254
 
/**
9255
 
  Perform the handshake, authorize the client and update thd sctx variables.
9256
 
 
9257
 
  @param thd                     thread handle
9258
 
  @param connect_errors          number of previous failed connect attemps
9259
 
                                 from this host
9260
 
  @param com_change_user_pkt_len size of the COM_CHANGE_USER packet
9261
 
                                 (without the first, command, byte) or 0
9262
 
                                 if it's not a COM_CHANGE_USER (that is, if
9263
 
                                 it's a new connection)
9264
 
 
9265
 
  @retval 0  success, thd is updated.
9266
 
  @retval 1  error
9267
 
*/
9268
 
bool 
9269
 
acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len)
9270
 
{
9271
 
  int res= CR_OK;
9272
 
  MPVIO_EXT mpvio;
9273
 
  Thd_charset_adapter charset_adapter(thd);
9274
 
 
9275
 
  const LEX_STRING *auth_plugin_name= default_auth_plugin_name;
9276
 
  enum  enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
9277
 
                                                             : COM_CONNECT;
9278
 
 
9279
 
  DBUG_ENTER("acl_authenticate");
9280
 
  compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
9281
 
 
9282
 
  server_mpvio_initialize(thd, &mpvio, connect_errors, &charset_adapter);
9283
 
 
9284
 
  DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
9285
 
 
9286
 
  /*
9287
 
    Clear thd->db as it points to something, that will be freed when
9288
 
    connection is closed. We don't want to accidentally free a wrong
9289
 
    pointer if connect failed.
9290
 
  */
9291
 
  thd->reset_db(NULL, 0);
9292
 
 
9293
 
  if (command == COM_CHANGE_USER)
9294
 
  {
9295
 
    mpvio.packets_written++; // pretend that a server handshake packet was sent
9296
 
    mpvio.packets_read++;    // take COM_CHANGE_USER packet into account
9297
 
 
9298
 
    /* Clear variables that are allocated */
9299
 
    thd->set_user_connect(NULL);
9300
 
 
9301
 
    if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
9302
 
    {
9303
 
      server_mpvio_update_thd(thd, &mpvio);
9304
 
      DBUG_RETURN(1);
9305
 
    }
9306
 
 
9307
 
    DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
9308
 
                mpvio.status == MPVIO_EXT::SUCCESS);
9309
 
  }
9310
 
  else
9311
 
  {
9312
 
    /* mark the thd as having no scramble yet */
9313
 
    mpvio.scramble[SCRAMBLE_LENGTH]= 1;
9314
 
    
9315
 
    /*
9316
 
     perform the first authentication attempt, with the default plugin.
9317
 
     This sends the server handshake packet, reads the client reply
9318
 
     with a user name, and performs the authentication if everyone has used
9319
 
     the correct plugin.
9320
 
    */
9321
 
 
9322
 
    res= do_auth_once(thd, auth_plugin_name, &mpvio);  
9323
 
  }
9324
 
 
9325
 
  /*
9326
 
   retry the authentication, if - after receiving the user name -
9327
 
   we found that we need to switch to a non-default plugin
9328
 
  */
9329
 
  if (mpvio.status == MPVIO_EXT::RESTART)
9330
 
  {
9331
 
    DBUG_ASSERT(mpvio.acl_user);
9332
 
    DBUG_ASSERT(command == COM_CHANGE_USER ||
9333
 
                my_strcasecmp(system_charset_info, auth_plugin_name->str,
9334
 
                              mpvio.acl_user->plugin.str));
9335
 
    auth_plugin_name= &mpvio.acl_user->plugin;
9336
 
    res= do_auth_once(thd, auth_plugin_name, &mpvio);
9337
 
  }
9338
 
 
9339
 
  server_mpvio_update_thd(thd, &mpvio);
9340
 
 
9341
 
  Security_context *sctx= thd->security_ctx;
9342
 
  const ACL_USER *acl_user= mpvio.acl_user;
9343
 
 
9344
 
  thd->password= mpvio.auth_info.password_used;  // remember for error messages 
9345
 
 
9346
 
  /*
9347
 
    Log the command here so that the user can check the log
9348
 
    for the tried logins and also to detect break-in attempts.
9349
 
 
9350
 
    if sctx->user is unset it's protocol failure, bad packet.
9351
 
  */
9352
 
  if (mpvio.auth_info.user_name)
9353
 
  {
9354
 
    if (strcmp(mpvio.auth_info.authenticated_as, mpvio.auth_info.user_name))
9355
 
    {
9356
 
      general_log_print(thd, command, "%s@%s as %s on %s",
9357
 
                        mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
9358
 
                        mpvio.auth_info.authenticated_as ? 
9359
 
                          mpvio.auth_info.authenticated_as : "anonymous",
9360
 
                        mpvio.db.str ? mpvio.db.str : (char*) "");
9361
 
    }
9362
 
    else
9363
 
      general_log_print(thd, command, (char*) "%s@%s on %s",
9364
 
                        mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
9365
 
                        mpvio.db.str ? mpvio.db.str : (char*) "");
9366
 
  }
9367
 
 
9368
 
  if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
9369
 
  {
9370
 
    DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
9371
 
 
9372
 
    if (!thd->is_error())
9373
 
      login_failed_error(&mpvio, mpvio.auth_info.password_used);
9374
 
    DBUG_RETURN (1);
9375
 
  }
9376
 
 
9377
 
  sctx->proxy_user[0]= 0;
9378
 
 
9379
 
  if (initialized) // if not --skip-grant-tables
9380
 
  {
9381
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
9382
 
    bool is_proxy_user= FALSE;
9383
 
    const char *auth_user = acl_user->user ? acl_user->user : "";
9384
 
    ACL_PROXY_USER *proxy_user;
9385
 
    /* check if the user is allowed to proxy as another user */
9386
 
    proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
9387
 
                                    mpvio.auth_info.authenticated_as,
9388
 
                                          &is_proxy_user);
9389
 
    if (is_proxy_user)
9390
 
    {
9391
 
      ACL_USER *acl_proxy_user;
9392
 
 
9393
 
      /* we need to find the proxy user, but there was none */
9394
 
      if (!proxy_user)
9395
 
      {
9396
 
        if (!thd->is_error())
9397
 
          login_failed_error(&mpvio, mpvio.auth_info.password_used);
9398
 
        DBUG_RETURN(1);
9399
 
      }
9400
 
 
9401
 
      my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
9402
 
                  "'%s'@'%s'", auth_user,
9403
 
                  acl_user->host.hostname ? acl_user->host.hostname : "");
9404
 
 
9405
 
      /* we're proxying : find the proxy user definition */
9406
 
      mysql_mutex_lock(&acl_cache->lock);
9407
 
      acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ? 
9408
 
                                    proxy_user->get_proxied_host() : "",
9409
 
                                    mpvio.auth_info.authenticated_as, TRUE);
9410
 
      if (!acl_proxy_user)
9411
 
      {
9412
 
        if (!thd->is_error())
9413
 
          login_failed_error(&mpvio, mpvio.auth_info.password_used);
9414
 
        mysql_mutex_unlock(&acl_cache->lock);
9415
 
        DBUG_RETURN(1);
9416
 
      }
9417
 
      acl_user= acl_proxy_user->copy(thd->mem_root);
9418
 
      mysql_mutex_unlock(&acl_cache->lock);
9419
 
    }
9420
 
#endif
9421
 
 
9422
 
    sctx->master_access= acl_user->access;
9423
 
    if (acl_user->user)
9424
 
      strmake(sctx->priv_user, acl_user->user, USERNAME_LENGTH - 1);
9425
 
    else
9426
 
      *sctx->priv_user= 0;
9427
 
 
9428
 
    if (acl_user->host.hostname)
9429
 
      strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
9430
 
    else
9431
 
      *sctx->priv_host= 0;
9432
 
 
9433
 
#ifndef NO_EMBEDDED_ACCESS_CHECKS
9434
 
    /*
9435
 
      OK. Let's check the SSL. Historically it was checked after the password,
9436
 
      as an additional layer, not instead of the password
9437
 
      (in which case it would've been a plugin too).
9438
 
    */
9439
 
    if (acl_check_ssl(thd, acl_user))
9440
 
    {
9441
 
      if (!thd->is_error())
9442
 
        login_failed_error(&mpvio, thd->password);
9443
 
      DBUG_RETURN(1);
9444
 
    }
9445
 
 
9446
 
    /* Don't allow the user to connect if he has done too many queries */
9447
 
    if ((acl_user->user_resource.questions || acl_user->user_resource.updates ||
9448
 
         acl_user->user_resource.conn_per_hour ||
9449
 
         acl_user->user_resource.user_conn || 
9450
 
         global_system_variables.max_user_connections) &&
9451
 
        get_or_create_user_conn(thd,
9452
 
          (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
9453
 
          (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
9454
 
          &acl_user->user_resource))
9455
 
      DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
9456
 
 
9457
 
#endif
9458
 
  }
9459
 
  else
9460
 
    sctx->skip_grants();
9461
 
 
9462
 
  const USER_CONN *uc;
9463
 
  if ((uc= thd->get_user_connect()) &&
9464
 
      (uc->user_resources.conn_per_hour || uc->user_resources.user_conn ||
9465
 
       global_system_variables.max_user_connections) &&
9466
 
       check_for_max_user_connections(thd, uc))
9467
 
  {
9468
 
    DBUG_RETURN(1); // The error is set in check_for_max_user_connections()
9469
 
  }
9470
 
 
9471
 
  DBUG_PRINT("info",
9472
 
             ("Capabilities: %lu  packet_length: %ld  Host: '%s'  "
9473
 
              "Login user: '%s' Priv_user: '%s'  Using password: %s "
9474
 
              "Access: %lu  db: '%s'",
9475
 
              thd->client_capabilities, thd->max_client_packet_length,
9476
 
              sctx->host_or_ip, sctx->user, sctx->priv_user,
9477
 
              thd->password ? "yes": "no",
9478
 
              sctx->master_access, mpvio.db.str));
9479
 
 
9480
 
  if (command == COM_CONNECT &&
9481
 
      !(thd->main_security_ctx.master_access & SUPER_ACL))
9482
 
  {
9483
 
    mysql_mutex_lock(&LOCK_connection_count);
9484
 
    bool count_ok= (connection_count <= max_connections);
9485
 
    mysql_mutex_unlock(&LOCK_connection_count);
9486
 
    if (!count_ok)
9487
 
    {                                         // too many connections
9488
 
      release_user_connection(thd);
9489
 
      my_error(ER_CON_COUNT_ERROR, MYF(0));
9490
 
      DBUG_RETURN(1);
9491
 
    }
9492
 
  }
9493
 
 
9494
 
  /*
9495
 
    This is the default access rights for the current database.  It's
9496
 
    set to 0 here because we don't have an active database yet (and we
9497
 
    may not have an active database to set.
9498
 
  */
9499
 
  sctx->db_access=0;
9500
 
 
9501
 
  /* Change a database if necessary */
9502
 
  if (mpvio.db.length)
9503
 
  {
9504
 
    if (mysql_change_db(thd, &mpvio.db, FALSE))
9505
 
    {
9506
 
      /* mysql_change_db() has pushed the error message. */
9507
 
      release_user_connection(thd);
9508
 
      DBUG_RETURN(1);
9509
 
    }
9510
 
  }
9511
 
 
9512
 
  if (mpvio.auth_info.external_user[0])
9513
 
    sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));
9514
 
 
9515
 
  if (res == CR_OK_HANDSHAKE_COMPLETE)
9516
 
    thd->stmt_da->disable_status();
9517
 
  else
9518
 
    my_ok(thd);
9519
 
 
9520
 
#if defined(MYSQL_SERVER) && !defined(EMBEDDED_LIBRARY)
9521
 
  /*
9522
 
    Allow the network layer to skip big packets. Although a malicious
9523
 
    authenticated session might use this to trick the server to read
9524
 
    big packets indefinitely, this is a previously established behavior
9525
 
    that needs to be preserved as to not break backwards compatibility.
9526
 
  */
9527
 
  thd->net.skip_big_packet= TRUE;
9528
 
#endif
9529
 
 
9530
 
  /* Ready to handle queries */
9531
 
  DBUG_RETURN(0);
9532
 
}
9533
 
 
9534
 
/**
9535
 
  MySQL Server Password Authentication Plugin
9536
 
 
9537
 
  In the MySQL authentication protocol:
9538
 
  1. the server sends the random scramble to the client
9539
 
  2. client sends the encrypted password back to the server
9540
 
  3. the server checks the password.
9541
 
*/
9542
 
static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
9543
 
                                        MYSQL_SERVER_AUTH_INFO *info)
9544
 
{
9545
 
  uchar *pkt;
9546
 
  int pkt_len;
9547
 
  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
9548
 
 
9549
 
  DBUG_ENTER("native_password_authenticate");
9550
 
 
9551
 
  /* generate the scramble, or reuse the old one */
9552
 
  if (mpvio->scramble[SCRAMBLE_LENGTH])
9553
 
    create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
9554
 
 
9555
 
  /* send it to the client */
9556
 
  if (mpvio->write_packet(mpvio, (uchar*) mpvio->scramble, SCRAMBLE_LENGTH + 1))
9557
 
    DBUG_RETURN(CR_ERROR);
9558
 
 
9559
 
  /* reply and authenticate */
9560
 
 
9561
 
  /*
9562
 
    <digression>
9563
 
      This is more complex than it looks.
9564
 
 
9565
 
      The plugin (we) may be called right after the client was connected -
9566
 
      and will need to send a scramble, read reply, authenticate.
9567
 
 
9568
 
      Or the plugin may be called after another plugin has sent a scramble,
9569
 
      and read the reply. If the client has used the correct client-plugin,
9570
 
      we won't need to read anything here from the client, the client
9571
 
      has already sent a reply with everything we need for authentication.
9572
 
 
9573
 
      Or the plugin may be called after another plugin has sent a scramble,
9574
 
      and read the reply, but the client has used the wrong client-plugin.
9575
 
      We'll need to sent a "switch to another plugin" packet to the
9576
 
      client and read the reply. "Use the short scramble" packet is a special
9577
 
      case of "switch to another plugin" packet.
9578
 
 
9579
 
      Or, perhaps, the plugin may be called after another plugin has
9580
 
      done the handshake but did not send a useful scramble. We'll need
9581
 
      to send a scramble (and perhaps a "switch to another plugin" packet)
9582
 
      and read the reply.
9583
 
 
9584
 
      Besides, a client may be an old one, that doesn't understand plugins.
9585
 
      Or doesn't even understand 4.0 scramble.
9586
 
 
9587
 
      And we want to keep the same protocol on the wire  unless non-native
9588
 
      plugins are involved.
9589
 
 
9590
 
      Anyway, it still looks simple from a plugin point of view:
9591
 
      "send the scramble, read the reply and authenticate"
9592
 
      All the magic is transparently handled by the server.
9593
 
    </digression>
9594
 
  */
9595
 
 
9596
 
  /* read the reply with the encrypted password */
9597
 
  if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
9598
 
    DBUG_RETURN(CR_ERROR);
9599
 
  DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
9600
 
 
9601
 
#ifdef NO_EMBEDDED_ACCESS_CHECKS
9602
 
  DBUG_RETURN(CR_OK);
9603
 
#endif
9604
 
 
9605
 
  if (pkt_len == 0) /* no password */
9606
 
    DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_ERROR : CR_OK);
9607
 
 
9608
 
  info->password_used= PASSWORD_USED_YES;
9609
 
  if (pkt_len == SCRAMBLE_LENGTH)
9610
 
  {
9611
 
    if (!mpvio->acl_user->salt_len)
9612
 
      DBUG_RETURN(CR_ERROR);
9613
 
 
9614
 
    DBUG_RETURN(check_scramble(pkt, mpvio->scramble, mpvio->acl_user->salt) ?
9615
 
                CR_ERROR : CR_OK);
9616
 
  }
9617
 
 
9618
 
  inc_host_errors(mpvio->ip);
9619
 
  my_error(ER_HANDSHAKE_ERROR, MYF(0));
9620
 
  DBUG_RETURN(CR_ERROR);
9621
 
}
9622
 
 
9623
 
static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, 
9624
 
                                     MYSQL_SERVER_AUTH_INFO *info)
9625
 
{
9626
 
  uchar *pkt;
9627
 
  int pkt_len;
9628
 
  MPVIO_EXT *mpvio= (MPVIO_EXT *) vio;
9629
 
 
9630
 
  /* generate the scramble, or reuse the old one */
9631
 
  if (mpvio->scramble[SCRAMBLE_LENGTH])
9632
 
    create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
9633
 
 
9634
 
  /* send it to the client */
9635
 
  if (mpvio->write_packet(mpvio, (uchar*) mpvio->scramble, SCRAMBLE_LENGTH + 1))
9636
 
    return CR_ERROR;
9637
 
 
9638
 
  /* read the reply and authenticate */
9639
 
  if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
9640
 
    return CR_ERROR;
9641
 
 
9642
 
#ifdef NO_EMBEDDED_ACCESS_CHECKS
9643
 
  return CR_OK;
9644
 
#endif
9645
 
 
9646
 
  /*
9647
 
    legacy: if switch_from_long_to_short_scramble,
9648
 
    the password is sent \0-terminated, the pkt_len is always 9 bytes.
9649
 
    We need to figure out the correct scramble length here.
9650
 
  */
9651
 
  if (pkt_len == SCRAMBLE_LENGTH_323 + 1)
9652
 
    pkt_len= strnlen((char*)pkt, pkt_len);
9653
 
 
9654
 
  if (pkt_len == 0) /* no password */
9655
 
    return mpvio->acl_user->salt_len != 0 ? CR_ERROR : CR_OK;
9656
 
 
9657
 
  if (secure_auth(mpvio))
9658
 
    return CR_ERROR;
9659
 
 
9660
 
  info->password_used= PASSWORD_USED_YES;
9661
 
 
9662
 
  if (pkt_len == SCRAMBLE_LENGTH_323)
9663
 
  {
9664
 
    if (!mpvio->acl_user->salt_len)
9665
 
      return CR_ERROR;
9666
 
 
9667
 
    return check_scramble_323(pkt, mpvio->scramble,
9668
 
                             (ulong *) mpvio->acl_user->salt) ? 
9669
 
                             CR_ERROR : CR_OK;
9670
 
  }
9671
 
 
9672
 
  inc_host_errors(mpvio->ip);
9673
 
  my_error(ER_HANDSHAKE_ERROR, MYF(0));
9674
 
  return CR_ERROR;
9675
 
}
9676
 
 
9677
 
static struct st_mysql_auth native_password_handler=
9678
 
{
9679
 
  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
9680
 
  native_password_plugin_name.str,
9681
 
  native_password_authenticate
9682
 
};
9683
 
 
9684
 
static struct st_mysql_auth old_password_handler=
9685
 
{
9686
 
  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
9687
 
  old_password_plugin_name.str,
9688
 
  old_password_authenticate
9689
 
};
9690
 
 
9691
 
mysql_declare_plugin(mysql_password)
9692
 
{
9693
 
  MYSQL_AUTHENTICATION_PLUGIN,                  /* type constant    */
9694
 
  &native_password_handler,                     /* type descriptor  */
9695
 
  native_password_plugin_name.str,              /* Name             */
9696
 
  "R.J.Silk, Sergei Golubchik",                 /* Author           */
9697
 
  "Native MySQL authentication",                /* Description      */
9698
 
  PLUGIN_LICENSE_GPL,                           /* License          */
9699
 
  NULL,                                         /* Init function    */
9700
 
  NULL,                                         /* Deinit function  */
9701
 
  0x0100,                                       /* Version (1.0)    */
9702
 
  NULL,                                         /* status variables */
9703
 
  NULL,                                         /* system variables */
9704
 
  NULL,                                         /* config options   */
9705
 
  0,                                            /* flags            */
9706
 
},
9707
 
{
9708
 
  MYSQL_AUTHENTICATION_PLUGIN,                  /* type constant    */
9709
 
  &old_password_handler,                        /* type descriptor  */
9710
 
  old_password_plugin_name.str,                 /* Name             */
9711
 
  "R.J.Silk, Sergei Golubchik",                 /* Author           */
9712
 
  "Old MySQL-4.0 authentication",               /* Description      */
9713
 
  PLUGIN_LICENSE_GPL,                           /* License          */
9714
 
  NULL,                                         /* Init function    */
9715
 
  NULL,                                         /* Deinit function  */
9716
 
  0x0100,                                       /* Version (1.0)    */
9717
 
  NULL,                                         /* status variables */
9718
 
  NULL,                                         /* system variables */
9719
 
  NULL,                                         /* config options   */
9720
 
  0,                                            /* flags            */
9721
 
}
9722
 
mysql_declare_plugin_end;
9723