mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
SCRUM: Secure auth
Implement mysql_change_user Get rid of double user search at authentication Some cleanups client/mysqladmin.c: Fix long line include/mysql_com.h: Fix long lines libmysql/libmysql.c: mysql_change_user() for new auth + some fixes sql/password.c: Add author info so who is guilty in errors would be known :) sql/sql_acl.cc: Move class definitions to .h sql/sql_acl.h: Add class definitions sql/sql_parse.cc: Get rid of double user search. Implement mysql_change_user
This commit is contained in:
@ -768,8 +768,9 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (argv[1][0])
|
if (argv[1][0])
|
||||||
make_scrambled_password(crypted_pw,argv[1],(find_type(argv[0],&command_typelib,2)
|
make_scrambled_password(crypted_pw,argv[1],(find_type(argv[0],
|
||||||
==ADMIN_OLD_PASSWORD),&rand_st);
|
&command_typelib,2)==ADMIN_OLD_PASSWORD),
|
||||||
|
&rand_st);
|
||||||
else
|
else
|
||||||
crypted_pw[0]=0; /* No password */
|
crypted_pw[0]=0; /* No password */
|
||||||
sprintf(buff,"set password='%s',sql_log_off=0",crypted_pw);
|
sprintf(buff,"set password='%s',sql_log_off=0",crypted_pw);
|
||||||
|
@ -280,18 +280,22 @@ extern unsigned long net_buffer_length;
|
|||||||
void randominit(struct rand_struct *,unsigned long seed1,
|
void randominit(struct rand_struct *,unsigned long seed1,
|
||||||
unsigned long seed2);
|
unsigned long seed2);
|
||||||
double rnd(struct rand_struct *);
|
double rnd(struct rand_struct *);
|
||||||
void make_scrambled_password(char *to,const char *password,my_bool force_old_scramble,struct rand_struct *rand_st);
|
void make_scrambled_password(char *to,const char *password,
|
||||||
|
my_bool force_old_scramble,struct rand_struct *rand_st);
|
||||||
int get_password_length(my_bool force_old_scramble);
|
int get_password_length(my_bool force_old_scramble);
|
||||||
char get_password_version(const char* password);
|
char get_password_version(const char* password);
|
||||||
void create_random_string(int length,struct rand_struct *rand_st,char* target);
|
void create_random_string(int length,struct rand_struct *rand_st,char* target);
|
||||||
my_bool validate_password(const char* password, const char* message, ulong* salt);
|
my_bool validate_password(const char* password, const char* message,
|
||||||
|
ulong* salt);
|
||||||
void password_hash_stage1(char *to, const char *password);
|
void password_hash_stage1(char *to, const char *password);
|
||||||
void password_hash_stage2(char *to,const char *salt);
|
void password_hash_stage2(char *to,const char *salt);
|
||||||
void password_crypt(const char* from,char* to, const char* password,int length);
|
void password_crypt(const char* from,char* to, const char* password,int length);
|
||||||
void get_hash_and_password(ulong* salt, uint8 pversion,char* hash, unsigned char* bin_password);
|
void get_hash_and_password(ulong* salt, unsigned char pversion,char* hash,
|
||||||
|
unsigned char* bin_password);
|
||||||
void get_salt_from_password(unsigned long *res,const char *password);
|
void get_salt_from_password(unsigned long *res,const char *password);
|
||||||
void create_key_from_old_password(const char* password,char* key);
|
void create_key_from_old_password(const char* password,char* key);
|
||||||
void make_password_from_salt(char *to, unsigned long *hash_res, uint8 password_version);
|
void make_password_from_salt(char *to, unsigned long *hash_res,
|
||||||
|
unsigned char password_version);
|
||||||
char *scramble(char *to,const char *message,const char *password,
|
char *scramble(char *to,const char *message,const char *password,
|
||||||
my_bool old_ver);
|
my_bool old_ver);
|
||||||
my_bool check_scramble(const char *, const char *message,
|
my_bool check_scramble(const char *, const char *message,
|
||||||
|
@ -2220,7 +2220,7 @@ Try also with PIPE or TCP/IP
|
|||||||
#include "_cust_libmysql.h";
|
#include "_cust_libmysql.h";
|
||||||
#endif
|
#endif
|
||||||
DBUG_PRINT("info",("user: %s",buff+5));
|
DBUG_PRINT("info",("user: %s",buff+5));
|
||||||
/*
|
/*
|
||||||
We always start with old type handshake the only difference is message sent
|
We always start with old type handshake the only difference is message sent
|
||||||
If server handles secure connection type we'll not send the real scramble
|
If server handles secure connection type we'll not send the real scramble
|
||||||
*/
|
*/
|
||||||
@ -2228,10 +2228,10 @@ Try also with PIPE or TCP/IP
|
|||||||
{
|
{
|
||||||
if (passwd[0])
|
if (passwd[0])
|
||||||
{
|
{
|
||||||
/* Use something for not empty password not to match it against empty one */
|
/* Use something for not empty password not to match against empty one */
|
||||||
end=scramble(strend(buff+5)+1, mysql->scramble_buff,"~MySQL#!",
|
end=scramble(strend(buff+5)+1, mysql->scramble_buff,"\1~MySQL#!\2",
|
||||||
(my_bool) (mysql->protocol_version == 9));
|
(my_bool) (mysql->protocol_version == 9));
|
||||||
}
|
}
|
||||||
else /* For empty password*/
|
else /* For empty password*/
|
||||||
{
|
{
|
||||||
end=strend(buff+5)+1;
|
end=strend(buff+5)+1;
|
||||||
@ -2242,11 +2242,11 @@ Try also with PIPE or TCP/IP
|
|||||||
else
|
else
|
||||||
end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd,
|
end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd,
|
||||||
(my_bool) (mysql->protocol_version == 9));
|
(my_bool) (mysql->protocol_version == 9));
|
||||||
|
|
||||||
/* Add database if needed */
|
/* Add database if needed */
|
||||||
if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
|
if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
|
||||||
{
|
{
|
||||||
end=strmake(end+1,db,NAME_LEN);
|
end=strmake(end+1,db,NAME_LEN);
|
||||||
mysql->db=my_strdup(db,MYF(MY_WME));
|
mysql->db=my_strdup(db,MYF(MY_WME));
|
||||||
db=0;
|
db=0;
|
||||||
}
|
}
|
||||||
@ -2409,21 +2409,96 @@ static my_bool mysql_reconnect(MYSQL *mysql)
|
|||||||
my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
|
my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
|
||||||
const char *passwd, const char *db)
|
const char *passwd, const char *db)
|
||||||
{
|
{
|
||||||
char buff[512],*pos=buff;
|
char buff[512],*end=buff;
|
||||||
|
ulong pkt_length;
|
||||||
|
char password_hash[20]; /* Used for tmp storage of stage1 hash */
|
||||||
|
NET *net= &mysql->net;
|
||||||
|
|
||||||
DBUG_ENTER("mysql_change_user");
|
DBUG_ENTER("mysql_change_user");
|
||||||
|
|
||||||
if (!user)
|
if (!user)
|
||||||
user="";
|
user="";
|
||||||
if (!passwd)
|
if (!passwd)
|
||||||
passwd="";
|
passwd="";
|
||||||
|
|
||||||
|
/* Store user into the buffer */
|
||||||
|
end=strmov(end,user)+1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
We always start with old type handshake the only difference is message sent
|
||||||
|
If server handles secure connection type we'll not send the real scramble
|
||||||
|
*/
|
||||||
|
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||||
|
{
|
||||||
|
if (passwd[0])
|
||||||
|
{
|
||||||
|
/* Use something for not empty password not to match it against empty one */
|
||||||
|
end=scramble(end, mysql->scramble_buff,"\1~MySQL#!\2",
|
||||||
|
(my_bool) (mysql->protocol_version == 9));
|
||||||
|
}
|
||||||
|
else /* For empty password*/
|
||||||
|
*end=0; /* Store zero length scramble */
|
||||||
|
}
|
||||||
|
/* Real scramble is sent only for servers. This is to be blocked by option */
|
||||||
|
else
|
||||||
|
end=scramble(end, mysql->scramble_buff, passwd,
|
||||||
|
(my_bool) (mysql->protocol_version == 9));
|
||||||
|
|
||||||
|
/* Add database if needed */
|
||||||
|
end=strmov(end+1,db ? db : "");
|
||||||
|
|
||||||
|
/* Write authentication package */
|
||||||
|
|
||||||
|
simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1);
|
||||||
|
|
||||||
|
/* We shall only query sever if it expect us to do so */
|
||||||
|
if ( (pkt_length=net_safe_read(mysql)) == packet_error)
|
||||||
|
goto error;
|
||||||
|
|
||||||
pos=strmov(pos,user)+1;
|
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||||
pos=scramble(pos, mysql->scramble_buff, passwd,
|
{
|
||||||
(my_bool) (mysql->protocol_version == 9));
|
/* This should basically always happen with new server unless empty password */
|
||||||
pos=strmov(pos+1,db ? db : "");
|
if (pkt_length==24) /* We have new hash back */
|
||||||
if (simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (pos-buff),0))
|
{
|
||||||
DBUG_RETURN(1);
|
/* Old passwords will have zero at the first byte of hash */
|
||||||
|
if (net->read_pos[0])
|
||||||
|
{
|
||||||
|
/* Build full password hash as it is required to decode scramble */
|
||||||
|
password_hash_stage1(buff, passwd);
|
||||||
|
/* Store copy as we'll need it later */
|
||||||
|
memcpy(password_hash,buff,20);
|
||||||
|
/* Finally hash complete password using hash we got from server */
|
||||||
|
password_hash_stage2(password_hash,net->read_pos);
|
||||||
|
/* Decypt and store scramble 4 = hash for stage2 */
|
||||||
|
password_crypt(net->read_pos+4,mysql->scramble_buff,password_hash,20);
|
||||||
|
mysql->scramble_buff[20]=0;
|
||||||
|
/* Encode scramble with password. Recycle buffer */
|
||||||
|
password_crypt(mysql->scramble_buff,buff,buff,20);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Create password to decode scramble */
|
||||||
|
create_key_from_old_password(passwd,password_hash);
|
||||||
|
/* Decypt and store scramble 4 = hash for stage2 */
|
||||||
|
password_crypt(net->read_pos+4,mysql->scramble_buff,password_hash,20);
|
||||||
|
mysql->scramble_buff[20]=0;
|
||||||
|
/* Finally scramble decoded scramble with password */
|
||||||
|
scramble(buff, mysql->scramble_buff, passwd,
|
||||||
|
(my_bool) (mysql->protocol_version == 9));
|
||||||
|
}
|
||||||
|
/* Write second package of authentication */
|
||||||
|
if (my_net_write(net,buff,20) || net_flush(net))
|
||||||
|
{
|
||||||
|
net->last_errno= CR_SERVER_LOST;
|
||||||
|
strmov(net->last_error,ER(net->last_errno));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
/* Read What server thinks about out new auth message report */
|
||||||
|
if (net_safe_read(mysql) == packet_error)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
|
my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
|
||||||
my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR));
|
my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR));
|
||||||
my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR));
|
my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR));
|
||||||
@ -2432,6 +2507,10 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
|
|||||||
mysql->passwd=my_strdup(passwd,MYF(MY_WME));
|
mysql->passwd=my_strdup(passwd,MYF(MY_WME));
|
||||||
mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0;
|
mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0;
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
|
|
||||||
|
error:
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,6 +48,8 @@
|
|||||||
This authentication needs 2 packet round trips instead of one but it is much
|
This authentication needs 2 packet round trips instead of one but it is much
|
||||||
stronger. Now if one will steal mysql database content he will not be able
|
stronger. Now if one will steal mysql database content he will not be able
|
||||||
to break into MySQL.
|
to break into MySQL.
|
||||||
|
|
||||||
|
New Password handling functions by Peter Zaitsev
|
||||||
|
|
||||||
|
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
353
sql/sql_acl.cc
353
sql/sql_acl.cc
@ -33,52 +33,6 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
struct acl_host_and_ip
|
|
||||||
{
|
|
||||||
char *hostname;
|
|
||||||
long ip,ip_mask; // Used with masked ip:s
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class ACL_ACCESS {
|
|
||||||
public:
|
|
||||||
ulong sort;
|
|
||||||
ulong access;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* ACL_HOST is used if no host is specified */
|
|
||||||
|
|
||||||
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;
|
|
||||||
USER_RESOURCES user_resource;
|
|
||||||
char *user,*password;
|
|
||||||
ulong salt[6]; // New password has longer length
|
|
||||||
uint8 pversion; // password version
|
|
||||||
enum SSL_type ssl_type;
|
|
||||||
const char *ssl_cipher, *x509_issuer, *x509_subject;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class ACL_DB :public ACL_ACCESS
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
acl_host_and_ip host;
|
|
||||||
char *user,*db;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class acl_entry :public hash_filo_element
|
class acl_entry :public hash_filo_element
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -105,6 +59,7 @@ static HASH acl_check_hosts, hash_tables;
|
|||||||
static DYNAMIC_ARRAY acl_wild_hosts;
|
static DYNAMIC_ARRAY acl_wild_hosts;
|
||||||
static hash_filo *acl_cache;
|
static hash_filo *acl_cache;
|
||||||
static uint grant_version=0;
|
static uint grant_version=0;
|
||||||
|
static uint priv_version=0; /* Version of priv tables. incremented by acl_init */
|
||||||
static ulong get_access(TABLE *form,uint fieldnr);
|
static ulong get_access(TABLE *form,uint fieldnr);
|
||||||
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
|
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
|
||||||
static ulong get_sort(uint count,...);
|
static ulong get_sort(uint count,...);
|
||||||
@ -148,7 +103,9 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
|
|||||||
{
|
{
|
||||||
DBUG_RETURN(0); /* purecov: tested */
|
DBUG_RETURN(0); /* purecov: tested */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
priv_version++; /* Priveleges updated */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
To be able to run this from boot, we allocate a temporary THD
|
To be able to run this from boot, we allocate a temporary THD
|
||||||
*/
|
*/
|
||||||
@ -515,15 +472,23 @@ void prepare_scramble(THD* thd, ACL_USER *acl_user,char* prepared_scramble)
|
|||||||
/*
|
/*
|
||||||
Get master privilges for user (priviliges for all tables).
|
Get master privilges for user (priviliges for all tables).
|
||||||
Required before connecting to MySQL
|
Required before connecting to MySQL
|
||||||
|
|
||||||
|
as we have 2 stage handshake now we cache user not to lookup
|
||||||
|
it second time. At the second stage we do not lookup user in case
|
||||||
|
we already know it;
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
|
ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
|
||||||
const char *password,const char *message,char **priv_user,
|
const char *password,const char *message,char **priv_user,
|
||||||
bool old_ver, USER_RESOURCES *mqh,char* prepared_scramble,int stage)
|
bool old_ver, USER_RESOURCES *mqh,char* prepared_scramble,
|
||||||
|
int stage,uint *cur_priv_version,ACL_USER** hint_user)
|
||||||
{
|
{
|
||||||
ulong user_access=NO_ACCESS;
|
ulong user_access=NO_ACCESS;
|
||||||
*priv_user=(char*) user;
|
*priv_user=(char*) user;
|
||||||
bool password_correct=0;
|
bool password_correct=0;
|
||||||
|
ACL_USER *acl_user=NULL;
|
||||||
|
|
||||||
DBUG_ENTER("acl_getroot");
|
DBUG_ENTER("acl_getroot");
|
||||||
|
|
||||||
bzero(mqh,sizeof(USER_RESOURCES));
|
bzero(mqh,sizeof(USER_RESOURCES));
|
||||||
@ -533,159 +498,175 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
|
|||||||
DBUG_RETURN((ulong) ~NO_ACCESS); /* purecov: tested */
|
DBUG_RETURN((ulong) ~NO_ACCESS); /* purecov: tested */
|
||||||
}
|
}
|
||||||
VOID(pthread_mutex_lock(&acl_cache->lock));
|
VOID(pthread_mutex_lock(&acl_cache->lock));
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get possible access from user_list. This is or'ed to others not
|
Get possible access from user_list. This is or'ed to others not
|
||||||
fully specified
|
fully specified
|
||||||
|
|
||||||
|
If we have cached user use it, in other case look it up.
|
||||||
*/
|
*/
|
||||||
for (uint i=0 ; i < acl_users.elements ; i++)
|
|
||||||
{
|
if (stage && (*cur_priv_version==priv_version))
|
||||||
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
|
acl_user=*hint_user;
|
||||||
if (!acl_user->user || !strcmp(user,acl_user->user))
|
else
|
||||||
|
for (uint i=0 ; i < acl_users.elements ; i++)
|
||||||
{
|
{
|
||||||
if (compare_hostname(&acl_user->host,host,ip))
|
ACL_USER *acl_user_search=dynamic_element(&acl_users,i,ACL_USER*);
|
||||||
|
if (!acl_user_search->user || !strcmp(user,acl_user_search->user))
|
||||||
{
|
{
|
||||||
if (!acl_user->password && !*password ||
|
if (compare_hostname(&acl_user_search->host,host,ip))
|
||||||
(acl_user->password && *password))
|
{
|
||||||
{
|
/* Found mathing user */
|
||||||
/* Quick check and accept for empty passwords*/
|
acl_user=acl_user_search;
|
||||||
if (!acl_user->password && !*password)
|
/* Store it as a cache */
|
||||||
password_correct=1;
|
*hint_user=acl_user;
|
||||||
else
|
*cur_priv_version=priv_version;
|
||||||
{
|
break;
|
||||||
/* New version password is checked differently */
|
}
|
||||||
if (acl_user->pversion)
|
}
|
||||||
{
|
}
|
||||||
if (stage) /* We check password only on the second stage */
|
|
||||||
{
|
|
||||||
if (!validate_password(password,message,acl_user->salt))
|
/* Now we have acl_user found and may start our checks */
|
||||||
password_correct=1;
|
|
||||||
}
|
if (acl_user)
|
||||||
else /* First stage - just prepare scramble */
|
{
|
||||||
prepare_scramble(thd,acl_user,prepared_scramble);
|
/* Password should present for both or absend for both */
|
||||||
}
|
if (!acl_user->password && !*password ||
|
||||||
/* Old way to check password */
|
(acl_user->password && *password))
|
||||||
else
|
{
|
||||||
{
|
/* Quick check and accept for empty passwords*/
|
||||||
/* Checking the scramble at any stage. First - old clients */
|
if (!acl_user->password && !*password)
|
||||||
if (!check_scramble(password,message,acl_user->salt,
|
password_correct=1;
|
||||||
(my_bool) old_ver))
|
else /* Normal password presents */
|
||||||
password_correct=1;
|
{
|
||||||
else /* Password incorrect */
|
/* New version password is checked differently */
|
||||||
/* At the first stage - prepare scramble */
|
if (acl_user->pversion)
|
||||||
if (!stage)
|
{
|
||||||
prepare_scramble(thd,acl_user,prepared_scramble);
|
if (stage) /* We check password only on the second stage */
|
||||||
}
|
{
|
||||||
|
if (!validate_password(password,message,acl_user->salt))
|
||||||
|
password_correct=1;
|
||||||
}
|
}
|
||||||
/* If password correct continue with checking other limitations */
|
else /* First stage - just prepare scramble */
|
||||||
if (password_correct)
|
prepare_scramble(thd,acl_user,prepared_scramble);
|
||||||
{
|
}
|
||||||
#ifdef HAVE_OPENSSL
|
/* Old way to check password */
|
||||||
Vio *vio=thd->net.vio;
|
else
|
||||||
/*
|
{
|
||||||
In this point we know that user is allowed to connect
|
/* Checking the scramble at any stage. First - old clients */
|
||||||
from given host by given username/password pair. Now
|
if (!check_scramble(password,message,acl_user->salt,
|
||||||
we check if SSL is required, if user is using SSL and
|
(my_bool) old_ver))
|
||||||
if X509 certificate attributes are OK
|
password_correct=1;
|
||||||
*/
|
else /* Password incorrect */
|
||||||
switch (acl_user->ssl_type) {
|
/* At the first stage - prepare scramble */
|
||||||
case SSL_TYPE_NOT_SPECIFIED: // Impossible
|
if (!stage)
|
||||||
case SSL_TYPE_NONE: /* SSL is not required to connect */
|
prepare_scramble(thd,acl_user,prepared_scramble);
|
||||||
user_access=acl_user->access;
|
}
|
||||||
break;
|
|
||||||
case SSL_TYPE_ANY: /* Any kind of SSL is good enough */
|
|
||||||
if (vio_type(vio) == VIO_TYPE_SSL)
|
|
||||||
user_access=acl_user->access;
|
|
||||||
break;
|
|
||||||
case SSL_TYPE_X509: /* Client should have any valid certificate. */
|
|
||||||
/*
|
|
||||||
Connections with non-valid certificates are dropped already
|
|
||||||
in sslaccept() anyway, so we do not check validity here.
|
|
||||||
*/
|
|
||||||
if (SSL_get_peer_certificate(vio->ssl_))
|
|
||||||
user_access=acl_user->access;
|
|
||||||
break;
|
|
||||||
case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
|
|
||||||
/*
|
|
||||||
We do not check for absence of SSL because without SSL it does
|
|
||||||
not pass all checks here anyway.
|
|
||||||
If cipher name is specified, we compare it to actual cipher in
|
|
||||||
use.
|
|
||||||
*/
|
|
||||||
if (acl_user->ssl_cipher)
|
|
||||||
{
|
|
||||||
DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
|
|
||||||
acl_user->ssl_cipher,
|
|
||||||
SSL_get_cipher(vio->ssl_)));
|
|
||||||
if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)))
|
|
||||||
user_access=acl_user->access;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
user_access=NO_ACCESS;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Prepare certificate (if exists) */
|
|
||||||
DBUG_PRINT("info",("checkpoint 1"));
|
|
||||||
X509* cert=SSL_get_peer_certificate(vio->ssl_);
|
|
||||||
DBUG_PRINT("info",("checkpoint 2"));
|
|
||||||
/* If X509 issuer is speified, we check it... */
|
|
||||||
if (acl_user->x509_issuer)
|
|
||||||
{
|
|
||||||
DBUG_PRINT("info",("checkpoint 3"));
|
|
||||||
char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
|
|
||||||
DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
|
|
||||||
acl_user->x509_issuer, ptr));
|
|
||||||
if (strcmp(acl_user->x509_issuer, ptr))
|
|
||||||
{
|
|
||||||
user_access=NO_ACCESS;
|
|
||||||
free(ptr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
user_access=acl_user->access;
|
|
||||||
free(ptr);
|
|
||||||
}
|
|
||||||
DBUG_PRINT("info",("checkpoint 4"));
|
|
||||||
/* X509 subject is specified, we check it .. */
|
|
||||||
if (acl_user->x509_subject)
|
|
||||||
{
|
|
||||||
char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
|
||||||
DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
|
|
||||||
acl_user->x509_subject, ptr));
|
|
||||||
if (strcmp(acl_user->x509_subject,ptr))
|
|
||||||
user_access=NO_ACCESS;
|
|
||||||
else
|
|
||||||
user_access=acl_user->access;
|
|
||||||
free(ptr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#else /* HAVE_OPENSSL */
|
|
||||||
user_access=acl_user->access;
|
|
||||||
#endif /* HAVE_OPENSSL */
|
|
||||||
*mqh=acl_user->user_resource;
|
|
||||||
if (!acl_user->user)
|
|
||||||
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
|
|
||||||
break;
|
|
||||||
} // correct password
|
|
||||||
} // found matching user
|
|
||||||
|
|
||||||
#ifndef ALLOW_DOWNGRADE_OF_USERS
|
|
||||||
break; // Wrong password breaks loop /* purecov: inspected */
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If user not found password_correct will also be zero */
|
||||||
|
if (!password_correct)
|
||||||
|
goto unlock_and_exit;
|
||||||
|
|
||||||
|
/* OK. User found and password checked continue validation */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENSSL
|
||||||
|
Vio *vio=thd->net.vio;
|
||||||
|
/*
|
||||||
|
In this point we know that user is allowed to connect
|
||||||
|
from given host by given username/password pair. Now
|
||||||
|
we check if SSL is required, if user is using SSL and
|
||||||
|
if X509 certificate attributes are OK
|
||||||
|
*/
|
||||||
|
switch (acl_user->ssl_type) {
|
||||||
|
case SSL_TYPE_NOT_SPECIFIED: // Impossible
|
||||||
|
case SSL_TYPE_NONE: /* SSL is not required to connect */
|
||||||
|
user_access=acl_user->access;
|
||||||
|
break;
|
||||||
|
case SSL_TYPE_ANY: /* Any kind of SSL is good enough */
|
||||||
|
if (vio_type(vio) == VIO_TYPE_SSL)
|
||||||
|
user_access=acl_user->access;
|
||||||
|
break;
|
||||||
|
case SSL_TYPE_X509: /* Client should have any valid certificate. */
|
||||||
|
/*
|
||||||
|
Connections with non-valid certificates are dropped already
|
||||||
|
in sslaccept() anyway, so we do not check validity here.
|
||||||
|
*/
|
||||||
|
if (SSL_get_peer_certificate(vio->ssl_))
|
||||||
|
user_access=acl_user->access;
|
||||||
|
break;
|
||||||
|
case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
|
||||||
|
/*
|
||||||
|
We do not check for absence of SSL because without SSL it does
|
||||||
|
not pass all checks here anyway.
|
||||||
|
If cipher name is specified, we compare it to actual cipher in
|
||||||
|
use.
|
||||||
|
*/
|
||||||
|
if (acl_user->ssl_cipher)
|
||||||
|
{
|
||||||
|
DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
|
||||||
|
acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)));
|
||||||
|
if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)))
|
||||||
|
user_access=acl_user->access;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user_access=NO_ACCESS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Prepare certificate (if exists) */
|
||||||
|
DBUG_PRINT("info",("checkpoint 1"));
|
||||||
|
X509* cert=SSL_get_peer_certificate(vio->ssl_);
|
||||||
|
DBUG_PRINT("info",("checkpoint 2"));
|
||||||
|
/* If X509 issuer is speified, we check it... */
|
||||||
|
if (acl_user->x509_issuer)
|
||||||
|
{
|
||||||
|
DBUG_PRINT("info",("checkpoint 3"));
|
||||||
|
char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
|
||||||
|
DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
|
||||||
|
acl_user->x509_issuer, ptr));
|
||||||
|
if (strcmp(acl_user->x509_issuer, ptr))
|
||||||
|
{
|
||||||
|
user_access=NO_ACCESS;
|
||||||
|
free(ptr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
user_access=acl_user->access;
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
DBUG_PRINT("info",("checkpoint 4"));
|
||||||
|
/* X509 subject is specified, we check it .. */
|
||||||
|
if (acl_user->x509_subject)
|
||||||
|
{
|
||||||
|
char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
||||||
|
DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
|
||||||
|
acl_user->x509_subject, ptr));
|
||||||
|
if (strcmp(acl_user->x509_subject,ptr))
|
||||||
|
user_access=NO_ACCESS;
|
||||||
|
else
|
||||||
|
user_access=acl_user->access;
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#else /* HAVE_OPENSSL */
|
||||||
|
user_access=acl_user->access;
|
||||||
|
#endif /* HAVE_OPENSSL */
|
||||||
|
*mqh=acl_user->user_resource;
|
||||||
|
if (!acl_user->user)
|
||||||
|
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
|
||||||
|
|
||||||
|
unlock_and_exit:
|
||||||
VOID(pthread_mutex_unlock(&acl_cache->lock));
|
VOID(pthread_mutex_unlock(&acl_cache->lock));
|
||||||
DBUG_RETURN(user_access);
|
DBUG_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,
|
static byte* check_get_key(ACL_USER *buff,uint *length,
|
||||||
my_bool not_used __attribute__((unused)))
|
my_bool not_used __attribute__((unused)))
|
||||||
{
|
{
|
||||||
|
@ -79,6 +79,55 @@
|
|||||||
#define fix_rights_for_column(A) (((A) & COL_ACLS) | ((A & ~COL_ACLS) << 7))
|
#define fix_rights_for_column(A) (((A) & COL_ACLS) | ((A & ~COL_ACLS) << 7))
|
||||||
#define get_rights_for_column(A) (((A) & COL_ACLS) | ((A & ~COL_ACLS) >> 7))
|
#define get_rights_for_column(A) (((A) & COL_ACLS) | ((A & ~COL_ACLS) >> 7))
|
||||||
|
|
||||||
|
/* Classes */
|
||||||
|
|
||||||
|
struct acl_host_and_ip
|
||||||
|
{
|
||||||
|
char *hostname;
|
||||||
|
long ip,ip_mask; // Used with masked ip:s
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ACL_ACCESS {
|
||||||
|
public:
|
||||||
|
ulong sort;
|
||||||
|
ulong access;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ACL_HOST is used if no host is specified */
|
||||||
|
|
||||||
|
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;
|
||||||
|
USER_RESOURCES user_resource;
|
||||||
|
char *user,*password;
|
||||||
|
ulong salt[6]; // New password has longer length
|
||||||
|
uint8 pversion; // password version
|
||||||
|
enum SSL_type ssl_type;
|
||||||
|
const char *ssl_cipher, *x509_issuer, *x509_subject;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ACL_DB :public ACL_ACCESS
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
acl_host_and_ip host;
|
||||||
|
char *user,*db;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* prototypes */
|
/* prototypes */
|
||||||
|
|
||||||
my_bool acl_init(THD *thd, bool dont_read_acl_tables);
|
my_bool acl_init(THD *thd, bool dont_read_acl_tables);
|
||||||
@ -88,7 +137,8 @@ ulong acl_get(const char *host, const char *ip, const char *bin_ip,
|
|||||||
const char *user, const char *db);
|
const char *user, const char *db);
|
||||||
ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
|
ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
|
||||||
const char *password,const char *scramble,char **priv_user,
|
const char *password,const char *scramble,char **priv_user,
|
||||||
bool old_ver, USER_RESOURCES *max,char* prepared_scramble, int stage);
|
bool old_ver, USER_RESOURCES *max,char* prepared_scramble,
|
||||||
|
int stage, uint *cur_priv_version, ACL_USER **cached_user);
|
||||||
bool acl_check_host(const char *host, const char *ip);
|
bool acl_check_host(const char *host, const char *ip);
|
||||||
bool check_change_password(THD *thd, const char *host, const char *user);
|
bool check_change_password(THD *thd, const char *host, const char *user);
|
||||||
bool change_password(THD *thd, const char *host, const char *user,
|
bool change_password(THD *thd, const char *host, const char *user,
|
||||||
|
141
sql/sql_parse.cc
141
sql/sql_parse.cc
@ -188,14 +188,16 @@ end:
|
|||||||
thd->user, thd->master_access, thd->priv_user, thd->db, thd->db_access
|
thd->user, thd->master_access, thd->priv_user, thd->db, thd->db_access
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool check_user(THD *thd,enum_server_command command, const char *user,
|
static int check_user(THD *thd,enum_server_command command, const char *user,
|
||||||
const char *passwd, const char *db, bool check_count,
|
const char *passwd, const char *db, bool check_count,
|
||||||
bool do_send_error, char* crypted_scramble,int stage,
|
bool do_send_error, char* crypted_scramble,int stage,
|
||||||
bool had_password)
|
bool had_password,uint *cur_priv_version,
|
||||||
|
ACL_USER** hint_user)
|
||||||
{
|
{
|
||||||
thd->db=0;
|
thd->db=0;
|
||||||
thd->db_length=0;
|
thd->db_length=0;
|
||||||
USER_RESOURCES ur;
|
USER_RESOURCES ur;
|
||||||
|
|
||||||
/* We shall avoid dupplicate user allocations here */
|
/* We shall avoid dupplicate user allocations here */
|
||||||
if (!(thd->user))
|
if (!(thd->user))
|
||||||
if (!(thd->user = my_strdup(user, MYF(0))))
|
if (!(thd->user = my_strdup(user, MYF(0))))
|
||||||
@ -207,7 +209,9 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
|
|||||||
passwd, thd->scramble, &thd->priv_user,
|
passwd, thd->scramble, &thd->priv_user,
|
||||||
protocol_version == 9 ||
|
protocol_version == 9 ||
|
||||||
!(thd->client_capabilities &
|
!(thd->client_capabilities &
|
||||||
CLIENT_LONG_PASSWORD),&ur,crypted_scramble,stage);
|
CLIENT_LONG_PASSWORD),&ur,crypted_scramble,
|
||||||
|
stage,cur_priv_version,hint_user);
|
||||||
|
|
||||||
DBUG_PRINT("info",
|
DBUG_PRINT("info",
|
||||||
("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'",
|
("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'",
|
||||||
thd->client_capabilities, thd->max_client_packet_length,
|
thd->client_capabilities, thd->max_client_packet_length,
|
||||||
@ -233,7 +237,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
|
|||||||
else
|
else
|
||||||
return(-1); // do not report error in special handshake
|
return(-1); // do not report error in special handshake
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_count)
|
if (check_count)
|
||||||
{
|
{
|
||||||
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
||||||
@ -261,12 +265,13 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
|
|||||||
if (thd->user_connect && thd->user_connect->user_resources.connections &&
|
if (thd->user_connect && thd->user_connect->user_resources.connections &&
|
||||||
check_for_max_user_connections(thd, thd->user_connect))
|
check_for_max_user_connections(thd, thd->user_connect))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (db && db[0])
|
if (db && db[0])
|
||||||
{
|
{
|
||||||
bool error=test(mysql_change_db(thd,db));
|
bool error=test(mysql_change_db(thd,db));
|
||||||
if (error && thd->user_connect)
|
if (error && thd->user_connect)
|
||||||
decrease_user_connections(thd->user_connect);
|
decrease_user_connections(thd->user_connect);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
send_ok(thd); // Ready to handle questions
|
send_ok(thd); // Ready to handle questions
|
||||||
@ -616,7 +621,7 @@ check_connections(THD *thd)
|
|||||||
/* We can get only old hash at this point */
|
/* We can get only old hash at this point */
|
||||||
if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH)
|
if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH)
|
||||||
return ER_HANDSHAKE_ERROR;
|
return ER_HANDSHAKE_ERROR;
|
||||||
|
|
||||||
if (thd->client_capabilities & CLIENT_INTERACTIVE)
|
if (thd->client_capabilities & CLIENT_INTERACTIVE)
|
||||||
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
|
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
|
||||||
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
|
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
|
||||||
@ -625,6 +630,9 @@ check_connections(THD *thd)
|
|||||||
net->read_timeout=(uint) thd->variables.net_read_timeout;
|
net->read_timeout=(uint) thd->variables.net_read_timeout;
|
||||||
|
|
||||||
char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble and hash */
|
char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble and hash */
|
||||||
|
|
||||||
|
ACL_USER* cached_user;
|
||||||
|
uint cur_priv_version;
|
||||||
|
|
||||||
/* Simple connect only for old clients. New clients always use secure auth */
|
/* Simple connect only for old clients. New clients always use secure auth */
|
||||||
bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
|
bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
|
||||||
@ -634,7 +642,7 @@ check_connections(THD *thd)
|
|||||||
|
|
||||||
/* Check user permissions. If password failure we'll get scramble back */
|
/* Check user permissions. If password failure we'll get scramble back */
|
||||||
if (check_user(thd,COM_CONNECT, user, passwd, db, 1, simple_connect,
|
if (check_user(thd,COM_CONNECT, user, passwd, db, 1, simple_connect,
|
||||||
prepared_scramble,0,using_password))
|
prepared_scramble,0,using_password,&cur_priv_version,&cached_user)<0)
|
||||||
{
|
{
|
||||||
/* If The client is old we just have to return error */
|
/* If The client is old we just have to return error */
|
||||||
if (simple_connect)
|
if (simple_connect)
|
||||||
@ -682,7 +690,8 @@ check_connections(THD *thd)
|
|||||||
}
|
}
|
||||||
/* Final attempt to check the user based on reply */
|
/* Final attempt to check the user based on reply */
|
||||||
if (check_user(thd,COM_CONNECT, tmp_user, (char*)net->read_pos,
|
if (check_user(thd,COM_CONNECT, tmp_user, (char*)net->read_pos,
|
||||||
tmp_db, 1, 1,prepared_scramble,1,using_password))
|
tmp_db, 1, 1,prepared_scramble,1,using_password,&cur_priv_version,
|
||||||
|
&cached_user))
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
thd->password=using_password;
|
thd->password=using_password;
|
||||||
@ -1034,43 +1043,117 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||||||
char *user= (char*) packet;
|
char *user= (char*) packet;
|
||||||
char *passwd= strend(user)+1;
|
char *passwd= strend(user)+1;
|
||||||
char *db= strend(passwd)+1;
|
char *db= strend(passwd)+1;
|
||||||
|
|
||||||
/* Save user and privileges */
|
/* Save user and privileges */
|
||||||
uint save_master_access=thd->master_access;
|
uint save_master_access=thd->master_access;
|
||||||
uint save_db_access= thd->db_access;
|
uint save_db_access= thd->db_access;
|
||||||
uint save_db_length= thd->db_length;
|
uint save_db_length= thd->db_length;
|
||||||
char *save_user= thd->user;
|
char *save_user= thd->user;
|
||||||
|
thd->user=NULL; /* Needed for check_user to allocate new user */
|
||||||
char *save_priv_user= thd->priv_user;
|
char *save_priv_user= thd->priv_user;
|
||||||
char *save_db= thd->db;
|
char *save_db= thd->db;
|
||||||
USER_CONN *save_uc= thd->user_connect;
|
USER_CONN *save_uc= thd->user_connect;
|
||||||
|
bool simple_connect;
|
||||||
|
bool using_password;
|
||||||
|
|
||||||
|
ulong pkt_len=0; /* Length of reply packet */
|
||||||
|
|
||||||
|
/* Small check for incomming packet */
|
||||||
|
|
||||||
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
|
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
|
||||||
{ // Check if protocol is ok
|
goto restore_user_err;
|
||||||
send_error(thd, ER_UNKNOWN_COM_ERROR);
|
|
||||||
break;
|
/* Now we shall basically perform authentication again */
|
||||||
}
|
|
||||||
/* WARNING THIS HAS TO BE REWRITTEN */
|
/* We can get only old hash at this point */
|
||||||
char tmp_buffer[64];
|
if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH)
|
||||||
printf("Change user called: %s %s %s\n",user,passwd,db);
|
goto restore_user_err;
|
||||||
if (check_user(thd, COM_CHANGE_USER, user, passwd, db, 0,1,tmp_buffer,0,1))
|
|
||||||
{ // Restore old user
|
char prepared_scramble[SCRAMBLE41_LENGTH+4];/* Buffer for scramble,hash */
|
||||||
x_free(thd->user);
|
ACL_USER* cached_user; /* Cached user */
|
||||||
x_free(thd->db);
|
uint cur_priv_version; /* Cached grant version */
|
||||||
thd->master_access=save_master_access;
|
|
||||||
thd->db_access=save_db_access;
|
/* Simple connect only for old clients. New clients always use sec. auth*/
|
||||||
thd->db=save_db;
|
simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
|
||||||
thd->db_length=save_db_length;
|
|
||||||
thd->user=save_user;
|
/* Store information if we used password. passwd will be dammaged */
|
||||||
thd->priv_user=save_priv_user;
|
using_password=test(passwd[0]);
|
||||||
break;
|
|
||||||
|
/*
|
||||||
|
Check user permissions. If password failure we'll get scramble back
|
||||||
|
Do not retry if we already have sent error (result>0)
|
||||||
|
*/
|
||||||
|
if (check_user(thd,COM_CHANGE_USER, user, passwd, db, 0, simple_connect,
|
||||||
|
prepared_scramble,0,using_password,&cur_priv_version,&cached_user)<0)
|
||||||
|
{
|
||||||
|
/* If The client is old we just have to have auth failure */
|
||||||
|
if (simple_connect)
|
||||||
|
goto restore_user; /* Error is already reported */
|
||||||
|
|
||||||
|
/* Store current used and database as they are erased with next packet */
|
||||||
|
|
||||||
|
char tmp_user[USERNAME_LENGTH+1];
|
||||||
|
char tmp_db[NAME_LEN+1];
|
||||||
|
|
||||||
|
if (user)
|
||||||
|
{
|
||||||
|
strncpy(tmp_user,user,USERNAME_LENGTH+1);
|
||||||
|
/* Extra safety if we have too long data */
|
||||||
|
tmp_user[USERNAME_LENGTH]=0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tmp_user[0]=0;
|
||||||
|
if (db)
|
||||||
|
{
|
||||||
|
strncpy(tmp_db,db,NAME_LEN+1);
|
||||||
|
tmp_db[NAME_LEN]=0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tmp_db[0]=0;
|
||||||
|
|
||||||
|
/* Write hash and encrypted scramble to client */
|
||||||
|
if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4)
|
||||||
|
|| net_flush(net))
|
||||||
|
goto restore_user_err;
|
||||||
|
|
||||||
|
/* Reading packet back */
|
||||||
|
if ((pkt_len=my_net_read(net)) == packet_error)
|
||||||
|
goto restore_user_err;
|
||||||
|
|
||||||
|
/* We have to get very specific packet size */
|
||||||
|
if (pkt_len!=SCRAMBLE41_LENGTH)
|
||||||
|
goto restore_user;
|
||||||
|
|
||||||
|
/* Final attempt to check the user based on reply */
|
||||||
|
if (check_user(thd,COM_CONNECT, tmp_user, (char*)net->read_pos,
|
||||||
|
tmp_db, 0, 1,prepared_scramble,1,using_password,&cur_priv_version,
|
||||||
|
&cached_user))
|
||||||
|
goto restore_user;
|
||||||
}
|
}
|
||||||
|
/* Finally we've authenticated new user */
|
||||||
if (max_connections && save_uc)
|
if (max_connections && save_uc)
|
||||||
decrease_user_connections(save_uc);
|
decrease_user_connections(save_uc);
|
||||||
x_free((gptr) save_db);
|
x_free((gptr) save_db);
|
||||||
x_free((gptr) save_user);
|
x_free((gptr) save_user);
|
||||||
thd->password=test(passwd[0]);
|
thd->password=using_password;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Bad luck we shall restore old user */
|
||||||
|
restore_user_err:
|
||||||
|
send_error(thd, ER_UNKNOWN_COM_ERROR);
|
||||||
|
|
||||||
|
restore_user:
|
||||||
|
x_free(thd->user);
|
||||||
|
x_free(thd->db);
|
||||||
|
thd->master_access=save_master_access;
|
||||||
|
thd->db_access=save_db_access;
|
||||||
|
thd->db=save_db;
|
||||||
|
thd->db_length=save_db_length;
|
||||||
|
thd->user=save_user;
|
||||||
|
thd->priv_user=save_priv_user;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case COM_EXECUTE:
|
case COM_EXECUTE:
|
||||||
{
|
{
|
||||||
mysql_stmt_execute(thd, packet);
|
mysql_stmt_execute(thd, packet);
|
||||||
|
Reference in New Issue
Block a user