home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2001 January
/
Chip_2001-01_cd1.bin
/
tema
/
mysql
/
mysql-3.23.28g-win-source.exe
/
sql
/
sql_acl.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2000-10-06
|
73KB
|
2,549 lines
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*
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/db ; database privileges / user
data in tables is sorted according to how many not-wild-cards there is
in the relevant fields. Empty strings comes last.
*/
#include "mysql_priv.h"
#include "sql_acl.h"
#include "hash_filo.h"
#include <m_ctype.h>
#include <stdarg.h>
/*
ACL_HOST is used if no host is specified
*/
struct acl_host_and_ip
{
char *hostname;
long ip,ip_mask; // Used with masked ip:s
};
class ACL_ACCESS {
public:
ulong sort;
uint access;
};
class ACL_HOST :public ACL_ACCESS
{
public:
acl_host_and_ip host;
char *db;
};
class ACL_USER :public ACL_ACCESS
{
public:
acl_host_and_ip host;
uint hostname_length;
char *user,*password;
ulong salt[2];
};
class ACL_DB :public ACL_ACCESS
{
public:
acl_host_and_ip host;
char *user,*db;
};
class acl_entry :public hash_filo_element
{
public:
uint access;
uint16 length;
char key[1]; // Key will be stored here
};
static byte* acl_entry_get_key(acl_entry *entry,uint *length,
my_bool not_used __attribute__((unused)))
{
*length=(uint) entry->length;
return (byte*) entry->key;
}
#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+17)
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, hash_tables;
static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
static uint grant_version=0;
static uint get_access(TABLE *form,uint fieldnr);
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
static ACL_USER *find_acl_user(const char *host, const char *user);
static bool update_user_table(THD *thd, const char *host, const char *user,
const char *new_password);
static void update_hostname(acl_host_and_ip *host, const char *hostname);
static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
const char *ip);
int acl_init(bool dont_read_acl_tables)
{
THD *thd;
TABLE_LIST tables[3];
TABLE *table;
READ_RECORD read_record_info;
DBUG_ENTER("acl_init");
if (!acl_cache)
acl_cache=new hash_filo(ACL_CACHE_SIZE,0,0,
(hash_get_key) acl_entry_get_key,
(void (*)(void*)) free);
if (dont_read_acl_tables)
DBUG_RETURN(0); /* purecov: tested */
if (!(thd=new THD))
DBUG_RETURN(1); /* purecov: inspected */
acl_cache->clear(1); // Clear locked hostname cache
thd->version=refresh_version;
thd->mysys_var=my_thread_var;
thd->current_tablenr=0;
thd->open_tables=0;
thd->db=my_strdup("mysql",MYF(0));
bzero((char*) &tables,sizeof(tables));
tables[0].name=tables[0].real_name=(char*) "host";
tables[1].name=tables[1].real_name=(char*) "user";
tables[2].name=tables[2].real_name=(char*) "db";
tables[0].next=tables+1;
tables[1].next=tables+2;
tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
tables[0].db=tables[1].db=tables[2].db=thd->db;
if (open_tables(thd,tables))
{
close_thread_tables(thd); /* purecov: inspected */
delete thd; /* purecov: inspected */
DBUG_RETURN(1); /* purecov: inspected */
}
TABLE *ptr[3]; // Lock tables for quick update
ptr[0]= tables[0].table;
ptr[1]= tables[1].table;
ptr[2]= tables[2].table;
MYSQL_LOCK *lock=mysql_lock_tables(thd,ptr,3);
if (!lock)
{
close_thread_tables(thd); /* purecov: inspected */
delete thd; /* purecov: inspected */
DBUG_RETURN(1); /* purecov: inspected */
}
init_sql_alloc(&mem,1024,0);
init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0);
VOID(init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_HOST host;
update_hostname(&host.host,get_field(&mem, table,0));
host.db=get_field(&mem, table,1);
host.access=get_access(table,2);
host.access=fix_rights_for_db(host.access);
host.sort=get_sort(2,host.host.hostname,host.db);
#ifndef TO_BE_REMOVED
if (table->fields == 8)
{ // Without grant
if (host.access & CREATE_ACL)
host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
}
#endif
VOID(push_dynamic(&acl_hosts,(gptr) &host));
}
qsort((gptr) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
sizeof(ACL_HOST),(qsort_cmp) acl_compare);
end_read_record(&read_record_info);
freeze_size(&acl_hosts);
init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0);
VOID(init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
if (table->field[2]->field_length == 8 &&
protocol_version == PROTOCOL_VERSION)
{
sql_print_error(
"Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */
protocol_version=9; /* purecov: tested */
}
allow_all_hosts=0;
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_USER user;
uint length=0;
update_hostname(&user.host,get_field(&mem, table,0));
user.user=get_field(&mem, table,1);
user.password=get_field(&mem, table,2);
if (user.password && (length=(uint) strlen(user.password)) == 8 &&
protocol_version == PROTOCOL_VERSION)
{
sql_print_error(
"Found old style password for user '%s'. Ignoring user. (You may want to restart using --old-protocol)",
user.user ? user.user : ""); /* purecov: tested */
}
else if (length % 8) // This holds true for passwords
{
sql_print_error(
"Found invalid password for user: '%s@%s'; Ignoring user",
user.user ? user.user : "",
user.host.hostname ? user.host.hostname : ""); /* purecov: tested */
continue; /* purecov: tested */
}
get_salt_from_password(user.salt,user.password);
user.access=get_access(table,3);
user.sort=get_sort(2,user.host.hostname,user.user);
user.hostname_length=user.host.hostname ? (uint) strlen(user.host.hostname) : 0;
#ifndef TO_BE_REMOVED
if (table->fields <= 13)
{ // Without grant
if (user.access & CREATE_ACL)
user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
}
#endif
VOID(push_dynamic(&acl_users,(gptr) &user));
if (!user.host.hostname || user.host.hostname[0] == wild_many &&
!user.host.hostname[1])
allow_all_hosts=1; // Anyone can connect
}
qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
sizeof(ACL_USER),(qsort_cmp) acl_compare);
end_read_record(&read_record_info);
freeze_size(&acl_users);
init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0);
VOID(init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100));
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_DB db;
update_hostname(&db.host,get_field(&mem, table,0));
db.db=get_field(&mem, table,1);
db.user=get_field(&mem, table,2);
db.access=get_access(table,3);
db.access=fix_rights_for_db(db.access);
db.sort=get_sort(3,db.host.hostname,db.db,db.user);
#ifndef TO_BE_REMOVED
if (table->fields <= 9)
{ // Without grant
if (db.access & CREATE_ACL)
db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
}
#endif
VOID(push_dynamic(&acl_dbs,(gptr) &db));
}
qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
sizeof(ACL_DB),(qsort_cmp) acl_compare);
end_read_record(&read_record_info);
freeze_size(&acl_dbs);
init_check_host();
mysql_unlock_tables(thd, lock);
thd->version--; // Force close to free memory
close_thread_tables(thd);
delete thd;
initialized=1;
DBUG_RETURN(0);
}
void acl_free(bool end)
{
free_root(&mem,MYF(0));
delete_dynamic(&acl_hosts);
delete_dynamic(&acl_users);
delete_dynamic(&acl_dbs);
delete_dynamic(&acl_wild_hosts);
hash_free(&acl_check_hosts);
if (!end)
acl_cache->clear(1); /* purecov: inspected */
else
{
delete acl_cache;
acl_cache=0;
}
}
/* Reload acl list if possible */
void acl_reload(void)
{
DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs;
MEM_ROOT old_mem;
bool old_initialized;
DBUG_ENTER("acl_reload");
if (current_thd && current_thd->locked_tables)
{ // Can't have locked tables here
current_thd->lock=current_thd->locked_tables;
current_thd->locked_tables=0;
close_thread_tables(current_thd);
}
if ((old_initialized=initialized))
VOID(pthread_mutex_lock(&acl_cache->lock));
old_acl_hosts=acl_hosts;
old_acl_users=acl_users;
old_acl_dbs=acl_dbs;
old_mem=mem;
delete_dynamic(&acl_wild_hosts);
hash_free(&acl_check_hosts);
if (acl_init(0))
{ // Error. Revert to old list
acl_free(); /* purecov: inspected */
acl_hosts=old_acl_hosts;
acl_users=old_acl_users;
acl_dbs=old_acl_dbs;
mem=old_mem;
init_check_host();
}
else
{
free_root(&old_mem,MYF(0));
delete_dynamic(&old_acl_hosts);
delete_dynamic(&old_acl_users);
delete_dynamic(&old_acl_dbs);
}
if (old_initialized)
VOID(pthread_mutex_unlock(&acl_cache->lock));
DBUG_VOID_RETURN;
}
/* Get all access bits from table after fieldnr */
static uint get_access(TABLE *form,uint fieldnr)
{
uint access_bits=0,bit;
char buff[2];
String res(buff,sizeof(buff));
Field **pos;
for (pos=form->field+fieldnr,bit=1 ; *pos ; pos++ , bit<<=1)
{
(*pos)->val_str(&res,&res);
if (toupper(res[0]) == 'Y')
access_bits|=bit;
}
return access_bits;
}
/*
return a number with if sorted put string in this order:
no wildcards
wildcards
empty string
*/
static ulong get_sort(uint count,...)
{
va_list args;
va_start(args,count);
ulong sort=0;
while (count--)
{
char *str=va_arg(args,char*);
uint chars=0,wild=0;
if (str)
{
for (; *str ; str++)
{
if (*str == wild_many || *str == wild_one || *str == wild_prefix)
wild++;
else
chars++;
}
}
sort= (sort << 8) + (wild ? 1 : chars ? 2 : 0);
}
va_end(args);
return sort;
}
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
{
if (a->sort > b->sort)
return -1;
if (a->sort < b->sort)
return 1;
return 0;
}
/* Get master privilges for user (priviliges for all tables) */
uint acl_getroot(const char *host, const char *ip, const char *user,
const char *password,const char *message,char **priv_user,
bool old_ver)
{
uint user_access=NO_ACCESS;
*priv_user=(char*) user;
if (!initialized)
return (uint) ~NO_ACCESS; // If no data allow anything /* purecov: tested */
VOID(pthread_mutex_lock(&acl_cache->lock));
/*
Get possible access from user_list. This is or'ed to others not
fully specified
*/
for (uint i=0 ; i < acl_users.elements ; i++)
{
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
if (!acl_user->user || !strcmp(user,acl_user->user))
{
if (compare_hostname(&acl_user->host,host,ip))
{
if (!acl_user->password && !*password ||
(acl_user->password && *password &&
!check_scramble(password,message,acl_user->salt,
(my_bool) old_ver)))
{
user_access=acl_user->access;
if (!acl_user->user)
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
break;
}
#ifndef ALLOW_DOWNGRADE_OF_USERS
break; // Wrong password breaks loop /* purecov: inspected */
#endif
}
}
}
VOID(pthread_mutex_unlock(&acl_cache->lock));
return user_access;
}
/*
** Functions to add and change user and database privileges when one
** changes things with GRANT
*/
static byte* check_get_key(ACL_USER *buff,uint *length,
my_bool not_used __attribute__((unused)))
{
*length=buff->hostname_length;
return (byte*) buff->host.hostname;
}
static void acl_update_user(const char *user, const char *host,
const char *password, uint privileges)
{
for (uint i=0 ; i < acl_users.elements ; i++)
{
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))
{
if (!acl_user->host.hostname && !host[0] ||
acl_user->host.hostname && !strcmp(host,acl_user->host.hostname))
{
acl_user->access=privileges;
if (password)
{
if (!password[0])
acl_user->password=0;
else
{
acl_user->password=(char*) ""; // Just point at something
get_salt_from_password(acl_user->salt,password);
}
}
break;
}
}
}
}
static void acl_insert_user(const char *user, const char *host,
const char *password,
uint privileges)
{
ACL_USER acl_user;
acl_user.user=strdup_root(&mem,user);
update_hostname(&acl_user.host,strdup_root(&mem,host));
acl_user.password=0;
acl_user.access=privileges;
acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
acl_user.hostname_length=(uint) strlen(acl_user.host.hostname);
if (password)
{
acl_user.password=(char*) ""; // Just point at something
get_salt_from_password(acl_user.salt,password);
}
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])
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();
}
static void acl_update_db(const char *user, const char *host, const char *db,
uint privileges)
{
for (uint i=0 ; i < acl_dbs.elements ; i++)
{
ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
if (!acl_db->user && !user[0] ||
acl_db->user &&
!strcmp(user,acl_db->user))
{
if (!acl_db->host.hostname && !host[0] ||
acl_db->host.hostname && !strcmp(host,acl_db->host.hostname))
{
if (!acl_db->db && !db[0] ||
acl_db->db && !strcmp(db,acl_db->db))
{
if (privileges)
acl_db->access=privileges;
else
delete_dynamic_element(&acl_dbs,i);
}
}
}
}
}
static void acl_insert_db(const char *user, const char *host, const char *db,
uint privileges)
{
ACL_DB acl_db;
/* The acl_cache mutex is locked by mysql_grant */
acl_db.user=strdup_root(&mem,user);
update_hostname(&acl_db.host,strdup_root(&mem,host));
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);
VOID(push_dynamic(&acl_dbs,(gptr) &acl_db));
qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
sizeof(ACL_DB),(qsort_cmp) acl_compare);
}
/*****************************************************************************
** Get privilege for a host, user and db combination
*****************************************************************************/
uint acl_get(const char *host, const char *ip, const char *bin_ip,
const char *user, const char *db)
{
uint host_access,db_access,i,key_length;
db_access=0; host_access= ~0;
char key[ACL_KEY_LENGTH],*end;
acl_entry *entry;
VOID(pthread_mutex_lock(&acl_cache->lock));
memcpy_fixed(&key,bin_ip,sizeof(struct in_addr));
end=strmov(strmov(key+sizeof(struct in_addr),user)+1,db);
key_length=(uint) (end-key);
if ((entry=(acl_entry*) acl_cache->search(key,key_length)))
{
db_access=entry->access;
VOID(pthread_mutex_unlock(&acl_cache->lock));
return db_access;
}
/*
Check if there are some access rights for database and user
*/
for (i=0 ; i < acl_dbs.elements ; i++)
{
ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
if (!acl_db->user || !strcmp(user,acl_db->user))
{
if (compare_hostname(&acl_db->host,host,ip))
{
if (!acl_db->db || !wild_compare(db,acl_db->db))
{
db_access=acl_db->access;
if (acl_db->host.hostname)
goto exit; // Fully specified. Take it
break; /* purecov: tested */
}
}
}
}
if (!db_access)
goto exit; // Can't be better
/*
No host specified for user. Get hostdata from host table
*/
host_access=0; // Host must be found
for (i=0 ; i < acl_hosts.elements ; i++)
{
ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
if (compare_hostname(&acl_host->host,host,ip))
{
if (!acl_host->db || !wild_compare(db,acl_host->db))
{
host_access=acl_host->access; // Fully specified. Take it
break;
}
}
}
exit:
/* Save entry in cache for quick retrieval */
if ((entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
{
entry->access=(db_access & host_access);
entry->length=key_length;
memcpy((gptr) entry->key,key,key_length);
acl_cache->add(entry);
}
VOID(pthread_mutex_unlock(&acl_cache->lock));
return (db_access & host_access);
}
int wild_case_compare(const char *str,const char *wildstr)
{
reg3 int flag;
DBUG_ENTER("wild_case_compare");
while (*wildstr)
{
while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
{
if (*wildstr == wild_prefix && wildstr[1])
wildstr++;
if (toupper(*wildstr++) != toupper(*str++)) DBUG_RETURN(1);
}
if (! *wildstr ) DBUG_RETURN (*str != 0);
if (*wildstr++ == wild_one)
{
if (! *str++) DBUG_RETURN (1); /* One char; skipp */
}
else
{ /* Found '*' */
if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */
flag=(*wildstr != wild_many && *wildstr != wild_one);
do
{
if (flag)
{
char cmp;
if ((cmp= *wildstr) == wild_prefix && wildstr[1])
cmp=wildstr[1];
cmp=toupper(cmp);
while (*str && toupper(*str) != cmp)
str++;
if (!*str) DBUG_RETURN (1);
}
if (wild_case_compare(str,wildstr) == 0) DBUG_RETURN (0);
} while (*str++);
DBUG_RETURN(1);
}
}
DBUG_RETURN (*str != '\0');
}
/*****************************************************************************
** check if there are any possible matching entries for this host
** All host names without wild cards are stored in a hash table,
** entries with wildcards are stored in a dynamic array
*****************************************************************************/
static void init_check_host(void)
{
DBUG_ENTER("init_check_host");
VOID(init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
acl_users.elements,1));
VOID(hash_init(&acl_check_hosts,acl_users.elements,0,0,
(hash_get_key) check_get_key,0,HASH_CASE_INSENSITIVE));
if (!allow_all_hosts)
{
for (uint i=0 ; i < acl_users.elements ; i++)
{
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
if (strchr(acl_user->host.hostname,wild_many) ||
strchr(acl_user->host.hostname,wild_one) ||
acl_user->host.ip_mask)
{ // Has wildcard
uint j;
for (j=0 ; j < acl_wild_hosts.elements ; j++)
{ // Check if host already exists
acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j,
acl_host_and_ip *);
if (!my_strcasecmp(acl_user->host.hostname,acl->hostname))
break; // already stored
}
if (j == acl_wild_hosts.elements) // If new
(void) push_dynamic(&acl_wild_hosts,(char*) &acl_user->host);
}
else if (!hash_search(&acl_check_hosts,(byte*) &acl_user->host,
(uint) strlen(acl_user->host.hostname)))
{
if (hash_insert(&acl_check_hosts,(byte*) acl_user))
{ // End of memory
allow_all_hosts=1; // Should never happen
DBUG_VOID_RETURN;
}
}
}
}
freeze_size(&acl_wild_hosts);
freeze_size(&acl_check_hosts.array);
DBUG_VOID_RETURN;
}
/* Return true if there is no users that can match the given host */
bool acl_check_host(const char *host, const char *ip)
{
if (allow_all_hosts)
return 0;
VOID(pthread_mutex_lock(&acl_cache->lock));
if (host && hash_search(&acl_check_hosts,(byte*) host,(uint) strlen(host)) ||
ip && hash_search(&acl_check_hosts,(byte*) ip,(uint) strlen(ip)))
{
VOID(pthread_mutex_unlock(&acl_cache->lock));
return 0; // Found host
}
for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
{
acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
if (compare_hostname(acl, host, ip))
{
VOID(pthread_mutex_unlock(&acl_cache->lock));
return 0; // Host ok
}
}
VOID(pthread_mutex_unlock(&acl_cache->lock));
return 1; // Host is not allowed
}
/*****************************************************************************
** Change password for the user if it's not an anonymous user
** Note: This should write the error directly to the client!
*****************************************************************************/
bool change_password(THD *thd, const char *host, const char *user,
char *new_password)
{
uint length=0;
if (!user[0])
{
send_error(&thd->net, ER_PASSWORD_ANONYMOUS_USER);
return 1;
}
if (!initialized)
{
send_error(&thd->net, ER_PASSWORD_NOT_ALLOWED); /* purecov: inspected */
return 1; /* purecov: inspected */
}
if (!host)
host=thd->ip; /* purecov: tested */
/* password should always be 0 or 16 chars; simple hack to avoid cracking */
length=(uint) strlen(new_password);
new_password[length & 16]=0;
if (!thd || strcmp(thd->user,user) ||
my_strcasecmp(host,thd->host ? thd->host : thd->ip))
{
if (check_access(thd, UPDATE_ACL, "mysql",0,1))
return 1;
}
VOID(pthread_mutex_lock(&acl_cache->lock));
ACL_USER *acl_user;
if (!(acl_user= find_acl_user(host,user)) || !acl_user->user)
{
send_error(&thd->net, ER_PASSWORD_NO_MATCH);
VOID(pthread_mutex_unlock(&acl_cache->lock));
return 1;
}
if (update_user_table(thd,
acl_user->host.hostname ? acl_user->host.hostname : "",
acl_user->user, new_password))
{
VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
send_error(&thd->net,0); /* purecov: deadcode */
return 1; /* purecov: deadcode */
}
get_salt_from_password(acl_user->salt,new_password);
if (!new_password[0])
acl_user->password=0;
else
acl_user->password=(char*) ""; // Point at something
acl_cache->clear(1); // Clear locked hostname cache
VOID(pthread_mutex_unlock(&acl_cache->lock));
char buff[460];
Query_log_event qinfo(thd, buff);
qinfo.q_len =
my_sprintf(buff,
(buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"",
acl_user->user,
acl_user->host.hostname ? acl_user->host.hostname : "",
new_password));
mysql_update_log.write(thd,buff,qinfo.q_len);
mysql_bin_log.write(&qinfo);
return 0;
}
/*
Find first entry that matches the current user
*/
static ACL_USER *
find_acl_user(const char *host, const char *user)
{
for (uint i=0 ; i < acl_users.elements ; i++)
{
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))
{
if (compare_hostname(&acl_user->host,host,host))
return acl_user;
}
}
return 0;
}
/*****************************************************************************
Handle comparing of hostname
A hostname may be of type:
hostname (May include wildcards); monty.pp.sci.fi
ip (May include wildcards); 192.168.0.0
ip/netmask 192.168.0.0/255.255.255.0
A net mask of 0.0.0.0 is not allowed.
*****************************************************************************/
static const char *calc_ip(const char *ip, long *val, char end)
{
long ip_val,tmp;
if (!(ip=str2int(ip,10,0,255,&ip_val)) || *ip != '.')
return 0;
ip_val<<=24;
if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
return 0;
ip_val+=tmp<<16;
if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
return 0;
ip_val+=tmp<<8;
if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != end)
return 0;
*val=ip_val+tmp;
return ip;
}
static void update_hostname(acl_host_and_ip *host, const char *hostname)
{
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'))))
{
host->ip=host->ip_mask=0; // Not a masked ip
}
}
static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
const char *ip)
{
long tmp;
if (host->ip_mask && ip && calc_ip(ip,&tmp,'\0'))
{
return (tmp & host->ip_mask) == host->ip;
}
return (!host->hostname ||
(hostname && !wild_case_compare(hostname,host->hostname)) ||
(ip && !wild_compare(ip,host->hostname)));
}
/****************************************************************************
** Code to update grants in the user and database privilege tables
****************************************************************************/
static bool update_user_table(THD *thd, const char *host, const char *user,
const char *new_password)
{
TABLE_LIST tables;
TABLE *table;
bool error=1;
DBUG_ENTER("update_user_table");
DBUG_PRINT("enter",("user: %s host: %s",user,host));
bzero((char*) &tables,sizeof(tables));
tables.name=tables.real_name=(char*) "user";
tables.db=(char*) "mysql";
if (!(table=open_ltable(thd,&tables,TL_WRITE)))
DBUG_RETURN(1); /* purecov: deadcode */
table->field[0]->store(host,(uint) strlen(host));
table->field[1]->store(user,(uint) strlen(user));
if (table->file->index_read_idx(table->record[0],0,
(byte*) table->field[0]->ptr,0,
HA_READ_KEY_EXACT))
{
my_error(ER_PASSWORD_NO_MATCH,MYF(0)); /* purecov: deadcode */
DBUG_RETURN(1); /* purecov: deadcode */
}
store_record(table,1);
table->field[2]->store(new_password,(uint) strlen(new_password));
if ((error=table->file->update_row(table->record[1],table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
goto end; /* purecov: deadcode */
}
error=0; // Record updated
end:
close_thread_tables(thd);
DBUG_RETURN(error);
}
/****************************************************************************
** Handle GRANT commands
****************************************************************************/
static int replace_user_table(TABLE *table, const LEX_USER &combo,
uint rights, char what)
{
int error = -1;
uint i,j;
bool ima=0;
char *password,empty_string[1];
DBUG_ENTER("replace_user_table");
if (combo.password.str && combo.password.str[0])
password=combo.password.str;
else
{
password=empty_string;
empty_string[0]=0;
}
table->field[0]->store(combo.host.str,combo.host.length);
table->field[1]->store(combo.user.str,combo.user.length);
table->file->index_init(0);
if (table->file->index_read(table->record[0],
(byte*) table->field[0]->ptr,0,
HA_READ_KEY_EXACT))
{
if (what == 'N')
{
my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),
MYF(0),combo.user.str,combo.host.str);
error= -1;
goto end;
}
ima = 0; // no row; ima on Serbian means 'there is something'
restore_record(table,2); // cp empty row from record[2]
table->field[0]->store(combo.host.str,combo.host.length);
table->field[1]->store(combo.user.str,combo.user.length);
table->field[2]->store(password,(uint) strlen(password));
}
else
{
ima = 1;
store_record(table,1); // Save copy for update
if (combo.password.str) // If password given
table->field[2]->store(password,(uint) strlen(password));
}
for (i = 3, j = SELECT_ACL; // starting from reload
i < table->fields;
i++, j <<= 1)
{
if (j & rights) // set requested privileges
table->field[i]->store(&what,1);
}
rights=get_access(table,3);
if (ima) // there is a row, therefore go to update, instead of insert
{
/*
We should NEVER delete from the user table, as a uses can still
use mysqld even if he doesn't have any privileges in the user table!
*/
if (cmp_record(table,1) &&
(error=table->file->update_row(table->record[1],table->record[0])))
{ // This should never happen
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
error= -1; /* purecov: deadcode */
goto end; /* purecov: deadcode */
}
}
else if ((error=table->file->write_row(table->record[0]))) // insert
{ // This should never happen
if (error && error != HA_ERR_FOUND_DUPP_KEY &&
error != HA_ERR_FOUND_DUPP_UNIQUE) /* purecov: inspected */
{
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
error= -1; /* purecov: deadcode */
goto end; /* purecov: deadcode */
}
}
error=0; // Privileges granted / revoked
end:
if (!error)
{
acl_cache->clear(1); // Clear privilege cache
if (!combo.password.str)
password=0; // No password given on command
if (ima)
acl_update_user(combo.user.str,combo.host.str,password,rights);
else
acl_insert_user(combo.user.str,combo.host.str,password,rights);
}
table->file->index_end();
DBUG_RETURN(error);
}
/*
** change grants in the mysql.db table
*/
static int replace_db_table(TABLE *table, const char *db,
const LEX_USER &combo,
uint rights, char what)
{
uint i,j,store_rights;
bool ima=0;
int error;
DBUG_ENTER("replace_db_table");
// is there such a user in user table in memory ????
if (!initialized || !find_acl_user(combo.host.str,combo.user.str))
{
my_error(ER_PASSWORD_NO_MATCH,MYF(0));
DBUG_RETURN(-1);
}
table->field[0]->store(combo.host.str,combo.host.length);
table->field[1]->store(db,(uint) strlen(db));
table->field[2]->store(combo.user.str,combo.user.length);
table->file->index_init(0);
if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr,0,
HA_READ_KEY_EXACT))
{
if (what == 'N')
{ // no row, no revoke
my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),MYF(0),
combo.user.str,combo.host.str);
goto abort;
}
ima = 0; // no row
restore_record(table,2); // cp empty row from record[2]
table->field[0]->store(combo.host.str,combo.host.length);
table->field[1]->store(db,(uint) strlen(db));
table->field[2]->store(combo.user.str,combo.user.length);
}
else
{
ima = 1;
store_record(table,1);
}
store_rights=get_rights_for_db(rights);
for (i = 3, j = 1; i < table->fields; i++, j <<= 1)
{
if (j & store_rights) // do it if priv is chosen
table->field [i]->store(&what,1); // set requested privileges
}
rights=get_access(table,3);
rights=fix_rights_for_db(rights);
if (ima) // there is a row, therefore go update, else insert
{
if (rights)
{
if ((error=table->file->update_row(table->record[1],table->record[0])))
goto table_error; /* purecov: deadcode */
}
else /* must have been a revoke of all privileges */
{
if ((error = table->file->delete_row(table->record[1])))
goto table_error; /* purecov: deadcode */
}
}
else if ((error=table->file->write_row(table->record[0])))
{
if (error && error != HA_ERR_FOUND_DUPP_KEY) /* purecov: inspected */
goto table_error; /* purecov: deadcode */
}
acl_cache->clear(1); // Clear privilege cache
if (ima)
acl_update_db(combo.user.str,combo.host.str,db,rights);
else
acl_insert_db(combo.user.str,combo.host.str,db,rights);
table->file->index_end();
DBUG_RETURN(0);
/* This could only happen if the grant tables got corrupted */
table_error:
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
table->file->index_end();
abort:
DBUG_RETURN(-1);
}
class GRANT_COLUMN :public Sql_alloc
{
public:
char *column;
uint rights, key_length;
GRANT_COLUMN(String &c, uint y) :rights (y)
{
column= memdup_root(&memex,c.ptr(),key_length=c.length());
}
};
static byte* get_key_column(GRANT_COLUMN *buff,uint *length,
my_bool not_used __attribute__((unused)))
{
*length=buff->key_length;
return (byte*) buff->column;
}
class GRANT_TABLE :public Sql_alloc
{
public:
char *host,*db,*user,*tname, *hash_key;
uint privs, cols, key_length;
HASH hash_columns;
GRANT_TABLE (const char *h, const char *d,const char *u, const char *t,
uint p,uint c)
: privs(p), cols(c)
{
host = strdup_root(&memex,h);
db = strdup_root(&memex,d);
user = strdup_root(&memex,u);
tname= strdup_root(&memex,t);
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);
(void) hash_init(&hash_columns,0,0,0, (hash_get_key) get_key_column,0,
HASH_CASE_INSENSITIVE);
}
GRANT_TABLE (TABLE *form, TABLE *col_privs)
{
byte key[MAX_KEY_LENGTH];
host = get_field(&memex,form,0);
db = get_field(&memex,form,1);
user = get_field(&memex,form,2); if (!user) user=(char*) "";
tname = get_field(&memex,form,3);
if (!host || !db || !tname)
{
/* Wrong table row; Ignore it */
privs = cols = 0; /* purecov: inspected */
return; /* purecov: inspected */
}
key_length = (uint) strlen(db) + (uint) strlen(user) + (uint) strlen (tname) + 3;
hash_key = (char*) alloc_root(&memex,key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
privs = (uint) form->field[6]->val_int();
cols = (uint) form->field[7]->val_int();
privs = fix_rights_for_table(privs);
cols = fix_rights_for_column(cols);
(void) hash_init(&hash_columns,0,0,0, (hash_get_key) get_key_column,0,
HASH_CASE_INSENSITIVE);
if (cols)
{
int key_len;
col_privs->field[0]->store(host,(uint) strlen(host));
col_privs->field[1]->store(db,(uint) strlen(db));
col_privs->field[2]->store(user,(uint) strlen(user));
col_privs->field[3]->store(tname,(uint) strlen(tname));
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);
col_privs->field[4]->store("",0);
col_privs->file->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))
{
cols = 0; /* purecov: deadcode */
return;
}
do
{
String *res,column_name;
GRANT_COLUMN *mem_check;
/* As column name is a string, we don't have to supply a buffer */
res=col_privs->field[4]->val_str(&column_name,&column_name);
uint priv= (uint) col_privs->field[6]->val_int();
if (!(mem_check = new GRANT_COLUMN(*res,
fix_rights_for_column(priv))))
{
// Don't use this entry
privs = cols = 0; /* purecov: deadcode */
return; /* purecov: deadcode */
}
hash_insert(&hash_columns, (byte *) mem_check);
} while (!col_privs->file->index_next(col_privs->record[0]) &&
!key_cmp(col_privs,key,0,key_len));
}
}
bool ok() { return privs != 0 || cols != 0; }
};
static byte* get_grant_table(GRANT_TABLE *buff,uint *length,
my_bool not_used __attribute__((unused)))
{
*length=buff->key_length;
return (byte*) buff->hash_key;
}
void free_grant_table(GRANT_TABLE *grant_table)
{
hash_free(&grant_table->hash_columns);
}
/* Search after a matching grant. Prefer exact grants before not exact ones */
static GRANT_TABLE *table_hash_search(const char *host,const char* ip,
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;
len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1;
for (grant_table=(GRANT_TABLE*) hash_search(&hash_tables,(byte*) helping,
len) ;
grant_table ;
grant_table= (GRANT_TABLE*) hash_next(&hash_tables,(byte*) helping,len))
{
if (exact)
{
if ((host && !strcmp(host,grant_table->host)) ||
(ip && !strcmp(ip,grant_table->host)))
return grant_table;
}
else
{
if ((host && !wild_case_compare(host,grant_table->host)) ||
(ip && !wild_case_compare(ip,grant_table->host)))
found=grant_table; // Host ok
}
}
return found;
}
static inline GRANT_COLUMN *
column_hash_search(GRANT_TABLE *t, const char *cname,
uint length)
{
return (GRANT_COLUMN*) hash_search(&t->hash_columns, (byte*) cname,length);
}
static int replace_column_table(GRANT_TABLE *g_t,
TABLE *table, const LEX_USER &combo,
List <LEX_COLUMN> &columns,
const char *db, const char *table_name,
uint rights, bool revoke_grant)
{
int error=0,result=0;
uint key_length;
byte key[MAX_KEY_LENGTH];
DBUG_ENTER("replace_column_table");
table->field[0]->store(combo.host.str,combo.host.length);
table->field[1]->store(db,(uint) strlen(db));
table->field[2]->store(combo.user.str,combo.user.length);
table->field[3]->store(table_name,(uint) strlen(table_name));
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);
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;
table->file->index_init(0);
while ((xx=iter++))
{
uint privileges = xx->rights;
bool ima=0;
key_restore(table,key,0,key_length);
table->field[4]->store(xx->column.ptr(),xx->column.length());
if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr,
0, HA_READ_KEY_EXACT))
{
if (revoke_grant)
{
my_printf_error(ER_NONEXISTING_TABLE_GRANT,
ER(ER_NONEXISTING_TABLE_GRANT),MYF(0),
combo.user.str, combo.host.str,table_name); /* purecov: inspected */
result= -1; /* purecov: inspected */
continue; /* purecov: inspected */
}
ima = 0;
restore_record(table,2); // Get empty record
key_restore(table,key,0,key_length);
table->field[4]->store(xx->column.ptr(),xx->column.length());
}
else
{
uint tmp= (uint) table->field[6]->val_int();
tmp=fix_rights_for_column(tmp);
if (revoke_grant)
privileges = tmp & ~(privileges | rights);
else
privileges |= tmp;
ima = 1;
store_record(table,1); // copy original row
}
table->field[6]->store((longlong) get_rights_for_column(privileges));
if (ima) // there is a row, therefore go update, else insert
{
if (privileges)
error=table->file->update_row(table->record[1],table->record[0]);
else
error=table->file->delete_row(table->record[1]);
if (error)
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
result= -1; /* purecov: inspected */
goto end; /* purecov: inspected */
}
GRANT_COLUMN *grant_column = column_hash_search(g_t,
xx->column.ptr(),
xx->column.length());
if (grant_column) // Should always be true
grant_column->rights = privileges; // Update hash
}
else // new grant
{
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);
hash_insert(&g_t->hash_columns,(byte*) grant_column);
}
}
table->file->index_end();
/*
If revoke of privileges on the table level, remove all such privileges
for all columns
*/
if (revoke_grant)
{
table->file->index_init(0);
if (table->file->index_read(table->record[0], (byte*) table->field[0]->ptr,
key_length, HA_READ_KEY_EXACT))
goto end;
// Scan trough all rows with the same host,db,user and table
do
{
uint privileges = (uint) table->field[6]->val_int();
privileges=fix_rights_for_column(privileges);
store_record(table,1);
if (privileges & rights) // is in this record the priv to be revoked ??
{
GRANT_COLUMN *grant_column = NULL;
char colum_name_buf[HOSTNAME_LENGTH+1];
String column_name(colum_name_buf,sizeof(colum_name_buf));
privileges&= ~rights;
table->field[6]->store((longlong)
get_rights_for_column(privileges));
table->field[4]->val_str(&column_name,&column_name);
grant_column = column_hash_search(g_t,
column_name.ptr(),
column_name.length());
if (privileges)
{
int tmp_error;
if ((tmp_error=table->file->update_row(table->record[1],
table->record[0])))
{ /* purecov: deadcode */
table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
result= -1; /* purecov: deadcode */
goto end; /* purecov: deadcode */
}
if (grant_column)
grant_column->rights = privileges; // Update hash
}
else
{
int tmp_error;
if ((tmp_error = table->file->delete_row(table->record[1])))
{ /* purecov: deadcode */
table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
result= -1; /* purecov: deadcode */
goto end; /* purecov: deadcode */
}
if (grant_column)
hash_delete(&g_t->hash_columns,(byte*) grant_column);
}
}
} while (!table->file->index_next(table->record[0]) &&
!key_cmp(table,key,0,key_length));
}
end:
table->file->index_end();
DBUG_RETURN(result);
}
static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
TABLE *table, const LEX_USER &combo,
const char *db, const char *table_name,
uint rights, uint kolone, bool revoke_grant)
{
char grantor[HOSTNAME_LENGTH+1+USERNAME_LENGTH];
int ima = 1;
int error=0;
uint store_table_rights,store_col_rights;
DBUG_ENTER("replace_table_table");
strxmov(grantor,thd->user,"@",thd->host ? thd->host : thd->ip ? thd->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))
{
my_error(ER_PASSWORD_NO_MATCH,MYF(0)); /* purecov: deadcode */
DBUG_RETURN(-1); /* purecov: deadcode */
}
restore_record(table,2); // Get empty record
table->field[0]->store(combo.host.str,combo.host.length);
table->field[1]->store(db,(uint) strlen(db));
table->field[2]->store(combo.user.str,combo.user.length);
table->field[3]->store(table_name,(uint) strlen(table_name));
store_record(table,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_printf_error(ER_NONEXISTING_TABLE_GRANT,
ER(ER_NONEXISTING_TABLE_GRANT),MYF(0),
combo.user.str,combo.host.str,
table_name); /* purecov: deadcode */
DBUG_RETURN(-1); /* purecov: deadcode */
}
ima = 0; // no row
restore_record(table,1); // Get saved record
}
store_table_rights=get_rights_for_table(rights);
store_col_rights=get_rights_for_column(kolone);
if (ima)
{
uint j,k;
store_record(table,1);
j = (uint) table->field[6]->val_int();
k = (uint) table->field[7]->val_int();
if (revoke_grant)
{
// column rights are already fixed in mysql_table_grant !
store_table_rights=j & ~store_table_rights;
}
else
{
store_table_rights|=j;
store_col_rights|=k;
}
}
table->field[4]->store(grantor,(uint) strlen(grantor));
table->field[6]->store((longlong) store_table_rights);
table->field[7]->store((longlong) store_col_rights);
rights=fix_rights_for_table(store_table_rights);
kolone=fix_rights_for_column(store_col_rights);
if (ima) // there is a row, therefore go update, else insert
{
if (store_table_rights || store_col_rights)
{
if ((error=table->file->update_row(table->record[1],table->record[0])))
goto table_error; /* purecov: deadcode */
}
else if ((error = table->file->delete_row(table->record[1])))
goto table_error; /* purecov: deadcode */
}
else
{
error=table->file->write_row(table->record[0]);
if (error && error != HA_ERR_FOUND_DUPP_KEY)
goto table_error; /* purecov: deadcode */
}
if (rights | kolone)
{
grant_table->privs = rights;
grant_table->cols = kolone;
}
else
{
hash_delete(&hash_tables,(byte*) grant_table);
}
DBUG_RETURN(0);
/* This should never happen */
table_error:
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
DBUG_RETURN(-1); /* purecov: deadcode */
}
int mysql_table_grant (THD *thd, TABLE_LIST *table_list,
List <LEX_USER> &user_list,
List <LEX_COLUMN> &columns, uint rights,
bool revoke_grant)
{
uint column_priv = 0;
List_iterator <LEX_USER> str_list (user_list);
LEX_USER *Str;
TABLE_LIST tables[3];
DBUG_ENTER("mysql_table_grant");
if (!initialized)
{
send_error(&(thd->net), ER_UNKNOWN_COM_ERROR); /* purecov: inspected */
return 1; /* purecov: inspected */
}
if (rights & ~TABLE_ACLS)
{
my_error(ER_ILLEGAL_GRANT_FOR_TABLE,MYF(0));
DBUG_RETURN(-1);
}
if (columns.elements && !revoke_grant)
{
TABLE *table;
class LEX_COLUMN *check;
List_iterator <LEX_COLUMN> iter(columns);
if (!(table=open_ltable(thd,table_list,TL_READ)))
DBUG_RETURN(-1);
while ((check = iter++))
{
if (!find_field_in_table(thd,table,check->column.ptr(),
check->column.length(),0,0))
{
my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0),
check->column.c_ptr(),table_list->name);
DBUG_RETURN(-1);
}
column_priv |= check->rights | (rights & COL_ACLS);
}
close_thread_tables(thd);
}
else if (!(rights & CREATE_ACL) && !revoke_grant)
{
char buf[FN_REFLEN];
sprintf(buf,"%s/%s/%s.frm",mysql_data_home,table_list->db,
table_list->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->name);
DBUG_RETURN(-1);
}
}
/* open the mysql.tables_priv and mysql.columns_priv tables */
bzero((char*) &tables,sizeof(tables));
tables[0].name=tables[0].real_name= (char*) "user";
tables[1].name=tables[1].real_name= (char*) "tables_priv";
tables[2].name=tables[2].real_name= (char*) "columns_priv";
tables[0].next=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[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE;
tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
if (open_and_lock_tables(thd,tables))
{ // Should never happen
close_thread_tables(thd); /* purecov: deadcode */
DBUG_RETURN(-1); /* purecov: deadcode */
}
int result=0;
pthread_mutex_lock(&LOCK_grant);
MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
my_pthread_setspecific_ptr(THR_MALLOC,&memex);
while ((Str = str_list++))
{
GRANT_TABLE *grant_table;
if (!Str->host.str)
{
Str->host.str=(char*) "%";
Str->host.length=1;
}
if (Str->host.length > HOSTNAME_LENGTH ||
Str->user.length > USERNAME_LENGTH)
{
my_error(ER_GRANT_WRONG_HOST_OR_USER,MYF(0));
result= -1;
continue;
}
/* Create user if needed */
if ((replace_user_table(tables[0].table,
*Str,
0,
revoke_grant ? 'N' : 'Y')))
{
result= -1; // Remember error
continue; // Add next user
}
/* Find/create cached table grant */
grant_table= table_hash_search(Str->host.str,NullS,table_list->db,
Str->user.str,
table_list->name,1);
if (!grant_table)
{
if (revoke_grant)
{
my_printf_error(ER_NONEXISTING_TABLE_GRANT,
ER(ER_NONEXISTING_TABLE_GRANT),MYF(0),
Str->user.str, Str->host.str,table_list->name);
result= -1;
continue;
}
grant_table = new GRANT_TABLE (Str->host.str,table_list->db,
Str->user.str,
table_list->name,
rights,
column_priv);
if (!grant_table) // end of memory
{
result= -1; /* purecov: deadcode */
continue; /* purecov: deadcode */
}
hash_insert(&hash_tables,(byte*) grant_table);
}
/* If revoke_grant, calculate the new column privilege for tables_priv */
if (revoke_grant)
{
class LEX_COLUMN *check;
List_iterator <LEX_COLUMN> iter(columns);
GRANT_COLUMN *grant_column;
/* Fix old grants */
while ((check = iter++))
{
grant_column = column_hash_search(grant_table,
check->column.ptr(),
check->column.length());
if (grant_column)
grant_column->rights&= ~(check->rights | rights);
}
/* scan trough all columns to get new column grant */
column_priv=0;
for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
{
grant_column= (GRANT_COLUMN*) hash_element(&grant_table->hash_columns,
idx);
grant_column->rights&= ~rights; // Fix other columns
column_priv|= grant_column->rights;
}
}
else
{
column_priv|= grant_table->cols;
}
/* update table and columns */
if (replace_table_table(thd,grant_table,tables[1].table,*Str,
table_list->db,
table_list->name,
rights, column_priv, revoke_grant))
{ // Crashend table ??
result= -1; /* purecov: deadcode */
}
else if (tables[2].table)
{
if ((replace_column_table(grant_table,tables[2].table, *Str,
columns,
table_list->db,
table_list->name,
rights, revoke_grant)))
{
result= -1;
}
}
}
grant_option=TRUE;
my_pthread_setspecific_ptr(THR_MALLOC,old_root);
pthread_mutex_unlock(&LOCK_grant);
if (!result)
send_ok(&thd->net);
/* Tables are automaticly closed */
DBUG_RETURN(result);
}
int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights,
bool revoke_grant)
{
List_iterator <LEX_USER> str_list (list);
LEX_USER *Str;
char what;
TABLE_LIST tables[2];
DBUG_ENTER("mysql_grant");
if (!initialized)
{
send_error(&(thd->net), ER_UNKNOWN_COM_ERROR); /* purecov: tested */
return 1; /* purecov: tested */
}
what = (revoke_grant) ? 'N' : 'Y';
/* open the mysql.user and mysql.db tables */
tables[0].name=tables[0].real_name=(char*) "user";
tables[1].name=tables[1].real_name=(char*) "db";
tables[0].next=tables+1;
tables[1].next=0;
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;
if (open_and_lock_tables(thd,tables))
{ // This should never happen
close_thread_tables(thd); /* purecov: deadcode */
DBUG_RETURN(-1); /* purecov: deadcode */
}
// go through users in user_list
pthread_mutex_lock(&LOCK_grant);
VOID(pthread_mutex_lock(&acl_cache->lock));
grant_version++;
int result=0;
while ((Str = str_list++))
{
if (!Str->host.str)
{
Str->host.str=(char*) "%";
Str->host.length=1;
}
if (Str->host.length > HOSTNAME_LENGTH ||
Str->user.length > USERNAME_LENGTH)
{
my_error(ER_GRANT_WRONG_HOST_OR_USER,MYF(0));
result= -1;
continue;
}
if ((replace_user_table(tables[0].table,
*Str,
(!db ? rights : 0), what)))
result= -1;
if (db)
if (( replace_db_table(tables[1].table, db, *Str, rights,
what)))
result= -1;
}
VOID(pthread_mutex_unlock(&acl_cache->lock));
pthread_mutex_unlock(&LOCK_grant);
close_thread_tables(thd);
if (!result)
send_ok(&thd->net);
DBUG_RETURN(result);
}
/* Free grant array if possible */
void grant_free(void)
{
DBUG_ENTER("grant_free");
grant_option = FALSE;
hash_free(&hash_tables);
free_root(&memex,MYF(0));
DBUG_VOID_RETURN;
}
/* Init grant array if possible */
int grant_init (void)
{
THD *thd;
TABLE_LIST tables[2];
int error = 0;
TABLE *t_table, *c_table;
DBUG_ENTER("grant_init");
grant_option = FALSE;
(void) hash_init(&hash_tables,0,0,0, (hash_get_key) get_grant_table,
(hash_free_key) free_grant_table,0);
init_sql_alloc(&memex,1024,0);
if (!initialized)
DBUG_RETURN(0); /* purecov: tested */
if (!(thd=new THD))
DBUG_RETURN(1); /* purecov: deadcode */
thd->version=refresh_version;
thd->mysys_var=my_thread_var;
thd->current_tablenr=0;
thd->open_tables=0;
thd->db=my_strdup("mysql",MYF(0));
bzero((char*) &tables,sizeof(tables));
tables[0].name=tables[0].real_name= (char*) "tables_priv";
tables[1].name=tables[1].real_name= (char*) "columns_priv";
tables[0].next=tables+1;
tables[0].lock_type=tables[1].lock_type=TL_READ;
tables[0].db=tables[1].db=thd->db;
if (open_tables(thd,tables))
{ // No grant tables
close_thread_tables(thd); /* purecov: deadcode */
delete thd; /* purecov: deadcode */
DBUG_RETURN(1); /* purecov: deadcode */
}
TABLE *ptr[2]; // Lock tables for quick update
ptr[0]= tables[0].table;
ptr[1]= tables[1].table;
MYSQL_LOCK *lock=mysql_lock_tables(thd,ptr,2);
if (!lock)
{
close_thread_tables(thd); /* purecov: deadcode */
delete thd; /* purecov: deadcode */
DBUG_RETURN(1); /* purecov: deadcode */
}
t_table = tables[0].table; c_table = tables[1].table;
t_table->file->index_init(0);
if (t_table->file->index_first(t_table->record[0]))
{
t_table->file->index_end();
mysql_unlock_tables(thd, lock);
thd->version--; // Force close to free memory
close_thread_tables(thd);
delete thd;
DBUG_RETURN(0); // Empty table is ok!
}
grant_option = TRUE;
t_table->file->index_end();
MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
my_pthread_setspecific_ptr(THR_MALLOC,&memex);
while (!error)
{
GRANT_TABLE *mem_check;
if (!(mem_check=new GRANT_TABLE(t_table,c_table)) ||
mem_check->ok() && hash_insert(&hash_tables,(byte*) mem_check))
{
/* This could only happen if we are out memory */
my_pthread_setspecific_ptr(THR_MALLOC,old_root); /* purecov: deadcode */
grant_option = FALSE; /* purecov: deadcode */
mysql_unlock_tables(thd, lock); /* purecov: deadcode */
close_thread_tables(thd); /* purecov: deadcode */
delete thd; /* purecov: deadcode */
DBUG_RETURN(1); /* purecov: deadcode */
}
error = t_table->file->index_next(t_table->record[0]);
}
my_pthread_setspecific_ptr(THR_MALLOC,old_root);
mysql_unlock_tables(thd, lock);
thd->version--; // Force close to free memory
close_thread_tables(thd);
delete thd;
DBUG_RETURN(0);
}
/* Reload grant array if possible */
void grant_reload(void)
{
HASH old_hash_tables;bool old_grant_option;
MEM_ROOT old_mem;
DBUG_ENTER("grant_reload");
// Locked tables are checked by acl_init and doesn't have to be checked here
pthread_mutex_lock(&LOCK_grant);
grant_version++;
old_hash_tables=hash_tables;
old_grant_option = grant_option;
old_mem = memex;
if (grant_init())
{ // Error. Revert to old hash
grant_free(); /* purecov: deadcode */
hash_tables=old_hash_tables; /* purecov: deadcode */
grant_option = old_grant_option; /* purecov: deadcode */
memex = old_mem; /* purecov: deadcode */
}
else
{
hash_free(&old_hash_tables);
free_root(&old_mem,MYF(0));
}
pthread_mutex_unlock(&LOCK_grant);
DBUG_VOID_RETURN;
}
/****************************************************************************
** Check grants
** All errors are written directly to the client if command name is given !
****************************************************************************/
bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables,
uint show_table)
{
TABLE_LIST *table;
char *user = thd->priv_user;
want_access &= ~thd->master_access;
if (!want_access)
return 0; // ok
pthread_mutex_lock(&LOCK_grant);
for (table=tables; table ;table=table->next)
{
if (!(~table->grant.privilege & want_access))
{
table->grant.want_privilege=0;
continue; // Already checked
}
const char *db = table->db ? table->db : thd->db;
GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip,db,user,
table->real_name,0);
if (!grant_table)
{
want_access &= ~table->grant.privilege;
goto err; // No grants
}
if (show_table)
continue; // We have some priv on this
table->grant.grant_table=grant_table; // Remember for column test
table->grant.version=grant_version;
table->grant.privilege|= grant_table->privs;
table->grant.want_privilege= ((want_access & COL_ACLS)
& ~table->grant.privilege);
if (!(~table->grant.privilege & want_access))
continue;
if (want_access & ~(grant_table->cols | table->grant.privilege))
{
want_access &= ~(grant_table->cols | table->grant.privilege);
goto err; // impossible
}
}
pthread_mutex_unlock(&LOCK_grant);
return 0;
err:
pthread_mutex_unlock(&LOCK_grant);
if (show_table != 1) // Not a silent skip of table
{
const char *command="";
if (want_access & SELECT_ACL)
command ="select";
else if (want_access & INSERT_ACL)
command = "insert";
else if (want_access & UPDATE_ACL)
command = "update";
else if (want_access & DELETE_ACL)
command = "delete";
else if (want_access & DROP_ACL)
command = "drop";
else if (want_access & CREATE_ACL)
command = "create";
else if (want_access & ALTER_ACL)
command = "alter";
else if (want_access & INDEX_ACL)
command = "index";
else if (want_access & GRANT_ACL)
command = "grant";
net_printf(&thd->net,ER_TABLEACCESS_DENIED_ERROR,
command,
thd->priv_user,
thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"),
table ? table->real_name : "unknown");
}
return 1;
}
bool check_grant_column (THD *thd,TABLE *table, const char *name,
uint length, uint show_tables)
{
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
uint want_access=table->grant.want_privilege;
if (!want_access)
return 0; // Already checked
pthread_mutex_lock(&LOCK_grant);
// reload table if someone has modified any grants
if (table->grant.version != grant_version)
{
table->grant.grant_table=
table_hash_search(thd->host,thd->ip,thd->db,
thd->priv_user,
table->real_name,0); /* purecov: inspected */
table->grant.version=grant_version; /* purecov: inspected */
}
if (!(grant_table=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))
{
pthread_mutex_unlock(&LOCK_grant);
return 0;
}
#ifdef NOT_USED
if (show_tables && (grant_column || table->grant.privilege & COL_ACLS))
{
pthread_mutex_unlock(&LOCK_grant); /* purecov: deadcode */
return 0; /* purecov: deadcode */
}
#endif
/* We must use my_printf_error() here! */
err:
pthread_mutex_unlock(&LOCK_grant);
if (!show_tables)
{
const char *command="";
if (want_access & SELECT_ACL)
command ="select";
else if (want_access & INSERT_ACL)
command = "insert";
else if (want_access & UPDATE_ACL)
command = "update";
my_printf_error(ER_COLUMNACCESS_DENIED_ERROR,
ER(ER_COLUMNACCESS_DENIED_ERROR),
MYF(0),
command,
thd->priv_user,
thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"),
name,
table ? table->real_name : "unknown");
}
return 1;
}
bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table)
{
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
Field *field=0,**ptr;
want_access &= ~table->grant.privilege;
if (!want_access)
return 0; // Already checked
pthread_mutex_lock(&LOCK_grant);
// reload table if someone has modified any grants
if (table->grant.version != grant_version)
{
table->grant.grant_table=
table_hash_search(thd->host,thd->ip,thd->db,
thd->priv_user,
table->real_name,0); /* purecov: inspected */
table->grant.version=grant_version; /* purecov: inspected */
}
// The following should always be true
if (!(grant_table=table->grant.grant_table))
goto err; /* purecov: inspected */
for (ptr=table->field; (field= *ptr) ; ptr++)
{
grant_column=column_hash_search(grant_table, field->field_name,
(uint) strlen(field->field_name));
if (!grant_column || (~grant_column->rights & want_access))
goto err;
}
pthread_mutex_unlock(&LOCK_grant);
return 0;
/* We must use my_printf_error() here! */
err:
pthread_mutex_unlock(&LOCK_grant);
const char *command="";
if (want_access & SELECT_ACL)
command ="select";
else if (want_access & INSERT_ACL)
command = "insert";
my_printf_error(ER_COLUMNACCESS_DENIED_ERROR,
ER(ER_COLUMNACCESS_DENIED_ERROR),
MYF(0),
command,
thd->priv_user,
thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"),
field ? field->field_name : "unknown",
table->real_name);
return 1;
}
/****************************************************************************
** Check if a user has the right to access a database
** Access is accepted if he has a grant for any table in the database
** Return 1 if access is denied
****************************************************************************/
bool check_grant_db(THD *thd,const char *db)
{
char helping [NAME_LEN+USERNAME_LENGTH+2];
uint len;
bool error=1;
len = (uint) (strmov(strmov(helping,thd->priv_user)+1,db)-helping)+ 1;
pthread_mutex_lock(&LOCK_grant);
for (uint idx=0 ; idx < hash_tables.records ; idx++)
{
GRANT_TABLE *grant_table = (GRANT_TABLE*) hash_element(&hash_tables,idx);
if (len < grant_table->key_length &&
!memcmp(grant_table->hash_key,helping,len) &&
(thd->host && !wild_case_compare(thd->host,grant_table->host) ||
(thd->ip && !wild_case_compare(thd->ip,grant_table->host))))
{
error=0; // Found match
break;
}
}
pthread_mutex_unlock(&LOCK_grant);
return error;
}
/*****************************************************************************
** Functions to retrieve the grant for a table/column (for SHOW functions)
*****************************************************************************/
uint get_table_grant(THD *thd, TABLE_LIST *table)
{
char *user = thd->priv_user;
const char *db = table->db ? table->db : thd->db;
GRANT_TABLE *grant_table;
pthread_mutex_lock(&LOCK_grant);
grant_table = table_hash_search(thd->host,thd->ip,db,user,
table->real_name,0);
table->grant.grant_table=grant_table; // Remember for column test
table->grant.version=grant_version;
if (grant_table)
table->grant.privilege|= grant_table->privs;
pthread_mutex_unlock(&LOCK_grant);
return table->grant.privilege;
}
uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field)
{
GRANT_TABLE *grant_table;
GRANT_COLUMN *grant_column;
uint priv;
pthread_mutex_lock(&LOCK_grant);
// reload table if someone has modified any grants
if (table->grant.version != grant_version)
{
table->grant.grant_table=
table_hash_search(thd->host,thd->ip,thd->db,
thd->priv_user,
table->real_name,0); /* purecov: inspected */
table->grant.version=grant_version; /* purecov: inspected */
}
if (!(grant_table=table->grant.grant_table))
priv=table->grant.privilege;
else
{
grant_column=column_hash_search(grant_table, field->field_name,
(uint) strlen(field->field_name));
if (!grant_column)
priv=table->grant.privilege;
else
priv=table->grant.privilege | grant_column->rights;
}
pthread_mutex_unlock(&LOCK_grant);
return priv;
}
/*****************************************************************************
** SHOW GRANTS : send to client grant-like strings depicting user@host
** privileges
*****************************************************************************/
static const char *command_array[]=
{"SELECT", "INSERT","UPDATE","DELETE","CREATE", "DROP","RELOAD","SHUTDOWN",
"PROCESS","FILE","GRANT","REFERENCES","INDEX","ALTER"};
static int command_lengths[]={6,6,6,6,6,4,6,8,7,4,5,9,5,5};
int mysql_show_grants(THD *thd,LEX_USER *lex_user)
{
uint counter, want_access,index;
int error = 0;
ACL_USER *acl_user; ACL_DB *acl_db;
char buff[1024];
DBUG_ENTER("mysql_grant");
LINT_INIT(acl_user);
if (!initialized)
{
send_error(&(thd->net), ER_UNKNOWN_COM_ERROR);
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);
}
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) &&
!strcmp(lex_user->host.str,host))
break;
}
if (counter == acl_users.elements)
{
my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),
MYF(0),lex_user->user.str,lex_user->host.str);
DBUG_RETURN(-1);
}
Item_string *field=new Item_string("",0);
List<Item> field_list;
field->name=buff;
field->max_length=1024;
strxmov(buff,"Grants for ",lex_user->user.str,"@",
lex_user->host.str,NullS);
field_list.push_back(field);
if (send_fields(thd,field_list,1))
DBUG_RETURN(-1);
VOID(pthread_mutex_lock(&acl_cache->lock));
/* Add first global access grants */
if (acl_user->access || acl_user->password)
{
want_access=acl_user->access;
String global(buff,sizeof(buff));
global.length(0);
global.append("GRANT ",6);
if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
global.append("ALL PRIVILEGES",14);
else if (!(want_access & ~GRANT_ACL))
global.append("USAGE",5);
else
{
bool found=0;
uint j,test_access= want_access & ~GRANT_ACL;
for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
{
if (test_access & j)
{
if (found)
global.append(", ",2);
found=1;
global.append(command_array[counter],command_lengths[counter]);
}
}
}
global.append (" ON *.* TO '",12);
global.append(lex_user->user.str,lex_user->user.length);
global.append ("'@'",3);
global.append(lex_user->host.str,lex_user->host.length);
global.append ('\'');
if (acl_user->password)
{
char passd_buff[HASH_PASSWORD_LENGTH+1];
make_password_from_salt(passd_buff,acl_user->salt);
global.append(" IDENTIFIED BY PASSWORD '",25);
global.append(passd_buff);
global.append('\'');
}
if (want_access & GRANT_ACL)
global.append(" WITH GRANT OPTION",18);
thd->packet.length(0);
net_store_data(&thd->packet,global.ptr(),global.length());
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),
thd->packet.length()))
{
error=-1; goto end;
}
}
/* Add database access */
for (counter=0 ; counter < acl_dbs.elements ; counter++)
{
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) &&
!strcmp(lex_user->host.str,host))
{
want_access=acl_db->access;
if (want_access)
{
String db(buff,sizeof(buff));
db.length(0);
db.append("GRANT ",6);
if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
db.append("ALL PRIVILEGES",14);
else
{
int found=0, cnt;
uint j,test_access= want_access & ~GRANT_ACL;
for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
{
if (test_access & j)
{
if (found)
db.append(", ",2);
found = 1;
db.append(command_array[cnt],command_lengths[cnt]);
}
}
}
db.append (" ON ",4);
db.append(acl_db->db);
db.append (".* TO '",7);
db.append(lex_user->user.str,lex_user->user.length);
db.append ("'@'",3);
db.append(lex_user->host.str, lex_user->host.length);
db.append ('\'');
if (want_access & GRANT_ACL)
db.append(" WITH GRANT OPTION",18);
thd->packet.length(0);
net_store_data(&thd->packet,db.ptr(),db.length());
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),
thd->packet.length()))
{
error=-1;
goto end;
}
}
}
}
/* Add column access */
for (index=0 ; index < hash_tables.records ; index++)
{
const char *user,*host;
GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&hash_tables,index);
if (!(user=grant_table->user))
user="";
if (!(host=grant_table->host))
host="";
if (!strcmp(lex_user->user.str,user) &&
!strcmp(lex_user->host.str,host))
{
want_access=grant_table->privs;
if ((want_access | grant_table->cols) != 0)
{
String global(buff,sizeof(buff));
global.length(0);
global.append("GRANT ",6);
if (test_all_bits(grant_table->privs,(TABLE_ACLS & ~GRANT_ACL)))
global.append("ALL PRIVILEGES",14);
else
{
int found=0;
uint j,test_access= (want_access | grant_table->cols) & ~GRANT_ACL;
for (counter=0, j = SELECT_ACL;j <= TABLE_ACLS; counter++,j <<= 1)
{
if (test_access & j)
{
if (found)
global.append(", ",2);
found = 1;
global.append(command_array[counter],command_lengths[counter]);
if (grant_table->cols)
{
uint found_col=0;
for (uint col_index=0 ;
col_index < grant_table->hash_columns.records ;
col_index++)
{
GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
hash_element(&grant_table->hash_columns,col_index);
if (grant_column->rights & j)
{
if (!found_col)
{
global.append(" (",2);
found_col=1;
}
else
global.append(", ",2);
global.append(grant_column->column,
grant_column->key_length);
}
}
if (found_col)
global.append(')');
}
}
}
}
global.append(" ON ",4);
global.append(grant_table->db);
global.append(".",1);
global.append(grant_table->tname);
global.append(" TO '",5);
global.append(lex_user->user.str,lex_user->user.length);
global.append("'@'",3);
global.append(lex_user->host.str,lex_user->host.length);
global.append('\'');
if (want_access & GRANT_ACL)
global.append(" WITH GRANT OPTION",18);
thd->packet.length(0);
net_store_data(&thd->packet,global.ptr(),global.length());
if (my_net_write(&thd->net,(char*) thd->packet.ptr(),
thd->packet.length()))
{
error=-1;
goto end;
}
}
}
}
end:
VOID(pthread_mutex_unlock(&acl_cache->lock));
send_eof(&thd->net);
DBUG_RETURN(error);
}
/*****************************************************************************
** Instantiate used templates
*****************************************************************************/
#ifdef __GNUC__
template class List_iterator<LEX_COLUMN>;
template class List_iterator<LEX_USER>;
template class List<LEX_COLUMN>;
template class List<LEX_USER>;
#endif