summaryrefslogtreecommitdiff
path: root/sql/sql_acl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_acl.cc')
-rw-r--r--sql/sql_acl.cc1356
1 files changed, 984 insertions, 372 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 56884ad9dd1..b76bbe8c730 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -1,15 +1,15 @@
-/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
-
+/* Copyright (C) 2000-2003 MySQL AB
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
@@ -28,25 +28,29 @@
#include "mysql_priv.h"
#include "sql_acl.h"
#include "hash_filo.h"
+#ifdef HAVE_REPLICATION
+#include "sql_repl.h" //for tables_ok()
+#endif
#include <m_ctype.h>
+#include <assert.h>
#include <stdarg.h>
-/*
- ACL_HOST is used if no host is specified
- */
-
struct acl_host_and_ip
{
char *hostname;
long ip,ip_mask; // Used with masked ip:s
};
+
class ACL_ACCESS {
public:
ulong sort;
- uint access;
+ ulong access;
};
+
+/* ACL_HOST is used if no host is specified */
+
class ACL_HOST :public ACL_ACCESS
{
public:
@@ -54,15 +58,20 @@ public:
char *db;
};
+
class ACL_USER :public ACL_ACCESS
{
public:
acl_host_and_ip host;
uint hostname_length;
+ USER_RESOURCES user_resource;
char *user,*password;
ulong salt[2];
+ enum SSL_type ssl_type;
+ const char *ssl_cipher, *x509_issuer, *x509_subject;
};
+
class ACL_DB :public ACL_ACCESS
{
public:
@@ -70,14 +79,16 @@ public:
char *user,*db;
};
+
class acl_entry :public hash_filo_element
{
public:
- uint access;
+ ulong access;
uint16 length;
char key[1]; // Key will be stored here
};
+
static byte* acl_entry_get_key(acl_entry *entry,uint *length,
my_bool not_used __attribute__((unused)))
{
@@ -95,7 +106,7 @@ static HASH acl_check_hosts, hash_tables;
static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
static uint grant_version=0;
-static uint get_access(TABLE *form,uint fieldnr);
+static ulong get_access(TABLE *form,uint fieldnr);
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
@@ -103,32 +114,50 @@ static ACL_USER *find_acl_user(const char *host, const char *user);
static bool update_user_table(THD *thd, const char *host, const char *user,
const char *new_password);
static void update_hostname(acl_host_and_ip *host, const char *hostname);
-static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
+static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
const char *ip);
-int acl_init(bool dont_read_acl_tables)
+/*
+ Read grant privileges from the privilege tables in the 'mysql' database.
+
+ SYNOPSIS
+ acl_init()
+ thd Thread handler
+ dont_read_acl_tables Set to 1 if run with --skip-grant
+
+ RETURN VALUES
+ 0 ok
+ 1 Could not initialize grant's
+*/
+
+
+my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
{
THD *thd;
TABLE_LIST tables[3];
TABLE *table;
READ_RECORD read_record_info;
+ MYSQL_LOCK *lock;
+ my_bool return_val=1;
DBUG_ENTER("acl_init");
if (!acl_cache)
acl_cache=new hash_filo(ACL_CACHE_SIZE,0,0,
(hash_get_key) acl_entry_get_key,
- (void (*)(void*)) free);
+ (hash_free_key) free);
if (dont_read_acl_tables)
DBUG_RETURN(0); /* purecov: tested */
+ /*
+ To be able to run this from boot, we allocate a temporary THD
+ */
if (!(thd=new THD))
DBUG_RETURN(1); /* purecov: inspected */
+ thd->store_globals();
+
acl_cache->clear(1); // Clear locked hostname cache
- thd->version=refresh_version;
- thd->mysys_var=my_thread_var;
- thd->current_tablenr=0;
- thd->open_tables=0;
- thd->db=my_strdup("mysql",MYF(0));
+ thd->db= my_strdup("mysql",MYF(0));
+ thd->db_length=5; // Safety
bzero((char*) &tables,sizeof(tables));
tables[0].alias=tables[0].real_name=(char*) "host";
tables[1].alias=tables[1].real_name=(char*) "user";
@@ -140,22 +169,20 @@ int acl_init(bool dont_read_acl_tables)
if (open_tables(thd,tables))
{
- close_thread_tables(thd); /* purecov: inspected */
- delete thd; /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
+ sql_print_error("Fatal error: Can't open privilege tables: %s",
+ thd->net.last_error);
+ goto end;
}
TABLE *ptr[3]; // Lock tables for quick update
ptr[0]= tables[0].table;
ptr[1]= tables[1].table;
ptr[2]= tables[2].table;
- MYSQL_LOCK *lock=mysql_lock_tables(thd,ptr,3);
- if (!lock)
+ if (!(lock=mysql_lock_tables(thd,ptr,3)))
{
- close_thread_tables(thd); /* purecov: inspected */
- delete thd; /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
+ sql_print_error("Fatal error: Can't lock privilege tables: %s",
+ thd->net.last_error);
+ goto end;
}
-
init_sql_alloc(&mem,1024,0);
init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0);
VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
@@ -163,15 +190,15 @@ int acl_init(bool dont_read_acl_tables)
{
ACL_HOST host;
update_hostname(&host.host,get_field(&mem, table,0));
- host.db=get_field(&mem, table,1);
- host.access=get_access(table,2);
- host.access=fix_rights_for_db(host.access);
- host.sort=get_sort(2,host.host.hostname,host.db);
+ host.db= get_field(&mem, table,1);
+ host.access= get_access(table,2);
+ host.access= fix_rights_for_db(host.access);
+ host.sort= get_sort(2,host.host.hostname,host.db);
#ifndef TO_BE_REMOVED
if (table->fields == 8)
{ // Without grant
if (host.access & CREATE_ACL)
- host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
+ host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
}
#endif
VOID(push_dynamic(&acl_hosts,(gptr) &host));
@@ -186,11 +213,11 @@ int acl_init(bool dont_read_acl_tables)
if (table->field[2]->field_length == 8 &&
protocol_version == PROTOCOL_VERSION)
{
- sql_print_error(
- "Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */
+ sql_print_error("Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */
protocol_version=9; /* purecov: tested */
}
+ DBUG_PRINT("info",("user table fields: %d",table->fields));
allow_all_hosts=0;
while (!(read_record_info.read_record(&read_record_info)))
{
@@ -203,7 +230,7 @@ int acl_init(bool dont_read_acl_tables)
protocol_version == PROTOCOL_VERSION)
{
sql_print_error(
- "Found old style password for user '%s'. Ignoring user. (You may want to restart using --old-protocol)",
+ "Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)",
user.user ? user.user : ""); /* purecov: tested */
}
else if (length % 8 || length > 16)
@@ -212,19 +239,57 @@ int acl_init(bool dont_read_acl_tables)
"Found invalid password for user: '%s'@'%s'; Ignoring user",
user.user ? user.user : "",
user.host.hostname ? user.host.hostname : ""); /* purecov: tested */
- continue; /* purecov: tested */
+ continue; /* purecov: tested */
}
get_salt_from_password(user.salt,user.password);
user.access=get_access(table,3) & GLOBAL_ACLS;
user.sort=get_sort(2,user.host.hostname,user.user);
- user.hostname_length=user.host.hostname ? (uint) strlen(user.host.hostname) : 0;
-#ifndef TO_BE_REMOVED
- if (table->fields <= 13)
- { // Without grant
- if (user.access & CREATE_ACL)
- user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
+ user.hostname_length= (user.host.hostname ?
+ (uint) strlen(user.host.hostname) : 0);
+ if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */
+ {
+ char *ssl_type=get_field(&mem, table, 24);
+ if (!ssl_type)
+ user.ssl_type=SSL_TYPE_NONE;
+ else if (!strcmp(ssl_type, "ANY"))
+ user.ssl_type=SSL_TYPE_ANY;
+ else if (!strcmp(ssl_type, "X509"))
+ user.ssl_type=SSL_TYPE_X509;
+ else /* !strcmp(ssl_type, "SPECIFIED") */
+ user.ssl_type=SSL_TYPE_SPECIFIED;
+
+ user.ssl_cipher= get_field(&mem, table, 25);
+ user.x509_issuer= get_field(&mem, table, 26);
+ user.x509_subject= get_field(&mem, table, 27);
+
+ char *ptr = get_field(&mem, table, 28);
+ user.user_resource.questions=atoi(ptr);
+ ptr = get_field(&mem, table, 29);
+ user.user_resource.updates=atoi(ptr);
+ ptr = get_field(&mem, table, 30);
+ user.user_resource.connections=atoi(ptr);
+ if (user.user_resource.questions || user.user_resource.updates ||
+ user.user_resource.connections)
+ mqh_used=1;
}
+ else
+ {
+ user.ssl_type=SSL_TYPE_NONE;
+ bzero((char *)&(user.user_resource),sizeof(user.user_resource));
+#ifndef TO_BE_REMOVED
+ if (table->fields <= 13)
+ { // Without grant
+ if (user.access & CREATE_ACL)
+ user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
+ }
+ /* Convert old privileges */
+ user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
+ if (user.access & FILE_ACL)
+ user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
+ if (user.access & PROCESS_ACL)
+ user.access|= SUPER_ACL | EXECUTE_ACL;
#endif
+ }
VOID(push_dynamic(&acl_users,(gptr) &user));
if (!user.host.hostname || user.host.hostname[0] == wild_many &&
!user.host.hostname[1])
@@ -267,11 +332,21 @@ int acl_init(bool dont_read_acl_tables)
init_check_host();
mysql_unlock_tables(thd, lock);
+ initialized=1;
thd->version--; // Force close to free memory
+ return_val=0;
+
+end:
close_thread_tables(thd);
delete thd;
- initialized=1;
- DBUG_RETURN(0);
+ if (org_thd)
+ org_thd->store_globals(); /* purecov: inspected */
+ else
+ {
+ /* Remember that we don't have a THD */
+ my_pthread_setspecific_ptr(THR_THD, 0);
+ }
+ DBUG_RETURN(return_val);
}
@@ -292,20 +367,27 @@ void acl_free(bool end)
}
}
- /* Reload acl list if possible */
-void acl_reload(void)
+/*
+ Forget current privileges and read new privileges from the privilege tables
+
+ SYNOPSIS
+ acl_reload()
+ thd Thread handle
+*/
+
+void acl_reload(THD *thd)
{
DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs;
MEM_ROOT old_mem;
bool old_initialized;
DBUG_ENTER("acl_reload");
- if (current_thd && current_thd->locked_tables)
+ if (thd && thd->locked_tables)
{ // Can't have locked tables here
- current_thd->lock=current_thd->locked_tables;
- current_thd->locked_tables=0;
- close_thread_tables(current_thd);
+ thd->lock=thd->locked_tables;
+ thd->locked_tables=0;
+ close_thread_tables(thd);
}
if ((old_initialized=initialized))
VOID(pthread_mutex_lock(&acl_cache->lock));
@@ -317,9 +399,9 @@ void acl_reload(void)
delete_dynamic(&acl_wild_hosts);
hash_free(&acl_check_hosts);
- if (acl_init(0))
+ if (acl_init(thd, 0))
{ // Error. Revert to old list
- acl_free(); /* purecov: inspected */
+ acl_free(); /* purecov: inspected */
acl_hosts=old_acl_hosts;
acl_users=old_acl_users;
acl_dbs=old_acl_dbs;
@@ -339,31 +421,38 @@ void acl_reload(void)
}
-/* Get all access bits from table after fieldnr */
+/*
+ Get all access bits from table after fieldnr
+ We know that the access privileges ends when there is no more fields
+ or the field is not an enum with two elements.
+*/
-static uint get_access(TABLE *form,uint fieldnr)
+static ulong get_access(TABLE *form, uint fieldnr)
{
- uint access_bits=0,bit;
+ ulong access_bits=0,bit;
char buff[2];
String res(buff,sizeof(buff));
Field **pos;
- for (pos=form->field+fieldnr,bit=1 ; *pos ; pos++ , bit<<=1)
+ for (pos=form->field+fieldnr, bit=1;
+ *pos && (*pos)->real_type() == FIELD_TYPE_ENUM &&
+ ((Field_enum*) (*pos))->typelib->count == 2 ;
+ pos++ , bit<<=1)
{
(*pos)->val_str(&res,&res);
if (toupper(res[0]) == 'Y')
- access_bits|=bit;
+ access_bits|= bit;
}
return access_bits;
}
/*
- return a number which, if sorted 'desc', puts strings in this order:
- no wildcards
- wildcards
- empty string
- */
+ Return a number which, if sorted 'desc', puts strings in this order:
+ no wildcards
+ wildcards
+ empty string
+*/
static ulong get_sort(uint count,...)
{
@@ -403,18 +492,26 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
}
-/* Get master privilges for user (priviliges for all tables) */
-
+/*
+ Get master privilges for user (priviliges for all tables).
+ Required before connecting to MySQL
+*/
-uint acl_getroot(const char *host, const char *ip, const char *user,
- const char *password,const char *message,char **priv_user,
- bool old_ver)
+ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
+ const char *password,const char *message,
+ char **priv_user, char *priv_host,
+ bool old_ver, USER_RESOURCES *mqh)
{
- uint user_access=NO_ACCESS;
+ ulong user_access=NO_ACCESS;
*priv_user=(char*) user;
+ DBUG_ENTER("acl_getroot");
+ bzero((char *)mqh,sizeof(USER_RESOURCES));
if (!initialized)
- return (uint) ~NO_ACCESS; // If no data allow anything /* purecov: tested */
+ {
+ // If no data allow anything
+ DBUG_RETURN((ulong) ~NO_ACCESS); /* purecov: tested */
+ }
VOID(pthread_mutex_lock(&acl_cache->lock));
/*
@@ -433,9 +530,121 @@ uint acl_getroot(const char *host, const char *ip, const char *user,
!check_scramble(password,message,acl_user->salt,
(my_bool) old_ver)))
{
- user_access=acl_user->access;
+ Vio *vio=thd->net.vio;
+#ifdef HAVE_OPENSSL
+ SSL *ssl= (SSL*) vio->ssl_arg;
+#endif
+ /*
+ In this point we know that user is allowed to connect
+ from given host by given username/password pair. Now
+ we check if SSL is required, if user is using SSL and
+ if X509 certificate attributes are OK
+ */
+ switch (acl_user->ssl_type) {
+ case SSL_TYPE_NOT_SPECIFIED: // Impossible
+ case SSL_TYPE_NONE: /* SSL is not required to connect */
+ user_access=acl_user->access;
+ break;
+#ifdef HAVE_OPENSSL
+ case SSL_TYPE_ANY: /* Any kind of SSL is good enough */
+ if (vio_type(vio) == VIO_TYPE_SSL)
+ user_access=acl_user->access;
+ break;
+ case SSL_TYPE_X509: /* Client should have any valid certificate. */
+ /*
+ We need to check for absence of SSL because without SSL
+ we should reject connection.
+ */
+ if (vio_type(vio) == VIO_TYPE_SSL &&
+ SSL_get_verify_result(ssl) == X509_V_OK &&
+ SSL_get_peer_certificate(ssl))
+ user_access=acl_user->access;
+ break;
+ case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
+ /*
+ We need to check for absence of SSL because without SSL
+ we should reject connection.
+ */
+ if (vio_type(vio) == VIO_TYPE_SSL &&
+ SSL_get_verify_result(ssl) == X509_V_OK)
+ {
+ if (acl_user->ssl_cipher)
+ {
+ DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
+ acl_user->ssl_cipher,
+ SSL_get_cipher(ssl)));
+ if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl)))
+ user_access=acl_user->access;
+ else
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_error("X509 ciphers mismatch: should be '%s' but is '%s'",
+ acl_user->ssl_cipher,
+ SSL_get_cipher(ssl));
+ user_access=NO_ACCESS;
+ break;
+ }
+ }
+ /* Prepare certificate (if exists) */
+ DBUG_PRINT("info",("checkpoint 1"));
+ X509* cert=SSL_get_peer_certificate(ssl);
+ DBUG_PRINT("info",("checkpoint 2"));
+ /* If X509 issuer is speified, we check it... */
+ if (acl_user->x509_issuer)
+ {
+ DBUG_PRINT("info",("checkpoint 3"));
+ char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
+ DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
+ acl_user->x509_issuer, ptr));
+ if (strcmp(acl_user->x509_issuer, ptr))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_error("X509 issuer mismatch: should be '%s' but is '%s'",
+ acl_user->x509_issuer, ptr);
+ user_access=NO_ACCESS;
+ free(ptr);
+ break;
+ }
+ user_access=acl_user->access;
+ free(ptr);
+ }
+ DBUG_PRINT("info",("checkpoint 4"));
+ /* X509 subject is specified, we check it .. */
+ if (acl_user->x509_subject)
+ {
+ char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
+ DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
+ acl_user->x509_subject, ptr));
+ if (strcmp(acl_user->x509_subject,ptr))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_error("X509 subject mismatch: '%s' vs '%s'",
+ acl_user->x509_subject, ptr);
+ user_access=NO_ACCESS;
+ }
+ else
+ user_access=acl_user->access;
+ free(ptr);
+ }
+ break;
+ }
+#else /* HAVE_OPENSSL */
+ default:
+ /*
+ If we don't have SSL but SSL is required for this user the
+ authentication should fail.
+ */
+ break;
+#endif /* HAVE_OPENSSL */
+ }
+
+ *mqh=acl_user->user_resource;
if (!acl_user->user)
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
+ if (acl_user->host.hostname)
+ strmake(priv_host, acl_user->host.hostname, MAX_HOSTNAME);
+ else
+ *priv_host= 0;
break;
}
#ifndef ALLOW_DOWNGRADE_OF_USERS
@@ -445,7 +654,7 @@ uint acl_getroot(const char *host, const char *ip, const char *user,
}
}
VOID(pthread_mutex_unlock(&acl_cache->lock));
- return user_access;
+ DBUG_RETURN(user_access);
}
@@ -462,7 +671,13 @@ static byte* check_get_key(ACL_USER *buff,uint *length,
}
static void acl_update_user(const char *user, const char *host,
- const char *password, uint privileges)
+ const char *password,
+ enum SSL_type ssl_type,
+ const char *ssl_cipher,
+ const char *x509_issuer,
+ const char *x509_subject,
+ USER_RESOURCES *mqh,
+ ulong privileges)
{
for (uint i=0 ; i < acl_users.elements ; i++)
{
@@ -472,9 +687,26 @@ static void acl_update_user(const char *user, const char *host,
!strcmp(user,acl_user->user))
{
if (!acl_user->host.hostname && !host[0] ||
- acl_user->host.hostname && !strcmp(host,acl_user->host.hostname))
+ acl_user->host.hostname &&
+ !my_strcasecmp(host,acl_user->host.hostname))
{
acl_user->access=privileges;
+ if (mqh->bits & 1)
+ acl_user->user_resource.questions=mqh->questions;
+ if (mqh->bits & 2)
+ acl_user->user_resource.updates=mqh->updates;
+ if (mqh->bits & 4)
+ acl_user->user_resource.connections=mqh->connections;
+ if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
+ {
+ acl_user->ssl_type= ssl_type;
+ acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&mem,ssl_cipher) :
+ 0);
+ acl_user->x509_issuer= (x509_issuer ? strdup_root(&mem,x509_issuer) :
+ 0);
+ acl_user->x509_subject= (x509_subject ?
+ strdup_root(&mem,x509_subject) : 0);
+ }
if (password)
{
if (!password[0])
@@ -494,15 +726,26 @@ static void acl_update_user(const char *user, const char *host,
static void acl_insert_user(const char *user, const char *host,
const char *password,
- uint privileges)
+ enum SSL_type ssl_type,
+ const char *ssl_cipher,
+ const char *x509_issuer,
+ const char *x509_subject,
+ USER_RESOURCES *mqh,
+ ulong privileges)
{
ACL_USER acl_user;
- acl_user.user=strdup_root(&mem,user);
+ acl_user.user=*user ? strdup_root(&mem,user) : 0;
update_hostname(&acl_user.host,strdup_root(&mem,host));
acl_user.password=0;
acl_user.access=privileges;
+ acl_user.user_resource = *mqh;
acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
acl_user.hostname_length=(uint) strlen(acl_user.host.hostname);
+ acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ?
+ ssl_type : SSL_TYPE_NONE);
+ acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0;
+ acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0;
+ acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
if (password)
{
acl_user.password=(char*) ""; // Just point at something
@@ -524,7 +767,7 @@ static void acl_insert_user(const char *user, const char *host,
static void acl_update_db(const char *user, const char *host, const char *db,
- uint privileges)
+ ulong privileges)
{
for (uint i=0 ; i < acl_dbs.elements ; i++)
{
@@ -534,7 +777,7 @@ static void acl_update_db(const char *user, const char *host, const char *db,
!strcmp(user,acl_db->user))
{
if (!acl_db->host.hostname && !host[0] ||
- acl_db->host.hostname && !strcmp(host,acl_db->host.hostname))
+ acl_db->host.hostname && !my_strcasecmp(host,acl_db->host.hostname))
{
if (!acl_db->db && !db[0] ||
acl_db->db && !strcmp(db,acl_db->db))
@@ -550,11 +793,25 @@ static void acl_update_db(const char *user, const char *host, const char *db,
}
+/*
+ Insert a user/db/host combination into the global acl_cache
+
+ SYNOPSIS
+ acl_insert_db()
+ user User name
+ host Host name
+ db Database name
+ privileges Bitmap of privileges
+
+ NOTES
+ acl_cache->lock must be locked when calling this
+*/
+
static void acl_insert_db(const char *user, const char *host, const char *db,
- uint privileges)
+ ulong privileges)
{
ACL_DB acl_db;
- /* The acl_cache mutex is locked by mysql_grant */
+ safe_mutex_assert_owner(&acl_cache->lock);
acl_db.user=strdup_root(&mem,user);
update_hostname(&acl_db.host,strdup_root(&mem,host));
acl_db.db=strdup_root(&mem,db);
@@ -566,21 +823,28 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
}
-/*****************************************************************************
-** Get privilege for a host, user and db combination
-*****************************************************************************/
-uint acl_get(const char *host, const char *ip, const char *bin_ip,
+/*
+ Get privilege for a host, user and db combination
+*/
+
+ulong acl_get(const char *host, const char *ip, const char *bin_ip,
const char *user, const char *db)
{
- uint host_access,db_access,i,key_length;
+ ulong host_access,db_access;
+ uint i,key_length;
db_access=0; host_access= ~0;
- char key[ACL_KEY_LENGTH],*end;
+ char key[ACL_KEY_LENGTH],*tmp_db,*end;
acl_entry *entry;
VOID(pthread_mutex_lock(&acl_cache->lock));
memcpy_fixed(&key,bin_ip,sizeof(struct in_addr));
- end=strmov(strmov(key+sizeof(struct in_addr),user)+1,db);
+ end=strmov((tmp_db=strmov(key+sizeof(struct in_addr),user)+1),db);
+ if (lower_case_table_names)
+ {
+ casedn_str(tmp_db);
+ db=tmp_db;
+ }
key_length=(uint) (end-key);
if ((entry=(acl_entry*) acl_cache->search(key,key_length)))
{
@@ -646,7 +910,7 @@ int wild_case_compare(const char *str,const char *wildstr)
{
reg3 int flag;
DBUG_ENTER("wild_case_compare");
-
+ DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr));
while (*wildstr)
{
while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
@@ -658,7 +922,7 @@ int wild_case_compare(const char *str,const char *wildstr)
if (! *wildstr ) DBUG_RETURN (*str != 0);
if (*wildstr++ == wild_one)
{
- if (! *str++) DBUG_RETURN (1); /* One char; skipp */
+ if (! *str++) DBUG_RETURN (1); /* One char; skip */
}
else
{ /* Found '*' */
@@ -684,11 +948,14 @@ int wild_case_compare(const char *str,const char *wildstr)
DBUG_RETURN (*str != '\0');
}
-/*****************************************************************************
-** check if there are any possible matching entries for this host
-** All host names without wild cards are stored in a hash table,
-** entries with wildcards are stored in a dynamic array
-*****************************************************************************/
+
+/*
+ Check if there are any possible matching entries for this host
+
+ NOTES
+ All host names without wild cards are stored in a hash table,
+ entries with wildcards are stored in a dynamic array
+*/
static void init_check_host(void)
{
@@ -761,45 +1028,78 @@ bool acl_check_host(const char *host, const char *ip)
return 1; // Host is not allowed
}
-/*****************************************************************************
-** Change password for the user if it's not an anonymous user
-** Note: This should write the error directly to the client!
-*****************************************************************************/
-bool change_password(THD *thd, const char *host, const char *user,
- char *new_password)
+/*
+ Check if the user is allowed to change password
+
+ SYNOPSIS:
+ check_change_password()
+ thd THD
+ host hostname for the user
+ user user name
+
+ RETURN VALUE
+ 0 OK
+ 1 ERROR ; In this case the error is sent to the client.
+*/
+
+bool check_change_password(THD *thd, const char *host, const char *user)
{
- uint length=0;
if (!initialized)
{
send_error(&thd->net, ER_PASSWORD_NOT_ALLOWED); /* purecov: inspected */
- return 1; /* purecov: inspected */
+ return(1); /* purecov: inspected */
}
- if (!host)
- host=thd->ip; /* purecov: tested */
- /* password should always be 0 or 16 chars; simple hack to avoid cracking */
- length=(uint) strlen(new_password);
- new_password[length & 16]=0;
-
if (!thd->slave_thread &&
(strcmp(thd->user,user) ||
- my_strcasecmp(host,thd->host ? thd->host : thd->ip)))
+ my_strcasecmp(host,thd->host_or_ip)))
{
if (check_access(thd, UPDATE_ACL, "mysql",0,1))
- return 1;
+ return(1);
}
if (!thd->slave_thread && !thd->user[0])
{
send_error(&thd->net, ER_PASSWORD_ANONYMOUS_USER);
- return 1;
+ return(1);
}
+ return(0);
+}
+
+
+/*
+ Change a password for a user
+
+ SYNOPSIS
+ change_password()
+ thd Thread handle
+ host Hostname
+ user User name
+ new_password New password for host@user
+
+ RETURN VALUES
+ 0 ok
+ 1 ERROR; In this case the error is sent to the client.
+*/
+
+bool change_password(THD *thd, const char *host, const char *user,
+ char *new_password)
+{
+ uint length=0;
+ DBUG_ENTER("change_password");
+ DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
+ host,user,new_password));
+ DBUG_ASSERT(host != 0); // Ensured by parent
+
+ length=(uint) strlen(new_password);
+ new_password[length & 16]=0;
+
VOID(pthread_mutex_lock(&acl_cache->lock));
ACL_USER *acl_user;
if (!(acl_user= find_acl_user(host,user)))
{
send_error(&thd->net, ER_PASSWORD_NO_MATCH);
VOID(pthread_mutex_unlock(&acl_cache->lock));
- return 1;
+ DBUG_RETURN(1);
}
if (update_user_table(thd,
acl_user->host.hostname ? acl_user->host.hostname : "",
@@ -808,7 +1108,7 @@ bool change_password(THD *thd, const char *host, const char *user,
{
VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
send_error(&thd->net,0); /* purecov: deadcode */
- return 1; /* purecov: deadcode */
+ DBUG_RETURN(1); /* purecov: deadcode */
}
get_salt_from_password(acl_user->salt,new_password);
if (!new_password[0])
@@ -819,17 +1119,16 @@ bool change_password(THD *thd, const char *host, const char *user,
VOID(pthread_mutex_unlock(&acl_cache->lock));
char buff[460];
-
- Query_log_event qinfo(thd, buff);
- qinfo.q_len =
+ ulong query_length=
my_sprintf(buff,
(buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"",
acl_user->user ? acl_user->user : "",
acl_user->host.hostname ? acl_user->host.hostname : "",
new_password));
- mysql_update_log.write(thd,buff,qinfo.q_len);
+ mysql_update_log.write(thd, buff, query_length);
+ Query_log_event qinfo(thd, buff, query_length, 0);
mysql_bin_log.write(&qinfo);
- return 0;
+ DBUG_RETURN(0);
}
@@ -840,28 +1139,41 @@ bool change_password(THD *thd, const char *host, const char *user,
static ACL_USER *
find_acl_user(const char *host, const char *user)
{
+ DBUG_ENTER("find_acl_user");
+ DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user));
for (uint i=0 ; i < acl_users.elements ; i++)
{
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
+ DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
+ user,
+ acl_user->user ? acl_user->user : "",
+ host,
+ acl_user->host.hostname ? acl_user->host.hostname :
+ ""));
if (!acl_user->user && !user[0] ||
acl_user->user && !strcmp(user,acl_user->user))
{
- if (compare_hostname(&acl_user->host,host,host))
- return acl_user;
+ if (compare_hostname(&(acl_user->host),host,host))
+ {
+ DBUG_RETURN(acl_user);
+ }
}
}
- return 0;
+ DBUG_RETURN(0);
}
-/*****************************************************************************
- Handle comparing of hostname
- A hostname may be of type:
- hostname (May include wildcards); monty.pp.sci.fi
- ip (May include wildcards); 192.168.0.0
- ip/netmask 192.168.0.0/255.255.255.0
- A net mask of 0.0.0.0 is not allowed.
-*****************************************************************************/
+/*
+ Comparing of hostnames
+
+ NOTES
+ A hostname may be of type:
+ hostname (May include wildcards); monty.pp.sci.fi
+ ip (May include wildcards); 192.168.0.0
+ ip/netmask 192.168.0.0/255.255.255.0
+
+ A net mask of 0.0.0.0 is not allowed.
+*/
static const char *calc_ip(const char *ip, long *val, char end)
{
@@ -908,9 +1220,9 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
}
-/****************************************************************************
-** Code to update grants in the user and database privilege tables
-****************************************************************************/
+/*
+ Update grants in the user and database privilege tables
+*/
static bool update_user_table(THD *thd, const char *host, const char *user,
const char *new_password)
@@ -924,6 +1236,24 @@ static bool update_user_table(THD *thd, const char *host, const char *user,
bzero((char*) &tables,sizeof(tables));
tables.alias=tables.real_name=(char*) "user";
tables.db=(char*) "mysql";
+#ifdef HAVE_REPLICATION
+ /*
+ GRANT and REVOKE are applied the slave in/exclusion rules as they are
+ some kind of updates to the mysql.% tables.
+ */
+ if (thd->slave_thread && table_rules_on)
+ {
+ /*
+ The tables must be marked "updating" so that tables_ok() takes them into
+ account in tests. It's ok to leave 'updating' set after tables_ok.
+ */
+ tables.updating= 1;
+ /* Thanks to bzero, tables.next==0 */
+ if (!tables_ok(0, &tables))
+ DBUG_RETURN(0);
+ }
+#endif
+
if (!(table=open_ltable(thd,&tables,TL_WRITE)))
DBUG_RETURN(1); /* purecov: deadcode */
table->field[0]->store(host,(uint) strlen(host));
@@ -959,10 +1289,10 @@ static bool test_if_create_new_users(THD *thd)
if (opt_safe_user_create && !(thd->master_access & INSERT_ACL))
{
TABLE_LIST tl;
- uint db_access;
+ ulong db_access;
bzero((char*) &tl,sizeof(tl));
tl.db= (char*) "mysql";
- tl.real_name= (char*) "user";
+ tl.real_name= (char*) "user";
db_access=acl_get(thd->host, thd->ip, (char*) &thd->remote.sin_addr,
thd->priv_user, tl.db);
if (!(db_access & INSERT_ACL))
@@ -976,17 +1306,19 @@ static bool test_if_create_new_users(THD *thd)
/****************************************************************************
-** Handle GRANT commands
+ Handle GRANT commands
****************************************************************************/
-static int replace_user_table(TABLE *table, const LEX_USER &combo,
- uint rights, char what, bool create_user)
+static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
+ ulong rights, bool revoke_grant,
+ bool create_user)
{
int error = -1;
- uint i,j;
bool old_row_exists=0;
char *password,empty_string[1];
+ char what= (revoke_grant) ? 'N' : 'Y';
DBUG_ENTER("replace_user_table");
+ safe_mutex_assert_owner(&acl_cache->lock);
password=empty_string;
empty_string[0]=0;
@@ -996,8 +1328,8 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo,
if (combo.password.length != HASH_PASSWORD_LENGTH)
{
my_printf_error(ER_PASSWORD_NO_MATCH,
- "Password hash should be a %d-digit hexadecimal number",
- MYF(0),HASH_PASSWORD_LENGTH);
+ "Password hash should be a %d-digit hexadecimal number",
+ MYF(0),HASH_PASSWORD_LENGTH);
DBUG_RETURN(-1);
}
password=combo.password.str;
@@ -1012,7 +1344,6 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo,
{
if (!create_user)
{
- THD *thd=current_thd;
if (what == 'N')
my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),
MYF(0),combo.user.str,combo.host.str);
@@ -1020,7 +1351,7 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo,
my_printf_error(ER_NO_PERMISSION_TO_CREATE_USER,
ER(ER_NO_PERMISSION_TO_CREATE_USER),
MYF(0),thd->user,
- thd->host ? thd->host : thd->ip ? thd->ip: "");
+ thd->host_or_ip);
error= -1;
goto end;
}
@@ -1038,15 +1369,70 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo,
table->field[2]->store(password,(uint) strlen(password));
}
- for (i = 3, j = SELECT_ACL; // starting from reload
- i < table->fields;
- i++, j <<= 1)
+ /* Update table columns with new privileges */
+
+ Field **tmp_field;
+ ulong priv;
+ for (tmp_field= table->field+3, priv = SELECT_ACL;
+ *tmp_field && (*tmp_field)->real_type() == FIELD_TYPE_ENUM &&
+ ((Field_enum*) (*tmp_field))->typelib->count == 2 ;
+ tmp_field++, priv <<= 1)
{
- if (j & rights) // set requested privileges
- table->field[i]->store(&what,1);
+ if (priv & rights) // set requested privileges
+ (*tmp_field)->store(&what,1);
}
rights=get_access(table,3);
+ DBUG_PRINT("info",("table->fields: %d",table->fields));
+ if (table->fields >= 31) /* From 4.0.0 we have more fields */
+ {
+ /* We write down SSL related ACL stuff */
+ switch (thd->lex.ssl_type) {
+ case SSL_TYPE_ANY:
+ table->field[24]->store("ANY",3);
+ table->field[25]->store("",0);
+ table->field[26]->store("",0);
+ table->field[27]->store("",0);
+ break;
+ case SSL_TYPE_X509:
+ table->field[24]->store("X509",4);
+ table->field[25]->store("",0);
+ table->field[26]->store("",0);
+ table->field[27]->store("",0);
+ break;
+ case SSL_TYPE_SPECIFIED:
+ table->field[24]->store("SPECIFIED",9);
+ table->field[25]->store("",0);
+ table->field[26]->store("",0);
+ table->field[27]->store("",0);
+ if (thd->lex.ssl_cipher)
+ table->field[25]->store(thd->lex.ssl_cipher,
+ strlen(thd->lex.ssl_cipher));
+ if (thd->lex.x509_issuer)
+ table->field[26]->store(thd->lex.x509_issuer,
+ strlen(thd->lex.x509_issuer));
+ if (thd->lex.x509_subject)
+ table->field[27]->store(thd->lex.x509_subject,
+ strlen(thd->lex.x509_subject));
+ break;
+ case SSL_TYPE_NOT_SPECIFIED:
+ break;
+ case SSL_TYPE_NONE:
+ table->field[24]->store("",0);
+ table->field[25]->store("",0);
+ table->field[26]->store("",0);
+ table->field[27]->store("",0);
+ break;
+ }
+ USER_RESOURCES mqh = thd->lex.mqh;
+ if (mqh.bits & 1)
+ table->field[28]->store((longlong) mqh.questions);
+ if (mqh.bits & 2)
+ table->field[29]->store((longlong) mqh.updates);
+ if (mqh.bits & 4)
+ table->field[30]->store((longlong) mqh.connections);
+ mqh_used = mqh_used || mqh.questions || mqh.updates || mqh.connections;
+ }
if (old_row_exists)
{
/*
@@ -1073,16 +1459,28 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo,
}
error=0; // Privileges granted / revoked
- end:
+end:
if (!error)
{
acl_cache->clear(1); // Clear privilege cache
if (!combo.password.str)
password=0; // No password given on command
if (old_row_exists)
- acl_update_user(combo.user.str,combo.host.str,password,rights);
+ acl_update_user(combo.user.str,combo.host.str,password,
+ thd->lex.ssl_type,
+ thd->lex.ssl_cipher,
+ thd->lex.x509_issuer,
+ thd->lex.x509_subject,
+ &thd->lex.mqh,
+ rights);
else
- acl_insert_user(combo.user.str,combo.host.str,password,rights);
+ acl_insert_user(combo.user.str,combo.host.str,password,
+ thd->lex.ssl_type,
+ thd->lex.ssl_cipher,
+ thd->lex.x509_issuer,
+ thd->lex.x509_subject,
+ &thd->lex.mqh,
+ rights);
}
table->file->index_end();
DBUG_RETURN(error);
@@ -1090,19 +1488,21 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo,
/*
-** change grants in the mysql.db table
+ change grants in the mysql.db table
*/
static int replace_db_table(TABLE *table, const char *db,
const LEX_USER &combo,
- uint rights, char what)
+ ulong rights, bool revoke_grant)
{
- uint i,j,store_rights;
+ uint i;
+ ulong priv,store_rights;
bool old_row_exists=0;
int error;
+ char what= (revoke_grant) ? 'N' : 'Y';
DBUG_ENTER("replace_db_table");
- // is there such a user in user table in memory ????
+ /* Check if there is such a user in user table in memory? */
if (!initialized || !find_acl_user(combo.host.str,combo.user.str))
{
my_error(ER_PASSWORD_NO_MATCH,MYF(0));
@@ -1114,7 +1514,7 @@ static int replace_db_table(TABLE *table, const char *db,
table->field[2]->store(combo.user.str,combo.user.length);
table->file->index_init(0);
if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr,0,
- HA_READ_KEY_EXACT))
+ HA_READ_KEY_EXACT))
{
if (what == 'N')
{ // no row, no revoke
@@ -1135,9 +1535,9 @@ static int replace_db_table(TABLE *table, const char *db,
}
store_rights=get_rights_for_db(rights);
- for (i = 3, j = 1; i < table->fields; i++, j <<= 1)
+ for (i= 3, priv= 1; i < table->fields; i++, priv <<= 1)
{
- if (j & store_rights) // do it if priv is chosen
+ if (priv & store_rights) // do it if priv is chosen
table->field [i]->store(&what,1); // set requested privileges
}
rights=get_access(table,3);
@@ -1145,7 +1545,7 @@ static int replace_db_table(TABLE *table, const char *db,
if (old_row_exists)
{
- // update old existing row
+ /* update old existing row */
if (rights)
{
if ((error=table->file->update_row(table->record[1],table->record[0])))
@@ -1172,11 +1572,11 @@ static int replace_db_table(TABLE *table, const char *db,
DBUG_RETURN(0);
/* This could only happen if the grant tables got corrupted */
- table_error:
+table_error:
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
table->file->index_end();
- abort:
+abort:
DBUG_RETURN(-1);
}
@@ -1185,13 +1585,15 @@ class GRANT_COLUMN :public Sql_alloc
{
public:
char *column;
- uint rights, key_length;
- GRANT_COLUMN(String &c, uint y) :rights (y)
+ ulong rights;
+ uint key_length;
+ GRANT_COLUMN(String &c, ulong y) :rights (y)
{
- column= memdup_root(&memex,c.ptr(),key_length=c.length());
+ column= memdup_root(&memex,c.ptr(), key_length=c.length());
}
};
+
static byte* get_key_column(GRANT_COLUMN *buff,uint *length,
my_bool not_used __attribute__((unused)))
{
@@ -1199,15 +1601,17 @@ static byte* get_key_column(GRANT_COLUMN *buff,uint *length,
return (byte*) buff->column;
}
+
class GRANT_TABLE :public Sql_alloc
{
public:
char *host,*db,*user,*tname, *hash_key;
- uint privs, cols, key_length;
+ ulong privs, cols;
ulong sort;
+ uint key_length;
HASH hash_columns;
GRANT_TABLE (const char *h, const char *d,const char *u, const char *t,
- uint p,uint c)
+ ulong p, ulong c)
: privs(p), cols(c)
{
host = strdup_root(&memex,h);
@@ -1215,6 +1619,11 @@ public:
user = strdup_root(&memex,u);
sort= get_sort(3,host,db,user);
tname= strdup_root(&memex,t);
+ if (lower_case_table_names)
+ {
+ casedn_str(db);
+ casedn_str(tname);
+ }
key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3;
hash_key = (char*) alloc_root(&memex,key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
@@ -1228,7 +1637,9 @@ public:
host = get_field(&memex,form,0);
db = get_field(&memex,form,1);
- user = get_field(&memex,form,2); if (!user) user=(char*) "";
+ user = get_field(&memex,form,2);
+ if (!user)
+ user=(char*) "";
sort= get_sort(3,host,db,user);
tname = get_field(&memex,form,3);
if (!host || !db || !tname)
@@ -1237,11 +1648,17 @@ public:
privs = cols = 0; /* purecov: inspected */
return; /* purecov: inspected */
}
- key_length = (uint) strlen(db) + (uint) strlen(user) + (uint) strlen (tname) + 3;
+ if (lower_case_table_names)
+ {
+ casedn_str(db);
+ casedn_str(tname);
+ }
+ key_length = ((uint) strlen(db) + (uint) strlen(user) +
+ (uint) strlen(tname) + 3);
hash_key = (char*) alloc_root(&memex,key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
- privs = (uint) form->field[6]->val_int();
- cols = (uint) form->field[7]->val_int();
+ privs = (ulong) form->field[6]->val_int();
+ cols = (ulong) form->field[7]->val_int();
privs = fix_rights_for_table(privs);
cols = fix_rights_for_column(cols);
@@ -1274,11 +1691,11 @@ public:
GRANT_COLUMN *mem_check;
/* As column name is a string, we don't have to supply a buffer */
res=col_privs->field[4]->val_str(&column_name,&column_name);
- uint priv= (uint) col_privs->field[6]->val_int();
+ ulong priv= (ulong) col_privs->field[6]->val_int();
if (!(mem_check = new GRANT_COLUMN(*res,
fix_rights_for_column(priv))))
{
- // Don't use this entry
+ /* Don't use this entry */
privs = cols = 0; /* purecov: deadcode */
return; /* purecov: deadcode */
}
@@ -1290,6 +1707,7 @@ public:
bool ok() { return privs != 0 || cols != 0; }
};
+
static byte* get_grant_table(GRANT_TABLE *buff,uint *length,
my_bool not_used __attribute__((unused)))
{
@@ -1297,11 +1715,13 @@ static byte* get_grant_table(GRANT_TABLE *buff,uint *length,
return (byte*) buff->hash_key;
}
+
void free_grant_table(GRANT_TABLE *grant_table)
{
hash_free(&grant_table->hash_columns);
}
+
/* Search after a matching grant. Prefer exact grants before not exact ones */
static GRANT_TABLE *table_hash_search(const char *host,const char* ip,
@@ -1312,6 +1732,7 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip,
char helping [NAME_LEN*2+USERNAME_LENGTH+3];
uint len;
GRANT_TABLE *grant_table,*found=0;
+ safe_mutex_assert_owner(&LOCK_grant);
len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1;
for (grant_table=(GRANT_TABLE*) hash_search(&hash_tables,(byte*) helping,
@@ -1321,7 +1742,7 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip,
{
if (exact)
{
- if ((host && !strcmp(host,grant_table->host)) ||
+ if ((host && !my_strcasecmp(host,grant_table->host)) ||
(ip && !strcmp(ip,grant_table->host)))
return grant_table;
}
@@ -1339,8 +1760,7 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip,
inline GRANT_COLUMN *
-column_hash_search(GRANT_TABLE *t, const char *cname,
- uint length)
+column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
{
return (GRANT_COLUMN*) hash_search(&t->hash_columns, (byte*) cname,length);
}
@@ -1350,7 +1770,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
TABLE *table, const LEX_USER &combo,
List <LEX_COLUMN> &columns,
const char *db, const char *table_name,
- uint rights, bool revoke_grant)
+ ulong rights, bool revoke_grant)
{
int error=0,result=0;
uint key_length;
@@ -1374,7 +1794,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
table->file->index_init(0);
while ((xx=iter++))
{
- uint privileges = xx->rights;
+ ulong privileges = xx->rights;
bool old_row_exists=0;
key_restore(table,key,0,key_length);
table->field[4]->store(xx->column.ptr(),xx->column.length());
@@ -1397,7 +1817,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
}
else
{
- uint tmp= (uint) table->field[6]->val_int();
+ ulong tmp= (ulong) table->field[6]->val_int();
tmp=fix_rights_for_column(tmp);
if (revoke_grant)
@@ -1454,10 +1874,10 @@ static int replace_column_table(GRANT_TABLE *g_t,
key_length, HA_READ_KEY_EXACT))
goto end;
- // Scan trough all rows with the same host,db,user and table
+ /* Scan through all rows with the same host,db,user and table */
do
{
- uint privileges = (uint) table->field[6]->val_int();
+ ulong privileges = (ulong) table->field[6]->val_int();
privileges=fix_rights_for_column(privileges);
store_record(table,1);
@@ -1504,7 +1924,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
!key_cmp(table,key,0,key_length));
}
- end:
+end:
table->file->index_end();
DBUG_RETURN(result);
}
@@ -1513,19 +1933,22 @@ static int replace_column_table(GRANT_TABLE *g_t,
static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
TABLE *table, const LEX_USER &combo,
const char *db, const char *table_name,
- uint rights, uint kolone, bool revoke_grant)
+ ulong rights, ulong col_rights,
+ bool revoke_grant)
{
- char grantor[HOSTNAME_LENGTH+1+USERNAME_LENGTH];
+ char grantor[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
int old_row_exists = 1;
int error=0;
- uint store_table_rights,store_col_rights;
+ ulong store_table_rights, store_col_rights;
DBUG_ENTER("replace_table_table");
+ safe_mutex_assert_owner(&LOCK_grant);
- strxmov(grantor,thd->user,"@",thd->host ? thd->host : thd->ip ? thd->ip :"",
- NullS);
+ strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS);
- // The following should always succeed as new users are created before
- // this function is called!
+ /*
+ The following should always succeed as new users are created before
+ this function is called!
+ */
if (!find_acl_user(combo.host.str,combo.user.str))
{
my_error(ER_PASSWORD_NO_MATCH,MYF(0)); /* purecov: deadcode */
@@ -1560,24 +1983,24 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
restore_record(table,1); // Get saved record
}
- store_table_rights=get_rights_for_table(rights);
- store_col_rights=get_rights_for_column(kolone);
+ store_table_rights= get_rights_for_table(rights);
+ store_col_rights= get_rights_for_column(col_rights);
if (old_row_exists)
{
- uint j,k;
+ ulong j,k;
store_record(table,1);
- j = (uint) table->field[6]->val_int();
- k = (uint) table->field[7]->val_int();
+ j = (ulong) table->field[6]->val_int();
+ k = (ulong) table->field[7]->val_int();
if (revoke_grant)
{
- // column rights are already fixed in mysql_table_grant !
+ /* column rights are already fixed in mysql_table_grant */
store_table_rights=j & ~store_table_rights;
}
else
{
- store_table_rights|=j;
- store_col_rights|=k;
+ store_table_rights|= j;
+ store_col_rights|= k;
}
}
@@ -1585,7 +2008,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
table->field[6]->store((longlong) store_table_rights);
table->field[7]->store((longlong) store_col_rights);
rights=fix_rights_for_table(store_table_rights);
- kolone=fix_rights_for_column(store_col_rights);
+ col_rights=fix_rights_for_column(store_col_rights);
if (old_row_exists)
{
@@ -1604,10 +2027,10 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
goto table_error; /* purecov: deadcode */
}
- if (rights | kolone)
+ if (rights | col_rights)
{
- grant_table->privs = rights;
- grant_table->cols = kolone;
+ grant_table->privs= rights;
+ grant_table->cols= col_rights;
}
else
{
@@ -1615,19 +2038,36 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
}
DBUG_RETURN(0);
- /* This should never happen */
- table_error:
+ /* This should never happen */
+table_error:
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
DBUG_RETURN(-1); /* purecov: deadcode */
}
-int mysql_table_grant (THD *thd, TABLE_LIST *table_list,
- List <LEX_USER> &user_list,
- List <LEX_COLUMN> &columns, uint rights,
- bool revoke_grant)
+/*
+ Store table level and column level grants in the privilege tables
+
+ SYNOPSIS
+ mysql_table_grant()
+ thd Thread handle
+ table_list List of tables to give grant
+ user_list List of users to give grant
+ columns List of columns to give grant
+ rights Table level grant
+ revoke_grant Set to 1 if this is a REVOKE command
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
+ List <LEX_USER> &user_list,
+ List <LEX_COLUMN> &columns, ulong rights,
+ bool revoke_grant)
{
- uint column_priv = 0;
+ ulong column_priv= 0;
List_iterator <LEX_USER> str_list (user_list);
LEX_USER *Str;
TABLE_LIST tables[3];
@@ -1648,33 +2088,33 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list,
if (columns.elements && !revoke_grant)
{
TABLE *table;
- class LEX_COLUMN *check;
- List_iterator <LEX_COLUMN> iter(columns);
+ class LEX_COLUMN *column;
+ List_iterator <LEX_COLUMN> column_iter(columns);
if (!(table=open_ltable(thd,table_list,TL_READ)))
DBUG_RETURN(-1);
- while ((check = iter++))
+ while ((column = column_iter++))
{
- if (!find_field_in_table(thd,table,check->column.ptr(),
- check->column.length(),0,0))
+ if (!find_field_in_table(thd,table,column->column.ptr(),
+ column->column.length(),0,0))
{
my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0),
- check->column.c_ptr(),table_list->alias);
+ column->column.c_ptr(), table_list->alias);
DBUG_RETURN(-1);
}
- column_priv |= check->rights | (rights & COL_ACLS);
+ column_priv|= column->rights;
}
close_thread_tables(thd);
}
else if (!(rights & CREATE_ACL) && !revoke_grant)
{
char buf[FN_REFLEN];
- sprintf(buf,"%s/%s/%s.frm",mysql_data_home,table_list->db,
+ sprintf(buf,"%s/%s/%s.frm",mysql_data_home, table_list->db,
table_list->real_name);
fn_format(buf,buf,"","",4+16+32);
if (access(buf,F_OK))
{
- my_error(ER_NO_SUCH_TABLE,MYF(0),table_list->db,table_list->real_name);
+ my_error(ER_NO_SUCH_TABLE,MYF(0),table_list->db, table_list->alias);
DBUG_RETURN(-1);
}
}
@@ -1693,6 +2133,23 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list,
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE;
tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
+#ifdef HAVE_REPLICATION
+ /*
+ GRANT and REVOKE are applied the slave in/exclusion rules as they are
+ some kind of updates to the mysql.% tables.
+ */
+ if (thd->slave_thread && table_rules_on)
+ {
+ /*
+ The tables must be marked "updating" so that tables_ok() takes them into
+ account in tests.
+ */
+ tables[0].updating= tables[1].updating= tables[2].updating= 1;
+ if (!tables_ok(0, tables))
+ DBUG_RETURN(0);
+ }
+#endif
+
if (open_and_lock_tables(thd,tables))
{ // Should never happen
close_thread_tables(thd); /* purecov: deadcode */
@@ -1724,11 +2181,8 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list,
}
/* Create user if needed */
pthread_mutex_lock(&acl_cache->lock);
- error=replace_user_table(tables[0].table,
- *Str,
- 0,
- revoke_grant ? 'N' : 'Y',
- create_new_users);
+ error=replace_user_table(thd, tables[0].table, *Str,
+ 0, revoke_grant, create_new_users);
pthread_mutex_unlock(&acl_cache->lock);
if (error)
{
@@ -1766,21 +2220,21 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list,
/* If revoke_grant, calculate the new column privilege for tables_priv */
if (revoke_grant)
{
- class LEX_COLUMN *check;
- List_iterator <LEX_COLUMN> iter(columns);
+ class LEX_COLUMN *column;
+ List_iterator <LEX_COLUMN> column_iter(columns);
GRANT_COLUMN *grant_column;
/* Fix old grants */
- while ((check = iter++))
+ while ((column = column_iter++))
{
grant_column = column_hash_search(grant_table,
- check->column.ptr(),
- check->column.length());
+ column->column.ptr(),
+ column->column.length());
if (grant_column)
- grant_column->rights&= ~(check->rights | rights);
+ grant_column->rights&= ~(column->rights | rights);
}
/* scan trough all columns to get new column grant */
- column_priv=0;
+ column_priv= 0;
for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
{
grant_column= (GRANT_COLUMN*) hash_element(&grant_table->hash_columns,
@@ -1821,17 +2275,17 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list,
pthread_mutex_unlock(&LOCK_grant);
if (!result)
send_ok(&thd->net);
- /* Tables are automaticly closed */
+ /* Tables are automatically closed */
DBUG_RETURN(result);
}
-int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights,
- bool revoke_grant)
+int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list,
+ ulong rights, bool revoke_grant)
{
List_iterator <LEX_USER> str_list (list);
LEX_USER *Str;
- char what;
+ char tmp_db[NAME_LEN+1];
bool create_new_users=0;
TABLE_LIST tables[2];
DBUG_ENTER("mysql_grant");
@@ -1839,10 +2293,15 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights,
if (!initialized)
{
send_error(&(thd->net), ER_UNKNOWN_COM_ERROR); /* purecov: tested */
- return 1; /* purecov: tested */
+ DBUG_RETURN(1); /* purecov: tested */
}
- what = (revoke_grant) ? 'N' : 'Y';
+ if (lower_case_table_names && db)
+ {
+ strmov(tmp_db,db);
+ casedn_str(tmp_db);
+ db=tmp_db;
+ }
/* open the mysql.user and mysql.db tables */
@@ -1853,6 +2312,24 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights,
tables[0].lock_type=tables[1].lock_type=TL_WRITE;
tables[0].db=tables[1].db=(char*) "mysql";
tables[0].table=tables[1].table=0;
+
+#ifdef HAVE_REPLICATION
+ /*
+ GRANT and REVOKE are applied the slave in/exclusion rules as they are
+ some kind of updates to the mysql.% tables.
+ */
+ if (thd->slave_thread && table_rules_on)
+ {
+ /*
+ The tables must be marked "updating" so that tables_ok() takes them into
+ account in tests.
+ */
+ tables[0].updating= tables[1].updating= 1;
+ if (!tables_ok(0, tables))
+ DBUG_RETURN(0);
+ }
+#endif
+
if (open_and_lock_tables(thd,tables))
{ // This should never happen
close_thread_tables(thd); /* purecov: deadcode */
@@ -1862,7 +2339,7 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights,
if (!revoke_grant)
create_new_users= test_if_create_new_users(thd);
- // go through users in user_list
+ /* go through users in user_list */
pthread_mutex_lock(&LOCK_grant);
VOID(pthread_mutex_lock(&acl_cache->lock));
grant_version++;
@@ -1882,15 +2359,26 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights,
result= -1;
continue;
}
- if ((replace_user_table(tables[0].table,
+ if ((replace_user_table(thd,
+ tables[0].table,
*Str,
- (!db ? rights : 0), what, create_new_users)))
+ (!db ? rights : 0), revoke_grant,
+ create_new_users)))
result= -1;
- else
+ else if (db)
{
- if (db && replace_db_table(tables[1].table, db, *Str, rights & DB_ACLS,
- what))
- result= -1;
+ ulong db_rights= rights & DB_ACLS;
+ if (db_rights == rights)
+ {
+ if (replace_db_table(tables[1].table, db, *Str, db_rights,
+ revoke_grant))
+ result= -1;
+ }
+ else
+ {
+ net_printf(&thd->net,ER_WRONG_USAGE,"DB GRANT","GLOBAL PRIVILEGES");
+ result= 1;
+ }
}
}
VOID(pthread_mutex_unlock(&acl_cache->lock));
@@ -1902,7 +2390,8 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights,
DBUG_RETURN(result);
}
- /* Free grant array if possible */
+
+/* Free grant array if possible */
void grant_free(void)
{
@@ -1916,11 +2405,12 @@ void grant_free(void)
/* Init grant array if possible */
-int grant_init (void)
+my_bool grant_init(THD *org_thd)
{
THD *thd;
TABLE_LIST tables[2];
- int error = 0;
+ MYSQL_LOCK *lock;
+ my_bool return_val= 1;
TABLE *t_table, *c_table;
DBUG_ENTER("grant_init");
@@ -1929,16 +2419,15 @@ int grant_init (void)
(hash_free_key) free_grant_table,0);
init_sql_alloc(&memex,1024,0);
+ /* Don't do anything if running with --skip-grant */
if (!initialized)
DBUG_RETURN(0); /* purecov: tested */
+
if (!(thd=new THD))
DBUG_RETURN(1); /* purecov: deadcode */
-
- thd->version=refresh_version;
- thd->mysys_var=my_thread_var;
- thd->current_tablenr=0;
- thd->open_tables=0;
- thd->db=my_strdup("mysql",MYF(0));
+ thd->store_globals();
+ thd->db= my_strdup("mysql",MYF(0));
+ thd->db_length=5; // Safety
bzero((char*) &tables,sizeof(tables));
tables[0].alias=tables[0].real_name= (char*) "tables_priv";
tables[1].alias=tables[1].real_name= (char*) "columns_priv";
@@ -1947,84 +2436,89 @@ int grant_init (void)
tables[0].db=tables[1].db=thd->db;
if (open_tables(thd,tables))
- { // No grant tables
- close_thread_tables(thd); /* purecov: deadcode */
- delete thd; /* purecov: deadcode */
- DBUG_RETURN(1); /* purecov: deadcode */
- }
+ goto end;
+
TABLE *ptr[2]; // Lock tables for quick update
ptr[0]= tables[0].table;
ptr[1]= tables[1].table;
- MYSQL_LOCK *lock=mysql_lock_tables(thd,ptr,2);
- if (!lock)
- {
- close_thread_tables(thd); /* purecov: deadcode */
- delete thd; /* purecov: deadcode */
- DBUG_RETURN(1); /* purecov: deadcode */
- }
+ if (!(lock=mysql_lock_tables(thd,ptr,2)))
+ goto end;
t_table = tables[0].table; c_table = tables[1].table;
t_table->file->index_init(0);
if (t_table->file->index_first(t_table->record[0]))
{
t_table->file->index_end();
- mysql_unlock_tables(thd, lock);
- thd->version--; // Force close to free memory
- close_thread_tables(thd);
- delete thd;
- DBUG_RETURN(0); // Empty table is ok!
+ return_val= 0;
+ goto end_unlock;
}
- grant_option = TRUE;
+ grant_option= TRUE;
t_table->file->index_end();
- MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
+ /* Will be restored by org_thd->store_globals() */
my_pthread_setspecific_ptr(THR_MALLOC,&memex);
- while (!error)
+ do
{
GRANT_TABLE *mem_check;
if (!(mem_check=new GRANT_TABLE(t_table,c_table)) ||
mem_check->ok() && hash_insert(&hash_tables,(byte*) mem_check))
{
/* This could only happen if we are out memory */
- my_pthread_setspecific_ptr(THR_MALLOC,old_root); /* purecov: deadcode */
- grant_option = FALSE; /* purecov: deadcode */
- mysql_unlock_tables(thd, lock); /* purecov: deadcode */
- close_thread_tables(thd); /* purecov: deadcode */
- delete thd; /* purecov: deadcode */
- DBUG_RETURN(1); /* purecov: deadcode */
+ grant_option= FALSE; /* purecov: deadcode */
+ goto end_unlock;
}
- error = t_table->file->index_next(t_table->record[0]);
}
- my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ while (!t_table->file->index_next(t_table->record[0]));
+
+ return_val=0; // Return ok
+
+end_unlock:
mysql_unlock_tables(thd, lock);
thd->version--; // Force close to free memory
+
+end:
close_thread_tables(thd);
delete thd;
- DBUG_RETURN(0);
+ if (org_thd)
+ org_thd->store_globals();
+ else
+ {
+ /* Remember that we don't have a THD */
+ my_pthread_setspecific_ptr(THR_THD, 0);
+ }
+ DBUG_RETURN(return_val);
}
-/* Reload grant array if possible */
+/*
+ Reload grant array if possible
+
+ SYNOPSIS
+ grant_reload()
+ thd Thread handler
-void grant_reload(void)
+ NOTES
+ Locked tables are checked by acl_init and doesn't have to be checked here
+*/
+
+void grant_reload(THD *thd)
{
- HASH old_hash_tables;bool old_grant_option;
+ HASH old_hash_tables;
+ bool old_grant_option;
MEM_ROOT old_mem;
DBUG_ENTER("grant_reload");
- // Locked tables are checked by acl_init and doesn't have to be checked here
-
pthread_mutex_lock(&LOCK_grant);
grant_version++;
old_hash_tables=hash_tables;
- old_grant_option = grant_option;
+ old_grant_option= grant_option;
old_mem = memex;
- if (grant_init())
+ if (grant_init(thd))
{ // Error. Revert to old hash
grant_free(); /* purecov: deadcode */
hash_tables=old_hash_tables; /* purecov: deadcode */
- grant_option = old_grant_option; /* purecov: deadcode */
+ grant_option= old_grant_option; /* purecov: deadcode */
memex = old_mem; /* purecov: deadcode */
}
else
@@ -2038,11 +2532,11 @@ void grant_reload(void)
/****************************************************************************
-** Check grants
-** All errors are written directly to the client if command name is given !
+ Check grants
+ All errors are written directly to the client if command name is given !
****************************************************************************/
-bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables,
+bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
uint show_table, bool no_errors)
{
TABLE_LIST *table;
@@ -2060,8 +2554,8 @@ bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables,
table->grant.want_privilege=0;
continue; // Already checked
}
- const char *db = table->db ? table->db : thd->db;
- GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip,db,user,
+ GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip,
+ table->db,user,
table->real_name,0);
if (!grant_table)
{
@@ -2089,13 +2583,13 @@ bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables,
pthread_mutex_unlock(&LOCK_grant);
return 0;
- err:
+err:
pthread_mutex_unlock(&LOCK_grant);
if (!no_errors) // Not a silent skip of table
{
const char *command="";
if (want_access & SELECT_ACL)
- command ="select";
+ command ="select";
else if (want_access & INSERT_ACL)
command = "insert";
else if (want_access & UPDATE_ACL)
@@ -2115,7 +2609,7 @@ bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables,
net_printf(&thd->net,ER_TABLEACCESS_DENIED_ERROR,
command,
thd->priv_user,
- thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"),
+ thd->host_or_ip,
table ? table->real_name : "unknown");
}
return 1;
@@ -2128,7 +2622,7 @@ bool check_grant_column(THD *thd,TABLE *table, const char *name,
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
- uint want_access=table->grant.want_privilege;
+ ulong want_access=table->grant.want_privilege;
if (!want_access)
return 0; // Already checked
if (!grant_option)
@@ -2136,7 +2630,7 @@ bool check_grant_column(THD *thd,TABLE *table, const char *name,
pthread_mutex_lock(&LOCK_grant);
- // reload table if someone has modified any grants
+ /* reload table if someone has modified any grants */
if (table->grant.version != grant_version)
{
@@ -2169,19 +2663,14 @@ err:
err2:
if (!show_tables)
{
- const char *command="";
- if (want_access & SELECT_ACL)
- command ="select";
- else if (want_access & INSERT_ACL)
- command = "insert";
- else if (want_access & UPDATE_ACL)
- command = "update";
+ char command[128];
+ get_privilege_desc(command, sizeof(command), want_access);
my_printf_error(ER_COLUMNACCESS_DENIED_ERROR,
ER(ER_COLUMNACCESS_DENIED_ERROR),
MYF(0),
command,
thd->priv_user,
- thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"),
+ thd->host_or_ip,
name,
table ? table->real_name : "unknown");
}
@@ -2189,7 +2678,7 @@ err2:
}
-bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table)
+bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table)
{
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
@@ -2201,7 +2690,7 @@ bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table)
pthread_mutex_lock(&LOCK_grant);
- // reload table if someone has modified any grants
+ /* reload table if someone has modified any grants */
if (table->grant.version != grant_version)
{
@@ -2211,7 +2700,7 @@ bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table)
table->real_name,0); /* purecov: inspected */
table->grant.version=grant_version; /* purecov: inspected */
}
- // The following should always be true
+ /* The following should always be true */
if (!(grant_table=table->grant.grant_table))
goto err; /* purecov: inspected */
@@ -2226,7 +2715,7 @@ bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table)
return 0;
/* We must use my_printf_error() here! */
- err:
+err:
pthread_mutex_unlock(&LOCK_grant);
const char *command="";
@@ -2239,18 +2728,18 @@ bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table)
MYF(0),
command,
thd->priv_user,
- thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"),
+ thd->host_or_ip,
field ? field->field_name : "unknown",
table->real_name);
return 1;
}
-/****************************************************************************
-** Check if a user has the right to access a database
-** Access is accepted if he has a grant for any table in the database
-** Return 1 if access is denied
-****************************************************************************/
+/*
+ Check if a user has the right to access a database
+ Access is accepted if he has a grant for any table in the database
+ Return 1 if access is denied
+*/
bool check_grant_db(THD *thd,const char *db)
{
@@ -2278,10 +2767,10 @@ bool check_grant_db(THD *thd,const char *db)
}
/*****************************************************************************
-** Functions to retrieve the grant for a table/column (for SHOW functions)
+ Functions to retrieve the grant for a table/column (for SHOW functions)
*****************************************************************************/
-uint get_table_grant(THD *thd, TABLE_LIST *table)
+ulong get_table_grant(THD *thd, TABLE_LIST *table)
{
uint privilege;
char *user = thd->priv_user;
@@ -2290,7 +2779,7 @@ uint get_table_grant(THD *thd, TABLE_LIST *table)
pthread_mutex_lock(&LOCK_grant);
grant_table = table_hash_search(thd->host,thd->ip,db,user,
- table->real_name,0);
+ table->real_name, 0);
table->grant.grant_table=grant_table; // Remember for column test
table->grant.version=grant_version;
if (grant_table)
@@ -2301,14 +2790,14 @@ uint get_table_grant(THD *thd, TABLE_LIST *table)
}
-uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field)
+ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field)
{
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
- uint priv;
+ ulong priv;
pthread_mutex_lock(&LOCK_grant);
- // reload table if someone has modified any grants
+ /* reload table if someone has modified any grants */
if (table->grant.version != grant_version)
{
table->grant.grant_table=
@@ -2333,20 +2822,46 @@ uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field)
return priv;
}
+/* Help function for mysql_show_grants */
-/*****************************************************************************
-** SHOW GRANTS : send to client grant-like strings depicting user@host
-** privileges
-*****************************************************************************/
+static void add_user_option(String *grant, ulong value, const char *name)
+{
+ if (value)
+ {
+ char buff[22], *p; // just as in int2str
+ grant->append(' ');
+ grant->append(name, strlen(name));
+ grant->append(' ');
+ p=int10_to_str(value, buff, 10);
+ grant->append(buff,p-buff);
+ }
+}
static const char *command_array[]=
-{"SELECT", "INSERT","UPDATE","DELETE","CREATE", "DROP","RELOAD","SHUTDOWN",
- "PROCESS","FILE","GRANT","REFERENCES","INDEX","ALTER"};
-static int command_lengths[]={6,6,6,6,6,4,6,8,7,4,5,10,5,5};
+{
+ "SELECT", "INSERT","UPDATE","DELETE","CREATE", "DROP", "RELOAD","SHUTDOWN",
+ "PROCESS","FILE","GRANT","REFERENCES","INDEX", "ALTER", "SHOW DATABASES",
+ "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE",
+ "REPLICATION SLAVE", "REPLICATION CLIENT",
+};
+
+static uint command_lengths[]=
+{
+ 6,6,6,6,6,4,6,8,7,4,5,10,5,5,14,5,23,11,7,17,18
+};
-int mysql_show_grants(THD *thd,LEX_USER *lex_user)
+
+/*
+ SHOW GRANTS; Send grants for a user to the client
+
+ IMPLEMENTATION
+ Send to client grant-like strings depicting user@host privileges
+*/
+
+int mysql_show_grants(THD *thd,LEX_USER *lex_user)
{
- uint counter, want_access,index;
+ ulong want_access;
+ uint counter,index;
int error = 0;
ACL_USER *acl_user; ACL_DB *acl_db;
char buff[1024];
@@ -2379,10 +2894,10 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
if (!(host=acl_user->host.hostname))
host="%";
if (!strcmp(lex_user->user.str,user) &&
- !strcmp(lex_user->host.str,host))
+ !my_strcasecmp(lex_user->host.str,host))
break;
}
- if (counter == acl_users.elements)
+ if (counter == acl_users.elements)
{
my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),
MYF(0),lex_user->user.str,lex_user->host.str);
@@ -2403,7 +2918,6 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
VOID(pthread_mutex_lock(&acl_cache->lock));
/* Add first global access grants */
- if (acl_user->access || acl_user->password)
{
want_access=acl_user->access;
String global(buff,sizeof(buff));
@@ -2414,13 +2928,13 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
global.append("ALL PRIVILEGES",14);
else if (!(want_access & ~GRANT_ACL))
global.append("USAGE",5);
- else
+ else
{
bool found=0;
- uint j,test_access= want_access & ~GRANT_ACL;
+ ulong j,test_access= want_access & ~GRANT_ACL;
for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
{
- if (test_access & j)
+ if (test_access & j)
{
if (found)
global.append(", ",2);
@@ -2430,7 +2944,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
}
global.append (" ON *.* TO '",12);
- global.append(lex_user->user.str,lex_user->user.length);
+ global.append(lex_user->user.str,lex_user->user.length);
global.append ("'@'",3);
global.append(lex_user->host.str,lex_user->host.length);
global.append ('\'');
@@ -2442,8 +2956,53 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
global.append(passd_buff);
global.append('\'');
}
- if (want_access & GRANT_ACL)
- global.append(" WITH GRANT OPTION",18);
+ /* "show grants" SSL related stuff */
+ if (acl_user->ssl_type == SSL_TYPE_ANY)
+ global.append(" REQUIRE SSL",12);
+ else if (acl_user->ssl_type == SSL_TYPE_X509)
+ global.append(" REQUIRE X509",13);
+ else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
+ {
+ int ssl_options = 0;
+ global.append(" REQUIRE ",9);
+ if (acl_user->x509_issuer)
+ {
+ ssl_options++;
+ global.append("ISSUER \'",8);
+ global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
+ global.append('\'');
+ }
+ if (acl_user->x509_subject)
+ {
+ if (ssl_options++)
+ global.append(' ');
+ global.append("SUBJECT \'",9);
+ global.append(acl_user->x509_subject,strlen(acl_user->x509_subject));
+ global.append('\'');
+ }
+ if (acl_user->ssl_cipher)
+ {
+ if (ssl_options++)
+ global.append(' ');
+ global.append("CIPHER '",8);
+ global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher));
+ global.append('\'');
+ }
+ }
+ if ((want_access & GRANT_ACL) ||
+ (acl_user->user_resource.questions | acl_user->user_resource.updates |
+ acl_user->user_resource.connections))
+ {
+ global.append(" WITH",5);
+ if (want_access & GRANT_ACL)
+ global.append(" GRANT OPTION",13);
+ add_user_option(&global, acl_user->user_resource.questions,
+ "MAX_QUERIES_PER_HOUR");
+ add_user_option(&global, acl_user->user_resource.updates,
+ "MAX_UPDATES_PER_HOUR");
+ add_user_option(&global, acl_user->user_resource.connections,
+ "MAX_CONNECTIONS_PER_HOUR");
+ }
thd->packet.length(0);
net_store_data(&thd->packet,global.ptr(),global.length());
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),
@@ -2465,10 +3024,10 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
host="";
if (!strcmp(lex_user->user.str,user) &&
- !strcmp(lex_user->host.str,host))
+ !my_strcasecmp(lex_user->host.str,host))
{
want_access=acl_db->access;
- if (want_access)
+ if (want_access)
{
String db(buff,sizeof(buff));
db.length(0);
@@ -2476,10 +3035,12 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
db.append("ALL PRIVILEGES",14);
+ else if (!(want_access & ~GRANT_ACL))
+ db.append("USAGE",5);
else
{
int found=0, cnt;
- uint j,test_access= want_access & ~GRANT_ACL;
+ ulong j,test_access= want_access & ~GRANT_ACL;
for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
{
if (test_access & j)
@@ -2491,15 +3052,15 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
}
}
- db.append (" ON `",5);
+ db.append(" ON `",5);
db.append(acl_db->db);
- db.append ("`.* TO '",8);
- db.append(lex_user->user.str,lex_user->user.length);
- db.append ("'@'",3);
+ db.append("`.* TO '",8);
+ db.append(lex_user->user.str,lex_user->user.length);
+ db.append("'@'",3);
db.append(lex_user->host.str, lex_user->host.length);
- db.append ('\'');
+ db.append('\'');
if (want_access & GRANT_ACL)
- db.append(" WITH GRANT OPTION",18);
+ db.append(" WITH GRANT OPTION",18);
thd->packet.length(0);
net_store_data(&thd->packet,db.ptr(),db.length());
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),
@@ -2516,7 +3077,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
for (index=0 ; index < hash_tables.records ; index++)
{
const char *user,*host;
- GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&hash_tables,index);
+ GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&hash_tables,index);
if (!(user=grant_table->user))
user="";
@@ -2524,46 +3085,58 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
host="";
if (!strcmp(lex_user->user.str,user) &&
- !strcmp(lex_user->host.str,host))
+ !my_strcasecmp(lex_user->host.str,host))
{
- want_access=grant_table->privs;
- if ((want_access | grant_table->cols) != 0)
+ ulong table_access= grant_table->privs;
+ if ((table_access | grant_table->cols) != 0)
{
String global(buff,sizeof(buff));
global.length(0);
global.append("GRANT ",6);
- if (test_all_bits(grant_table->privs,(TABLE_ACLS & ~GRANT_ACL)))
+ if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
global.append("ALL PRIVILEGES",14);
- else
+ else if (!(table_access & ~GRANT_ACL))
+ global.append("USAGE",5);
+ else
{
- int found=0;
- uint j,test_access= (want_access | grant_table->cols) & ~GRANT_ACL;
+ int found= 0;
+ ulong j,test_access= (table_access | grant_table->cols) & ~GRANT_ACL;
- for (counter=0, j = SELECT_ACL;j <= TABLE_ACLS; counter++,j <<= 1)
+ for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
{
- if (test_access & j)
+ if (test_access & j)
{
if (found)
global.append(", ",2);
- found = 1;
+ found= 1;
global.append(command_array[counter],command_lengths[counter]);
- if (grant_table->cols)
+ if (grant_table->cols)
{
- uint found_col=0;
+ uint found_col= 0;
for (uint col_index=0 ;
col_index < grant_table->hash_columns.records ;
col_index++)
{
GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
hash_element(&grant_table->hash_columns,col_index);
- if (grant_column->rights & j)
+ if (grant_column->rights & j)
{
- if (!found_col)
+ if (!found_col)
{
+ found_col= 1;
+ /*
+ If we have a duplicated table level privilege, we
+ must write the access privilege name again.
+ */
+ if (table_access & j)
+ {
+ global.append(", ", 2);
+ global.append(command_array[counter],
+ command_lengths[counter]);
+ }
global.append(" (",2);
- found_col=1;
}
else
global.append(", ",2);
@@ -2577,17 +3150,17 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
}
}
- global.append(" ON ",4);
+ global.append(" ON `",5);
global.append(grant_table->db);
- global.append(".",1);
+ global.append("`.`",3);
global.append(grant_table->tname);
- global.append(" TO '",5);
- global.append(lex_user->user.str,lex_user->user.length);
+ global.append("` TO '",6);
+ global.append(lex_user->user.str,lex_user->user.length);
global.append("'@'",3);
- global.append(lex_user->host.str,lex_user->host.length);
+ global.append(lex_user->host.str,lex_user->host.length);
global.append('\'');
- if (want_access & GRANT_ACL)
- global.append(" WITH GRANT OPTION",18);
+ if (table_access & GRANT_ACL)
+ global.append(" WITH GRANT OPTION",18);
thd->packet.length(0);
net_store_data(&thd->packet,global.ptr(),global.length());
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),
@@ -2600,7 +3173,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
}
- end:
+end:
VOID(pthread_mutex_unlock(&acl_cache->lock));
pthread_mutex_unlock(&LOCK_grant);
@@ -2609,8 +3182,47 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
+/*
+ Make a clear-text version of the requested privilege.
+*/
+
+void get_privilege_desc(char *to, uint max_length, ulong access)
+{
+ uint pos;
+ char *start=to;
+ DBUG_ASSERT(max_length >= 30); // For end ',' removal
+
+ if (access)
+ {
+ max_length--; // Reserve place for end-zero
+ for (pos=0 ; access ; pos++, access>>=1)
+ {
+ if ((access & 1) &&
+ command_lengths[pos] + (uint) (to-start) < max_length)
+ {
+ to= strmov(to, command_array[pos]);
+ *to++=',';
+ }
+ }
+ to--; // Remove end ','
+ }
+ *to=0;
+}
+
+
+void get_mqh(const char *user, const char *host, USER_CONN *uc)
+{
+ ACL_USER *acl_user;
+ if (initialized && (acl_user= find_acl_user(host,user)))
+ uc->user_resources= acl_user->user_resource;
+ else
+ bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
+}
+
+
+
/*****************************************************************************
-** Instantiate used templates
+ Instantiate used templates
*****************************************************************************/
#ifdef __GNUC__