mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 14:33:32 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			2549 lines
		
	
	
		
			71 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2549 lines
		
	
	
		
			71 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* 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
 | 
						|
    }
 | 
						|
 | 
						|
    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,
 | 
						|
				    (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) 
 | 
						|
      {
 | 
						|
	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
 |