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.cc2547
1 files changed, 2547 insertions, 0 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
new file mode 100644
index 00000000000..d2f8680aee3
--- /dev/null
+++ b/sql/sql_acl.cc
@@ -0,0 +1,2547 @@
+/* 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);
+ 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=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 ? 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);
+ 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);
+ 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=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,
+ 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,strlen(host)) ||
+ ip && hash_search(&acl_check_hosts,(byte*) ip,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=strlen(new_password);
+ new_password[length & 16]=0;
+
+ if (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(buff,strlen(buff));
+ 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,strlen(host));
+ table->field[1]->store(user,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,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,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,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,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,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 =strlen(d)+strlen(u)+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 = strlen(db) + strlen(user) + 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,strlen(host));
+ col_privs->field[1]->store(db,strlen(db));
+ col_privs->field[2]->store(user,strlen(user));
+ col_privs->field[3]->store(tname,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,strlen(db));
+ table->field[2]->store(combo.user.str,combo.user.length);
+ table->field[3]->store(table_name,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,strlen(db));
+ table->field[2]->store(combo.user.str,combo.user.length);
+ table->field[3]->store(table_name,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,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);
+ 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);
+
+ 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);
+ 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);
+ }
+ 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
+ }
+
+ 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 (show_table && table->grant.privilege)
+ continue; // Test from show tables
+
+ 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,
+ 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,
+ 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)
+ {
+ String global(buff,sizeof(buff));
+ global.length(0);
+ global.append("GRANT ",6);
+
+ if (test_all_bits(want_access,(TABLE_ACLS & ~GRANT_ACL)))
+ global.append("ALL PRIVILEGES",14);
+ else
+ {
+ int found=0;
+ uint j,test_access= want_access & ~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