Commit a7339b76 authored by unknown's avatar unknown

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
parent 970a669d
...@@ -768,8 +768,9 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) ...@@ -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; ...@@ -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,
......
...@@ -2228,8 +2228,8 @@ Try also with PIPE or TCP/IP ...@@ -2228,8 +2228,8 @@ 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*/
...@@ -2409,7 +2409,11 @@ static my_bool mysql_reconnect(MYSQL *mysql) ...@@ -2409,7 +2409,11 @@ 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)
...@@ -2417,12 +2421,83 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, ...@@ -2417,12 +2421,83 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
if (!passwd) if (!passwd)
passwd=""; passwd="";
pos=strmov(pos,user)+1; /* Store user into the buffer */
pos=scramble(pos, mysql->scramble_buff, passwd, 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)); (my_bool) (mysql->protocol_version == 9));
pos=strmov(pos+1,db ? db : ""); }
if (simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (pos-buff),0)) else /* For empty password*/
DBUG_RETURN(1); *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;
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
{
/* This should basically always happen with new server unless empty password */
if (pkt_length==24) /* We have new hash back */
{
/* 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));
...@@ -2432,6 +2507,10 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, ...@@ -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);
} }
......
...@@ -49,6 +49,8 @@ ...@@ -49,6 +49,8 @@
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
*****************************************************************************/ *****************************************************************************/
......
...@@ -33,52 +33,6 @@ ...@@ -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; ...@@ -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,...);
...@@ -149,6 +104,8 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) ...@@ -149,6 +104,8 @@ 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) ...@@ -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));
...@@ -534,24 +499,47 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ...@@ -534,24 +499,47 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
} }
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.
*/ */
if (stage && (*cur_priv_version==priv_version))
acl_user=*hint_user;
else
for (uint i=0 ; i < acl_users.elements ; i++) for (uint i=0 ; i < acl_users.elements ; i++)
{ {
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); ACL_USER *acl_user_search=dynamic_element(&acl_users,i,ACL_USER*);
if (!acl_user->user || !strcmp(user,acl_user->user)) if (!acl_user_search->user || !strcmp(user,acl_user_search->user))
{ {
if (compare_hostname(&acl_user->host,host,ip)) if (compare_hostname(&acl_user_search->host,host,ip))
{ {
/* Found mathing user */
acl_user=acl_user_search;
/* Store it as a cache */
*hint_user=acl_user;
*cur_priv_version=priv_version;
break;
}
}
}
/* Now we have acl_user found and may start our checks */
if (acl_user)
{
/* Password should present for both or absend for both */
if (!acl_user->password && !*password || if (!acl_user->password && !*password ||
(acl_user->password && *password)) (acl_user->password && *password))
{ {
/* Quick check and accept for empty passwords*/ /* Quick check and accept for empty passwords*/
if (!acl_user->password && !*password) if (!acl_user->password && !*password)
password_correct=1; password_correct=1;
else else /* Normal password presents */
{ {
/* New version password is checked differently */ /* New version password is checked differently */
if (acl_user->pversion) if (acl_user->pversion)
...@@ -577,9 +565,16 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ...@@ -577,9 +565,16 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
prepare_scramble(thd,acl_user,prepared_scramble); prepare_scramble(thd,acl_user,prepared_scramble);
} }
} }
/* If password correct continue with checking other limitations */ }
if (password_correct) }
{
/* 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 #ifdef HAVE_OPENSSL
Vio *vio=thd->net.vio; Vio *vio=thd->net.vio;
/* /*
...@@ -615,8 +610,7 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ...@@ -615,8 +610,7 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
if (acl_user->ssl_cipher) if (acl_user->ssl_cipher)
{ {
DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'", DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
acl_user->ssl_cipher, acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)));
SSL_get_cipher(vio->ssl_)));
if (!strcmp(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; user_access=acl_user->access;
else else
...@@ -666,26 +660,13 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ...@@ -666,26 +660,13 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
*mqh=acl_user->user_resource; *mqh=acl_user->user_resource;
if (!acl_user->user) if (!acl_user->user)
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */ *priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
break;
} // correct password
} // found matching user
#ifndef ALLOW_DOWNGRADE_OF_USERS unlock_and_exit:
break; // Wrong password breaks loop /* purecov: inspected */
#endif
}
}
}
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 @@ ...@@ -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, ...@@ -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,
......
...@@ -188,14 +188,16 @@ static int get_or_create_user_conn(THD *thd, const char *user, ...@@ -188,14 +188,16 @@ static int get_or_create_user_conn(THD *thd, const char *user,
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, ...@@ -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,
...@@ -261,6 +265,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, ...@@ -261,6 +265,7 @@ 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));
...@@ -626,6 +631,9 @@ check_connections(THD *thd) ...@@ -626,6 +631,9 @@ check_connections(THD *thd)
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) ...@@ -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) ...@@ -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;
...@@ -1040,20 +1049,100 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -1040,20 +1049,100 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
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 */
/* We can get only old hash at this point */
if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH)
goto restore_user_err;
char prepared_scramble[SCRAMBLE41_LENGTH+4];/* Buffer for scramble,hash */
ACL_USER* cached_user; /* Cached user */
uint cur_priv_version; /* Cached grant version */
/* Simple connect only for old clients. New clients always use sec. auth*/
simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
/* Store information if we used password. passwd will be dammaged */
using_password=test(passwd[0]);
/*
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;
} }
/* WARNING THIS HAS TO BE REWRITTEN */ else
char tmp_buffer[64]; tmp_user[0]=0;
printf("Change user called: %s %s %s\n",user,passwd,db); if (db)
if (check_user(thd, COM_CHANGE_USER, user, passwd, db, 0,1,tmp_buffer,0,1)) {
{ // Restore old user 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)
decrease_user_connections(save_uc);
x_free((gptr) save_db);
x_free((gptr) save_user);
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->user);
x_free(thd->db); x_free(thd->db);
thd->master_access=save_master_access; thd->master_access=save_master_access;
...@@ -1064,13 +1153,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -1064,13 +1153,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->priv_user=save_priv_user; thd->priv_user=save_priv_user;
break; break;
} }
if (max_connections && save_uc)
decrease_user_connections(save_uc);
x_free((gptr) save_db);
x_free((gptr) save_user);
thd->password=test(passwd[0]);
break;
}
case COM_EXECUTE: case COM_EXECUTE:
{ {
mysql_stmt_execute(thd, packet); mysql_stmt_execute(thd, packet);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment