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.cc3496
1 files changed, 2850 insertions, 646 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 6cbe6554235..50cee6eeca4 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -17,8 +17,8 @@
/*
The privileges are saved in the following tables:
- mysql/user ; super user who are allowed to do almoust anything
- mysql/host ; host priviliges. This is used if host is empty in mysql/db.
+ mysql/user ; super user who are allowed to do almost anything
+ mysql/host ; host privileges. This is used if host is empty in mysql/db.
mysql/db ; database privileges / user
data in tables is sorted according to how many not-wild-cards there is
@@ -32,6 +32,8 @@
#endif
#include <m_ctype.h>
#include <stdarg.h>
+#include "sp_head.h"
+#include "sp.h"
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -58,15 +60,15 @@ static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
-static HASH acl_check_hosts, column_priv_hash;
+static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
-static uint grant_version=0;
-static uint priv_version=0; /* Version of priv tables. incremented by acl_load */
+static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
+static void rebuild_check_host(void);
static ACL_USER *find_acl_user(const char *host, const char *user,
my_bool exact);
static bool update_user_table(THD *thd, TABLE *table,
@@ -159,6 +161,7 @@ my_bool acl_init(bool dont_read_acl_tables)
*/
if (!(thd=new THD))
DBUG_RETURN(1); /* purecov: inspected */
+ thd->thread_stack= (char*) &thd;
thd->store_globals();
/*
It is safe to call acl_reload() since acl_* arrays and hashes which
@@ -198,7 +201,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
int password_length;
DBUG_ENTER("acl_load");
- priv_version++; /* Privileges updated */
+ grant_version++; /* Privileges updated */
acl_cache->clear(1); // Clear locked hostname cache
@@ -213,19 +216,18 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
if (lower_case_table_names && host.db)
{
/*
- We make a temporary copy of the database, force it to lower case,
- and then check it against the original name.
+ convert db to lower case and give a warning if the db wasn't
+ already in lower case
*/
- (void)strnmov(tmp_name, host.db, sizeof(tmp_name));
+ (void) strmov(tmp_name, host.db);
my_casedn_str(files_charset_info, host.db);
if (strcmp(host.db, tmp_name) != 0)
- {
sql_print_warning("'host' entry '%s|%s' had database in mixed "
"case that has been forced to lowercase because "
"lower_case_table_names is set. It will not be "
"possible to remove this privilege using REVOKE.",
- host.host.hostname, host.db);
- }
+ host.host.hostname ? host.host.hostname : "",
+ host.db ? host.db : "");
}
host.access= get_access(table,2);
host.access= fix_rights_for_db(host.access);
@@ -234,11 +236,12 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
{
sql_print_warning("'host' entry '%s|%s' "
"ignored in --skip-name-resolve mode.",
- host.host.hostname, host.db?host.db:"");
+ host.host.hostname ? host.host.hostname : "",
+ host.db ? host.db : "");
continue;
}
#ifndef TO_BE_REMOVED
- if (table->fields == 8)
+ if (table->s->fields == 8)
{ // Without grant
if (host.access & CREATE_ACL)
host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
@@ -263,8 +266,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
}
DBUG_PRINT("info",("user table fields: %d, password length: %d",
- table->fields, password_length));
-
+ table->s->fields, password_length));
+
pthread_mutex_lock(&LOCK_global_system_variables);
if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
{
@@ -304,7 +307,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
{
sql_print_warning("'user' entry '%s@%s' "
"ignored in --skip-name-resolve mode.",
- user.user, user.host.hostname);
+ user.user ? user.user : "",
+ user.host.hostname ? user.host.hostname : "");
continue;
}
@@ -332,10 +336,34 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
{
uint next_field;
user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
+ /*
+ if it is pre 5.0.1 privilege table then map CREATE privilege on
+ CREATE VIEW & SHOW VIEW privileges
+ */
+ if (table->s->fields <= 31 && (user.access & CREATE_ACL))
+ user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);
+
+ /*
+ if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
+ CREATE PROCEDURE & ALTER PROCEDURE privileges
+ */
+ if (table->s->fields <= 33 && (user.access & CREATE_ACL))
+ user.access|= CREATE_PROC_ACL;
+ if (table->s->fields <= 33 && (user.access & ALTER_ACL))
+ user.access|= ALTER_PROC_ACL;
+
+ /*
+ pre 5.0.3 did not have CREATE_USER_ACL
+ */
+ if (table->s->fields <= 36 && (user.access & GRANT_ACL))
+ user.access|= CREATE_USER_ACL;
+
user.sort= get_sort(2,user.host.hostname,user.user);
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 */
+
+ /* Starting from 4.0.2 we have more fields */
+ if (table->s->fields >= 31)
{
char *ssl_type=get_field(&mem, table->field[next_field++]);
if (!ssl_type)
@@ -356,17 +384,26 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
ptr = get_field(&mem, table->field[next_field++]);
user.user_resource.updates=ptr ? atoi(ptr) : 0;
ptr = get_field(&mem, table->field[next_field++]);
- user.user_resource.connections=ptr ? atoi(ptr) : 0;
+ user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
if (user.user_resource.questions || user.user_resource.updates ||
- user.user_resource.connections)
+ user.user_resource.conn_per_hour)
mqh_used=1;
+
+ if (table->s->fields >= 36)
+ {
+ /* Starting from 5.0.3 we have max_user_connections field */
+ ptr= get_field(&mem, table->field[next_field++]);
+ user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
+ }
+ else
+ user.user_resource.user_conn= 0;
}
else
{
user.ssl_type=SSL_TYPE_NONE;
bzero((char *)&(user.user_resource),sizeof(user.user_resource));
#ifndef TO_BE_REMOVED
- if (table->fields <= 13)
+ if (table->s->fields <= 13)
{ // Without grant
if (user.access & CREATE_ACL)
user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
@@ -380,8 +417,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
#endif
}
VOID(push_dynamic(&acl_users,(gptr) &user));
- if (!user.host.hostname || user.host.hostname[0] == wild_many &&
- !user.host.hostname[1])
+ if (!user.host.hostname ||
+ (user.host.hostname[0] == wild_many && !user.host.hostname[1]))
allow_all_hosts=1; // Anyone can connect
}
}
@@ -407,7 +444,9 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
{
sql_print_warning("'db' entry '%s %s@%s' "
"ignored in --skip-name-resolve mode.",
- db.db, db.user, db.host.hostname);
+ db.db,
+ db.user ? db.user : "",
+ db.host.hostname ? db.host.hostname : "");
continue;
}
db.access=get_access(table,3);
@@ -415,10 +454,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
if (lower_case_table_names)
{
/*
- We make a temporary copy of the database, force it to lower case,
- and then check it against the original name.
+ convert db to lower case and give a warning if the db wasn't
+ already in lower case
*/
- (void)strnmov(tmp_name, db.db, sizeof(tmp_name));
+ (void)strmov(tmp_name, db.db);
my_casedn_str(files_charset_info, db.db);
if (strcmp(db.db, tmp_name) != 0)
{
@@ -426,12 +465,14 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
"case that has been forced to lowercase because "
"lower_case_table_names is set. It will not be "
"possible to remove this privilege using REVOKE.",
- db.db, db.user, db.host.hostname);
+ db.db,
+ db.user ? db.user : "",
+ db.host.hostname ? db.host.hostname : "");
}
}
db.sort=get_sort(3,db.host.hostname,db.db,db.user);
#ifndef TO_BE_REMOVED
- if (table->fields <= 9)
+ if (table->s->fields <= 9)
{ // Without grant
if (db.access & CREATE_ACL)
db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
@@ -511,12 +552,12 @@ my_bool acl_reload(THD *thd)
obtaining acl_cache->lock mutex.
*/
bzero((char*) tables, sizeof(tables));
- tables[0].alias=tables[0].real_name=(char*) "host";
- tables[1].alias=tables[1].real_name=(char*) "user";
- tables[2].alias=tables[2].real_name=(char*) "db";
- tables[0].db=tables[1].db=tables[2].db= (char*) "mysql";
- tables[0].next= tables+1;
- tables[1].next= tables+2;
+ tables[0].alias= tables[0].table_name= (char*) "host";
+ tables[1].alias= tables[1].table_name= (char*) "user";
+ tables[2].alias= tables[2].table_name= (char*) "db";
+ tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
+ tables[0].next_local= tables[0].next_global= tables+1;
+ tables[1].next_local= tables[1].next_global= tables+2;
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
if (simple_open_n_lock_tables(thd, tables))
@@ -565,8 +606,8 @@ end:
Get all access bits from table after fieldnr
IMPLEMENTATION
- We know that the access privileges ends when there is no more fields
- or the field is not an enum with two elements.
+ We know that the access privileges ends when there is no more fields
+ or the field is not an enum with two elements.
SYNOPSIS
get_access()
@@ -665,11 +706,11 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
SYNOPSIS
acl_getroot()
thd thread handle. If all checks are OK,
- thd->priv_user, thd->master_access are updated.
- thd->host, thd->ip, thd->user are used for checks.
+ thd->security_ctx->priv_user/master_access are updated.
+ thd->security_ctx->host/ip/user are used for checks.
mqh user resources; on success mqh is reset, else
unchanged
- passwd scrambled & crypted password, recieved from client
+ passwd scrambled & crypted password, received from client
(to check): thd->scramble or thd->scramble_323 is
used to decrypt passwd, so they must contain
original random string,
@@ -680,7 +721,7 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
RETURN VALUE
0 success: thd->priv_user, thd->priv_host, thd->master_access, mqh are
updated
- 1 user not found or authentification failure
+ 1 user not found or authentication failure
2 user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
-1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
*/
@@ -691,6 +732,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
ulong user_access= NO_ACCESS;
int res= 1;
ACL_USER *acl_user= 0;
+ Security_context *sctx= thd->security_ctx;
DBUG_ENTER("acl_getroot");
if (!initialized)
@@ -698,9 +740,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
/*
here if mysqld's been started with --skip-grant-tables option.
*/
- thd->priv_user= (char *) ""; // privileges for
- *thd->priv_host= '\0'; // the user are unknown
- thd->master_access= ~NO_ACCESS; // everything is allowed
+ sctx->skip_grants();
bzero((char*) mqh, sizeof(*mqh));
DBUG_RETURN(0);
}
@@ -716,9 +756,9 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
for (uint i=0 ; i < acl_users.elements ; i++)
{
ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
- if (!acl_user_tmp->user || !strcmp(thd->user, acl_user_tmp->user))
+ if (!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user))
{
- if (compare_hostname(&acl_user_tmp->host, thd->host, thd->ip))
+ if (compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
{
/* check password: it should be empty or valid */
if (passwd_len == acl_user_tmp->salt_len)
@@ -820,12 +860,12 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
break;
}
DBUG_PRINT("info",("checkpoint 2"));
- /* If X509 issuer is speified, we check it... */
+ /* If X509 issuer is specified, 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'",
+ 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))
{
@@ -833,6 +873,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
sql_print_information("X509 issuer mismatch: should be '%s' "
"but is '%s'", acl_user->x509_issuer, ptr);
free(ptr);
+ user_access=NO_ACCESS;
break;
}
user_access= acl_user->access;
@@ -848,11 +889,13 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
if (strcmp(acl_user->x509_subject,ptr))
{
if (global_system_variables.log_warnings)
- sql_print_information("X509 subject mismatch: '%s' vs '%s'",
+ sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
acl_user->x509_subject, ptr);
+ free(ptr);
+ user_access=NO_ACCESS;
+ break;
}
- else
- user_access= acl_user->access;
+ user_access= acl_user->access;
free(ptr);
}
break;
@@ -865,20 +908,119 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
break;
#endif /* HAVE_OPENSSL */
}
- thd->master_access= user_access;
- thd->priv_user= acl_user->user ? thd->user : (char *) "";
+ sctx->master_access= user_access;
+ sctx->priv_user= acl_user->user ? sctx->user : (char *) "";
*mqh= acl_user->user_resource;
if (acl_user->host.hostname)
- strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
+ strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
else
- *thd->priv_host= 0;
+ *sctx->priv_host= 0;
}
VOID(pthread_mutex_unlock(&acl_cache->lock));
DBUG_RETURN(res);
}
+/*
+ This is like acl_getroot() above, but it doesn't check password,
+ and we don't care about the user resources.
+
+ SYNOPSIS
+ acl_getroot_no_password()
+ sctx Context which should be initialized
+ user user name
+ host host name
+ ip IP
+ db current data base name
+
+ RETURN
+ FALSE OK
+ TRUE Error
+*/
+
+bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
+ char *ip, char *db)
+{
+ int res= 1;
+ uint i;
+ ACL_USER *acl_user= 0;
+ DBUG_ENTER("acl_getroot_no_password");
+
+ DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
+ (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
+ user, (db ? db : "(NULL)")));
+ sctx->user= user;
+ sctx->host= host;
+ sctx->ip= ip;
+ sctx->host_or_ip= host ? host : (ip ? ip : "");
+
+ if (!initialized)
+ {
+ /*
+ here if mysqld's been started with --skip-grant-tables option.
+ */
+ sctx->skip_grants();
+ DBUG_RETURN(FALSE);
+ }
+
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ sctx->master_access= 0;
+ sctx->db_access= 0;
+ sctx->priv_user= (char *) "";
+ *sctx->priv_host= 0;
+
+ /*
+ Find acl entry in user database.
+ This is specially tailored to suit the check we do for CALL of
+ a stored procedure; user is set to what is actually a
+ priv_user, which can be ''.
+ */
+ for (i=0 ; i < acl_users.elements ; i++)
+ {
+ acl_user= dynamic_element(&acl_users,i,ACL_USER*);
+ if ((!acl_user->user && !user[0]) ||
+ (acl_user->user && strcmp(user, acl_user->user) == 0))
+ {
+ if (compare_hostname(&acl_user->host, host, ip))
+ {
+ res= 0;
+ break;
+ }
+ }
+ }
+
+ if (acl_user)
+ {
+ for (i=0 ; i < acl_dbs.elements ; i++)
+ {
+ ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
+ if (!acl_db->user ||
+ (user && user[0] && !strcmp(user, acl_db->user)))
+ {
+ if (compare_hostname(&acl_db->host, host, ip))
+ {
+ if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
+ {
+ sctx->db_access= acl_db->access;
+ break;
+ }
+ }
+ }
+ }
+ sctx->master_access= acl_user->access;
+ sctx->priv_user= acl_user->user ? user : (char *) "";
+
+ if (acl_user->host.hostname)
+ strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
+ else
+ *sctx->priv_host= 0;
+ }
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ DBUG_RETURN(res);
+}
+
static byte* check_get_key(ACL_USER *buff,uint *length,
my_bool not_used __attribute__((unused)))
{
@@ -902,20 +1044,21 @@ static void acl_update_user(const char *user, const char *host,
{
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
if (!acl_user->user && !user[0] ||
- acl_user->user &&
- !strcmp(user,acl_user->user))
+ acl_user->user && !strcmp(user,acl_user->user))
{
if (!acl_user->host.hostname && !host[0] ||
acl_user->host.hostname &&
!my_strcasecmp(system_charset_info, host, acl_user->host.hostname))
{
acl_user->access=privileges;
- if (mqh->bits & 1)
+ if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
acl_user->user_resource.questions=mqh->questions;
- if (mqh->bits & 2)
+ if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
acl_user->user_resource.updates=mqh->updates;
- if (mqh->bits & 4)
- acl_user->user_resource.connections=mqh->connections;
+ if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
+ acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
+ if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
+ acl_user->user_resource.user_conn= mqh->user_conn;
if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
{
acl_user->ssl_type= ssl_type;
@@ -964,16 +1107,14 @@ static void acl_insert_user(const char *user, const char *host,
set_user_salt(&acl_user, password, password_len);
VOID(push_dynamic(&acl_users,(gptr) &acl_user));
- if (!acl_user.host.hostname || acl_user.host.hostname[0] == wild_many
- && !acl_user.host.hostname[1])
+ if (!acl_user.host.hostname ||
+ (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1]))
allow_all_hosts=1; // Anyone can connect /* purecov: tested */
qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
sizeof(ACL_USER),(qsort_cmp) acl_compare);
- /* We must free acl_check_hosts as its memory is mapped to acl_user */
- delete_dynamic(&acl_wild_hosts);
- hash_free(&acl_check_hosts);
- init_check_host();
+ /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
+ rebuild_check_host();
}
@@ -1027,7 +1168,7 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
ACL_DB acl_db;
safe_mutex_assert_owner(&acl_cache->lock);
acl_db.user=strdup_root(&mem,user);
- update_hostname(&acl_db.host,strdup_root(&mem,host));
+ update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
acl_db.db=strdup_root(&mem,db);
acl_db.access=privileges;
acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
@@ -1048,7 +1189,7 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
ulong acl_get(const char *host, const char *ip,
const char *user, const char *db, my_bool db_is_pattern)
{
- ulong host_access= ~(ulong)0,db_access= 0;
+ ulong host_access= ~(ulong)0, db_access= 0;
uint i,key_length;
char key[ACL_KEY_LENGTH],*tmp_db,*end;
acl_entry *entry;
@@ -1177,6 +1318,22 @@ static void init_check_host(void)
}
+/*
+ Rebuild lists used for checking of allowed hosts
+
+ We need to rebuild 'acl_check_hosts' and 'acl_wild_hosts' after adding,
+ dropping or renaming user, since they contain pointers to elements of
+ 'acl_user' array, which are invalidated by drop operation, and use
+ ACL_USER::host::hostname as a key, which is changed by rename.
+*/
+void rebuild_check_host(void)
+{
+ delete_dynamic(&acl_wild_hosts);
+ hash_free(&acl_check_hosts);
+ init_check_host();
+}
+
+
/* Return true if there is no users that can match the given host */
bool acl_check_host(const char *host, const char *ip)
@@ -1228,29 +1385,28 @@ bool check_change_password(THD *thd, const char *host, const char *user,
{
if (!initialized)
{
- net_printf(thd,ER_OPTION_PREVENTS_STATEMENT,
- "--skip-grant-tables");
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
return(1);
}
if (!thd->slave_thread &&
- (strcmp(thd->user,user) ||
- my_strcasecmp(system_charset_info, host, thd->priv_host)))
+ (strcmp(thd->security_ctx->user, user) ||
+ my_strcasecmp(system_charset_info, host,
+ thd->security_ctx->priv_host)))
{
- if (check_access(thd, UPDATE_ACL, "mysql",0,1,0))
+ if (check_access(thd, UPDATE_ACL, "mysql",0,1,0,0))
return(1);
}
- if (!thd->slave_thread && !thd->user[0])
+ if (!thd->slave_thread && !thd->security_ctx->user[0])
{
- send_error(thd, ER_PASSWORD_ANONYMOUS_USER);
+ my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
+ MYF(0));
return(1);
}
uint len=strlen(new_password);
if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
{
- net_printf(thd, 0,
- "Password hash should be a %d-digit hexadecimal number",
- SCRAMBLED_PASSWORD_CHAR_LENGTH);
+ my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
return -1;
}
return(0);
@@ -1291,7 +1447,7 @@ bool change_password(THD *thd, const char *host, const char *user,
DBUG_RETURN(1);
bzero((char*) &tables, sizeof(tables));
- tables.alias=tables.real_name= (char*) "user";
+ tables.alias= tables.table_name= (char*) "user";
tables.db= (char*) "mysql";
#ifdef HAVE_REPLICATION
@@ -1307,7 +1463,7 @@ bool change_password(THD *thd, const char *host, const char *user,
*/
tables.updating= 1;
/* Thanks to bzero, tables.next==0 */
- if (!tables_ok(0, &tables))
+ if (!tables_ok(thd, &tables))
DBUG_RETURN(0);
}
#endif
@@ -1320,7 +1476,7 @@ bool change_password(THD *thd, const char *host, const char *user,
if (!(acl_user= find_acl_user(host, user, TRUE)))
{
VOID(pthread_mutex_unlock(&acl_cache->lock));
- send_error(thd, ER_PASSWORD_NO_MATCH);
+ my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
goto end;
}
/* update loaded acl entry: */
@@ -1332,22 +1488,20 @@ bool change_password(THD *thd, const char *host, const char *user,
new_password, new_password_len))
{
VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
- send_error(thd,0); /* purecov: deadcode */
goto end;
}
acl_cache->clear(1); // Clear locked hostname cache
VOID(pthread_mutex_unlock(&acl_cache->lock));
result= 0;
- 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, query_length);
if (mysql_bin_log.is_open())
{
+ 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));
thd->clear_error();
Query_log_event qinfo(thd, buff, query_length, 0, FALSE);
mysql_bin_log.write(&qinfo);
@@ -1359,6 +1513,34 @@ end:
/*
+ Find user in ACL
+
+ SYNOPSIS
+ is_acl_user()
+ host host name
+ user user name
+
+ RETURN
+ FALSE user not fond
+ TRUE there are such user
+*/
+
+bool is_acl_user(const char *host, const char *user)
+{
+ bool res;
+
+ /* --skip-grants */
+ if (!initialized)
+ return TRUE;
+
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+ res= find_acl_user(host, user, TRUE) != NULL;
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ return res;
+}
+
+
+/*
Find first entry that matches the current user
*/
@@ -1374,11 +1556,10 @@ find_acl_user(const char *host, const char *user, my_bool exact)
{
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 :
- ""));
+ 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))
{
@@ -1428,7 +1609,7 @@ static const char *calc_ip(const char *ip, long *val, char end)
static void update_hostname(acl_host_and_ip *host, const char *hostname)
{
- host->hostname=(char*) hostname; // This will not be modified!
+ host->hostname=(char*) hostname; // This will not be modified!
if (!hostname ||
(!(hostname=calc_ip(hostname,&host->ip,'/')) ||
!(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
@@ -1448,8 +1629,8 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
}
return (!host->hostname ||
(hostname && !wild_case_compare(system_charset_info,
- hostname,host->hostname)) ||
- (ip && !wild_compare(ip,host->hostname,0)));
+ hostname, host->hostname)) ||
+ (ip && !wild_compare(ip, host->hostname, 0)));
}
bool hostname_requires_resolving(const char *hostname)
@@ -1490,20 +1671,23 @@ static bool update_user_table(THD *thd, TABLE *table,
const char *host, const char *user,
const char *new_password, uint new_password_len)
{
+ char user_key[MAX_KEY_LENGTH];
int error;
DBUG_ENTER("update_user_table");
DBUG_PRINT("enter",("user: %s host: %s",user,host));
table->field[0]->store(host,(uint) strlen(host), system_charset_info);
table->field[1]->store(user,(uint) strlen(user), system_charset_info);
+ key_copy((byte *) user_key, table->record[0], table->key_info,
+ table->key_info->key_length);
table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (table->file->index_read_idx(table->record[0],0,
- (byte*) table->field[0]->ptr,
- table->key_info[0].key_length,
+ if (table->file->index_read_idx(table->record[0], 0,
+ (byte *) user_key, table->key_info->key_length,
HA_READ_KEY_EXACT))
{
- my_error(ER_PASSWORD_NO_MATCH,MYF(0)); /* purecov: deadcode */
+ my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
+ MYF(0)); /* purecov: deadcode */
DBUG_RETURN(1); /* purecov: deadcode */
}
store_record(table,record[1]);
@@ -1517,21 +1701,30 @@ static bool update_user_table(THD *thd, TABLE *table,
}
-/* Return 1 if we are allowed to create new users */
+/*
+ Return 1 if we are allowed to create new users
+ the logic here is: INSERT_ACL is sufficient.
+ It's also a requirement in opt_safe_user_create,
+ otherwise CREATE_USER_ACL is enough.
+*/
static bool test_if_create_new_users(THD *thd)
{
- bool create_new_users=1; // Assume that we are allowed to create new users
- if (opt_safe_user_create && !(thd->master_access & INSERT_ACL))
+ Security_context *sctx= thd->security_ctx;
+ bool create_new_users= test(sctx->master_access & INSERT_ACL) ||
+ (!opt_safe_user_create &&
+ test(sctx->master_access & CREATE_USER_ACL));
+ if (!create_new_users)
{
TABLE_LIST tl;
ulong db_access;
bzero((char*) &tl,sizeof(tl));
tl.db= (char*) "mysql";
- tl.real_name= (char*) "user";
+ tl.table_name= (char*) "user";
+ create_new_users= 1;
- db_access=acl_get(thd->host, thd->ip,
- thd->priv_user, tl.db, 0);
+ db_access=acl_get(sctx->host, sctx->ip,
+ sctx->priv_user, tl.db, 0);
if (!(db_access & INSERT_ACL))
{
if (check_grant(thd, INSERT_ACL, &tl, 0, UINT_MAX, 1))
@@ -1548,14 +1741,17 @@ static bool test_if_create_new_users(THD *thd)
static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
ulong rights, bool revoke_grant,
- bool create_user)
+ bool can_create_user, bool no_auto_create)
{
int error = -1;
bool old_row_exists=0;
const char *password= "";
uint password_len= 0;
char what= (revoke_grant) ? 'N' : 'Y';
+ byte user_key[MAX_KEY_LENGTH];
+ LEX *lex= thd->lex;
DBUG_ENTER("replace_user_table");
+
safe_mutex_assert_owner(&acl_cache->lock);
if (combo.password.str && combo.password.str[0])
@@ -1563,9 +1759,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
{
- my_printf_error(ER_UNKNOWN_ERROR,
- "Password hash should be a %d-digit hexadecimal number",
- MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
+ my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
DBUG_RETURN(-1);
}
password_len= combo.password.length;
@@ -1574,23 +1768,46 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
table->field[1]->store(combo.user.str,combo.user.length, system_charset_info);
+ key_copy(user_key, table->record[0], table->key_info,
+ table->key_info->key_length);
+
table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if (table->file->index_read_idx(table->record[0], 0,
- (byte*) table->field[0]->ptr,
- table->key_info[0].key_length,
- HA_READ_KEY_EXACT))
+ user_key, table->key_info->key_length,
+ HA_READ_KEY_EXACT))
{
- if (!create_user)
+ /* what == 'N' means revoke */
+ if (what == 'N')
{
- if (what == 'N')
- my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
- else
- my_error(ER_NO_PERMISSION_TO_CREATE_USER, MYF(0),
- thd->user, thd->host_or_ip);
+ my_error(ER_NONEXISTING_GRANT, MYF(0), combo.user.str, combo.host.str);
+ goto end;
+ }
+ /*
+ There are four options which affect the process of creation of
+ a new user (mysqld option --safe-create-user, 'insert' privilege
+ on 'mysql.user' table, using 'GRANT' with 'IDENTIFIED BY' and
+ SQL_MODE flag NO_AUTO_CREATE_USER). Below is the simplified rule
+ how it should work.
+ if (safe-user-create && ! INSERT_priv) => reject
+ else if (identified_by) => create
+ else if (no_auto_create_user) => reject
+ else create
+
+ see also test_if_create_new_users()
+ */
+ else if (!password_len && no_auto_create)
+ {
+ my_error(ER_PASSWORD_NO_MATCH, MYF(0), combo.user.str, combo.host.str);
+ goto end;
+ }
+ else if (!can_create_user)
+ {
+ my_error(ER_CANT_CREATE_USER_WITH_GRANT, MYF(0),
+ thd->security_ctx->user, thd->security_ctx->host_or_ip);
goto end;
}
old_row_exists = 0;
- restore_record(table,default_values); // cp empty row from default_values
+ restore_record(table,s->default_values);
table->field[0]->store(combo.host.str,combo.host.length,
system_charset_info);
table->field[1]->store(combo.user.str,combo.user.length,
@@ -1604,8 +1821,9 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
store_record(table,record[1]); // Save copy for update
if (combo.password.str) // If password given
table->field[2]->store(password, password_len, system_charset_info);
- else if (!rights && !revoke_grant && thd->lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
- !thd->lex->mqh.bits)
+ else if (!rights && !revoke_grant &&
+ lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
+ !lex->mqh.specified_limits)
{
DBUG_RETURN(0);
}
@@ -1625,40 +1843,40 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
(*tmp_field)->store(&what, 1, &my_charset_latin1);
}
rights= get_access(table, 3, &next_field);
- DBUG_PRINT("info",("table->fields: %d",table->fields));
- if (table->fields >= 31) /* From 4.0.0 we have more fields */
+ DBUG_PRINT("info",("table fields: %d",table->s->fields));
+ if (table->s->fields >= 31) /* From 4.0.0 we have more fields */
{
/* We write down SSL related ACL stuff */
- switch (thd->lex->ssl_type) {
+ switch (lex->ssl_type) {
case SSL_TYPE_ANY:
- table->field[next_field]->store("ANY", 3, &my_charset_latin1);
+ table->field[next_field]->store(STRING_WITH_LEN("ANY"),
+ &my_charset_latin1);
table->field[next_field+1]->store("", 0, &my_charset_latin1);
table->field[next_field+2]->store("", 0, &my_charset_latin1);
table->field[next_field+3]->store("", 0, &my_charset_latin1);
break;
case SSL_TYPE_X509:
- table->field[next_field]->store("X509", 4, &my_charset_latin1);
+ table->field[next_field]->store(STRING_WITH_LEN("X509"),
+ &my_charset_latin1);
table->field[next_field+1]->store("", 0, &my_charset_latin1);
table->field[next_field+2]->store("", 0, &my_charset_latin1);
table->field[next_field+3]->store("", 0, &my_charset_latin1);
break;
case SSL_TYPE_SPECIFIED:
- table->field[next_field]->store("SPECIFIED", 9, &my_charset_latin1);
+ table->field[next_field]->store(STRING_WITH_LEN("SPECIFIED"),
+ &my_charset_latin1);
table->field[next_field+1]->store("", 0, &my_charset_latin1);
table->field[next_field+2]->store("", 0, &my_charset_latin1);
table->field[next_field+3]->store("", 0, &my_charset_latin1);
- if (thd->lex->ssl_cipher)
- table->field[next_field+1]->store(thd->lex->ssl_cipher,
- strlen(thd->lex->ssl_cipher),
- system_charset_info);
- if (thd->lex->x509_issuer)
- table->field[next_field+2]->store(thd->lex->x509_issuer,
- strlen(thd->lex->x509_issuer),
- system_charset_info);
- if (thd->lex->x509_subject)
- table->field[next_field+3]->store(thd->lex->x509_subject,
- strlen(thd->lex->x509_subject),
- system_charset_info);
+ if (lex->ssl_cipher)
+ table->field[next_field+1]->store(lex->ssl_cipher,
+ strlen(lex->ssl_cipher), system_charset_info);
+ if (lex->x509_issuer)
+ table->field[next_field+2]->store(lex->x509_issuer,
+ strlen(lex->x509_issuer), system_charset_info);
+ if (lex->x509_subject)
+ table->field[next_field+3]->store(lex->x509_subject,
+ strlen(lex->x509_subject), system_charset_info);
break;
case SSL_TYPE_NOT_SPECIFIED:
break;
@@ -1669,18 +1887,19 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
table->field[next_field+3]->store("", 0, &my_charset_latin1);
break;
}
-
- /* Skip over SSL related fields to first user limits related field */
- next_field+= 4;
-
- USER_RESOURCES mqh= thd->lex->mqh;
- if (mqh.bits & 1)
- table->field[next_field]->store((longlong) mqh.questions);
- if (mqh.bits & 2)
- table->field[next_field+1]->store((longlong) mqh.updates);
- if (mqh.bits & 4)
- table->field[next_field+2]->store((longlong) mqh.connections);
- mqh_used = mqh_used || mqh.questions || mqh.updates || mqh.connections;
+ next_field+=4;
+
+ USER_RESOURCES mqh= lex->mqh;
+ if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
+ table->field[next_field]->store((longlong) mqh.questions, TRUE);
+ if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
+ table->field[next_field+1]->store((longlong) mqh.updates, TRUE);
+ if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
+ table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE);
+ if (table->s->fields >= 36 &&
+ (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
+ table->field[next_field+3]->store((longlong) mqh.user_conn);
+ mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
}
if (old_row_exists)
{
@@ -1716,19 +1935,19 @@ end:
if (old_row_exists)
acl_update_user(combo.user.str, combo.host.str,
combo.password.str, password_len,
- thd->lex->ssl_type,
- thd->lex->ssl_cipher,
- thd->lex->x509_issuer,
- thd->lex->x509_subject,
- &thd->lex->mqh,
+ lex->ssl_type,
+ lex->ssl_cipher,
+ lex->x509_issuer,
+ lex->x509_subject,
+ &lex->mqh,
rights);
else
acl_insert_user(combo.user.str, combo.host.str, password, password_len,
- thd->lex->ssl_type,
- thd->lex->ssl_cipher,
- thd->lex->x509_issuer,
- thd->lex->x509_subject,
- &thd->lex->mqh,
+ lex->ssl_type,
+ lex->ssl_cipher,
+ lex->x509_issuer,
+ lex->x509_subject,
+ &lex->mqh,
rights);
}
DBUG_RETURN(error);
@@ -1748,6 +1967,7 @@ static int replace_db_table(TABLE *table, const char *db,
bool old_row_exists=0;
int error;
char what= (revoke_grant) ? 'N' : 'Y';
+ byte user_key[MAX_KEY_LENGTH];
DBUG_ENTER("replace_db_table");
if (!initialized)
@@ -1759,18 +1979,20 @@ static int replace_db_table(TABLE *table, const char *db,
/* Check if there is such a user in user table in memory? */
if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
{
- my_error(ER_PASSWORD_NO_MATCH,MYF(0));
+ my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
DBUG_RETURN(-1);
}
table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
table->field[1]->store(db,(uint) strlen(db), system_charset_info);
table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
+ key_copy(user_key, table->record[0], table->key_info,
+ table->key_info->key_length);
+
table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
if (table->file->index_read_idx(table->record[0],0,
- (byte*) table->field[0]->ptr,
- table->key_info[0].key_length,
- HA_READ_KEY_EXACT))
+ user_key, table->key_info->key_length,
+ HA_READ_KEY_EXACT))
{
if (what == 'N')
{ // no row, no revoke
@@ -1778,7 +2000,7 @@ static int replace_db_table(TABLE *table, const char *db,
goto abort;
}
old_row_exists = 0;
- restore_record(table,default_values); // cp empty row from default_values
+ restore_record(table, s->default_values);
table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
table->field[1]->store(db,(uint) strlen(db), system_charset_info);
table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
@@ -1790,7 +2012,7 @@ static int replace_db_table(TABLE *table, const char *db,
}
store_rights=get_rights_for_db(rights);
- for (i= 3, priv= 1; i < table->fields; i++, priv <<= 1)
+ for (i= 3, priv= 1; i < table->s->fields; i++, priv <<= 1)
{
if (priv & store_rights) // do it if priv is chosen
table->field [i]->store(&what,1, &my_charset_latin1);// set requested privileges
@@ -1857,27 +2079,40 @@ static byte* get_key_column(GRANT_COLUMN *buff,uint *length,
}
-class GRANT_TABLE :public Sql_alloc
+class GRANT_NAME :public Sql_alloc
{
public:
acl_host_and_ip host;
char *db, *user, *tname, *hash_key;
- ulong privs, cols;
+ ulong privs;
ulong sort;
uint key_length;
+ GRANT_NAME(const char *h, const char *d,const char *u,
+ const char *t, ulong p);
+ GRANT_NAME (TABLE *form);
+ virtual ~GRANT_NAME() {};
+ virtual bool ok() { return privs != 0; }
+};
+
+
+class GRANT_TABLE :public GRANT_NAME
+{
+public:
+ ulong cols;
HASH hash_columns;
GRANT_TABLE(const char *h, const char *d,const char *u,
const char *t, ulong p, ulong c);
GRANT_TABLE (TABLE *form, TABLE *col_privs);
+ ~GRANT_TABLE();
bool ok() { return privs != 0 || cols != 0; }
};
-GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
- const char *t, ulong p, ulong c)
- :privs(p), cols(c)
+GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
+ const char *t, ulong p)
+ :privs(p)
{
/* Host given by user */
update_hostname(&host, strdup_root(&memex, h));
@@ -1893,15 +2128,20 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
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);
+}
+
+
+GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
+ const char *t, ulong p, ulong c)
+ :GRANT_NAME(h,d,u,t,p), cols(c)
+{
(void) hash_init(&hash_columns,system_charset_info,
0,0,0, (hash_get_key) get_key_column,0,0);
}
-GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
+GRANT_NAME::GRANT_NAME(TABLE *form)
{
- byte key[MAX_KEY_LENGTH];
-
update_hostname(&host, get_field(&memex, form->field[0]));
db= get_field(&memex,form->field[1]);
user= get_field(&memex,form->field[2]);
@@ -1912,7 +2152,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
if (!db || !tname)
{
/* Wrong table row; Ignore it */
- privs = cols = 0; /* purecov: inspected */
+ privs= 0;
return; /* purecov: inspected */
}
if (lower_case_table_names)
@@ -1925,31 +2165,49 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
hash_key = (char*) alloc_root(&memex,key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
privs = (ulong) form->field[6]->val_int();
- cols = (ulong) form->field[7]->val_int();
privs = fix_rights_for_table(privs);
+}
+
+
+GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
+ :GRANT_NAME(form)
+{
+ byte key[MAX_KEY_LENGTH];
+
+ if (!db || !tname)
+ {
+ /* Wrong table row; Ignore it */
+ hash_clear(&hash_columns); /* allow for destruction */
+ cols= 0;
+ return;
+ }
+ cols= (ulong) form->field[7]->val_int();
cols = fix_rights_for_column(cols);
(void) hash_init(&hash_columns,system_charset_info,
0,0,0, (hash_get_key) get_key_column,0,0);
if (cols)
{
- int key_len;
+ uint key_prefix_len;
+ KEY_PART_INFO *key_part= col_privs->key_info->key_part;
col_privs->field[0]->store(host.hostname,
host.hostname ? (uint) strlen(host.hostname) : 0,
system_charset_info);
col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info);
col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info);
col_privs->field[3]->store(tname,(uint) strlen(tname), system_charset_info);
- key_len=(col_privs->field[0]->pack_length()+
- col_privs->field[1]->pack_length()+
- col_privs->field[2]->pack_length()+
- col_privs->field[3]->pack_length());
- key_copy(key,col_privs,0,key_len);
+
+ key_prefix_len= (key_part[0].store_length +
+ key_part[1].store_length +
+ key_part[2].store_length +
+ key_part[3].store_length);
+ key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len);
col_privs->field[4]->store("",0, &my_charset_latin1);
+
col_privs->file->ha_index_init(0);
if (col_privs->file->index_read(col_privs->record[0],
- (byte*) col_privs->field[0]->ptr,
- key_len, HA_READ_KEY_EXACT))
+ (byte*) key,
+ key_prefix_len, HA_READ_KEY_EXACT))
{
cols = 0; /* purecov: deadcode */
col_privs->file->ha_index_end();
@@ -1971,13 +2229,19 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
}
my_hash_insert(&hash_columns, (byte *) mem_check);
} while (!col_privs->file->index_next(col_privs->record[0]) &&
- !key_cmp_if_same(col_privs,key,0,key_len));
+ !key_cmp_if_same(col_privs,key,0,key_prefix_len));
col_privs->file->ha_index_end();
}
}
-static byte* get_grant_table(GRANT_TABLE *buff,uint *length,
+GRANT_TABLE::~GRANT_TABLE()
+{
+ hash_free(&hash_columns);
+}
+
+
+static byte* get_grant_table(GRANT_NAME *buff,uint *length,
my_bool not_used __attribute__((unused)))
{
*length=buff->key_length;
@@ -1993,43 +2257,62 @@ void free_grant_table(GRANT_TABLE *grant_table)
/* Search after a matching grant. Prefer exact grants before not exact ones */
-static GRANT_TABLE *table_hash_search(const char *host,const char* ip,
+static GRANT_NAME *name_hash_search(HASH *name_hash,
+ const char *host,const char* ip,
const char *db,
const char *user, const char *tname,
bool exact)
{
char helping [NAME_LEN*2+USERNAME_LENGTH+3];
uint len;
- GRANT_TABLE *grant_table,*found=0;
+ GRANT_NAME *grant_name,*found=0;
HASH_SEARCH_STATE state;
len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1;
- for (grant_table=(GRANT_TABLE*) hash_first(&column_priv_hash,
- (byte*) helping,
- len, &state) ;
- grant_table ;
- grant_table= (GRANT_TABLE*) hash_next(&column_priv_hash,(byte*) helping,
- len, &state))
+ for (grant_name= (GRANT_NAME*) hash_first(name_hash, (byte*) helping,
+ len, &state);
+ grant_name ;
+ grant_name= (GRANT_NAME*) hash_next(name_hash,(byte*) helping,
+ len, &state))
{
if (exact)
{
- if ((host &&
+ if (!grant_name->host.hostname ||
+ (host &&
!my_strcasecmp(system_charset_info, host,
- grant_table->host.hostname)) ||
- (ip && !strcmp(ip, grant_table->host.hostname)))
- return grant_table;
+ grant_name->host.hostname)) ||
+ (ip && !strcmp(ip, grant_name->host.hostname)))
+ return grant_name;
}
else
{
- if (compare_hostname(&grant_table->host, host, ip) &&
- (!found || found->sort < grant_table->sort))
- found=grant_table; // Host ok
+ if (compare_hostname(&grant_name->host, host, ip) &&
+ (!found || found->sort < grant_name->sort))
+ found=grant_name; // Host ok
}
}
return found;
}
+inline GRANT_NAME *
+routine_hash_search(const char *host, const char *ip, const char *db,
+ const char *user, const char *tname, bool proc, bool exact)
+{
+ return (GRANT_TABLE*)
+ name_hash_search(proc ? &proc_priv_hash : &func_priv_hash,
+ host, ip, db, user, tname, exact);
+}
+
+
+inline GRANT_TABLE *
+table_hash_search(const char *host, const char *ip, const char *db,
+ const char *user, const char *tname, bool exact)
+{
+ return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db,
+ user, tname, exact);
+}
+
inline GRANT_COLUMN *
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
@@ -2045,49 +2328,64 @@ static int replace_column_table(GRANT_TABLE *g_t,
ulong rights, bool revoke_grant)
{
int error=0,result=0;
- uint key_length;
byte key[MAX_KEY_LENGTH];
+ uint key_prefix_length;
+ KEY_PART_INFO *key_part= table->key_info->key_part;
DBUG_ENTER("replace_column_table");
- table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
- table->field[1]->store(db,(uint) strlen(db), system_charset_info);
- table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
- table->field[3]->store(table_name,(uint) strlen(table_name), system_charset_info);
- key_length=(table->field[0]->pack_length()+ table->field[1]->pack_length()+
- table->field[2]->pack_length()+ table->field[3]->pack_length());
- key_copy(key,table,0,key_length);
+ table->field[0]->store(combo.host.str,combo.host.length,
+ system_charset_info);
+ table->field[1]->store(db,(uint) strlen(db),
+ system_charset_info);
+ table->field[2]->store(combo.user.str,combo.user.length,
+ system_charset_info);
+ table->field[3]->store(table_name,(uint) strlen(table_name),
+ system_charset_info);
+
+ /* Get length of 3 first key parts */
+ key_prefix_length= (key_part[0].store_length + key_part[1].store_length +
+ key_part[2].store_length + key_part[3].store_length);
+ key_copy(key, table->record[0], table->key_info, key_prefix_length);
- rights &= COL_ACLS; // Only ACL for columns
+ rights&= COL_ACLS; // Only ACL for columns
/* first fix privileges for all columns in column list */
List_iterator <LEX_COLUMN> iter(columns);
- class LEX_COLUMN *xx;
+ class LEX_COLUMN *column;
table->file->ha_index_init(0);
- while ((xx=iter++))
+ while ((column= iter++))
{
- ulong privileges = xx->rights;
+ ulong privileges= column->rights;
bool old_row_exists=0;
- key_restore(table,key,0,key_length);
- table->field[4]->store(xx->column.ptr(),xx->column.length(),
+ byte user_key[MAX_KEY_LENGTH];
+
+ key_restore(table->record[0],key,table->key_info,
+ key_prefix_length);
+ table->field[4]->store(column->column.ptr(), column->column.length(),
system_charset_info);
+ /* Get key for the first 4 columns */
+ key_copy(user_key, table->record[0], table->key_info,
+ table->key_info->key_length);
table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr,
- table->key_info[0].key_length,
- HA_READ_KEY_EXACT))
+ if (table->file->index_read(table->record[0], user_key,
+ table->key_info->key_length,
+ HA_READ_KEY_EXACT))
{
if (revoke_grant)
{
my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
- combo.user.str, combo.host.str, table_name); /* purecov: inspected */
- result= -1; /* purecov: inspected */
- continue; /* purecov: inspected */
+ combo.user.str, combo.host.str,
+ table_name); /* purecov: inspected */
+ result= -1; /* purecov: inspected */
+ continue; /* purecov: inspected */
}
old_row_exists = 0;
- restore_record(table,default_values); // Get empty record
- key_restore(table,key,0,key_length);
- table->field[4]->store(xx->column.ptr(),xx->column.length(),
+ restore_record(table, s->default_values); // Get empty record
+ key_restore(table->record[0],key,table->key_info,
+ key_prefix_length);
+ table->field[4]->store(column->column.ptr(),column->column.length(),
system_charset_info);
}
else
@@ -2103,10 +2401,11 @@ static int replace_column_table(GRANT_TABLE *g_t,
store_record(table,record[1]); // copy original row
}
- table->field[6]->store((longlong) get_rights_for_column(privileges));
+ table->field[6]->store((longlong) get_rights_for_column(privileges), TRUE);
if (old_row_exists)
{
+ GRANT_COLUMN *grant_column;
if (privileges)
error=table->file->update_row(table->record[1],table->record[0]);
else
@@ -2117,21 +2416,21 @@ static int replace_column_table(GRANT_TABLE *g_t,
result= -1; /* purecov: inspected */
goto end; /* purecov: inspected */
}
- GRANT_COLUMN *grant_column = column_hash_search(g_t,
- xx->column.ptr(),
- xx->column.length());
+ grant_column= column_hash_search(g_t, column->column.ptr(),
+ column->column.length());
if (grant_column) // Should always be true
- grant_column->rights = privileges; // Update hash
+ grant_column->rights= privileges; // Update hash
}
else // new grant
{
+ GRANT_COLUMN *grant_column;
if ((error=table->file->write_row(table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
result= -1; /* purecov: inspected */
goto end; /* purecov: inspected */
}
- GRANT_COLUMN *grant_column = new GRANT_COLUMN(xx->column,privileges);
+ grant_column= new GRANT_COLUMN(column->column,privileges);
my_hash_insert(&g_t->hash_columns,(byte*) grant_column);
}
}
@@ -2143,10 +2442,14 @@ static int replace_column_table(GRANT_TABLE *g_t,
if (revoke_grant)
{
+ byte user_key[MAX_KEY_LENGTH];
+ key_copy(user_key, table->record[0], table->key_info,
+ key_prefix_length);
+
table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (table->file->index_read(table->record[0], (byte*) table->field[0]->ptr,
- key_length,
- HA_READ_KEY_EXACT))
+ if (table->file->index_read(table->record[0], user_key,
+ key_prefix_length,
+ HA_READ_KEY_EXACT))
goto end;
/* Scan through all rows with the same host,db,user and table */
@@ -2165,7 +2468,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
privileges&= ~rights;
table->field[6]->store((longlong)
- get_rights_for_column(privileges));
+ get_rights_for_column(privileges), TRUE);
table->field[4]->val_str(&column_name);
grant_column = column_hash_search(g_t,
column_name.ptr(),
@@ -2197,7 +2500,7 @@ static int replace_column_table(GRANT_TABLE *g_t,
}
}
} while (!table->file->index_next(table->record[0]) &&
- !key_cmp_if_same(table,key,0,key_length));
+ !key_cmp_if_same(table, key, 0, key_prefix_length));
}
end:
@@ -2212,13 +2515,15 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
ulong rights, ulong col_rights,
bool revoke_grant)
{
- char grantor[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
+ char grantor[USER_HOST_BUFF_SIZE];
int old_row_exists = 1;
int error=0;
ulong store_table_rights, store_col_rights;
+ byte user_key[MAX_KEY_LENGTH];
DBUG_ENTER("replace_table_table");
- strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS);
+ strxmov(grantor, thd->security_ctx->user, "@",
+ thd->security_ctx->host_or_ip, NullS);
/*
The following should always succeed as new users are created before
@@ -2226,20 +2531,23 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
*/
if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
{
- my_error(ER_PASSWORD_NO_MATCH,MYF(0)); /* purecov: deadcode */
+ my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
+ MYF(0)); /* purecov: deadcode */
DBUG_RETURN(-1); /* purecov: deadcode */
}
- restore_record(table,default_values); // Get empty record
+ restore_record(table, s->default_values); // Get empty record
table->field[0]->store(combo.host.str,combo.host.length, system_charset_info);
table->field[1]->store(db,(uint) strlen(db), system_charset_info);
table->field[2]->store(combo.user.str,combo.user.length, system_charset_info);
table->field[3]->store(table_name,(uint) strlen(table_name), system_charset_info);
store_record(table,record[1]); // store at pos 1
+ key_copy(user_key, table->record[0], table->key_info,
+ table->key_info->key_length);
+
table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (table->file->index_read_idx(table->record[0],0,
- (byte*) table->field[0]->ptr,
- table->key_info[0].key_length,
+ if (table->file->index_read_idx(table->record[0], 0,
+ user_key, table->key_info->key_length,
HA_READ_KEY_EXACT))
{
/*
@@ -2251,7 +2559,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
{ // no row, no revoke
my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
combo.user.str, combo.host.str,
- table_name); /* purecov: deadcode */
+ table_name); /* purecov: deadcode */
DBUG_RETURN(-1); /* purecov: deadcode */
}
old_row_exists = 0;
@@ -2280,8 +2588,8 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
}
table->field[4]->store(grantor,(uint) strlen(grantor), system_charset_info);
- table->field[6]->store((longlong) store_table_rights);
- table->field[7]->store((longlong) store_col_rights);
+ table->field[6]->store((longlong) store_table_rights, TRUE);
+ table->field[7]->store((longlong) store_col_rights, TRUE);
rights=fix_rights_for_table(store_table_rights);
col_rights=fix_rights_for_column(store_col_rights);
@@ -2320,6 +2628,122 @@ table_error:
}
+static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
+ TABLE *table, const LEX_USER &combo,
+ const char *db, const char *routine_name,
+ bool is_proc, ulong rights, bool revoke_grant)
+{
+ char grantor[USER_HOST_BUFF_SIZE];
+ int old_row_exists= 1;
+ int error=0;
+ ulong store_proc_rights;
+ DBUG_ENTER("replace_routine_table");
+
+ if (!initialized)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
+ DBUG_RETURN(-1);
+ }
+
+ strxmov(grantor, thd->security_ctx->user, "@",
+ thd->security_ctx->host_or_ip, NullS);
+
+ /*
+ The following should always succeed as new users are created before
+ this function is called!
+ */
+ if (!find_acl_user(combo.host.str, combo.user.str, FALSE))
+ {
+ my_error(ER_PASSWORD_NO_MATCH,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ restore_record(table, s->default_values); // Get empty record
+ table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
+ table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1);
+ table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1);
+ table->field[3]->store(routine_name,(uint) strlen(routine_name),
+ &my_charset_latin1);
+ table->field[4]->store((longlong)(is_proc ?
+ TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION),
+ TRUE);
+ store_record(table,record[1]); // store at pos 1
+
+ if (table->file->index_read_idx(table->record[0],0,
+ (byte*) table->field[0]->ptr,0,
+ HA_READ_KEY_EXACT))
+ {
+ /*
+ The following should never happen as we first check the in memory
+ grant tables for the user. There is however always a small change that
+ the user has modified the grant tables directly.
+ */
+ if (revoke_grant)
+ { // no row, no revoke
+ my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
+ combo.user.str, combo.host.str, routine_name);
+ DBUG_RETURN(-1);
+ }
+ old_row_exists= 0;
+ restore_record(table,record[1]); // Get saved record
+ }
+
+ store_proc_rights= get_rights_for_procedure(rights);
+ if (old_row_exists)
+ {
+ ulong j;
+ store_record(table,record[1]);
+ j= (ulong) table->field[6]->val_int();
+
+ if (revoke_grant)
+ {
+ /* column rights are already fixed in mysql_table_grant */
+ store_proc_rights=j & ~store_proc_rights;
+ }
+ else
+ {
+ store_proc_rights|= j;
+ }
+ }
+
+ table->field[5]->store(grantor,(uint) strlen(grantor), &my_charset_latin1);
+ table->field[6]->store((longlong) store_proc_rights, TRUE);
+ rights=fix_rights_for_procedure(store_proc_rights);
+
+ if (old_row_exists)
+ {
+ if (store_proc_rights)
+ {
+ if ((error=table->file->update_row(table->record[1],table->record[0])))
+ goto table_error;
+ }
+ else if ((error= table->file->delete_row(table->record[1])))
+ goto table_error;
+ }
+ else
+ {
+ error=table->file->write_row(table->record[0]);
+ if (error && error != HA_ERR_FOUND_DUPP_KEY)
+ goto table_error;
+ }
+
+ if (rights)
+ {
+ grant_name->privs= rights;
+ }
+ else
+ {
+ hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
+ }
+ DBUG_RETURN(0);
+
+ /* This should never happen */
+table_error:
+ table->file->print_error(error,MYF(0));
+ DBUG_RETURN(-1);
+}
+
+
/*
Store table level and column level grants in the privilege tables
@@ -2333,59 +2757,63 @@ table_error:
revoke_grant Set to 1 if this is a REVOKE command
RETURN
- 0 ok
- 1 error
+ FALSE ok
+ TRUE error
*/
-int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
+bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
List <LEX_USER> &user_list,
List <LEX_COLUMN> &columns, ulong rights,
bool revoke_grant)
{
ulong column_priv= 0;
List_iterator <LEX_USER> str_list (user_list);
- LEX_USER *Str;
+ LEX_USER *Str, *tmp_Str;
TABLE_LIST tables[3];
bool create_new_users=0;
+ char *db_name, *table_name;
DBUG_ENTER("mysql_table_grant");
if (!initialized)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
"--skip-grant-tables"); /* purecov: inspected */
- DBUG_RETURN(-1); /* purecov: inspected */
+ DBUG_RETURN(TRUE); /* purecov: inspected */
}
if (rights & ~TABLE_ACLS)
{
- my_error(ER_ILLEGAL_GRANT_FOR_TABLE,MYF(0));
- DBUG_RETURN(-1);
+ my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
+ MYF(0));
+ DBUG_RETURN(TRUE);
}
if (!revoke_grant)
{
- if (columns.elements && !revoke_grant)
+ if (columns.elements)
{
- TABLE *table;
class LEX_COLUMN *column;
List_iterator <LEX_COLUMN> column_iter(columns);
- if (!(table=open_ltable(thd,table_list,TL_READ)))
- DBUG_RETURN(-1);
+ if (open_and_lock_tables(thd, table_list))
+ DBUG_RETURN(TRUE);
+
while ((column = column_iter++))
{
uint unused_field_idx= NO_CACHED_FIELD_INDEX;
- Field *f= find_field_in_table(thd,table,column->column.ptr(),
- column->column.length(),1,0,&unused_field_idx);
- if (!f)
+ TABLE_LIST *dummy;
+ Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
+ column->column.length(),
+ column->column.ptr(), NULL, NULL,
+ NULL, TRUE, FALSE,
+ &unused_field_idx, FALSE, &dummy);
+ if (f == (Field*)0)
{
my_error(ER_BAD_FIELD_ERROR, MYF(0),
column->column.c_ptr(), table_list->alias);
- DBUG_RETURN(-1);
- }
- if (f == (Field*)-1)
- {
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
+ if (f == (Field *)-1)
+ DBUG_RETURN(TRUE);
column_priv|= column->rights;
}
close_thread_tables(thd);
@@ -2396,12 +2824,12 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
{
char buf[FN_REFLEN];
sprintf(buf,"%s/%s/%s.frm",mysql_data_home, table_list->db,
- table_list->real_name);
+ table_list->table_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->alias);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
}
if (table_list->grant.want_privilege)
@@ -2410,7 +2838,8 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
get_privilege_desc(command, sizeof(command),
table_list->grant.want_privilege);
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
- command, thd->priv_user, thd->host_or_ip, table_list->alias);
+ command, thd->security_ctx->priv_user,
+ thd->security_ctx->host_or_ip, table_list->alias);
DBUG_RETURN(-1);
}
}
@@ -2419,14 +2848,16 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
/* open the mysql.tables_priv and mysql.columns_priv tables */
bzero((char*) &tables,sizeof(tables));
- tables[0].alias=tables[0].real_name= (char*) "user";
- tables[1].alias=tables[1].real_name= (char*) "tables_priv";
- tables[2].alias=tables[2].real_name= (char*) "columns_priv";
- tables[0].next=tables+1;
+ tables[0].alias=tables[0].table_name= (char*) "user";
+ tables[1].alias=tables[1].table_name= (char*) "tables_priv";
+ tables[2].alias=tables[2].table_name= (char*) "columns_priv";
+ tables[0].next_local= tables[0].next_global= tables+1;
/* Don't open column table if we don't need it ! */
- tables[1].next=((column_priv ||
- (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
- ? tables+2 : 0);
+ tables[1].next_local=
+ tables[1].next_global= ((column_priv ||
+ (revoke_grant &&
+ ((rights & COL_ACLS) || columns.elements)))
+ ? tables+2 : 0);
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";
@@ -2442,66 +2873,72 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
account in tests.
*/
tables[0].updating= tables[1].updating= tables[2].updating= 1;
- if (!tables_ok(0, tables))
- DBUG_RETURN(0);
+ if (!tables_ok(thd, tables))
+ DBUG_RETURN(FALSE);
}
#endif
if (simple_open_n_lock_tables(thd,tables))
{ // Should never happen
close_thread_tables(thd); /* purecov: deadcode */
- DBUG_RETURN(-1); /* purecov: deadcode */
+ DBUG_RETURN(TRUE); /* purecov: deadcode */
}
if (!revoke_grant)
create_new_users= test_if_create_new_users(thd);
- int result=0;
+ bool result= FALSE;
rw_wrlock(&LOCK_grant);
pthread_mutex_lock(&acl_cache->lock);
MEM_ROOT *old_root= thd->mem_root;
thd->mem_root= &memex;
+ grant_version++;
- while ((Str = str_list++))
+ while ((tmp_Str = str_list++))
{
int error;
GRANT_TABLE *grant_table;
- if (Str->host.length > HOSTNAME_LENGTH ||
- Str->user.length > USERNAME_LENGTH)
+ if (!(Str= get_current_user(thd, tmp_Str)))
{
- my_error(ER_GRANT_WRONG_HOST_OR_USER,MYF(0));
- result= -1;
+ result= TRUE;
continue;
- }
+ }
/* Create user if needed */
error=replace_user_table(thd, tables[0].table, *Str,
- 0, revoke_grant, create_new_users);
+ 0, revoke_grant, create_new_users,
+ test(thd->variables.sql_mode &
+ MODE_NO_AUTO_CREATE_USER));
if (error)
{
- result= -1; // Remember error
+ result= TRUE; // Remember error
continue; // Add next user
}
+ db_name= (table_list->view_db.length ?
+ table_list->view_db.str :
+ table_list->db);
+ table_name= (table_list->view_name.length ?
+ table_list->view_name.str :
+ table_list->table_name);
+
/* Find/create cached table grant */
- grant_table= table_hash_search(Str->host.str,NullS,table_list->db,
- Str->user.str,
- table_list->real_name,1);
+ grant_table= table_hash_search(Str->host.str, NullS, db_name,
+ Str->user.str, table_name, 1);
if (!grant_table)
{
if (revoke_grant)
{
my_error(ER_NONEXISTING_TABLE_GRANT, MYF(0),
- Str->user.str, Str->host.str, table_list->real_name);
- result= -1;
+ Str->user.str, Str->host.str, table_list->table_name);
+ result= TRUE;
continue;
}
- grant_table = new GRANT_TABLE (Str->host.str,table_list->db,
- Str->user.str,
- table_list->real_name,
+ grant_table = new GRANT_TABLE (Str->host.str, db_name,
+ Str->user.str, table_name,
rights,
column_priv);
if (!grant_table) // end of memory
{
- result= -1; /* purecov: deadcode */
+ result= TRUE; /* purecov: deadcode */
continue; /* purecov: deadcode */
}
my_hash_insert(&column_priv_hash,(byte*) grant_table);
@@ -2541,23 +2978,21 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
/* update table and columns */
- if (replace_table_table(thd,grant_table,tables[1].table,*Str,
- table_list->db,
- table_list->real_name,
+ if (replace_table_table(thd, grant_table, tables[1].table, *Str,
+ db_name, table_name,
rights, column_priv, revoke_grant))
{
/* Should only happen if table is crashed */
- result= -1; /* purecov: deadcode */
+ result= TRUE; /* purecov: deadcode */
}
else if (tables[2].table)
{
- if ((replace_column_table(grant_table,tables[2].table, *Str,
+ if ((replace_column_table(grant_table, tables[2].table, *Str,
columns,
- table_list->db,
- table_list->real_name,
+ db_name, table_name,
rights, revoke_grant)))
{
- result= -1;
+ result= TRUE;
}
}
}
@@ -2572,11 +3007,165 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
}
-int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
- ulong rights, bool revoke_grant)
+/*
+ Store routine level grants in the privilege tables
+
+ SYNOPSIS
+ mysql_routine_grant()
+ thd Thread handle
+ table_list List of routines to give grant
+ is_proc true indicates routine list are procedures
+ user_list List of users to give grant
+ rights Table level grant
+ revoke_grant Set to 1 if this is a REVOKE command
+
+ RETURN
+ 0 ok
+ 1 error
+*/
+
+bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
+ List <LEX_USER> &user_list, ulong rights,
+ bool revoke_grant, bool no_error)
+{
+ List_iterator <LEX_USER> str_list (user_list);
+ LEX_USER *Str, *tmp_Str;
+ TABLE_LIST tables[2];
+ bool create_new_users=0, result=0;
+ char *db_name, *table_name;
+ DBUG_ENTER("mysql_routine_grant");
+
+ if (!initialized)
+ {
+ if (!no_error)
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
+ "--skip-grant-tables");
+ DBUG_RETURN(TRUE);
+ }
+ if (rights & ~PROC_ACLS)
+ {
+ if (!no_error)
+ my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
+ MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ if (!revoke_grant)
+ {
+ if (sp_exist_routines(thd, table_list, is_proc, no_error)<0)
+ DBUG_RETURN(TRUE);
+ }
+
+ /* open the mysql.user and mysql.procs_priv tables */
+
+ bzero((char*) &tables,sizeof(tables));
+ tables[0].alias=tables[0].table_name= (char*) "user";
+ tables[1].alias=tables[1].table_name= (char*) "procs_priv";
+ tables[0].next_local= tables[0].next_global= tables+1;
+ tables[0].lock_type=tables[1].lock_type=TL_WRITE;
+ tables[0].db=tables[1].db=(char*) "mysql";
+
+#ifdef HAVE_REPLICATION
+ /*
+ GRANT and REVOKE are applied the slave in/exclusion rules as they are
+ some kind of updates to the mysql.% tables.
+ */
+ if (thd->slave_thread && table_rules_on)
+ {
+ /*
+ The tables must be marked "updating" so that tables_ok() takes them into
+ account in tests.
+ */
+ tables[0].updating= tables[1].updating= 1;
+ if (!tables_ok(thd, tables))
+ DBUG_RETURN(FALSE);
+ }
+#endif
+
+ if (simple_open_n_lock_tables(thd,tables))
+ { // Should never happen
+ close_thread_tables(thd);
+ DBUG_RETURN(TRUE);
+ }
+
+ if (!revoke_grant)
+ create_new_users= test_if_create_new_users(thd);
+ rw_wrlock(&LOCK_grant);
+ pthread_mutex_lock(&acl_cache->lock);
+ MEM_ROOT *old_root= thd->mem_root;
+ thd->mem_root= &memex;
+
+ DBUG_PRINT("info",("now time to iterate and add users"));
+
+ while ((tmp_Str= str_list++))
+ {
+ int error;
+ GRANT_NAME *grant_name;
+ if (!(Str= get_current_user(thd, tmp_Str)))
+ {
+ result= TRUE;
+ continue;
+ }
+ /* Create user if needed */
+ error=replace_user_table(thd, tables[0].table, *Str,
+ 0, revoke_grant, create_new_users,
+ test(thd->variables.sql_mode &
+ MODE_NO_AUTO_CREATE_USER));
+ if (error)
+ {
+ result= TRUE; // Remember error
+ continue; // Add next user
+ }
+
+ db_name= table_list->db;
+ table_name= table_list->table_name;
+
+ grant_name= routine_hash_search(Str->host.str, NullS, db_name,
+ Str->user.str, table_name, is_proc, 1);
+ if (!grant_name)
+ {
+ if (revoke_grant)
+ {
+ if (!no_error)
+ my_error(ER_NONEXISTING_PROC_GRANT, MYF(0),
+ Str->user.str, Str->host.str, table_name);
+ result= TRUE;
+ continue;
+ }
+ grant_name= new GRANT_NAME(Str->host.str, db_name,
+ Str->user.str, table_name,
+ rights);
+ if (!grant_name)
+ {
+ result= TRUE;
+ continue;
+ }
+ my_hash_insert(is_proc ? &proc_priv_hash : &func_priv_hash,(byte*) grant_name);
+ }
+
+ if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
+ db_name, table_name, is_proc, rights, revoke_grant))
+ {
+ result= TRUE;
+ continue;
+ }
+ }
+ grant_option=TRUE;
+ thd->mem_root= old_root;
+ pthread_mutex_unlock(&acl_cache->lock);
+ rw_unlock(&LOCK_grant);
+ if (!result && !no_error)
+ send_ok(thd);
+ /* Tables are automatically closed */
+ DBUG_RETURN(result);
+}
+
+
+bool 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;
+ LEX_USER *Str, *tmp_Str;
char tmp_db[NAME_LEN+1];
bool create_new_users=0;
TABLE_LIST tables[2];
@@ -2585,7 +3174,7 @@ int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
"--skip-grant-tables"); /* purecov: tested */
- DBUG_RETURN(-1); /* purecov: tested */
+ DBUG_RETURN(TRUE); /* purecov: tested */
}
if (lower_case_table_names && db)
@@ -2597,13 +3186,11 @@ int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
/* open the mysql.user and mysql.db tables */
bzero((char*) &tables,sizeof(tables));
- tables[0].alias=tables[0].real_name=(char*) "user";
- tables[1].alias=tables[1].real_name=(char*) "db";
- tables[0].next=tables+1;
- tables[1].next=0;
+ tables[0].alias=tables[0].table_name=(char*) "user";
+ tables[1].alias=tables[1].table_name=(char*) "db";
+ tables[0].next_local= tables[0].next_global= tables+1;
tables[0].lock_type=tables[1].lock_type=TL_WRITE;
tables[0].db=tables[1].db=(char*) "mysql";
- tables[0].table=tables[1].table=0;
#ifdef HAVE_REPLICATION
/*
@@ -2617,15 +3204,15 @@ int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
account in tests.
*/
tables[0].updating= tables[1].updating= 1;
- if (!tables_ok(0, tables))
- DBUG_RETURN(0);
+ if (!tables_ok(thd, tables))
+ DBUG_RETURN(FALSE);
}
#endif
if (simple_open_n_lock_tables(thd,tables))
{ // This should never happen
close_thread_tables(thd); /* purecov: deadcode */
- DBUG_RETURN(-1); /* purecov: deadcode */
+ DBUG_RETURN(TRUE); /* purecov: deadcode */
}
if (!revoke_grant)
@@ -2637,20 +3224,17 @@ int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
grant_version++;
int result=0;
- while ((Str = str_list++))
+ while ((tmp_Str = str_list++))
{
- if (Str->host.length > HOSTNAME_LENGTH ||
- Str->user.length > USERNAME_LENGTH)
+ if (!(Str= get_current_user(thd, tmp_Str)))
{
- my_error(ER_GRANT_WRONG_HOST_OR_USER,MYF(0));
- result= -1;
+ result= TRUE;
continue;
}
- if ((replace_user_table(thd,
- tables[0].table,
- *Str,
- (!db ? rights : 0), revoke_grant,
- create_new_users)))
+ if (replace_user_table(thd, tables[0].table, *Str,
+ (!db ? rights : 0), revoke_grant, create_new_users,
+ test(thd->variables.sql_mode &
+ MODE_NO_AUTO_CREATE_USER)))
result= -1;
else if (db)
{
@@ -2685,6 +3269,8 @@ void grant_free(void)
DBUG_ENTER("grant_free");
grant_option = FALSE;
hash_free(&column_priv_hash);
+ hash_free(&proc_priv_hash);
+ hash_free(&func_priv_hash);
free_root(&memex,MYF(0));
DBUG_VOID_RETURN;
}
@@ -2710,6 +3296,7 @@ my_bool grant_init()
if (!(thd= new THD))
DBUG_RETURN(1); /* purecov: deadcode */
+ thd->thread_stack= (char*) &thd;
thd->store_globals();
return_val= grant_reload(thd);
delete thd;
@@ -2738,7 +3325,7 @@ static my_bool grant_load(TABLE_LIST *tables)
{
MEM_ROOT *memex_ptr;
my_bool return_val= 1;
- TABLE *t_table, *c_table;
+ TABLE *t_table, *c_table, *p_table;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
THR_MALLOC);
@@ -2748,53 +3335,119 @@ static my_bool grant_load(TABLE_LIST *tables)
(void) hash_init(&column_priv_hash,system_charset_info,
0,0,0, (hash_get_key) get_grant_table,
(hash_free_key) free_grant_table,0);
+ (void) hash_init(&proc_priv_hash,system_charset_info,
+ 0,0,0, (hash_get_key) get_grant_table,
+ 0,0);
+ (void) hash_init(&func_priv_hash,system_charset_info,
+ 0,0,0, (hash_get_key) get_grant_table,
+ 0,0);
init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
t_table = tables[0].table; c_table = tables[1].table;
+ p_table= tables[2].table;
t_table->file->ha_index_init(0);
- if (t_table->file->index_first(t_table->record[0]))
+ p_table->file->ha_index_init(0);
+ if (!t_table->file->index_first(t_table->record[0]))
{
- return_val= 0;
- goto end_unlock;
- }
- grant_option= TRUE;
+ memex_ptr= &memex;
+ my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
+ do
+ {
+ GRANT_TABLE *mem_check;
+ if (!(mem_check=new GRANT_TABLE(t_table,c_table)))
+ {
+ /* This could only happen if we are out memory */
+ grant_option= FALSE;
+ goto end_unlock;
+ }
- memex_ptr= &memex;
- my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
- do
+ if (check_no_resolve)
+ {
+ if (hostname_requires_resolving(mem_check->host.hostname))
+ {
+ sql_print_warning("'tables_priv' entry '%s %s@%s' "
+ "ignored in --skip-name-resolve mode.",
+ mem_check->tname,
+ mem_check->user ? mem_check->user : "",
+ mem_check->host.hostname ?
+ mem_check->host.hostname : "");
+ continue;
+ }
+ }
+
+ if (! mem_check->ok())
+ delete mem_check;
+ else if (my_hash_insert(&column_priv_hash,(byte*) mem_check))
+ {
+ delete mem_check;
+ grant_option= FALSE;
+ goto end_unlock;
+ }
+ }
+ while (!t_table->file->index_next(t_table->record[0]));
+ }
+ if (!p_table->file->index_first(p_table->record[0]))
{
- GRANT_TABLE *mem_check;
- if (!(mem_check=new GRANT_TABLE(t_table,c_table)))
+ memex_ptr= &memex;
+ my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
+ do
{
- /* This could only happen if we are out memory */
- grant_option= FALSE; /* purecov: deadcode */
- goto end_unlock;
- }
+ GRANT_NAME *mem_check;
+ HASH *hash;
+ if (!(mem_check=new GRANT_NAME(p_table)))
+ {
+ /* This could only happen if we are out memory */
+ grant_option= FALSE;
+ goto end_unlock;
+ }
- if (check_no_resolve)
- {
- if (hostname_requires_resolving(mem_check->host.hostname))
+ if (check_no_resolve)
+ {
+ if (hostname_requires_resolving(mem_check->host.hostname))
+ {
+ sql_print_warning("'procs_priv' entry '%s %s@%s' "
+ "ignored in --skip-name-resolve mode.",
+ mem_check->tname, mem_check->user,
+ mem_check->host.hostname ?
+ mem_check->host.hostname : "");
+ continue;
+ }
+ }
+ if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
+ {
+ hash= &proc_priv_hash;
+ }
+ else
+ if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
{
- sql_print_warning("'tables_priv' entry '%s %s@%s' "
- "ignored in --skip-name-resolve mode.",
- mem_check->tname, mem_check->user,
- mem_check->host.hostname);
+ hash= &func_priv_hash;
+ }
+ else
+ {
+ sql_print_warning("'procs_priv' entry '%s' "
+ "ignored, bad routine type",
+ mem_check->tname);
continue;
}
- }
- if (mem_check->ok() && my_hash_insert(&column_priv_hash,(byte*) mem_check))
- {
- grant_option= FALSE;
- goto end_unlock;
+ mem_check->privs= fix_rights_for_procedure(mem_check->privs);
+ if (! mem_check->ok())
+ delete mem_check;
+ else if (my_hash_insert(hash, (byte*) mem_check))
+ {
+ delete mem_check;
+ grant_option= FALSE;
+ goto end_unlock;
+ }
}
+ while (!p_table->file->index_next(p_table->record[0]));
}
- while (!t_table->file->index_next(t_table->record[0]));
-
+ grant_option= TRUE;
return_val=0; // Return ok
end_unlock:
t_table->file->ha_index_end();
+ p_table->file->ha_index_end();
my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
DBUG_RETURN(return_val);
}
@@ -2820,8 +3473,8 @@ end_unlock:
my_bool grant_reload(THD *thd)
{
- TABLE_LIST tables[2];
- HASH old_column_priv_hash;
+ TABLE_LIST tables[3];
+ HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
bool old_grant_option;
MEM_ROOT old_mem;
my_bool return_val= 1;
@@ -2832,11 +3485,13 @@ my_bool grant_reload(THD *thd)
DBUG_RETURN(0);
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";
- tables[0].db=tables[1].db= (char *) "mysql";
- tables[0].next=tables+1;
- tables[0].lock_type=tables[1].lock_type=TL_READ;
+ tables[0].alias= tables[0].table_name= (char*) "tables_priv";
+ tables[1].alias= tables[1].table_name= (char*) "columns_priv";
+ tables[2].alias= tables[2].table_name= (char*) "procs_priv";
+ tables[0].db= tables[1].db= tables[2].db= (char *) "mysql";
+ tables[0].next_local= tables[0].next_global= tables+1;
+ tables[1].next_local= tables[1].next_global= tables+2;
+ tables[0].lock_type= tables[1].lock_type= tables[2].lock_type= TL_READ;
/*
To avoid deadlocks we should obtain table locks before
@@ -2848,6 +3503,8 @@ my_bool grant_reload(THD *thd)
rw_wrlock(&LOCK_grant);
grant_version++;
old_column_priv_hash= column_priv_hash;
+ old_proc_priv_hash= proc_priv_hash;
+ old_func_priv_hash= func_priv_hash;
old_grant_option= grant_option;
old_mem= memex;
@@ -2856,12 +3513,16 @@ my_bool grant_reload(THD *thd)
DBUG_PRINT("error",("Reverting to old privileges"));
grant_free(); /* purecov: deadcode */
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
+ proc_priv_hash= old_proc_priv_hash;
+ func_priv_hash= old_func_priv_hash;
grant_option= old_grant_option; /* purecov: deadcode */
memex= old_mem; /* purecov: deadcode */
}
else
{
hash_free(&old_column_priv_hash);
+ hash_free(&old_proc_priv_hash);
+ hash_free(&old_func_priv_hash);
free_root(&old_mem,MYF(0));
}
rw_unlock(&LOCK_grant);
@@ -2873,32 +3534,94 @@ end:
/****************************************************************************
Check table level grants
- All errors are written directly to the client if no_errors is given !
+
+ SYNOPSIS
+ bool check_grant()
+ thd Thread handler
+ want_access Bits of privileges user needs to have
+ tables List of tables to check. The user should have 'want_access'
+ to all tables in list.
+ show_table <> 0 if we are in show table. In this case it's enough to have
+ any privilege for the table
+ number Check at most this number of tables.
+ no_errors If 0 then we write an error. The error is sent directly to
+ the client
+
+ RETURN
+ 0 ok
+ 1 Error: User did not have the requested privileges
+
+ NOTE
+ This functions assumes that either number of tables to be inspected
+ by it is limited explicitly (i.e. is is not UINT_MAX) or table list
+ used and thd->lex->query_tables_own_last value correspond to each
+ other (the latter should be either 0 or point to next_global member
+ of one of elements of this table list).
****************************************************************************/
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
uint show_table, uint number, bool no_errors)
{
- TABLE_LIST *table;
- char *user = thd->priv_user;
+ TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table();
+ Security_context *sctx= thd->security_ctx;
+ uint i;
+ ulong orig_want_access= want_access;
DBUG_ENTER("check_grant");
+ DBUG_ASSERT(number > 0);
- want_access &= ~thd->master_access;
- if (!want_access)
- DBUG_RETURN(0); // ok
+ /*
+ Walk through the list of tables that belong to the query and save the
+ requested access (orig_want_privilege) to be able to use it when
+ checking access rights to the underlying tables of a view. Our grant
+ system gradually eliminates checked bits from want_privilege and thus
+ after all checks are done we can no longer use it.
+ The check that first_not_own_table is not reached is for the case when
+ the given table list refers to the list for prelocking (contains tables
+ of other queries). For simple queries first_not_own_table is 0.
+ */
+ for (i= 0, table= tables;
+ table != first_not_own_table && i < number;
+ table= table->next_global, i++)
+ {
+ /* Remove SHOW_VIEW_ACL, because it will be checked during making view */
+ table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
+ }
rw_rdlock(&LOCK_grant);
- for (table= tables; table && number--; table= table->next)
+ for (table= tables;
+ table && number-- && table != first_not_own_table;
+ table= table->next_global)
{
- if (!(~table->grant.privilege & want_access) || table->derived)
+ GRANT_TABLE *grant_table;
+ sctx = test(table->security_ctx) ?
+ table->security_ctx : thd->security_ctx;
+
+ want_access= orig_want_access;
+ want_access&= ~sctx->master_access;
+ if (!want_access)
+ continue; // ok
+
+ if (!(~table->grant.privilege & want_access) ||
+ table->derived || table->schema_table)
{
- table->grant.want_privilege=0;
- continue; // Already checked
+ /*
+ It is subquery in the FROM clause. VIEW set table->derived after
+ table opening, but this function always called before table opening.
+ */
+ if (!table->referencing_view)
+ {
+ /*
+ If it's a temporary table created for a subquery in the FROM
+ clause, or an INFORMATION_SCHEMA table, drop the request for
+ a privilege.
+ */
+ table->grant.want_privilege= 0;
+ }
+ continue;
}
- GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip,
- table->db,user,
- table->real_name,0);
- if (!grant_table)
+ if (!(grant_table= table_hash_search(sctx->host, sctx->ip,
+ table->db, sctx->priv_user,
+ table->table_name,0)))
{
want_access &= ~table->grant.privilege;
goto err; // No grants
@@ -2930,147 +3653,224 @@ err:
{
char command[128];
get_privilege_desc(command, sizeof(command), want_access);
- net_printf(thd,ER_TABLEACCESS_DENIED_ERROR,
- command,
- thd->priv_user,
- thd->host_or_ip,
- table ? table->real_name : "unknown");
+ my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
+ command,
+ sctx->priv_user,
+ sctx->host_or_ip,
+ table ? table->table_name : "unknown");
}
DBUG_RETURN(1);
}
-bool check_grant_column(THD *thd,TABLE *table, const char *name,
- uint length, uint show_tables)
+/*
+ Check column rights in given security context
+
+ SYNOPSIS
+ check_grant_column()
+ thd thread handler
+ grant grant information structure
+ db_name db name
+ table_name table name
+ name column name
+ length column name length
+ sctx security context
+
+ RETURN
+ FALSE OK
+ TRUE access denied
+*/
+
+bool check_grant_column(THD *thd, GRANT_INFO *grant,
+ const char *db_name, const char *table_name,
+ const char *name, uint length, Security_context *sctx)
{
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
+ ulong want_access= grant->want_privilege & ~grant->privilege;
+ DBUG_ENTER("check_grant_column");
+ DBUG_PRINT("enter", ("table: %s want_access: %u", table_name, want_access));
- ulong want_access=table->grant.want_privilege;
if (!want_access)
- return 0; // Already checked
+ DBUG_RETURN(0); // Already checked
rw_rdlock(&LOCK_grant);
/* reload table if someone has modified any grants */
- if (table->grant.version != grant_version)
+ if (grant->version != grant_version)
{
- table->grant.grant_table=
- table_hash_search(thd->host, thd->ip, table->table_cache_key,
- thd->priv_user,
- table->real_name, 0); /* purecov: inspected */
- table->grant.version=grant_version; /* purecov: inspected */
+ grant->grant_table=
+ table_hash_search(sctx->host, sctx->ip, db_name,
+ sctx->priv_user,
+ table_name, 0); /* purecov: inspected */
+ grant->version= grant_version; /* purecov: inspected */
}
- if (!(grant_table=table->grant.grant_table))
+ if (!(grant_table= grant->grant_table))
goto err; /* purecov: deadcode */
grant_column=column_hash_search(grant_table, name, length);
if (grant_column && !(~grant_column->rights & want_access))
{
rw_unlock(&LOCK_grant);
- return 0;
- }
-#ifdef NOT_USED
- if (show_tables && (grant_column || table->grant.privilege & COL_ACLS))
- {
- rw_unlock(&LOCK_grant); /* purecov: deadcode */
- return 0; /* purecov: deadcode */
+ DBUG_RETURN(0);
}
-#endif
- /* We must use my_printf_error() here! */
err:
rw_unlock(&LOCK_grant);
- if (!show_tables)
+ char command[128];
+ get_privilege_desc(command, sizeof(command), want_access);
+ my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
+ command,
+ sctx->priv_user,
+ sctx->host_or_ip,
+ name,
+ table_name);
+ DBUG_RETURN(1);
+}
+
+
+/*
+ Check the access right to a column depending on the type of table.
+
+ SYNOPSIS
+ check_column_grant_in_table_ref()
+ thd thread handler
+ table_ref table reference where to check the field
+ name name of field to check
+ length length of name
+
+ DESCRIPTION
+ Check the access rights to a column depending on the type of table
+ reference where the column is checked. The function provides a
+ generic interface to check column access rights that hides the
+ heterogeneity of the column representation - whether it is a view
+ or a stored table colum.
+
+ RETURN
+ FALSE OK
+ TRUE access denied
+*/
+
+bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
+ const char *name, uint length)
+{
+ GRANT_INFO *grant;
+ const char *db_name;
+ const char *table_name;
+ Security_context *sctx= test(table_ref->security_ctx) ?
+ table_ref->security_ctx : thd->security_ctx;
+
+ if (table_ref->view || table_ref->field_translation)
{
- 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_or_ip,
- name,
- table ? table->real_name : "unknown");
+ /* View or derived information schema table. */
+ ulong view_privs;
+ grant= &(table_ref->grant);
+ db_name= table_ref->view_db.str;
+ table_name= table_ref->view_name.str;
+ if (table_ref->belong_to_view &&
+ (thd->lex->sql_command == SQLCOM_SHOW_FIELDS ||
+ thd->lex->sql_command == SQLCOM_SHOW_CREATE))
+ {
+ view_privs= get_column_grant(thd, grant, db_name, table_name, name);
+ if (view_privs & VIEW_ANY_ACL)
+ {
+ table_ref->belong_to_view->allowed_show= TRUE;
+ return FALSE;
+ }
+ table_ref->belong_to_view->allowed_show= FALSE;
+ my_message(ER_VIEW_NO_EXPLAIN, ER(ER_VIEW_NO_EXPLAIN), MYF(0));
+ return TRUE;
+ }
}
- return 1;
+ else
+ {
+ /* Normal or temporary table. */
+ TABLE *table= table_ref->table;
+ grant= &(table->grant);
+ db_name= table->s->db;
+ table_name= table->s->table_name;
+ }
+
+ if (grant->want_privilege)
+ return check_grant_column(thd, grant, db_name, table_name, name,
+ length, sctx);
+ else
+ return FALSE;
+
}
-bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table)
+bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant,
+ const char* db_name, const char *table_name,
+ Field_iterator *fields)
{
+ Security_context *sctx= thd->security_ctx;
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
- Field *field=0,**ptr;
- want_access &= ~table->grant.privilege;
+ want_access &= ~grant->privilege;
if (!want_access)
return 0; // Already checked
if (!grant_option)
- {
- field= table->field[0]; // To give a meaningful error message
goto err2;
- }
rw_rdlock(&LOCK_grant);
/* reload table if someone has modified any grants */
- if (table->grant.version != grant_version)
+ if (grant->version != grant_version)
{
- table->grant.grant_table=
- table_hash_search(thd->host, thd->ip, table->table_cache_key,
- thd->priv_user,
- table->real_name,0); /* purecov: inspected */
- table->grant.version=grant_version; /* purecov: inspected */
+ grant->grant_table=
+ table_hash_search(sctx->host, sctx->ip, db_name,
+ sctx->priv_user,
+ table_name, 0); /* purecov: inspected */
+ grant->version= grant_version; /* purecov: inspected */
}
/* The following should always be true */
- if (!(grant_table=table->grant.grant_table))
+ if (!(grant_table= grant->grant_table))
goto err; /* purecov: inspected */
- for (ptr=table->field; (field= *ptr) ; ptr++)
+ for (; !fields->end_of_fields(); fields->next())
{
- grant_column=column_hash_search(grant_table, field->field_name,
- (uint) strlen(field->field_name));
+ const char *field_name= fields->name();
+ grant_column= column_hash_search(grant_table, field_name,
+ (uint) strlen(field_name));
if (!grant_column || (~grant_column->rights & want_access))
goto err;
}
rw_unlock(&LOCK_grant);
return 0;
- /* We must use my_printf_error() here! */
err:
rw_unlock(&LOCK_grant);
err2:
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_or_ip,
- field ? field->field_name : "unknown",
- table->real_name);
+ my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
+ command,
+ sctx->priv_user,
+ sctx->host_or_ip,
+ fields->name(),
+ table_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
+ Access is accepted if he has a grant for any table/routine in the database
Return 1 if access is denied
*/
bool check_grant_db(THD *thd,const char *db)
{
+ Security_context *sctx= thd->security_ctx;
char helping [NAME_LEN+USERNAME_LENGTH+2];
uint len;
- bool error=1;
+ bool error= 1;
- len = (uint) (strmov(strmov(helping,thd->priv_user)+1,db)-helping)+ 1;
+ len= (uint) (strmov(strmov(helping, sctx->priv_user) + 1, db) - helping) + 1;
rw_rdlock(&LOCK_grant);
for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
@@ -3079,7 +3879,7 @@ bool check_grant_db(THD *thd,const char *db)
idx);
if (len < grant_table->key_length &&
!memcmp(grant_table->hash_key,helping,len) &&
- compare_hostname(&grant_table->host, thd->host, thd->ip))
+ compare_hostname(&grant_table->host, sctx->host, sctx->ip))
{
error=0; // Found match
break;
@@ -3089,6 +3889,109 @@ bool check_grant_db(THD *thd,const char *db)
return error;
}
+
+/****************************************************************************
+ Check routine level grants
+
+ SYNPOSIS
+ bool check_grant_routine()
+ thd Thread handler
+ want_access Bits of privileges user needs to have
+ procs List of routines to check. The user should have 'want_access'
+ is_proc True if the list is all procedures, else functions
+ no_errors If 0 then we write an error. The error is sent directly to
+ the client
+
+ RETURN
+ 0 ok
+ 1 Error: User did not have the requested privielges
+****************************************************************************/
+
+bool check_grant_routine(THD *thd, ulong want_access,
+ TABLE_LIST *procs, bool is_proc, bool no_errors)
+{
+ TABLE_LIST *table;
+ Security_context *sctx= thd->security_ctx;
+ char *user= sctx->priv_user;
+ char *host= sctx->priv_host;
+ DBUG_ENTER("check_grant_routine");
+
+ want_access&= ~sctx->master_access;
+ if (!want_access)
+ DBUG_RETURN(0); // ok
+
+ rw_rdlock(&LOCK_grant);
+ for (table= procs; table; table= table->next_global)
+ {
+ GRANT_NAME *grant_proc;
+ if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
+ table->table_name, is_proc, 0)))
+ table->grant.privilege|= grant_proc->privs;
+
+ if (want_access & ~table->grant.privilege)
+ {
+ want_access &= ~table->grant.privilege;
+ goto err;
+ }
+ }
+ rw_unlock(&LOCK_grant);
+ DBUG_RETURN(0);
+err:
+ rw_unlock(&LOCK_grant);
+ if (!no_errors)
+ {
+ char buff[1024];
+ const char *command="";
+ if (table)
+ strxmov(buff, table->db, ".", table->table_name, NullS);
+ if (want_access & EXECUTE_ACL)
+ command= "execute";
+ else if (want_access & ALTER_PROC_ACL)
+ command= "alter routine";
+ else if (want_access & GRANT_ACL)
+ command= "grant";
+ my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0),
+ command, user, host, table ? buff : "unknown");
+ }
+ DBUG_RETURN(1);
+}
+
+
+/*
+ Check if routine has any of the
+ routine level grants
+
+ SYNPOSIS
+ bool check_routine_level_acl()
+ thd Thread handler
+ db Database name
+ name Routine name
+
+ RETURN
+ 0 Ok
+ 1 error
+*/
+
+bool check_routine_level_acl(THD *thd, const char *db, const char *name,
+ bool is_proc)
+{
+ bool no_routine_acl= 1;
+ if (grant_option)
+ {
+ GRANT_NAME *grant_proc;
+ Security_context *sctx= thd->security_ctx;
+ rw_rdlock(&LOCK_grant);
+ if ((grant_proc= routine_hash_search(sctx->priv_host,
+ sctx->ip, db,
+ sctx->priv_user,
+ name, is_proc, 0)))
+ no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
+ rw_unlock(&LOCK_grant);
+ }
+ return no_routine_acl;
+}
+
+
/*****************************************************************************
Functions to retrieve the grant for a table/column (for SHOW functions)
*****************************************************************************/
@@ -3096,7 +3999,7 @@ bool check_grant_db(THD *thd,const char *db)
ulong get_table_grant(THD *thd, TABLE_LIST *table)
{
ulong privilege;
- char *user = thd->priv_user;
+ Security_context *sctx= thd->security_ctx;
const char *db = table->db ? table->db : thd->db;
GRANT_TABLE *grant_table;
@@ -3104,8 +4007,8 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
#ifdef EMBEDDED_LIBRARY
grant_table= NULL;
#else
- grant_table= table_hash_search(thd->host, thd->ip, db, user,
- table->real_name, 0);
+ grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
+ table->table_name, 0);
#endif
table->grant.grant_table=grant_table; // Remember for column test
table->grant.version=grant_version;
@@ -3117,7 +4020,27 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
}
-ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field)
+/*
+ Determine the access priviliges for a field.
+
+ SYNOPSIS
+ get_column_grant()
+ thd thread handler
+ grant grants table descriptor
+ db_name name of database that the field belongs to
+ table_name name of table that the field belongs to
+ field_name name of field
+
+ DESCRIPTION
+ The procedure may also modify: grant->grant_table and grant->version.
+
+ RETURN
+ The access priviliges for the field db_name.table_name.field_name
+*/
+
+ulong get_column_grant(THD *thd, GRANT_INFO *grant,
+ const char *db_name, const char *table_name,
+ const char *field_name)
{
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
@@ -3125,30 +4048,32 @@ ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field)
rw_rdlock(&LOCK_grant);
/* reload table if someone has modified any grants */
- if (table->grant.version != grant_version)
+ if (grant->version != grant_version)
{
- table->grant.grant_table=
- table_hash_search(thd->host, thd->ip, table->db,
- thd->priv_user,
- table->real_name,0); /* purecov: inspected */
- table->grant.version=grant_version; /* purecov: inspected */
+ Security_context *sctx= thd->security_ctx;
+ grant->grant_table=
+ table_hash_search(sctx->host, sctx->ip,
+ db_name, sctx->priv_user,
+ table_name, 0); /* purecov: inspected */
+ grant->version= grant_version; /* purecov: inspected */
}
- if (!(grant_table=table->grant.grant_table))
- priv=table->grant.privilege;
+ if (!(grant_table= grant->grant_table))
+ priv= grant->privilege;
else
{
- grant_column=column_hash_search(grant_table, field->field_name,
- (uint) strlen(field->field_name));
+ grant_column= column_hash_search(grant_table, field_name,
+ (uint) strlen(field_name));
if (!grant_column)
- priv=table->grant.privilege;
+ priv= (grant->privilege | grant_table->privs);
else
- priv=table->grant.privilege | grant_column->rights;
+ priv= (grant->privilege | grant_table->privs | grant_column->rights);
}
rw_unlock(&LOCK_grant);
return priv;
}
+
/* Help function for mysql_show_grants */
static void add_user_option(String *grant, ulong value, const char *name)
@@ -3166,18 +4091,26 @@ static void add_user_option(String *grant, ulong value, const char *name)
static const char *command_array[]=
{
- "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",
+ "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",
+ "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE",
+ "CREATE USER"
};
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
+ 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9,
+ 14, 13, 11
};
+static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
+ const char *type, int typelen,
+ char *buff, int buffsize);
+
+
/*
SHOW GRANTS; Send grants for a user to the client
@@ -3185,7 +4118,7 @@ static uint command_lengths[]=
Send to client grant-like strings depicting user@host privileges
*/
-int mysql_show_grants(THD *thd,LEX_USER *lex_user)
+bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
{
ulong want_access;
uint counter,index;
@@ -3200,44 +4133,21 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
if (!initialized)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
- DBUG_RETURN(-1);
- }
-
- if (!lex_user->host.str)
- {
- lex_user->host.str= (char*) "%";
- lex_user->host.length=1;
- }
- if (lex_user->host.length > HOSTNAME_LENGTH ||
- lex_user->user.length > USERNAME_LENGTH)
- {
- my_error(ER_GRANT_WRONG_HOST_OR_USER,MYF(0));
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
rw_rdlock(&LOCK_grant);
VOID(pthread_mutex_lock(&acl_cache->lock));
- for (counter=0 ; counter < acl_users.elements ; counter++)
- {
- const char *user,*host;
- acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
- if (!(user=acl_user->user))
- user= "";
- if (!(host=acl_user->host.hostname))
- host= "";
- if (!strcmp(lex_user->user.str,user) &&
- !my_strcasecmp(system_charset_info, lex_user->host.str, host))
- break;
- }
- if (counter == acl_users.elements)
+ acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE);
+ if (!acl_user)
{
VOID(pthread_mutex_unlock(&acl_cache->lock));
rw_unlock(&LOCK_grant);
my_error(ER_NONEXISTING_GRANT, MYF(0),
lex_user->user.str, lex_user->host.str);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
Item_string *field=new Item_string("",0,&my_charset_latin1);
@@ -3247,25 +4157,26 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
strxmov(buff,"Grants for ",lex_user->user.str,"@",
lex_user->host.str,NullS);
field_list.push_back(field);
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
VOID(pthread_mutex_unlock(&acl_cache->lock));
rw_unlock(&LOCK_grant);
- DBUG_RETURN(-1);
+ DBUG_RETURN(TRUE);
}
/* Add first global access grants */
{
String global(buff,sizeof(buff),system_charset_info);
global.length(0);
- global.append("GRANT ",6);
+ global.append(STRING_WITH_LEN("GRANT "));
want_access= acl_user->access;
if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
- global.append("ALL PRIVILEGES",14);
+ global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
else if (!(want_access & ~GRANT_ACL))
- global.append("USAGE",5);
+ global.append(STRING_WITH_LEN("USAGE"));
else
{
bool found=0;
@@ -3275,17 +4186,18 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
if (test_access & j)
{
if (found)
- global.append(", ",2);
+ global.append(STRING_WITH_LEN(", "));
found=1;
global.append(command_array[counter],command_lengths[counter]);
}
}
}
- global.append (" ON *.* TO '",12);
+ global.append (STRING_WITH_LEN(" ON *.* TO '"));
global.append(lex_user->user.str, lex_user->user.length,
system_charset_info);
- global.append ("'@'",3);
- global.append(lex_user->host.str,lex_user->host.length);
+ global.append (STRING_WITH_LEN("'@'"));
+ global.append(lex_user->host.str,lex_user->host.length,
+ system_charset_info);
global.append ('\'');
if (acl_user->salt_len)
{
@@ -3294,23 +4206,23 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
make_password_from_salt(passwd_buff, acl_user->salt);
else
make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt);
- global.append(" IDENTIFIED BY PASSWORD '",25);
+ global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '"));
global.append(passwd_buff);
global.append('\'');
}
/* "show grants" SSL related stuff */
if (acl_user->ssl_type == SSL_TYPE_ANY)
- global.append(" REQUIRE SSL",12);
+ global.append(STRING_WITH_LEN(" REQUIRE SSL"));
else if (acl_user->ssl_type == SSL_TYPE_X509)
- global.append(" REQUIRE X509",13);
+ global.append(STRING_WITH_LEN(" REQUIRE X509"));
else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED)
{
int ssl_options = 0;
- global.append(" REQUIRE ",9);
+ global.append(STRING_WITH_LEN(" REQUIRE "));
if (acl_user->x509_issuer)
{
ssl_options++;
- global.append("ISSUER \'",8);
+ global.append(STRING_WITH_LEN("ISSUER \'"));
global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
global.append('\'');
}
@@ -3318,32 +4230,38 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
{
if (ssl_options++)
global.append(' ');
- global.append("SUBJECT \'",9);
- global.append(acl_user->x509_subject,strlen(acl_user->x509_subject));
+ global.append(STRING_WITH_LEN("SUBJECT \'"));
+ global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
+ system_charset_info);
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(STRING_WITH_LEN("CIPHER '"));
+ global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
+ system_charset_info);
global.append('\'');
}
}
if ((want_access & GRANT_ACL) ||
- (acl_user->user_resource.questions | acl_user->user_resource.updates |
- acl_user->user_resource.connections))
+ (acl_user->user_resource.questions ||
+ acl_user->user_resource.updates ||
+ acl_user->user_resource.conn_per_hour ||
+ acl_user->user_resource.user_conn))
{
- global.append(" WITH",5);
+ global.append(STRING_WITH_LEN(" WITH"));
if (want_access & GRANT_ACL)
- global.append(" GRANT OPTION",13);
+ global.append(STRING_WITH_LEN(" GRANT OPTION"));
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,
+ add_user_option(&global, acl_user->user_resource.conn_per_hour,
"MAX_CONNECTIONS_PER_HOUR");
+ add_user_option(&global, acl_user->user_resource.user_conn,
+ "MAX_USER_CONNECTIONS");
}
protocol->prepare_for_resend();
protocol->store(global.ptr(),global.length(),global.charset());
@@ -3373,12 +4291,12 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
{
String db(buff,sizeof(buff),system_charset_info);
db.length(0);
- db.append("GRANT ",6);
+ db.append(STRING_WITH_LEN("GRANT "));
if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
- db.append("ALL PRIVILEGES",14);
+ db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
else if (!(want_access & ~GRANT_ACL))
- db.append("USAGE",5);
+ db.append(STRING_WITH_LEN("USAGE"));
else
{
int found=0, cnt;
@@ -3388,22 +4306,23 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
if (test_access & j)
{
if (found)
- db.append(", ",2);
+ db.append(STRING_WITH_LEN(", "));
found = 1;
db.append(command_array[cnt],command_lengths[cnt]);
}
}
}
- db.append (" ON ",4);
+ db.append (STRING_WITH_LEN(" ON "));
append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
- db.append (".* TO '",7);
+ db.append (STRING_WITH_LEN(".* TO '"));
db.append(lex_user->user.str, lex_user->user.length,
system_charset_info);
- db.append ("'@'",3);
- db.append(lex_user->host.str, lex_user->host.length);
+ db.append (STRING_WITH_LEN("'@'"));
+ db.append(lex_user->host.str, lex_user->host.length,
+ system_charset_info);
db.append ('\'');
if (want_access & GRANT_ACL)
- db.append(" WITH GRANT OPTION",18);
+ db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
protocol->prepare_for_resend();
protocol->store(db.ptr(),db.length(),db.charset());
if (protocol->write())
@@ -3418,16 +4337,17 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
/* Add table & column access */
for (index=0 ; index < column_priv_hash.records ; index++)
{
- const char *user;
+ const char *user, *host;
GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
index);
if (!(user=grant_table->user))
user= "";
+ if (!(host= grant_table->host.hostname))
+ host= "";
if (!strcmp(lex_user->user.str,user) &&
- !my_strcasecmp(system_charset_info, lex_user->host.str,
- grant_table->host.hostname))
+ !my_strcasecmp(system_charset_info, lex_user->host.str, host))
{
ulong table_access= grant_table->privs;
if ((table_access | grant_table->cols) != 0)
@@ -3436,12 +4356,12 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;
global.length(0);
- global.append("GRANT ",6);
+ global.append(STRING_WITH_LEN("GRANT "));
if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
- global.append("ALL PRIVILEGES",14);
+ global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
else if (!test_access)
- global.append("USAGE",5);
+ global.append(STRING_WITH_LEN("USAGE"));
else
{
/* Add specific column access */
@@ -3453,7 +4373,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
if (test_access & j)
{
if (found)
- global.append(", ",2);
+ global.append(STRING_WITH_LEN(", "));
found= 1;
global.append(command_array[counter],command_lengths[counter]);
@@ -3477,14 +4397,14 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
*/
if (table_access & j)
{
- global.append(", ", 2);
+ global.append(STRING_WITH_LEN(", "));
global.append(command_array[counter],
command_lengths[counter]);
}
- global.append(" (",2);
+ global.append(STRING_WITH_LEN(" ("));
}
else
- global.append(", ",2);
+ global.append(STRING_WITH_LEN(", "));
global.append(grant_column->column,
grant_column->key_length,
system_charset_info);
@@ -3496,20 +4416,21 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
}
}
- global.append(" ON ",4);
+ global.append(STRING_WITH_LEN(" ON "));
append_identifier(thd, &global, grant_table->db,
strlen(grant_table->db));
global.append('.');
append_identifier(thd, &global, grant_table->tname,
strlen(grant_table->tname));
- global.append(" TO '",5);
+ global.append(STRING_WITH_LEN(" TO '"));
global.append(lex_user->user.str, lex_user->user.length,
system_charset_info);
- global.append("'@'",3);
- global.append(lex_user->host.str,lex_user->host.length);
+ global.append(STRING_WITH_LEN("'@'"));
+ global.append(lex_user->host.str,lex_user->host.length,
+ system_charset_info);
global.append('\'');
if (table_access & GRANT_ACL)
- global.append(" WITH GRANT OPTION",18);
+ global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
protocol->prepare_for_resend();
protocol->store(global.ptr(),global.length(),global.charset());
if (protocol->write())
@@ -3520,6 +4441,21 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
}
}
+
+ if (show_routine_grants(thd, lex_user, &proc_priv_hash,
+ STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
+ {
+ error= -1;
+ goto end;
+ }
+
+ if (show_routine_grants(thd, lex_user, &func_priv_hash,
+ STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
+ {
+ error= -1;
+ goto end;
+ }
+
end:
VOID(pthread_mutex_unlock(&acl_cache->lock));
rw_unlock(&LOCK_grant);
@@ -3528,6 +4464,84 @@ end:
DBUG_RETURN(error);
}
+static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
+ const char *type, int typelen,
+ char *buff, int buffsize)
+{
+ uint counter, index;
+ int error= 0;
+ Protocol *protocol= thd->protocol;
+ /* Add routine access */
+ for (index=0 ; index < hash->records ; index++)
+ {
+ const char *user, *host;
+ GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, index);
+
+ if (!(user=grant_proc->user))
+ user= "";
+ if (!(host= grant_proc->host.hostname))
+ host= "";
+
+ if (!strcmp(lex_user->user.str,user) &&
+ !my_strcasecmp(system_charset_info, lex_user->host.str, host))
+ {
+ ulong proc_access= grant_proc->privs;
+ if (proc_access != 0)
+ {
+ String global(buff, buffsize, system_charset_info);
+ ulong test_access= proc_access & ~GRANT_ACL;
+
+ global.length(0);
+ global.append(STRING_WITH_LEN("GRANT "));
+
+ if (!test_access)
+ global.append(STRING_WITH_LEN("USAGE"));
+ else
+ {
+ /* Add specific procedure access */
+ int found= 0;
+ ulong j;
+
+ for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1)
+ {
+ if (test_access & j)
+ {
+ if (found)
+ global.append(STRING_WITH_LEN(", "));
+ found= 1;
+ global.append(command_array[counter],command_lengths[counter]);
+ }
+ }
+ }
+ global.append(STRING_WITH_LEN(" ON "));
+ global.append(type,typelen);
+ global.append(' ');
+ append_identifier(thd, &global, grant_proc->db,
+ strlen(grant_proc->db));
+ global.append('.');
+ append_identifier(thd, &global, grant_proc->tname,
+ strlen(grant_proc->tname));
+ global.append(STRING_WITH_LEN(" TO '"));
+ global.append(lex_user->user.str, lex_user->user.length,
+ system_charset_info);
+ global.append(STRING_WITH_LEN("'@'"));
+ global.append(lex_user->host.str,lex_user->host.length,
+ system_charset_info);
+ global.append('\'');
+ if (proc_access & GRANT_ACL)
+ global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
+ protocol->prepare_for_resend();
+ protocol->store(global.ptr(),global.length(),global.charset());
+ if (protocol->write())
+ {
+ error= -1;
+ break;
+ }
+ }
+ }
+ }
+ return error;
+}
/*
Make a clear-text version of the requested privilege.
@@ -3571,28 +4585,53 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
pthread_mutex_unlock(&acl_cache->lock);
}
+/*
+ Open the grant tables.
+
+ SYNOPSIS
+ open_grant_tables()
+ thd The current thread.
+ tables (out) The 4 elements array for the opened tables.
+
+ DESCRIPTION
+ Tables are numbered as follows:
+ 0 user
+ 1 db
+ 2 tables_priv
+ 3 columns_priv
+
+ RETURN
+ 1 Skip GRANT handling during replication.
+ 0 OK.
+ < 0 Error.
+*/
+
+#define GRANT_TABLES 5
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("open_grant_tables");
if (!initialized)
{
- net_printf(thd,ER_OPTION_PREVENTS_STATEMENT, "--skip-grant-tables");
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
DBUG_RETURN(-1);
}
- bzero((char*) tables, 4*sizeof(*tables));
- tables->alias= tables->real_name= (char*) "user";
- (tables+1)->alias= (tables+1)->real_name= (char*) "db";
- (tables+2)->alias= (tables+2)->real_name= (char*) "tables_priv";
- (tables+3)->alias= (tables+3)->real_name= (char*) "columns_priv";
- tables->next= tables+1;
- (tables+1)->next= tables+2;
- (tables+2)->next= tables+3;
- (tables+3)->next= 0;
+ bzero((char*) tables, GRANT_TABLES*sizeof(*tables));
+ tables->alias= tables->table_name= (char*) "user";
+ (tables+1)->alias= (tables+1)->table_name= (char*) "db";
+ (tables+2)->alias= (tables+2)->table_name= (char*) "tables_priv";
+ (tables+3)->alias= (tables+3)->table_name= (char*) "columns_priv";
+ (tables+4)->alias= (tables+4)->table_name= (char*) "procs_priv";
+ tables->next_local= tables->next_global= tables+1;
+ (tables+1)->next_local= (tables+1)->next_global= tables+2;
+ (tables+2)->next_local= (tables+2)->next_global= tables+3;
+ (tables+3)->next_local= (tables+3)->next_global= tables+4;
tables->lock_type= (tables+1)->lock_type=
- (tables+2)->lock_type= (tables+3)->lock_type= TL_WRITE;
- tables->db= (tables+1)->db= (tables+2)->db= (tables+3)->db=(char*) "mysql";
+ (tables+2)->lock_type= (tables+3)->lock_type=
+ (tables+4)->lock_type= TL_WRITE;
+ tables->db= (tables+1)->db= (tables+2)->db=
+ (tables+3)->db= (tables+4)->db= (char*) "mysql";
#ifdef HAVE_REPLICATION
/*
@@ -3605,10 +4644,12 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
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=tables[3].updating=1;
- if (!tables_ok(0, tables))
+ tables[0].updating=tables[1].updating=tables[2].updating=
+ tables[3].updating=tables[4].updating=1;
+ if (!tables_ok(thd, tables))
DBUG_RETURN(1);
- tables[0].updating=tables[1].updating=tables[2].updating=tables[3].updating=0;
+ tables[0].updating=tables[1].updating=tables[2].updating=
+ tables[3].updating=tables[4].updating=0;;
}
#endif
@@ -3648,145 +4689,746 @@ ACL_USER *check_acl_user(LEX_USER *user_name,
return acl_user;
}
+/*
+ Modify a privilege table.
+
+ SYNOPSIS
+ modify_grant_table()
+ table The table to modify.
+ host_field The host name field.
+ user_field The user name field.
+ user_to The new name for the user if to be renamed,
+ NULL otherwise.
-int mysql_drop_user(THD *thd, List <LEX_USER> &list)
+ DESCRIPTION
+ Update user/host in the current record if user_to is not NULL.
+ Delete the current record if user_to is NULL.
+
+ RETURN
+ 0 OK.
+ != 0 Error.
+*/
+
+static int modify_grant_table(TABLE *table, Field *host_field,
+ Field *user_field, LEX_USER *user_to)
{
- uint counter, acl_userd;
- int result;
- ACL_USER *acl_user;
- ACL_DB *acl_db;
- TABLE_LIST tables[4];
+ int error;
+ DBUG_ENTER("modify_grant_table");
- DBUG_ENTER("mysql_drop_user");
+ if (user_to)
+ {
+ /* rename */
+ store_record(table, record[1]);
+ host_field->store(user_to->host.str, user_to->host.length,
+ system_charset_info);
+ user_field->store(user_to->user.str, user_to->user.length,
+ system_charset_info);
+ if ((error= table->file->update_row(table->record[1], table->record[0])))
+ table->file->print_error(error, MYF(0));
+ }
+ else
+ {
+ /* delete */
+ if ((error=table->file->delete_row(table->record[0])))
+ table->file->print_error(error, MYF(0));
+ }
- if ((result= open_grant_tables(thd, tables)))
- DBUG_RETURN(result == 1 ? 0 : 1);
+ DBUG_RETURN(error);
+}
- rw_wrlock(&LOCK_grant);
- VOID(pthread_mutex_lock(&acl_cache->lock));
+/*
+ Handle a privilege table.
- LEX_USER *user_name;
- List_iterator <LEX_USER> user_list(list);
- while ((user_name=user_list++))
+ SYNOPSIS
+ handle_grant_table()
+ tables The array with the four open tables.
+ table_no The number of the table to handle (0..4).
+ drop If user_from is to be dropped.
+ user_from The the user to be searched/dropped/renamed.
+ user_to The new name for the user if to be renamed,
+ NULL otherwise.
+
+ DESCRIPTION
+ Scan through all records in a grant table and apply the requested
+ operation. For the "user" table, a single index access is sufficient,
+ since there is an unique index on (host, user).
+ Delete from grant table if drop is true.
+ Update in grant table if drop is false and user_to is not NULL.
+ Search in grant table if drop is false and user_to is NULL.
+ Tables are numbered as follows:
+ 0 user
+ 1 db
+ 2 tables_priv
+ 3 columns_priv
+ 4 procs_priv
+
+ RETURN
+ > 0 At least one record matched.
+ 0 OK, but no record matched.
+ < 0 Error.
+*/
+
+static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
+ LEX_USER *user_from, LEX_USER *user_to)
+{
+ int result= 0;
+ int error;
+ TABLE *table= tables[table_no].table;
+ Field *host_field= table->field[0];
+ Field *user_field= table->field[table_no ? 2 : 1];
+ char *host_str= user_from->host.str;
+ char *user_str= user_from->user.str;
+ const char *host;
+ const char *user;
+ byte user_key[MAX_KEY_LENGTH];
+ uint key_prefix_length;
+ DBUG_ENTER("handle_grant_table");
+
+ if (! table_no) // mysql.user table
{
- if (!(acl_user= check_acl_user(user_name, &counter)))
+ /*
+ The 'user' table has an unique index on (host, user).
+ Thus, we can handle everything with a single index access.
+ The host- and user fields are consecutive in the user table records.
+ So we set host- and user fields of table->record[0] and use the
+ pointer to the host field as key.
+ index_read_idx() will replace table->record[0] (its first argument)
+ by the searched record, if it exists.
+ */
+ DBUG_PRINT("info",("read table: '%s' search: '%s'@'%s'",
+ table->s->table_name, user_str, host_str));
+ host_field->store(host_str, user_from->host.length, system_charset_info);
+ user_field->store(user_str, user_from->user.length, system_charset_info);
+
+ key_prefix_length= (table->key_info->key_part[0].store_length +
+ table->key_info->key_part[1].store_length);
+ key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
+
+ if ((error= table->file->index_read_idx(table->record[0], 0,
+ user_key, key_prefix_length,
+ HA_READ_KEY_EXACT)))
{
- result= -1;
- continue;
+ if (error != HA_ERR_KEY_NOT_FOUND)
+ {
+ table->file->print_error(error, MYF(0));
+ result= -1;
+ }
}
- if ((acl_user->access & ~0))
+ else
+ {
+ /* If requested, delete or update the record. */
+ result= ((drop || user_to) &&
+ modify_grant_table(table, host_field, user_field, user_to)) ?
+ -1 : 1; /* Error or found. */
+ }
+ DBUG_PRINT("info",("read result: %d", result));
+ }
+ else
+ {
+ /*
+ The non-'user' table do not have indexes on (host, user).
+ And their host- and user fields are not consecutive.
+ Thus, we need to do a table scan to find all matching records.
+ */
+ if ((error= table->file->ha_rnd_init(1)))
{
+ table->file->print_error(error, MYF(0));
result= -1;
- continue;
}
- acl_userd= counter;
+ else
+ {
+#ifdef EXTRA_DEBUG
+ DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'",
+ table->s->table_name, user_str, host_str));
+#endif
+ while ((error= table->file->rnd_next(table->record[0])) !=
+ HA_ERR_END_OF_FILE)
+ {
+ if (error)
+ {
+ /* Most probable 'deleted record'. */
+ DBUG_PRINT("info",("scan error: %d", error));
+ continue;
+ }
+ if (! (host= get_field(&mem, host_field)))
+ host= "";
+ if (! (user= get_field(&mem, user_field)))
+ user= "";
+
+#ifdef EXTRA_DEBUG
+ DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
+ user, host,
+ get_field(&mem, table->field[1]) /*db*/,
+ get_field(&mem, table->field[3]) /*table*/,
+ get_field(&mem, table->field[4]) /*column*/));
+#endif
+ if (strcmp(user_str, user) ||
+ my_strcasecmp(system_charset_info, host_str, host))
+ continue;
+
+ /* If requested, delete or update the record. */
+ result= ((drop || user_to) &&
+ modify_grant_table(table, host_field, user_field, user_to)) ?
+ -1 : result ? result : 1; /* Error or keep result or found. */
+ /* If search is requested, we do not need to search further. */
+ if (! drop && ! user_to)
+ break ;
+ }
+ (void) table->file->ha_rnd_end();
+ DBUG_PRINT("info",("scan result: %d", result));
+ }
+ }
+
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Handle an in-memory privilege structure.
+
+ SYNOPSIS
+ handle_grant_struct()
+ struct_no The number of the structure to handle (0..3).
+ drop If user_from is to be dropped.
+ user_from The the user to be searched/dropped/renamed.
+ user_to The new name for the user if to be renamed,
+ NULL otherwise.
+
+ DESCRIPTION
+ Scan through all elements in an in-memory grant structure and apply
+ the requested operation.
+ Delete from grant structure if drop is true.
+ Update in grant structure if drop is false and user_to is not NULL.
+ Search in grant structure if drop is false and user_to is NULL.
+ Structures are numbered as follows:
+ 0 acl_users
+ 1 acl_dbs
+ 2 column_priv_hash
+ 3 procs_priv_hash
+
+ RETURN
+ > 0 At least one element matched.
+ 0 OK, but no element matched.
+ -1 Wrong arguments to function
+*/
+
+static int handle_grant_struct(uint struct_no, bool drop,
+ LEX_USER *user_from, LEX_USER *user_to)
+{
+ int result= 0;
+ uint idx;
+ uint elements;
+ const char *user;
+ const char *host;
+ ACL_USER *acl_user;
+ ACL_DB *acl_db;
+ GRANT_NAME *grant_name;
+ DBUG_ENTER("handle_grant_struct");
+ DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
+ struct_no, user_from->user.str, user_from->host.str));
+
+ LINT_INIT(acl_user);
+ LINT_INIT(acl_db);
+ LINT_INIT(grant_name);
+
+ safe_mutex_assert_owner(&acl_cache->lock);
+
+ /* Get the number of elements in the in-memory structure. */
+ switch (struct_no) {
+ case 0:
+ elements= acl_users.elements;
+ break;
+ case 1:
+ elements= acl_dbs.elements;
+ break;
+ case 2:
+ elements= column_priv_hash.records;
+ break;
+ case 3:
+ elements= proc_priv_hash.records;
+ break;
+ default:
+ return -1;
+ }
+
+#ifdef EXTRA_DEBUG
+ DBUG_PRINT("loop",("scan struct: %u search user: '%s' host: '%s'",
+ struct_no, user_from->user.str, user_from->host.str));
+#endif
+ /* Loop over all elements. */
+ for (idx= 0; idx < elements; idx++)
+ {
+ /*
+ Get a pointer to the element.
+ */
+ switch (struct_no) {
+ case 0:
+ acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
+ user= acl_user->user;
+ host= acl_user->host.hostname;
+ break;
+
+ case 1:
+ acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*);
+ user= acl_db->user;
+ host= acl_db->host.hostname;
+ break;
+
+ case 2:
+ grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx);
+ user= grant_name->user;
+ host= grant_name->host.hostname;
+ break;
+
+ case 3:
+ grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx);
+ user= grant_name->user;
+ host= grant_name->host.hostname;
+ break;
+ }
+ if (! user)
+ user= "";
+ if (! host)
+ host= "";
+
+#ifdef EXTRA_DEBUG
+ DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'",
+ struct_no, idx, user, host));
+#endif
+ if (strcmp(user_from->user.str, user) ||
+ my_strcasecmp(system_charset_info, user_from->host.str, host))
+ continue;
- for (counter= 0 ; counter < acl_dbs.elements ; counter++)
+ result= 1; /* At least one element found. */
+ if ( drop )
{
- const char *user,*host;
- acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
- if (!(user= acl_db->user))
- user= "";
- if (!(host= acl_db->host.hostname))
- host= "";
+ switch ( struct_no )
+ {
+ case 0:
+ delete_dynamic_element(&acl_users, idx);
+ break;
+
+ case 1:
+ delete_dynamic_element(&acl_dbs, idx);
+ break;
- if (!strcmp(user_name->user.str,user) &&
- !my_strcasecmp(system_charset_info, user_name->host.str, host))
+ case 2:
+ hash_delete(&column_priv_hash, (byte*) grant_name);
break;
+
+ case 3:
+ hash_delete(&proc_priv_hash, (byte*) grant_name);
+ break;
+ }
+ elements--;
+ idx--;
}
- if (counter != acl_dbs.elements)
+ else if ( user_to )
{
- result= -1;
- continue;
+ switch ( struct_no ) {
+ case 0:
+ acl_user->user= strdup_root(&mem, user_to->user.str);
+ acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
+ break;
+
+ case 1:
+ acl_db->user= strdup_root(&mem, user_to->user.str);
+ acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
+ break;
+
+ case 2:
+ case 3:
+ grant_name->user= strdup_root(&mem, user_to->user.str);
+ update_hostname(&grant_name->host,
+ strdup_root(&mem, user_to->host.str));
+ break;
+ }
}
+ else
+ {
+ /* If search is requested, we do not need to search further. */
+ break;
+ }
+ }
+#ifdef EXTRA_DEBUG
+ DBUG_PRINT("loop",("scan struct: %u result %d", struct_no, result));
+#endif
+
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Handle all privilege tables and in-memory privilege structures.
- for (counter= 0 ; counter < column_priv_hash.records ; counter++)
+ SYNOPSIS
+ handle_grant_data()
+ tables The array with the four open tables.
+ drop If user_from is to be dropped.
+ user_from The the user to be searched/dropped/renamed.
+ user_to The new name for the user if to be renamed,
+ NULL otherwise.
+
+ DESCRIPTION
+ Go through all grant tables and in-memory grant structures and apply
+ the requested operation.
+ Delete from grant data if drop is true.
+ Update in grant data if drop is false and user_to is not NULL.
+ Search in grant data if drop is false and user_to is NULL.
+
+ RETURN
+ > 0 At least one element matched.
+ 0 OK, but no element matched.
+ < 0 Error.
+*/
+
+static int handle_grant_data(TABLE_LIST *tables, bool drop,
+ LEX_USER *user_from, LEX_USER *user_to)
+{
+ int result= 0;
+ int found;
+ DBUG_ENTER("handle_grant_data");
+
+ /* Handle user table. */
+ if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch the in-memory array. */
+ result= -1;
+ }
+ else
+ {
+ /* Handle user array. */
+ if ((handle_grant_struct(0, drop, user_from, user_to) && ! result) ||
+ found)
{
- const char *user,*host;
- GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
- counter);
- if (!(user=grant_table->user))
- user= "";
- if (!(host=grant_table->host.hostname))
- host= "";
+ result= 1; /* At least one record/element found. */
+ /* If search is requested, we do not need to search further. */
+ if (! drop && ! user_to)
+ goto end;
+ }
+ }
- if (!strcmp(user_name->user.str,user) &&
- !my_strcasecmp(system_charset_info, user_name->host.str, host))
- break;
+ /* Handle db table. */
+ if ((found= handle_grant_table(tables, 1, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch the in-memory array. */
+ result= -1;
+ }
+ else
+ {
+ /* Handle db array. */
+ if (((handle_grant_struct(1, drop, user_from, user_to) && ! result) ||
+ found) && ! result)
+ {
+ result= 1; /* At least one record/element found. */
+ /* If search is requested, we do not need to search further. */
+ if (! drop && ! user_to)
+ goto end;
}
- if (counter != column_priv_hash.records)
+ }
+
+ /* Handle procedures table. */
+ if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch in-memory array. */
+ result= -1;
+ }
+ else
+ {
+ /* Handle procs array. */
+ if (((handle_grant_struct(3, drop, user_from, user_to) && ! result) ||
+ found) && ! result)
+ {
+ result= 1; /* At least one record/element found. */
+ /* If search is requested, we do not need to search further. */
+ if (! drop && ! user_to)
+ goto end;
+ }
+ }
+
+ /* Handle tables table. */
+ if ((found= handle_grant_table(tables, 2, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch columns and in-memory array. */
+ result= -1;
+ }
+ else
+ {
+ if (found && ! result)
{
+ result= 1; /* At least one record found. */
+ /* If search is requested, we do not need to search further. */
+ if (! drop && ! user_to)
+ goto end;
+ }
+
+ /* Handle columns table. */
+ if ((found= handle_grant_table(tables, 3, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch the in-memory array. */
result= -1;
+ }
+ else
+ {
+ /* Handle columns hash. */
+ if (((handle_grant_struct(2, drop, user_from, user_to) && ! result) ||
+ found) && ! result)
+ result= 1; /* At least one record/element found. */
+ }
+ }
+ end:
+ DBUG_RETURN(result);
+}
+
+
+static void append_user(String *str, LEX_USER *user)
+{
+ if (str->length())
+ str->append(',');
+ str->append('\'');
+ str->append(user->user.str);
+ str->append(STRING_WITH_LEN("'@'"));
+ str->append(user->host.str);
+ str->append('\'');
+}
+
+
+/*
+ Create a list of users.
+
+ SYNOPSIS
+ mysql_create_user()
+ thd The current thread.
+ list The users to create.
+
+ RETURN
+ FALSE OK.
+ TRUE Error.
+*/
+
+bool mysql_create_user(THD *thd, List <LEX_USER> &list)
+{
+ int result;
+ String wrong_users;
+ ulong sql_mode;
+ LEX_USER *user_name, *tmp_user_name;
+ List_iterator <LEX_USER> user_list(list);
+ TABLE_LIST tables[GRANT_TABLES];
+ DBUG_ENTER("mysql_create_user");
+
+ /* CREATE USER may be skipped on replication client. */
+ if ((result= open_grant_tables(thd, tables)))
+ DBUG_RETURN(result != 1);
+
+ rw_wrlock(&LOCK_grant);
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ while ((tmp_user_name= user_list++))
+ {
+ if (!(user_name= get_current_user(thd, tmp_user_name)))
+ {
+ result= TRUE;
continue;
}
- tables[0].table->field[0]->store(user_name->host.str,(uint)
- user_name->host.length,
- system_charset_info);
- tables[0].table->field[1]->store(user_name->user.str,(uint)
- user_name->user.length,
- system_charset_info);
- tables[0].table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
- if (!tables[0].table->file->index_read_idx(tables[0].table->record[0],0,
- (byte*) tables[0].table->
- field[0]->ptr,
- tables[0].table->
- key_info[0].key_length,
- HA_READ_KEY_EXACT))
- {
- int error;
- if ((error = tables[0].table->file->delete_row(tables[0].table->
- record[0])))
- {
- tables[0].table->file->print_error(error, MYF(0));
- result= -1;
- goto end;
- }
- delete_dynamic_element(&acl_users, acl_userd);
+ /*
+ Search all in-memory structures and grant tables
+ for a mention of the new user name.
+ */
+ if (handle_grant_data(tables, 0, user_name, NULL))
+ {
+ append_user(&wrong_users, user_name);
+ result= TRUE;
+ continue;
+ }
+
+ sql_mode= thd->variables.sql_mode;
+ if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
+ {
+ append_user(&wrong_users, user_name);
+ result= TRUE;
}
}
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ rw_unlock(&LOCK_grant);
+ close_thread_tables(thd);
if (result)
- my_error(ER_DROP_USER, MYF(0));
+ my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
+ DBUG_RETURN(result);
+}
-end:
- /* Reload acl_check_hosts as its memory is mapped to acl_user */
- delete_dynamic(&acl_wild_hosts);
- hash_free(&acl_check_hosts);
- init_check_host();
+
+/*
+ Drop a list of users and all their privileges.
+
+ SYNOPSIS
+ mysql_drop_user()
+ thd The current thread.
+ list The users to drop.
+
+ RETURN
+ FALSE OK.
+ TRUE Error.
+*/
+
+bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
+{
+ int result;
+ String wrong_users;
+ LEX_USER *user_name, *tmp_user_name;
+ List_iterator <LEX_USER> user_list(list);
+ TABLE_LIST tables[GRANT_TABLES];
+ DBUG_ENTER("mysql_drop_user");
+
+ /* DROP USER may be skipped on replication client. */
+ if ((result= open_grant_tables(thd, tables)))
+ DBUG_RETURN(result != 1);
+
+ rw_wrlock(&LOCK_grant);
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ while ((tmp_user_name= user_list++))
+ {
+ user_name= get_current_user(thd, tmp_user_name);
+ if (!(user_name= get_current_user(thd, tmp_user_name)))
+ {
+ result= TRUE;
+ continue;
+ }
+ if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
+ {
+ append_user(&wrong_users, user_name);
+ result= TRUE;
+ }
+ }
+
+ /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
+ rebuild_check_host();
VOID(pthread_mutex_unlock(&acl_cache->lock));
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
+ if (result)
+ my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
DBUG_RETURN(result);
}
-int mysql_revoke_all(THD *thd, List <LEX_USER> &list)
+
+/*
+ Rename a user.
+
+ SYNOPSIS
+ mysql_rename_user()
+ thd The current thread.
+ list The user name pairs: (from, to).
+
+ RETURN
+ FALSE OK.
+ TRUE Error.
+*/
+
+bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
{
- uint counter, revoked;
+ int result;
+ String wrong_users;
+ LEX_USER *user_from, *tmp_user_from;
+ LEX_USER *user_to, *tmp_user_to;
+ List_iterator <LEX_USER> user_list(list);
+ TABLE_LIST tables[GRANT_TABLES];
+ DBUG_ENTER("mysql_rename_user");
+
+ /* RENAME USER may be skipped on replication client. */
+ if ((result= open_grant_tables(thd, tables)))
+ DBUG_RETURN(result != 1);
+
+ rw_wrlock(&LOCK_grant);
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ while ((tmp_user_from= user_list++))
+ {
+ if (!(user_from= get_current_user(thd, tmp_user_from)))
+ {
+ result= TRUE;
+ continue;
+ }
+ tmp_user_to= user_list++;
+ if (!(user_to= get_current_user(thd, tmp_user_to)))
+ {
+ result= TRUE;
+ continue;
+ }
+ DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
+
+ /*
+ Search all in-memory structures and grant tables
+ for a mention of the new user name.
+ */
+ if (handle_grant_data(tables, 0, user_to, NULL) ||
+ handle_grant_data(tables, 0, user_from, user_to) <= 0)
+ {
+ append_user(&wrong_users, user_from);
+ result= TRUE;
+ }
+ }
+
+ /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
+ rebuild_check_host();
+
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ rw_unlock(&LOCK_grant);
+ close_thread_tables(thd);
+ if (result)
+ my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Revoke all privileges from a list of users.
+
+ SYNOPSIS
+ mysql_revoke_all()
+ thd The current thread.
+ list The users to revoke all privileges from.
+
+ RETURN
+ > 0 Error. Error message already sent.
+ 0 OK.
+ < 0 Error. Error message not yet sent.
+*/
+
+bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
+{
+ uint counter, revoked, is_proc;
int result;
ACL_DB *acl_db;
- TABLE_LIST tables[4];
+ TABLE_LIST tables[GRANT_TABLES];
DBUG_ENTER("mysql_revoke_all");
if ((result= open_grant_tables(thd, tables)))
- DBUG_RETURN(result == 1 ? 0 : 1);
+ DBUG_RETURN(result != 1);
rw_wrlock(&LOCK_grant);
VOID(pthread_mutex_lock(&acl_cache->lock));
- LEX_USER *lex_user;
+ LEX_USER *lex_user, *tmp_lex_user;
List_iterator <LEX_USER> user_list(list);
- while ((lex_user=user_list++))
+ while ((tmp_lex_user= user_list++))
{
- if (!check_acl_user(lex_user, &counter))
+ if (!(lex_user= get_current_user(thd, tmp_lex_user)))
+ {
+ result= -1;
+ continue;
+ }
+ if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
{
result= -1;
continue;
}
if (replace_user_table(thd, tables[0].table,
- *lex_user, ~(ulong)0, 1, 0))
+ *lex_user, ~(ulong)0, 1, 0, 0))
{
result= -1;
continue;
@@ -3803,13 +5445,13 @@ int mysql_revoke_all(THD *thd, List <LEX_USER> &list)
for (counter= 0, revoked= 0 ; counter < acl_dbs.elements ; )
{
const char *user,*host;
-
+
acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
if (!(user=acl_db->user))
user= "";
if (!(host=acl_db->host.hostname))
host= "";
-
+
if (!strcmp(lex_user->user.str,user) &&
!my_strcasecmp(system_charset_info, lex_user->host.str, host))
{
@@ -3840,7 +5482,7 @@ int mysql_revoke_all(THD *thd, List <LEX_USER> &list)
user= "";
if (!(host=grant_table->host.hostname))
host= "";
-
+
if (!strcmp(lex_user->user.str,user) &&
!my_strcasecmp(system_charset_info, lex_user->host.str, host))
{
@@ -3874,15 +5516,211 @@ int mysql_revoke_all(THD *thd, List <LEX_USER> &list)
counter++;
}
} while (revoked);
+
+ /* Remove procedure access */
+ for (is_proc=0; is_proc<2; is_proc++) do {
+ HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
+ for (counter= 0, revoked= 0 ; counter < hash->records ; )
+ {
+ const char *user,*host;
+ GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
+ if (!(user=grant_proc->user))
+ user= "";
+ if (!(host=grant_proc->host.hostname))
+ host= "";
+
+ if (!strcmp(lex_user->user.str,user) &&
+ !my_strcasecmp(system_charset_info, lex_user->host.str, host))
+ {
+ if (!replace_routine_table(thd,grant_proc,tables[4].table,*lex_user,
+ grant_proc->db,
+ grant_proc->tname,
+ is_proc,
+ ~(ulong)0, 1))
+ {
+ revoked= 1;
+ continue;
+ }
+ result= -1; // Something went wrong
+ }
+ counter++;
+ }
+ } while (revoked);
}
-
+
VOID(pthread_mutex_unlock(&acl_cache->lock));
rw_unlock(&LOCK_grant);
close_thread_tables(thd);
-
+
if (result)
- my_error(ER_REVOKE_GRANTS, MYF(0));
-
+ my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
+
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Revoke privileges for all users on a stored procedure
+
+ SYNOPSIS
+ sp_revoke_privileges()
+ thd The current thread.
+ db DB of the stored procedure
+ name Name of the stored procedure
+
+ RETURN
+ 0 OK.
+ < 0 Error. Error message not yet sent.
+*/
+
+bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
+ bool is_proc)
+{
+ uint counter, revoked;
+ int result;
+ TABLE_LIST tables[GRANT_TABLES];
+ HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
+ DBUG_ENTER("sp_revoke_privileges");
+
+ if ((result= open_grant_tables(thd, tables)))
+ DBUG_RETURN(result != 1);
+
+ rw_wrlock(&LOCK_grant);
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ /* Remove procedure access */
+ do
+ {
+ for (counter= 0, revoked= 0 ; counter < hash->records ; )
+ {
+ GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, counter);
+ if (!my_strcasecmp(system_charset_info, grant_proc->db, sp_db) &&
+ !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name))
+ {
+ LEX_USER lex_user;
+ lex_user.user.str= grant_proc->user;
+ lex_user.user.length= strlen(grant_proc->user);
+ lex_user.host.str= grant_proc->host.hostname ?
+ grant_proc->host.hostname : (char*)"";
+ lex_user.host.length= grant_proc->host.hostname ?
+ strlen(grant_proc->host.hostname) : 0;
+ if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
+ grant_proc->db, grant_proc->tname,
+ is_proc, ~(ulong)0, 1))
+ {
+ revoked= 1;
+ continue;
+ }
+ result= -1; // Something went wrong
+ }
+ counter++;
+ }
+ } while (revoked);
+
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ rw_unlock(&LOCK_grant);
+ close_thread_tables(thd);
+
+ if (result)
+ my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0));
+
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Grant EXECUTE,ALTER privilege for a stored procedure
+
+ SYNOPSIS
+ sp_grant_privileges()
+ thd The current thread.
+ db DB of the stored procedure
+ name Name of the stored procedure
+
+ RETURN
+ 0 OK.
+ < 0 Error. Error message not yet sent.
+*/
+
+bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
+ bool is_proc)
+{
+ Security_context *sctx= thd->security_ctx;
+ LEX_USER *combo;
+ TABLE_LIST tables[1];
+ List<LEX_USER> user_list;
+ bool result;
+ ACL_USER *au;
+ char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
+ DBUG_ENTER("sp_grant_privileges");
+
+ if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
+ DBUG_RETURN(TRUE);
+
+ combo->user.str= sctx->user;
+
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ if ((au= find_acl_user(combo->host.str=(char*)sctx->host_or_ip,combo->user.str,FALSE)))
+ goto found_acl;
+ if ((au= find_acl_user(combo->host.str=(char*)sctx->host, combo->user.str,FALSE)))
+ goto found_acl;
+ if ((au= find_acl_user(combo->host.str=(char*)sctx->ip, combo->user.str,FALSE)))
+ goto found_acl;
+ if((au= find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE)))
+ goto found_acl;
+
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ DBUG_RETURN(TRUE);
+
+ found_acl:
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+
+ bzero((char*)tables, sizeof(TABLE_LIST));
+ user_list.empty();
+
+ tables->db= (char*)sp_db;
+ tables->table_name= tables->alias= (char*)sp_name;
+
+ combo->host.length= strlen(combo->host.str);
+ combo->user.length= strlen(combo->user.str);
+ combo->host.str= thd->strmake(combo->host.str,combo->host.length);
+ combo->user.str= thd->strmake(combo->user.str,combo->user.length);
+
+
+ if(au && au->salt_len)
+ {
+ if (au->salt_len == SCRAMBLE_LENGTH)
+ {
+ make_password_from_salt(passwd_buff, au->salt);
+ combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
+ }
+ else if (au->salt_len == SCRAMBLE_LENGTH_323)
+ {
+ make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
+ combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ }
+ else
+ {
+ my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
+ return -1;
+ }
+ combo->password.str= passwd_buff;
+ }
+ else
+ {
+ combo->password.str= (char*)"";
+ combo->password.length= 0;
+ }
+
+ if (user_list.push_back(combo))
+ DBUG_RETURN(TRUE);
+
+ thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
+ bzero((char*) &thd->lex->mqh, sizeof(thd->lex->mqh));
+
+ result= mysql_routine_grant(thd, tables, is_proc, user_list,
+ DEFAULT_CREATE_PROC_ACLS, 0, 1);
DBUG_RETURN(result);
}
@@ -3891,7 +5729,7 @@ int mysql_revoke_all(THD *thd, List <LEX_USER> &list)
Instantiate used templates
*****************************************************************************/
-#ifdef __GNUC__
+#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
template class List_iterator<LEX_COLUMN>;
template class List_iterator<LEX_USER>;
template class List<LEX_COLUMN>;
@@ -3944,3 +5782,369 @@ int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
DBUG_RETURN (*str != '\0');
}
+
+void update_schema_privilege(TABLE *table, char *buff, const char* db,
+ const char* t_name, const char* column,
+ uint col_length, const char *priv,
+ uint priv_length, const char* is_grantable)
+{
+ int i= 2;
+ CHARSET_INFO *cs= system_charset_info;
+ restore_record(table, s->default_values);
+ table->field[0]->store(buff, strlen(buff), cs);
+ if (db)
+ table->field[i++]->store(db, strlen(db), cs);
+ if (t_name)
+ table->field[i++]->store(t_name, strlen(t_name), cs);
+ if (column)
+ table->field[i++]->store(column, col_length, cs);
+ table->field[i++]->store(priv, priv_length, cs);
+ table->field[i]->store(is_grantable, strlen(is_grantable), cs);
+ table->file->write_row(table->record[0]);
+}
+
+
+int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ uint counter;
+ ACL_USER *acl_user;
+ ulong want_access;
+ char buff[100];
+ TABLE *table= tables->table;
+ bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
+ char *curr_host= thd->security_ctx->priv_host_name();
+ DBUG_ENTER("fill_schema_user_privileges");
+
+ pthread_mutex_lock(&acl_cache->lock);
+
+ for (counter=0 ; counter < acl_users.elements ; counter++)
+ {
+ const char *user,*host, *is_grantable="YES";
+ acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
+ if (!(user=acl_user->user))
+ user= "";
+ if (!(host=acl_user->host.hostname))
+ host= "";
+
+ if (no_global_access &&
+ (strcmp(thd->security_ctx->priv_user, user) ||
+ my_strcasecmp(system_charset_info, curr_host, host)))
+ continue;
+
+ want_access= acl_user->access;
+ if (!(want_access & GRANT_ACL))
+ is_grantable= "NO";
+
+ strxmov(buff,"'",user,"'@'",host,"'",NullS);
+ if (!(want_access & ~GRANT_ACL))
+ update_schema_privilege(table, buff, 0, 0, 0, 0,
+ STRING_WITH_LEN("USAGE"), is_grantable);
+ else
+ {
+ uint priv_id;
+ ulong j,test_access= want_access & ~GRANT_ACL;
+ for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
+ {
+ if (test_access & j)
+ update_schema_privilege(table, buff, 0, 0, 0, 0,
+ command_array[priv_id],
+ command_lengths[priv_id], is_grantable);
+ }
+ }
+ }
+
+ pthread_mutex_unlock(&acl_cache->lock);
+
+ DBUG_RETURN(0);
+#else
+ return(0);
+#endif
+}
+
+
+int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ uint counter;
+ ACL_DB *acl_db;
+ ulong want_access;
+ char buff[100];
+ TABLE *table= tables->table;
+ bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
+ char *curr_host= thd->security_ctx->priv_host_name();
+ DBUG_ENTER("fill_schema_schema_privileges");
+
+ pthread_mutex_lock(&acl_cache->lock);
+
+ for (counter=0 ; counter < acl_dbs.elements ; counter++)
+ {
+ const char *user, *host, *is_grantable="YES";
+
+ acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
+ if (!(user=acl_db->user))
+ user= "";
+ if (!(host=acl_db->host.hostname))
+ host= "";
+
+ if (no_global_access &&
+ (strcmp(thd->security_ctx->priv_user, user) ||
+ my_strcasecmp(system_charset_info, curr_host, host)))
+ continue;
+
+ want_access=acl_db->access;
+ if (want_access)
+ {
+ if (!(want_access & GRANT_ACL))
+ {
+ is_grantable= "NO";
+ }
+ strxmov(buff,"'",user,"'@'",host,"'",NullS);
+ if (!(want_access & ~GRANT_ACL))
+ update_schema_privilege(table, buff, acl_db->db, 0, 0,
+ 0, STRING_WITH_LEN("USAGE"), is_grantable);
+ else
+ {
+ int cnt;
+ ulong j,test_access= want_access & ~GRANT_ACL;
+ for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
+ if (test_access & j)
+ update_schema_privilege(table, buff, acl_db->db, 0, 0, 0,
+ command_array[cnt], command_lengths[cnt],
+ is_grantable);
+ }
+ }
+ }
+
+ pthread_mutex_unlock(&acl_cache->lock);
+
+ DBUG_RETURN(0);
+#else
+ return (0);
+#endif
+}
+
+
+int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ uint index;
+ char buff[100];
+ TABLE *table= tables->table;
+ bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
+ char *curr_host= thd->security_ctx->priv_host_name();
+ DBUG_ENTER("fill_schema_table_privileges");
+
+ rw_rdlock(&LOCK_grant);
+
+ for (index=0 ; index < column_priv_hash.records ; index++)
+ {
+ const char *user, *host, *is_grantable= "YES";
+ GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
+ index);
+ if (!(user=grant_table->user))
+ user= "";
+ if (!(host= grant_table->host.hostname))
+ host= "";
+
+ if (no_global_access &&
+ (strcmp(thd->security_ctx->priv_user, user) ||
+ my_strcasecmp(system_charset_info, curr_host, host)))
+ continue;
+
+ ulong table_access= grant_table->privs;
+ if (table_access)
+ {
+ ulong test_access= table_access & ~GRANT_ACL;
+ /*
+ We should skip 'usage' privilege on table if
+ we have any privileges on column(s) of this table
+ */
+ if (!test_access && grant_table->cols)
+ continue;
+ if (!(table_access & GRANT_ACL))
+ is_grantable= "NO";
+
+ strxmov(buff, "'", user, "'@'", host, "'", NullS);
+ if (!test_access)
+ update_schema_privilege(table, buff, grant_table->db, grant_table->tname,
+ 0, 0, STRING_WITH_LEN("USAGE"), is_grantable);
+ else
+ {
+ ulong j;
+ int cnt;
+ for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
+ {
+ if (test_access & j)
+ update_schema_privilege(table, buff, grant_table->db,
+ grant_table->tname, 0, 0, command_array[cnt],
+ command_lengths[cnt], is_grantable);
+ }
+ }
+ }
+ }
+
+ rw_unlock(&LOCK_grant);
+
+ DBUG_RETURN(0);
+#else
+ return (0);
+#endif
+}
+
+
+int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ uint index;
+ char buff[100];
+ TABLE *table= tables->table;
+ bool no_global_access= check_access(thd, SELECT_ACL, "mysql",0,1,1,0);
+ char *curr_host= thd->security_ctx->priv_host_name();
+ DBUG_ENTER("fill_schema_table_privileges");
+
+ rw_rdlock(&LOCK_grant);
+
+ for (index=0 ; index < column_priv_hash.records ; index++)
+ {
+ const char *user, *host, *is_grantable= "YES";
+ GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash,
+ index);
+ if (!(user=grant_table->user))
+ user= "";
+ if (!(host= grant_table->host.hostname))
+ host= "";
+
+ if (no_global_access &&
+ (strcmp(thd->security_ctx->priv_user, user) ||
+ my_strcasecmp(system_charset_info, curr_host, host)))
+ continue;
+
+ ulong table_access= grant_table->cols;
+ if (table_access != 0)
+ {
+ if (!(grant_table->privs & GRANT_ACL))
+ is_grantable= "NO";
+
+ ulong test_access= table_access & ~GRANT_ACL;
+ strxmov(buff, "'", user, "'@'", host, "'", NullS);
+ if (!test_access)
+ continue;
+ else
+ {
+ ulong j;
+ int cnt;
+ for (cnt= 0, j= SELECT_ACL; j <= TABLE_ACLS; cnt++, j<<= 1)
+ {
+ if (test_access & j)
+ {
+ for (uint col_index=0 ;
+ col_index < grant_table->hash_columns.records ;
+ col_index++)
+ {
+ GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
+ hash_element(&grant_table->hash_columns,col_index);
+ if ((grant_column->rights & j) && (table_access & j))
+ update_schema_privilege(table, buff, grant_table->db,
+ grant_table->tname,
+ grant_column->column,
+ grant_column->key_length,
+ command_array[cnt],
+ command_lengths[cnt], is_grantable);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ rw_unlock(&LOCK_grant);
+
+ DBUG_RETURN(0);
+#else
+ return (0);
+#endif
+}
+
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/*
+ fill effective privileges for table
+
+ SYNOPSIS
+ fill_effective_table_privileges()
+ thd thread handler
+ grant grants table descriptor
+ db db name
+ table table name
+*/
+
+void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
+ const char *db, const char *table)
+{
+ Security_context *sctx= thd->security_ctx;
+ DBUG_ENTER("fill_effective_table_privileges");
+ DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
+ sctx->priv_host, (sctx->ip ? sctx->ip : "(NULL)"),
+ (sctx->priv_user ? sctx->priv_user : "(NULL)"),
+ db, table));
+ /* --skip-grants */
+ if (!initialized)
+ {
+ DBUG_PRINT("info", ("skip grants"));
+ grant->privilege= ~NO_ACCESS; // everything is allowed
+ DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
+ DBUG_VOID_RETURN;
+ }
+
+ /* global privileges */
+ grant->privilege= sctx->master_access;
+
+ if (!sctx->priv_user)
+ {
+ DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
+ DBUG_VOID_RETURN; // it is slave
+ }
+
+ /* db privileges */
+ grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
+
+ if (!grant_option)
+ {
+ DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
+ DBUG_VOID_RETURN;
+ }
+
+ /* table privileges */
+ rw_rdlock(&LOCK_grant);
+ if (grant->version != grant_version)
+ {
+ grant->grant_table=
+ table_hash_search(sctx->host, sctx->ip, db,
+ sctx->priv_user,
+ table, 0); /* purecov: inspected */
+ grant->version= grant_version; /* purecov: inspected */
+ }
+ if (grant->grant_table != 0)
+ {
+ grant->privilege|= grant->grant_table->privs;
+ }
+ rw_unlock(&LOCK_grant);
+
+ DBUG_PRINT("info", ("privilege 0x%lx", grant->privilege));
+ DBUG_VOID_RETURN;
+}
+
+#else /* NO_EMBEDDED_ACCESS_CHECKS */
+
+/****************************************************************************
+ Dummy wrappers when we don't have any access checks
+****************************************************************************/
+
+bool check_routine_level_acl(THD *thd, const char *db, const char *name,
+ bool is_proc)
+{
+ return FALSE;
+}
+
+#endif