1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

First version of new authentification procedure: now authentification is one-stage (instead of two-stage in 4.1)

For now following tasks have been done:
- PASSWORD() function was rewritten. PASSWORD() now returns SHA1
  hash_stage2; for new passwords user.password contains '*'hash_stage2; sql_yacc.yy also fixed; 

- password.c: new functions were implemented, old rolled back to 4.0 state

- server code was rewritten to use new authorization algorithm (check_user(), change
  user, and other stuff in sql/sql_parse.cc)

- client code was rewritten to use new authorization algorithm
  (mysql_real_connect, myslq_authenticate in sql-common/client.c)

- now server barks on 45-byte-length 4.1.0 passwords and refuses 4.1.0-style
  authentification. Users with 4.1.0 passwords are blocked (sql/sql_acl.cc)

- mysqladmin.c was fixed to work correctly with new passwords

Tests for 4.0-4.1.1, 4.1.1-4.1.1 (with or without db/password) logons was performed;
mysqladmin also was tested. Additional check are nevertheless necessary.


BitKeeper/etc/ignore:
  Added start_mysqld.sh mysys/main.cc to the ignore list
client/mysqladmin.c:
  fixed with new password api
include/mysql.h:
  So as scramble_323 accepts only null-terminated message, two scramble buffs are necessary.
  gotta be fixed
include/mysql_com.h:
  new constants and password.c api changes
libmysql/libmysql.c:
  mysql_change_user rewritten to work with new password api
scripts/mysql_create_system_tables.sh:
  fixed 'Password' column length to 41
scripts/mysql_fix_privilege_tables.sql:
  fixed 'Password' column length to 41
sql-common/client.c:
  mysql_real_connect rewritten to support new handshake procedure
sql/item_strfunc.cc:
  Item_func_password and Item_func_old_password rewritten with new password api
sql/item_strfunc.h:
  bit commented, numbers replaced with #defined constants
sql/mysql_priv.h:
  removed unnecessary declaration as now all constants defined is in mysql_com.h
sql/mysqld.cc:
  scramble initialization moved to sql_parce.cc:check_connection
sql/password.c:
  All 4.1 functions were rolled back to 4.0 with attempt to save all possible 4.0-4.1 changes.
  Names for 4.0 functions were suffixed with '_323'
  Functions for new handshake were added.
sql/slave.cc:
  Fixed to new constant; Bug #766 remains to be fixed
sql/slave.h:
  fixed to new constant; Buf #766 remains to be fixed
sql/sql_acl.cc:
  rewritten to support new passwords (41 byte-long) and password api
sql/sql_acl.h:
  ditto
sql/sql_class.cc:
  initialization for new members added
sql/sql_class.h:
  same thing as in struct mysql - scramble is used for new family of functions, scramble_323 - for old
sql/sql_parse.cc:
  check_connections was renamed to check_connection as this name reflects better what this function does
  authorization part of check_connection was rewritten
  check_user was rewritten with new password and acl api
  new function 'authenticate', which optionally re-request scramble from client was added
  fixed some typos
  COM_CHANGE_USER piece of dipsatch_command() was rewritten
sql/sql_repl.h:
  HASH_PASSWORD_LENGTH replaced with SCRAMBLED_PASSWORD_CHAR_LENGTH
  bug #766 remains
sql/sql_yacc.yy:
  Two-argument form of PASSWORD() was removed
  PASSWORD() function was fixed with new password api.
BitKeeper/etc/logging_ok:
  Logging to logging@openlogging.org accepted
This commit is contained in:
unknown
2003-07-01 23:40:59 +04:00
parent b871e549ee
commit dbb088b034
23 changed files with 1186 additions and 1505 deletions

View File

@ -622,3 +622,5 @@ vio/test-ssl
vio/test-sslclient
vio/test-sslserver
vio/viotest-ssl
start_mysqld.sh
mysys/main.cc

View File

@ -51,6 +51,7 @@ jcole@sarvik.tfr.cafe.ee
jcole@tetra.spaceapes.com
jorge@linux.jorge.mysql.com
kaj@work.mysql.com
kostja@oak.local
lenz@kallisto.mysql.com
lenz@mysql.com
miguel@hegel.(none)

View File

@ -769,9 +769,12 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv)
return 1;
}
if (argv[1][0])
make_scrambled_password(crypted_pw,argv[1],
(find_type(argv[0], &command_typelib, 2) ==
ADMIN_OLD_PASSWORD), &rand_st);
{
if (find_type(argv[0], &command_typelib, 2) == ADMIN_OLD_PASSWORD)
make_scrambled_password_323(crypted_pw, argv[1]);
else
make_scrambled_password(crypted_pw, argv[1]);
}
else
crypted_pw[0]=0; /* No password */
sprintf(buff,"set password='%s',sql_log_off=0",crypted_pw);

View File

@ -227,7 +227,9 @@ typedef struct st_mysql
enum mysql_status status;
my_bool free_me; /* If free in mysql_close */
my_bool reconnect; /* set to 1 if automatic reconnect */
char scramble_buff[21]; /* New protocol requires longer scramble*/
char scramble[SCRAMBLE_LENGTH+1]; /* for new servers */
char scramble_323[SCRAMBLE_LENGTH_323+1]; /* for old servers */
/*
Set if this is the original connection, not a master or a slave we have

View File

@ -48,8 +48,15 @@ enum enum_server_command
};
#define SCRAMBLE_LENGTH 8
#define SCRAMBLE41_LENGTH 20
/*
Length of random string sent by server on handshake; this is also length of
obfuscated password, recieved from client
*/
#define SCRAMBLE_LENGTH 20
#define SCRAMBLE_LENGTH_323 8
/* length of password stored in the db: new passwords are preceeded with '*' */
#define SCRAMBLED_PASSWORD_CHAR_LENGTH (SCRAMBLE_LENGTH*2+1)
#define SCRAMBLED_PASSWORD_CHAR_LENGTH_323 (SCRAMBLE_LENGTH_323*2)
#define NOT_NULL_FLAG 1 /* Field can't be NULL */
@ -300,31 +307,35 @@ extern "C" {
extern unsigned long max_allowed_packet;
extern unsigned long net_buffer_length;
/*
These functions are used for authentication by client and server and
implemented in sql/password.c
*/
void randominit(struct rand_struct *, unsigned long seed1,
unsigned long seed2);
double my_rnd(struct rand_struct *);
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);
char get_password_version(const char* password);
void create_random_string(int length,struct rand_struct *rand_st,char* target);
my_bool validate_password(const char* password, const char* message,
unsigned long* salt);
void password_hash_stage1(char *to, const char *password);
void password_hash_stage2(char *to,const char *salt);
void password_crypt(const char* from,char* to, const char* password,int length);
void get_hash_and_password(unsigned long* salt, unsigned char pversion,char* hash,
unsigned char* bin_password);
void get_salt_from_password(unsigned long *res,const char *password);
void create_key_from_old_password(const char* password,char* key);
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,
void create_random_string(char *to, uint length, struct rand_struct *rand_st);
void hash_password(ulong *to, const char *password);
void make_scrambled_password_323(char *to, const char *password);
char *scramble_323(char *to, const char *message, const char *password,
my_bool old_ver);
my_bool check_scramble(const char *, const char *message,
my_bool check_scramble_323(const char *, const char *message,
unsigned long *salt, my_bool old_ver);
void get_salt_from_password_323(unsigned long *res, const char *password);
void make_password_from_salt_323(char *to, const unsigned long *salt);
void make_scrambled_password(char *to, const char *password);
char *scramble(char *to, const char *message, const char *password);
my_bool check_scramble(const char *reply, const char *message,
const unsigned char *hash_stage2);
void get_salt_from_password(unsigned char *res, const char *password);
void make_password_from_salt(char *to, const unsigned char *hash_stage2);
/* end of password.c */
char *get_tty_password(char *opt_message);
void hash_password(unsigned long *result, const char *password);
const char *mysql_errno_to_sqlstate(unsigned int mysql_errno);
/* Some other useful functions */

View File

@ -616,41 +616,51 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
/* 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)
{
/* write scrambled password according to server capabilities */
if (passwd[0])
{
/* Prepare false scramble */
bfill(end, SCRAMBLE_LENGTH, 'x');
end+=SCRAMBLE_LENGTH;
*end=0;
}
else /* For empty password */
*end=0; /* zero length scramble */
}
else
{
/*
Real scramble is only sent to old servers. This can be blocked
by calling mysql_options(MYSQL *, MYSQL_SECURE_CONNECT, (char*) &1);
*/
end=scramble(end, mysql->scramble_buff, passwd,
/* Write NULL-terminated scrambled password: */
end= mysql->server_capabilities & CLIENT_SECURE_CONNECTION ?
scramble(end, mysql->scramble, passwd) :
scramble_323(end, mysql->scramble_323, passwd,
(my_bool) (mysql->protocol_version == 9));
}
else
*end= '\0'; // empty password
/* Add database if needed */
end=strmov(end+1,db ? db : "");
/* Write authentication package */
simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1);
if (mysql_autenticate(mysql, passwd))
NET *net= &mysql->net;
ulong pkt_length= net_safe_read(mysql);
if (pkt_length == packet_error)
goto error;
if (net->read_pos[0] == mysql->scramble_323[0] &&
pkt_length == SCRAMBLE_LENGTH_323 + 1 &&
mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
{
/*
By sending this very specific reply server asks us to send scrambled
password in old format. The reply contains scramble_323.
*/
scramble_323(buff, mysql->scramble_323, passwd,
(my_bool) (mysql->protocol_version == 9));
if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net))
{
net->last_errno= CR_SERVER_LOST;
strmov(net->sqlstate, unknown_sqlstate);
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;
}
/* Free old connect information */
my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR));

View File

@ -108,7 +108,7 @@ then
c_u="$c_u CREATE TABLE user ("
c_u="$c_u Host char(60) binary DEFAULT '' NOT NULL,"
c_u="$c_u User char(16) binary DEFAULT '' NOT NULL,"
c_u="$c_u Password char(45) binary DEFAULT '' NOT NULL,"
c_u="$c_u Password char(41) binary DEFAULT '' NOT NULL,"
c_u="$c_u Select_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u Insert_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u Update_priv enum('N','Y') DEFAULT 'N' NOT NULL,"

View File

@ -4,7 +4,7 @@ ALTER TABLE host type=MyISAM;
ALTER TABLE func type=MyISAM;
ALTER TABLE columns_priv type=MyISAM;
ALTER TABLE tables_priv type=MyISAM;
ALTER TABLE user change Password Password char(45) not null;
ALTER TABLE user change Password Password char(41) not null;
ALTER TABLE user add File_priv enum('N','Y') NOT NULL;
CREATE TABLE IF NOT EXISTS func (
name char(64) DEFAULT '' NOT NULL,

View File

@ -1334,76 +1334,6 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused)))
#endif /* HAVE_OPENSSL */
/*
Handle password authentication
*/
my_bool mysql_autenticate(MYSQL *mysql, const char *passwd)
{
ulong pkt_length;
NET *net= &mysql->net;
char buff[SCRAMBLE41_LENGTH];
char password_hash[SCRAMBLE41_LENGTH]; /* Used for storage of stage1 hash */
/* We shall only query server 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 always happen with new server unless empty password
OK/Error packets have zero as the first char
*/
if (pkt_length == 24 && net->read_pos[0])
{
/* Old passwords will have '*' 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,SCRAMBLE41_LENGTH);
/* Finally hash complete password using hash we got from server */
password_hash_stage2(password_hash,(const char*) net->read_pos);
/* Decypt and store scramble 4 = hash for stage2 */
password_crypt((const char*) net->read_pos+4,mysql->scramble_buff,
password_hash, SCRAMBLE41_LENGTH);
mysql->scramble_buff[SCRAMBLE41_LENGTH]=0;
/* Encode scramble with password. Recycle buffer */
password_crypt(mysql->scramble_buff,buff,buff,SCRAMBLE41_LENGTH);
}
else
{
/* Create password to decode scramble */
create_key_from_old_password(passwd,password_hash);
/* Decypt and store scramble 4 = hash for stage2 */
password_crypt((const char*) net->read_pos+4,mysql->scramble_buff,
password_hash, SCRAMBLE41_LENGTH);
mysql->scramble_buff[SCRAMBLE41_LENGTH]=0;
/* Finally scramble decoded scramble with password */
scramble(buff, mysql->scramble_buff, passwd,0);
}
/* Write second package of authentication */
if (my_net_write(net,buff,SCRAMBLE41_LENGTH) || net_flush(net))
{
net->last_errno= CR_SERVER_LOST;
strmov(net->sqlstate, unknown_sqlstate);
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;
}
}
return 0;
error:
return 1;
}
/*
Note that the mysql argument must be initialized with mysql_init()
before calling mysql_real_connect !
@ -1481,7 +1411,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
mysql->server_status=SERVER_STATUS_AUTOCOMMIT;
/*
Grab a socket and connect it to the server
Part 0: Grab a socket and connect it to the server
*/
#if defined(HAVE_SMEM)
if ((!mysql->options.protocol ||
@ -1682,6 +1612,11 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
strmov(net->last_error,ER(net->last_errno));
goto error;
}
/*
Part 1: Connection established, read and parse first packet
*/
if ((pkt_length=net_safe_read(mysql)) == packet_error)
goto error;
@ -1702,8 +1637,14 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
end=strend((char*) net->read_pos+1);
mysql->thread_id=uint4korr(end+1);
end+=5;
strmake(mysql->scramble_buff,end,8);
end+=9;
/*
Scramble is split into two parts because old clients does not understand
long scrambles; here goes the first part.
*/
strmake(mysql->scramble_323, end, SCRAMBLE_LENGTH_323);
end+= SCRAMBLE_LENGTH_323+1;
memcpy(mysql->scramble, mysql->scramble_323, SCRAMBLE_LENGTH_323);
if (pkt_length >= (uint) (end+1 - (char*) net->read_pos))
mysql->server_capabilities=uint2korr(end);
if (pkt_length >= (uint) (end+18 - (char*) net->read_pos))
@ -1712,6 +1653,13 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
mysql->server_language=end[2];
mysql->server_status=uint2korr(end+3);
}
end+= 18;
if (pkt_length >= (uint) (end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1 -
(char *) net->read_pos))
strmake(mysql->scramble+SCRAMBLE_LENGTH_323, end,
SCRAMBLE_LENGTH-SCRAMBLE_LENGTH_323);
else
mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION;
/* Set character set */
if ((charset_name=mysql->options.charset_name))
@ -1783,9 +1731,12 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
mysql->unix_socket=0;
strmov(mysql->server_version,(char*) net->read_pos+1);
mysql->port=port;
client_flag|=mysql->options.client_flag;
/* Send client information for access check */
/*
Part 2: format and send client info to the server for access check
*/
client_flag|=mysql->options.client_flag;
client_flag|=CLIENT_CAPABILITIES;
if (client_flag & CLIENT_MULTI_QUERIES)
client_flag|= CLIENT_MULTI_RESULTS;
@ -1881,35 +1832,18 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
#include "_cust_libmysql.h"
#endif
DBUG_PRINT("info",("user: %s",end));
/*
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)
{
end= strend(end) + 1;
if (passwd[0])
{
/* Prepare false scramble */
end=strend(end)+1;
bfill(end, SCRAMBLE_LENGTH, 'x');
end+=SCRAMBLE_LENGTH;
*end=0;
}
else /* For empty password*/
{
end=strend(end)+1;
*end=0; /* Store zero length scramble */
}
}
else
{
/*
Real scramble is only sent to old servers. This can be blocked
by calling mysql_options(MYSQL *, MYSQL_SECURE_CONNECT, (char*) &1);
*/
end=scramble(strend(end)+1, mysql->scramble_buff, passwd,
/* Write NULL-terminated scrambled password: */
end= mysql->server_capabilities & CLIENT_SECURE_CONNECTION ?
scramble(end, mysql->scramble, passwd) :
scramble_323(end, mysql->scramble_323, passwd,
(my_bool) (mysql->protocol_version == 9));
}
else
*end= '\0'; /* empty password */
/* Add database if needed */
if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
{
@ -1926,9 +1860,37 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
goto error;
}
if (mysql_autenticate(mysql, passwd))
/*
Part 3: Authorization data's been sent. Now server can reply with
OK-packet, or re-request scrambled password.
*/
if ((pkt_length=net_safe_read(mysql)) == packet_error)
goto error;
if (net->read_pos[0] == mysql->scramble_323[0] &&
pkt_length == SCRAMBLE_LENGTH_323 + 1 &&
mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
{
/*
By sending this very specific reply server asks us to send scrambled
password in old format. The reply contains scramble_323.
*/
scramble_323(buff, mysql->scramble_323, passwd,
(my_bool) (mysql->protocol_version == 9));
if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net))
{
net->last_errno= CR_SERVER_LOST;
strmov(net->sqlstate, unknown_sqlstate);
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;
}
if (client_flag & CLIENT_COMPRESS) /* We will use compression */
net->compress=1;

View File

@ -1346,82 +1346,21 @@ void Item_func_trim::fix_length_and_dec()
}
void Item_func_password::fix_length_and_dec()
{
/*
If PASSWORD() was called with only one argument, it depends on a random
number so we need to save this random number into the binary log.
If called with two arguments, it is repeatable.
*/
if (arg_count == 1)
{
THD *thd= current_thd;
thd->rand_used= 1;
thd->rand_saved_seed1= thd->rand.seed1;
thd->rand_saved_seed2= thd->rand.seed2;
}
max_length= get_password_length(use_old_passwords);
}
/*
Password() function has 2 arguments. Second argument can be used
to make results repeatable
*/
/* Item_func_password */
String *Item_func_password::val_str(String *str)
{
struct rand_struct rand_st; // local structure for 2 param version
ulong seed=0; // seed to initialise random generator to
String *res= args[0]->val_str(str);
if ((null_value=args[0]->null_value))
return 0;
if (arg_count == 1)
{
if (res->length() == 0)
return &empty_string;
make_scrambled_password(tmp_value,res->c_ptr(),use_old_passwords,
&current_thd->rand);
str->set(tmp_value,get_password_length(use_old_passwords),res->charset());
make_scrambled_password(tmp_value, res->c_ptr());
str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, res->charset());
return str;
}
else
{
/* We'll need the buffer to get second parameter */
char key_buff[80];
String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info);
String *key =args[1]->val_str(&tmp_key_value);
/* Check second argument for NULL value. First one is already checked */
if ((null_value=args[1]->null_value))
return 0;
/* This shall be done after checking for null for proper results */
if (res->length() == 0)
return &empty_string;
/* Generate the seed first this allows to avoid double allocation */
char* seed_ptr=key->c_ptr();
while (*seed_ptr)
{
seed=(seed*211+*seed_ptr) & 0xffffffffL; /* Use simple hashing */
seed_ptr++;
}
/* Use constants which allow nice random values even with small seed */
randominit(&rand_st,
(ulong) ((ulonglong) seed*111111+33333333L) & (ulong) 0xffffffff,
(ulong) ((ulonglong) seed*1111+55555555L) & (ulong) 0xffffffff);
make_scrambled_password(tmp_value,res->c_ptr(),use_old_passwords,
&rand_st);
str->set(tmp_value,get_password_length(use_old_passwords),res->charset());
return str;
}
}
/* Item_func_old_password */
String *Item_func_old_password::val_str(String *str)
{
@ -1430,13 +1369,12 @@ String *Item_func_old_password::val_str(String *str)
return 0;
if (res->length() == 0)
return &empty_string;
make_scrambled_password(tmp_value,res->c_ptr(),1,&current_thd->rand);
str->set(tmp_value,16,res->charset());
make_scrambled_password_323(tmp_value, res->c_ptr());
str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, res->charset());
return str;
}
#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
String *Item_func_encrypt::val_str(String *str)

View File

@ -254,30 +254,44 @@ public:
};
/*
Item_func_password -- new (4.1.1) PASSWORD() function implementation.
Returns strcat('*', octet2hex(sha1(sha1(password)))). '*' stands for new
password format, sha1(sha1(password) is so-called hash_stage2 value.
Length of returned string is always 41 byte. To find out how entire
authentification procedure works, see comments in password.c.
*/
class Item_func_password :public Item_str_func
{
char tmp_value[64]; /* This should be enough for new password format */
char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
public:
Item_func_password(Item *a) :Item_str_func(a) {}
Item_func_password(Item *a, Item *b) :Item_str_func(a,b) {}
String *val_str(String *);
void fix_length_and_dec();
String *val_str(String *str);
void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; }
const char *func_name() const { return "password"; }
};
/*
Item_func_old_password -- PASSWORD() implementation used in MySQL 3.21 - 4.0
compatibility mode. This item is created in sql_yacc.yy when
'use_old_passwords' session variable is set, and to handle OLD_PASSWORD()
function.
*/
class Item_func_old_password :public Item_str_func
{
char tmp_value[17]; /* old password length +1 */
char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1];
public:
Item_func_old_password(Item *a) :Item_str_func(a) {}
String *val_str(String *);
void fix_length_and_dec() { max_length = get_password_length(1); }
String *val_str(String *str);
void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; }
const char *func_name() const { return "old_password"; }
unsigned int size_of() { return sizeof(*this);}
};
class Item_func_des_encrypt :public Item_str_func
{
String tmp_value;

View File

@ -74,9 +74,6 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
****************************************************************************/
#define ACL_CACHE_SIZE 256
/* Password lengh for 4.1 version previous versions had 16 bytes password hash */
#define HASH_PASSWORD_LENGTH 45
#define HASH_OLD_PASSWORD_LENGTH 16
#define HOST_CACHE_SIZE 128
#define MAX_ACCEPT_RETRY 10 // Test accept this many times
#define MAX_FIELDS_BEFORE_HASH 32

View File

@ -2743,12 +2743,6 @@ static void create_new_thread(THD *thd)
if (thread_count-delayed_insert_threads > max_used_connections)
max_used_connections=thread_count-delayed_insert_threads;
thd->thread_id=thread_id++;
for (uint i=0; i < 8 ; i++) // Generate password teststring
thd->scramble[i]= (char) (my_rnd(&sql_rand)*94+33);
thd->scramble[8]=0;
// Back it up as old clients may need it
memcpy(thd->old_scramble,thd->scramble,9);
thd->real_id=pthread_self(); // Keep purify happy

View File

@ -29,28 +29,33 @@
The password is saved (in user.password) by using the PASSWORD() function in
mysql.
This is .c file because it's used in libmysqlclient, which is entirely in C.
(we need it to be portable to a variety of systems).
Example:
update user set password=PASSWORD("hello") where user="test"
This saves a hashed number as a string in the password field.
The new autentication is performed in following manner:
New in MySQL 4.1 authentication works even more secure way.
At the first step client sends user name to the sever, and password if
it is empty. So in case of empty password authentication is as fast as before.
At the second stap servers sends scramble to client, which is encoded with
password stage2 hash stored in the password database as well as salt, needed
for client to build stage2 password to decrypt scramble.
Client decrypts the scramble and encrypts it once again with stage1 password.
This information is sent to server.
Server decrypts the scramble to get stage1 password and hashes it to get
stage2 hash. This hash is when compared to hash stored in the database.
SERVER: public_seed=create_random_string()
send(public_seed)
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
to break into MySQL.
CLIENT: recv(public_seed)
hash_stage1=sha1("password")
hash_stage2=sha1(hash_stage1)
reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
New Password handling functions by Peter Zaitsev
// this three steps are done in scramble()
send(reply)
SERVER: recv(reply)
hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
candidate_hash2=sha1(hash_stage1)
check(candidate_hash2==hash_stage2)
// this three steps are done in check_scramble()
*****************************************************************************/
@ -60,25 +65,15 @@
#include <sha1.h>
#include "mysql.h"
/* Character to use as version identifier for version 4.1 */
#define PVERSION41_CHAR '*'
/* Scramble length for new password version */
/************ MySQL 3.23-4.0 authentification routines: untouched ***********/
/*
New (MySQL 3.21+) random generation structure initialization
SYNOPSIS
randominit()
rand_st OUT Structure to initialize
seed1 IN First initialization parameter
seed2 IN Second initialization parameter
RETURN
none
*/
void randominit(struct rand_struct *rand_st, ulong seed1, ulong seed2)
@ -95,14 +90,11 @@ void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
/*
Old (MySQL 3.20) random generation structure initialization
XXX: is to be deleted very soon!
SYNOPSIS
old_randominit()
rand_st OUT Structure to initialize
seed1 IN First initialization parameter
RETURN
none
*/
static void old_randominit(struct rand_struct *rand_st, ulong seed1)
@ -115,14 +107,12 @@ static void old_randominit(struct rand_struct *rand_st,ulong seed1)
/*
Generate Random number
Generate random number.
SYNOPSIS
my_rnd()
rand_st INOUT Structure used for number generation
RETURN
Generated pseudo random number
RETURN VALUE
generated pseudo random number
*/
double my_rnd(struct rand_struct *rand_st)
@ -134,63 +124,12 @@ double my_rnd(struct rand_struct *rand_st)
/*
Generate String of printable random characters of requested length
String will not be zero terminated.
Generate binary hash from raw text string
Used for Pre-4.1 password handling
SYNOPSIS
create_random_string()
length IN Lenght of
rand_st INOUT Structure used for number generation
target OUT Buffer for generation
RETURN
none
*/
void create_random_string(int length,struct rand_struct *rand_st,char *target)
{
char *end=target+length;
/* Use pointer arithmetics as it is faster way to do so. */
for (; target<end ; target++)
*target= (char) (my_rnd(rand_st)*94+33);
}
/*
Encrypt/Decrypt function used for password encryption in authentication
Simple XOR is used here but it is OK as we crypt random strings
SYNOPSIS
password_crypt()
from IN Data for encryption
to OUT Encrypt data to the buffer (may be the same)
password IN Password used for encryption (same length)
length IN Length of data to encrypt
RETURN
none
*/
void password_crypt(const char *from,char *to, const char *password,int length)
{
const char *from_end=from+length;
while (from < from_end)
*to++= *(from++) ^* (password++);
}
/*
Generate binary hash from raw text password
Used for Pre-4.1 Password handling
SYNOPSIS
hash_pasword()
result OUT Store hash in this location
password IN Plain text password to build hash
RETURN
none
hash_password()
result OUT store hash in this location
password IN plain text password to build hash
*/
void hash_password(ulong *result, const char *password)
@ -200,7 +139,7 @@ void hash_password(ulong *result, const char *password)
for (; *password ; password++)
{
if (*password == ' ' || *password == '\t')
continue; /* skipp space in password */
continue; /* skip space in password */
tmp= (ulong) (uchar) *password;
nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
nr2+=(nr2 << 8) ^ nr;
@ -212,446 +151,55 @@ void hash_password(ulong *result, const char *password)
}
/*
Stage one password hashing.
Used in MySQL 4.1 password handling
SYNOPSIS
password_hash_stage1()
to OUT Store stage one hash to this location
password IN Plain text password to build hash
RETURN
none
*/
void password_hash_stage1(char *to, const char *password)
{
SHA1_CONTEXT context;
sha1_reset(&context);
for (; *password ; password++)
{
if (*password == ' ' || *password == '\t')
continue;/* skip space in password */
sha1_input(&context,(uint8*) &password[0],1);
}
sha1_result(&context,(uint8*)to);
}
/*
Stage two password hashing.
Used in MySQL 4.1 password handling
SYNOPSIS
password_hash_stage2()
to INOUT Use this as stage one hash and store stage two hash here
salt IN Salt used for stage two hashing
RETURN
none
*/
void password_hash_stage2(char *to, const char *salt)
{
SHA1_CONTEXT context;
sha1_reset(&context);
sha1_input(&context,(uint8*) salt, 4);
sha1_input(&context,(uint8*) to, SHA1_HASH_SIZE);
sha1_result(&context,(uint8*) to);
}
/*
Create password to be stored in user database from raw string
Handles both MySQL 4.1 and Pre-MySQL 4.1 passwords
Used for pre-4.1 password handling
SYNOPSIS
make_scramble_password()
to OUT Store scrambled password here
password IN Raw string password
force_old_scramle
IN Force generation of old scramble variant
rand_st INOUT Structure for temporary number generation.
RETURN
none
make_scrambled_password_323()
to OUT store scrambled password here
password IN user-supplied password
*/
void make_scrambled_password(char *to,const char *password,
my_bool force_old_scramble,
struct rand_struct *rand_st)
{
ulong hash_res[2]; /* Used for pre 4.1 password hashing */
unsigned short salt; /* Salt for 4.1 version password */
uint8 digest[SHA1_HASH_SIZE];
if (force_old_scramble) /* Pre 4.1 password encryption */
void make_scrambled_password_323(char *to, const char *password)
{
ulong hash_res[2];
hash_password(hash_res, password);
sprintf(to, "%08lx%08lx", hash_res[0], hash_res[1]);
}
else /* New password 4.1 password scrambling */
{
to[0]=PVERSION41_CHAR; /* New passwords have version prefix */
/* Rnd returns number from 0 to 1 so this would be good salt generation.*/
salt=(unsigned short) (my_rnd(rand_st)*65535+1);
/* Use only 2 first bytes from it */
sprintf(to+1,"%04x",salt);
/* First hasing is done without salt */
password_hash_stage1((char*) digest, password);
/* Second stage is done with salt */
password_hash_stage2((char*) digest,(char*)to+1),
/* Print resulting hash into the password*/
sprintf(to+5,
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
digest[0],digest[1],digest[2],digest[3],digest[4],digest[5],digest[6],
digest[7],digest[8],digest[9],digest[10],digest[11],digest[12],digest[13],
digest[14],digest[15],digest[16],digest[17],digest[18],digest[19]);
}
}
/*
Convert password from binary string form to salt form
Used for MySQL 4.1 password handling
Scramble string with password.
Used in pre 4.1 authentication phase.
SYNOPSIS
get_salt_from_bin_password()
res OUT Store salt form password here
password IN Binary password to be converted
salt IN hashing-salt to be used for salt form generation
RETURN
none
*/
void get_salt_from_bin_password(ulong *res,unsigned char *password,ulong salt)
{
unsigned char *password_end=password+SCRAMBLE41_LENGTH;
*res=salt;
res++;
/* Process password of known length*/
while (password<password_end)
{
ulong val=0;
uint i;
for (i=0 ; i < 4 ; i++)
val=(val << 8)+(*password++);
*res++=val;
}
}
/*
Validate password for MySQL 4.1 password handling.
SYNOPSIS
validate_password()
password IN Encrypted Scramble which we got from the client
message IN Original scramble which we have sent to the client before
salt IN Password in the salted form to match to
RETURN
0 for correct password
!0 for invalid password
*/
my_bool validate_password(const char *password, const char *message,
ulong *salt)
{
char buffer[SCRAMBLE41_LENGTH]; /* Used for password validation */
char tmpsalt[8]; /* Temporary value to convert salt to string form */
ulong salt_candidate[6]; /* Computed candidate salt */
ulong *sc=salt_candidate; /* we need to be able to increment */
ulong *salt_end;
/* Now we shall get stage1 encrypted password in buffer*/
password_crypt(password,buffer,message,SCRAMBLE41_LENGTH);
/* For compatibility reasons we use ulong to store salt while we need char */
sprintf(tmpsalt,"%04x",(unsigned short)salt[0]);
password_hash_stage2(buffer,tmpsalt);
/* Convert password to salt to compare */
get_salt_from_bin_password(salt_candidate,(uchar*) buffer,salt[0]);
/* Now we shall get exactly the same password as we have stored for user */
for (salt_end=salt+5 ; salt < salt_end; )
if (*++salt != *++sc)
return 1;
/* Or password correct*/
return 0;
}
/*
Get length of password string which is stored in mysql.user table
SYNOPSIS
get_password_length()
force_old_scramble IN If we wish to use pre 4.1 scramble format
RETURN
password length >0
*/
int get_password_length(my_bool force_old_scramble)
{
return (force_old_scramble) ? 16 : SHA1_HASH_SIZE*2+4+1;
}
/*
Get version of the password based on mysql.user password string
SYNOPSIS
get_password_version()
password IN Password string as stored in mysql.user
RETURN
0 for pre 4.1 passwords
!0 password version char for newer passwords
*/
char get_password_version(const char *password)
{
if (password==NULL) return 0;
if (password[0]==PVERSION41_CHAR) return PVERSION41_CHAR;
return 0;
}
/*
Get integer value of Hex character
SYNOPSIS
char_val()
X IN Character to find value for
RETURN
Appropriate integer value
*/
static inline unsigned int char_val(char X)
{
return (uint) (X >= '0' && X <= '9' ? X-'0' :
X >= 'A' && X <= 'Z' ? X-'A'+10 :
X-'a'+10);
}
/*
Get Binary salt from password as in mysql.user format
SYNOPSIS
get_salt_from_password()
res OUT Store binary salt here
password IN Password string as stored in mysql.user
RETURN
none
NOTE
This function does not have length check for passwords. It will just crash
Password hashes in old format must have length divisible by 8
*/
void get_salt_from_password(ulong *res,const char *password)
{
if (password) /* zero salt corresponds to empty password */
{
if (password[0]==PVERSION41_CHAR) /* if new password */
{
uint val=0;
uint i;
password++; /* skip version identifier */
/*get hashing salt from password and store in in the start of array */
for (i=0 ; i < 4 ; i++)
val=(val << 4)+char_val(*password++);
*res++=val;
}
/* We process old passwords the same way as new ones in other case */
#ifdef EXTRA_DEBUG
if (strlen(password)%8!=0)
fprintf(stderr,"Warning: Incorrect password length for salting: %d\n",
strlen(password));
#endif
while (*password)
{
ulong val=0;
uint i;
for (i=0 ; i < 8 ; i++)
val=(val << 4)+char_val(*password++);
*res++=val;
}
}
return;
}
/*
Get string version as stored in mysql.user from salt form
SYNOPSIS
make_password_from_salt()
to OUT Store resulting string password here
hash_res IN Password in salt format
password_version
IN According to which version salt should be treated
RETURN
none
*/
void make_password_from_salt(char *to, ulong *hash_res,uint8 password_version)
{
if (!password_version) /* Handling of old passwords. */
sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
else
if (password_version==PVERSION41_CHAR)
sprintf(to,"%c%04x%08lx%08lx%08lx%08lx%08lx",PVERSION41_CHAR,(unsigned short)hash_res[0],hash_res[1],
hash_res[2],hash_res[3],hash_res[4],hash_res[5]);
else /* Just use empty password if we can't handle it. This should not happen */
to[0]='\0';
}
/*
Convert password in salted form to binary string password and hash-salt
For old password this involes one more hashing
SYNOPSIS
get_hash_and_password()
salt IN Salt to convert from
pversion IN Password version to use
hash OUT Store zero ended hash here
bin_password OUT Store binary password here (no zero at the end)
RETURN
0 for pre 4.1 passwords
!0 password version char for newer passwords
*/
void get_hash_and_password(ulong *salt, uint8 pversion, char *hash,
unsigned char *bin_password)
{
int t;
ulong* salt_end;
ulong val;
SHA1_CONTEXT context;
if (pversion) /* New password version assumed */
{
salt_end=salt+5;
sprintf(hash,"%04x",(unsigned short)salt[0]);
while (salt<salt_end)
{
val=*(++salt);
for (t=3; t>=0; t--)
{
bin_password[t]= (char) (val & 255);
val>>=8; /* Scroll 8 bits to get next part*/
}
bin_password+=4; /* Get to next 4 chars*/
}
}
else
{
unsigned char *bp= bin_password; /* Binary password loop pointer */
/* Use zero starting hash as an indication of old password */
hash[0]=0;
salt_end=salt+2;
/* Encode salt using SHA1 here */
sha1_reset(&context);
while (salt<salt_end) /* Iterate over these elements*/
{
val= *salt;
for (t=3;t>=0;t--)
{
bp[t]= (uchar) (val & 255);
val>>=8; /* Scroll 8 bits to get next part*/
}
bp+= 4; /* Get to next 4 chars*/
salt++;
}
/* Use 8 bytes of binary password for hash */
sha1_input(&context,(uint8*)bin_password,8);
sha1_result(&context,(uint8*)bin_password);
}
}
/*
Create key from old password to decode scramble
Used in 4.1 authentication with passwords stored old way
SYNOPSIS
create_key_from_old_password()
passwd IN Password used for key generation
key OUT Created 20 bytes key
RETURN
None
*/
void create_key_from_old_password(const char *passwd, char *key)
{
char buffer[SCRAMBLE41_LENGTH]; /* Buffer for various needs */
ulong salt[6]; /* Salt (large for safety) */
/* At first hash password to the string stored in password */
make_scrambled_password(buffer,passwd,1,(struct rand_struct *)NULL);
/* Now convert it to the salt form */
get_salt_from_password(salt,buffer);
/* Finally get hash and bin password from salt */
get_hash_and_password(salt,0,buffer,(unsigned char*) key);
}
/*
Scramble string with password
Used at pre 4.1 authentication phase.
SYNOPSIS
scramble()
to OUT Store scrambled message here
message IN Message to scramble
scramble_323()
to OUT Store scrambled message here. Buffer must be at least
SCRAMBLE_LENGTH_323+1 bytes long
message IN Message to scramble. Message must be exactly
SRAMBLE_LENGTH_323 long and NULL terminated.
password IN Password to use while scrambling
old_ver IN Forse old version random number generator
old_ver IN Force old version random number generator
RETURN
End of scrambled string
*/
char *scramble(char *to,const char *message,const char *password,
char *scramble_323(char *to, const char *message, const char *password,
my_bool old_ver)
{
struct rand_struct rand_st;
ulong hash_pass[2], hash_message[2];
char message_buffer[9]; /* Real message buffer */
char *msg=message_buffer;
/* We use special message buffer now as new server can provide longer hash */
memcpy(message_buffer,message,8);
message_buffer[8]=0;
if (password && password[0])
{
char *to_start=to;
hash_password(hash_pass,password);
hash_password(hash_message,message_buffer);
hash_password(hash_message, message);
if (old_ver)
old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
else
randominit(&rand_st,hash_pass[0] ^ hash_message[0],
hash_pass[1] ^ hash_message[1]);
while (*msg++)
while (*message++)
*to++= (char) (floor(my_rnd(&rand_st)*31)+64);
if (!old_ver)
{ /* Make it harder to break */
@ -667,43 +215,34 @@ char *scramble(char *to,const char *message,const char *password,
/*
Check scrambled message
Used for pre 4.1 password handling
Used in pre 4.1 password handling
SYNOPSIS
scramble()
scrambled IN Scrambled message to check
message IN Original message which was scramble
hash_pass IN Password which should be used for scrambling
old_ver IN Forse old version random number generator
RETURN
0 Password correct
!0 Password invalid
check_scramble_323()
scrambled IN scrambled message to check.
message IN original random message which was used for scrambling; must
be exactly SCRAMBLED_LENGTH_323 bytes long and
NULL-terminated.
hash_pass IN password which should be used for scrambling
old_ver IN force old (3.20) version random number generator
RETURN VALUE
0 - password correct
!0 - password invalid
*/
my_bool check_scramble(const char *scrambled, const char *message,
my_bool
check_scramble_323(const char *scrambled, const char *message,
ulong *hash_pass, my_bool old_ver)
{
struct rand_struct rand_st;
ulong hash_message[2];
char buff[16],*to,extra; /* Big enough for check */
const char *pos;
char message_buffer[SCRAMBLE_LENGTH+1]; /* Copy of message */
/* We need to copy the message as this function can be called for MySQL 4.1
scramble which is not zero ended and can have zeroes inside
We could just write zero to proper place in original message but
this would make it harder to understand code for next generations
*/
memcpy(message_buffer,message,SCRAMBLE_LENGTH); /* Ignore the rest */
message_buffer[SCRAMBLE_LENGTH]=0;
/* Check if this exactly N bytes. Overwise this is something fishy */
if (strlen(message_buffer)!=SCRAMBLE_LENGTH)
if (strlen(message) != SCRAMBLE_LENGTH_323)
return 1; /* Wrong password */
hash_password(hash_message,message_buffer);
hash_password(hash_message,message);
if (old_ver)
old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
else
@ -724,3 +263,296 @@ my_bool check_scramble(const char *scrambled, const char *message,
}
return 0;
}
static uint8 char_val(uint8 X)
{
return (uint) (X >= '0' && X <= '9' ? X-'0' :
X >= 'A' && X <= 'Z' ? X-'A'+10 : X-'a'+10);
}
/*
Convert password from hex string (as stored in mysql.user) to binary form.
SYNOPSIS
get_salt_from_password_323()
res OUT store salt here
password IN password string as stored in mysql.user
NOTE
This function does not have length check for passwords. It will just crash
Password hashes in old format must have length divisible by 8
*/
void get_salt_from_password_323(ulong *res, const char *password)
{
res[0]= res[1]= 0;
if (password)
{
while (*password)
{
ulong val=0;
uint i;
for (i=0 ; i < 8 ; i++)
val=(val << 4)+char_val(*password++);
*res++=val;
}
}
}
/*
Convert scrambled password from binary form to asciiz hex string.
SYNOPSIS
make_password_from_salt_323()
to OUT store resulting string password here, at least 17 bytes
salt IN password in salt format, 2 ulongs
*/
void make_password_from_salt_323(char *to, const ulong *salt)
{
sprintf(to,"%08lx%08lx", salt[0], salt[1]);
}
/******************* MySQL 4.1.1 authentification routines ******************/
/*
Generate string of printable random characters of requested length
SYNOPSIS
create_random_string()
to OUT buffer for generation; must be at least length+1 bytes
long; result string is always null-terminated
length IN how many random characters to put in buffer
rand_st INOUT structure used for number generation
*/
void create_random_string(char *to, uint length, struct rand_struct *rand_st)
{
char *end= to + length;
/* Use pointer arithmetics as it is faster way to do so. */
for (; to < end; to++)
*to= (char) (my_rnd(rand_st)*94+33);
*to= '\0';
}
/* Character to use as version identifier for version 4.1 */
#define PVERSION41_CHAR '*'
/*
Convert given octet sequence to asciiz string of hex characters;
str..str+len and 'to' may not overlap.
SYNOPSIS
octet2hex()
buf OUT output buffer. Must be at least 2*len+1 bytes
str, len IN the beginning and the length of the input string
*/
static
void
octet2hex(char *to, const uint8 *str, uint len)
{
static const char alphabet[] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
const uint8 *str_end= str + len;
for (; str != str_end; ++str)
{
*to++= alphabet[(*str & 0xF0) >> 4];
*to++= alphabet[*str & 0x0F];
}
*to++= '\0';
}
/*
Convert given asciiz string of hex (0..9 a..f) characters to octet
sequence.
SYNOPSIS
hex2octet()
to OUT buffer to place result; must be at least len/2 bytes
str, len IN begin, length for character string; str and to may not
overlap; len % 2 == 0
*/
static
void
hex2octet(uint8 *to, const char *str, uint len)
{
const char *str_end= str + len;
while (str < str_end)
{
*to= char_val(*str++) << 4;
*to++|= char_val(*str++);
}
}
/*
Encrypt/Decrypt function used for password encryption in authentication.
Simple XOR is used here but it is OK as we crypt random strings. Note,
that XOR(s1, XOR(s1, s2)) == s2, XOR(s1, s2) == XOR(s2, s1)
SYNOPSIS
my_crypt()
to OUT buffer to hold crypted string; must be at least len bytes
long; to and s1 (or s2) may be the same.
s1, s2 IN input strings (of equal length)
len IN length of s1 and s2
*/
static
void
my_crypt(char *to, const uint8 *s1, const uint8 *s2, uint len)
{
const uint8 *s1_end= s1 + len;
while (s1 < s1_end)
*to++= *s1++ ^ *s2++;
}
/*
MySQL 4.1.1 password hashing: SHA conversion (see RFC 2289, 3174) twice
applied to the password string, and then produced octet sequence is
converted to hex string.
The result of this function is used as return value from PASSWORD() and
is stored in the database.
SYNOPSIS
make_scrambled_password()
buf OUT buffer of size 2*SHA1_HASH_SIZE + 2 to store hex string
password IN NULL-terminated password string
*/
void
make_scrambled_password(char *to, const char *password)
{
SHA1_CONTEXT sha1_context;
uint8 hash_stage2[SHA1_HASH_SIZE];
sha1_reset(&sha1_context);
/* stage 1: hash password */
sha1_input(&sha1_context, (uint8 *) password, strlen(password));
sha1_result(&sha1_context, (uint8 *) to);
/* stage 2: hash stage1 output */
sha1_reset(&sha1_context);
sha1_input(&sha1_context, (uint8 *) to, SHA1_HASH_SIZE);
/* separate buffer is used to pass 'to' in octet2hex */
sha1_result(&sha1_context, hash_stage2);
/* convert hash_stage2 to hex string */
*to++= PVERSION41_CHAR;
octet2hex(to, hash_stage2, SHA1_HASH_SIZE);
}
/*
Produce an obscure octet sequence from password and random
string, recieved from the server. This sequence corresponds to the
password, but password can not be easily restored from it. The sequence
is then sent to the server for validation. Trailing zero is stored in
the buf.
This function is used by client to create authenticated reply to the
server's greeting.
SYNOPSIS
scramble()
buf OUT store scrambled string here. The buf must be at least
SHA1_HASH_SIZE+1 bytes long.
message IN random message, must be exactly SCRAMBLE_LENGTH long and
NULL-terminated.
password IN users' password
RETURN VALUE
end of scrambled string
*/
char *
scramble(char *to, const char *message, const char *password)
{
SHA1_CONTEXT sha1_context;
uint8 hash_stage1[SHA1_HASH_SIZE];
uint8 hash_stage2[SHA1_HASH_SIZE];
sha1_reset(&sha1_context);
/* stage 1: hash password */
sha1_input(&sha1_context, (uint8 *) password, strlen(password));
sha1_result(&sha1_context, hash_stage1);
/* stage 2: hash stage 1; note that hash_stage2 is stored in the database */
sha1_reset(&sha1_context);
sha1_input(&sha1_context, hash_stage1, SHA1_HASH_SIZE);
sha1_result(&sha1_context, hash_stage2);
/* create crypt string as sha1(message, hash_stage2) */;
sha1_reset(&sha1_context);
sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH);
sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE);
/* xor allows 'from' and 'to' overlap: lets take advantage of it */
sha1_result(&sha1_context, (uint8 *) to);
my_crypt(to, (const uint8 *) to, hash_stage1, SCRAMBLE_LENGTH);
to[SHA1_HASH_SIZE]= '\0';
return to + SHA1_HASH_SIZE;
}
/*
Check that scrambled message corresponds to the password; the function
is used by server to check that recieved reply is authentic.
This function does not check lengths of given strings: message must be
null-terminated, reply and hash_stage2 must be at least SHA1_HASH_SIZE
long (if not, something fishy is going on).
SYNOPSIS
check_scramble()
scramble IN clients' reply, presumably produced by scramble()
message IN original random string, previously sent to client
(presumably second argument of scramble()), must be
exactly SCRAMBLE_LENGTH long and NULL-terminated.
hash_stage2 IN hex2octet-decoded database entry
RETURN VALUE
0 password is correct
!0 password is invalid
*/
my_bool
check_scramble(const char *scramble, const char *message,
const uint8 *hash_stage2)
{
SHA1_CONTEXT sha1_context;
uint8 buf[SHA1_HASH_SIZE];
uint8 hash_stage2_reassured[SHA1_HASH_SIZE];
sha1_reset(&sha1_context);
/* create key to encrypt scramble */
sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH);
sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE);
sha1_result(&sha1_context, buf);
/* encrypt scramble */
my_crypt((char *) buf, buf, (const uint8 *) scramble, SCRAMBLE_LENGTH);
/* now buf supposedly contains hash_stage1: so we can get hash_stage2 */
sha1_reset(&sha1_context);
sha1_input(&sha1_context, buf, SHA1_HASH_SIZE);
sha1_result(&sha1_context, hash_stage2_reassured);
return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE);
}
/*
Convert scrambled password from asciiz hex string to binary form.
SYNOPSIS
get_salt_from_password()
res OUT buf to hold password. Must be at least SHA1_HASH_SIZE
bytes long.
password IN 4.1.1 version value of user.password
*/
void get_salt_from_password(uint8 *hash_stage2, const char *password)
{
hex2octet(hash_stage2, password+1 /* skip '*' */, SHA1_HASH_SIZE * 2);
}
/*
Convert scrambled password from binary form to asciiz hex string.
SYNOPSIS
make_password_from_salt()
to OUT store resulting string here, 2*SHA1_HASH_SIZE+2 bytes
salt IN password in salt format
*/
void make_password_from_salt(char *to, const uint8 *hash_stage2)
{
*to++= PVERSION41_CHAR;
octet2hex(to, hash_stage2, SHA1_HASH_SIZE);
}

View File

@ -1459,7 +1459,7 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname,
if (master_user)
strmake(mi->user, master_user, sizeof(mi->user) - 1);
if (master_password)
strmake(mi->password, master_password, HASH_PASSWORD_LENGTH);
strmake(mi->password, master_password, SCRAMBLED_PASSWORD_CHAR_LENGTH);
mi->port = master_port;
mi->connect_retry = master_connect_retry;
}
@ -1483,8 +1483,8 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname,
master_host) ||
init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file,
master_user) ||
init_strvar_from_file(mi->password, HASH_PASSWORD_LENGTH+1, &mi->file,
master_password) ||
init_strvar_from_file(mi->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1,
&mi->file, master_password) ||
init_intvar_from_file(&port, &mi->file, master_port) ||
init_intvar_from_file(&connect_retry, &mi->file,
master_connect_retry))

View File

@ -292,7 +292,7 @@ typedef struct st_master_info
/* the variables below are needed because we can change masters on the fly */
char host[HOSTNAME_LENGTH+1];
char user[USERNAME_LENGTH+1];
char password[HASH_PASSWORD_LENGTH+1];
char password[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
pthread_mutex_t data_lock,run_lock;
pthread_cond_t data_cond,start_cond,stop_cond;
THD *io_thd;

View File

@ -68,11 +68,36 @@ 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);
const char *new_password, uint new_password_len);
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);
/*
Convert scrambled password to binary form, according to scramble type,
Binary form is stored in user.salt.
*/
static
void
set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
{
if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
{
get_salt_from_password(acl_user->salt, password);
acl_user->salt_len= SCRAMBLE_LENGTH;
}
else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323
|| password_len == 8 && protocol_version == 9)
{
get_salt_from_password_323((ulong *) acl_user->salt, password);
acl_user->salt_len= password_len/2;
}
else
acl_user->salt_len= 0;
}
/*
Read grant privileges from the privilege tables in the 'mysql' database.
@ -175,16 +200,19 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
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 */
sql_print_error("Old 'user' table. "
"(Check README or the Reference manual). "
"Continuing --old-protocol"); /* purecov: tested */
protocol_version=9; /* purecov: tested */
}
DBUG_PRINT("info",("user table fields: %d, password length: %d",
table->fields, table->field[2]->field_length));
if (table->field[2]->field_length < 45 && !use_old_passwords)
if (table->field[2]->field_length < 41 && !use_old_passwords)
{
sql_print_error("mysql.user table is not updated to new password format; Disabling new password usage until mysql_fix_privilege_tables is run");
sql_print_error("mysql.user table is not updated to new password format; "
"Disabling new password usage until "
"mysql_fix_privilege_tables is run");
use_old_passwords= 1;
}
@ -192,31 +220,35 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_USER user;
uint length=0;
update_hostname(&user.host, get_field(&mem, table->field[0]));
user.user= get_field(&mem, table->field[1]);
user.password=get_field(&mem, table->field[2]);
if (user.password && (length=(uint) strlen(user.password)) == 8 &&
protocol_version == PROTOCOL_VERSION)
const char *password= get_field(&mem, table->field[2]);
uint password_len= password ? strlen(password) : 0;
set_user_salt(&user, password, password_len);
if (user.salt_len == 0 && password_len != 0)
{
sql_print_error(
"Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)",
user.user ? user.user : ""); /* purecov: tested */
}
else /* non empty and not short passwords */
{
user.pversion=get_password_version(user.password);
/* Only passwords of specific lengths depending on version are allowed */
if ( (!user.pversion && length % 8) || (user.pversion && length!=45 ))
{
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 */
switch (password_len) {
case 8: /* 3.20: to be removed */
sql_print_error("Found old style password for user '%s'. "
"Ignoring user. (You may want to restart mysqld "
"using --old-protocol) ",
user.user ? user.user : "");
break;
case 45: /* 4.1: to be removed */
sql_print_error("Found 4.1 style password for user '%s'. "
"Ignoring user. "
"You should change password for this user.",
user.user ? user.user : "");
break;
default:
sql_print_error("Found invalid password for user: '%s@%s'; "
"Ignoring user", user.user ? user.user : "",
user.host.hostname ? user.host.hostname : "");
break;
}
}
get_salt_from_password(user.salt,user.password);
else // password is correct
{
user.access= get_access(table,3) & GLOBAL_ACLS;
user.sort= get_sort(2,user.host.hostname,user.user);
user.hostname_length= (user.host.hostname ?
@ -270,6 +302,7 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
!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);
@ -462,136 +495,91 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
/*
Prepare crypted scramble to be sent to the client
Seek ACL entry for a user, check password, SSL cypher, and if
everything is OK, update THD user data and USER_RESOURCES struct.
This function does not check if the user has any sensible privileges:
only user's existence and validity is checked.
Note, that entire operation is protected by acl_cache_lock.
SYNOPSIS
thd INOUT thread handle. If all checks are OK,
thd->priv_user, thd->master_access are updated.
thd->host, thd->ip, thd->user are used for checks.
mqh OUT user resources; on success mqh is reset, else
unchanged
passwd IN scrambled & crypted password, recieved from client
(to check): thd->scramble or thd->scramble_323 is
used to decrypt passwd, so they must contain
original random string,
passwd_len IN length of passwd, must be one of 0, 8,
SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH
old_version IN if old (3.20) protocol is used
RETURN VALUE
0 success: thread data and mqh are updated
1 user not found or authentification failure
-1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
*/
void prepare_scramble(THD *thd, ACL_USER *acl_user,char* prepared_scramble)
int
acl_getroot(THD *thd, USER_RESOURCES *mqh,
const char *passwd, uint passwd_len, bool old_version)
{
/* Binary password format to be used for generation*/
char bin_password[SCRAMBLE41_LENGTH];
/* Generate new long scramble for the thread */
create_random_string(SCRAMBLE41_LENGTH,&thd->rand,thd->scramble);
thd->scramble[SCRAMBLE41_LENGTH]=0;
/* Get binary form, First 4 bytes of prepared scramble is salt */
get_hash_and_password(acl_user->salt,acl_user->pversion,prepared_scramble,
(unsigned char*) bin_password);
/* Store "*" as identifier for old passwords */
if (!acl_user->pversion)
prepared_scramble[0]='*';
/* Finally encrypt password to get prepared scramble */
password_crypt(thd->scramble, prepared_scramble+4, bin_password,
SCRAMBLE41_LENGTH);
}
/*
Get master privilges for user (priviliges for all tables).
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,
const char *password,const char *message,char **priv_user,
char *priv_host, bool old_ver, USER_RESOURCES *mqh,
char *prepared_scramble, uint *cur_priv_version,
ACL_USER **cached_user)
{
ulong user_access=NO_ACCESS;
*priv_user= (char*) user;
bool password_correct= 0;
int stage= (*cached_user != NULL); /* NULL passed as first stage */
ACL_USER *acl_user= NULL;
DBUG_ENTER("acl_getroot");
bzero(mqh,sizeof(USER_RESOURCES));
if (!initialized)
if (!initialized) /* if no data allow anything */
{
// If no data allow anything
DBUG_RETURN((ulong) ~NO_ACCESS); /* purecov: tested */
DBUG_RETURN(1);
}
int res= 1;
VOID(pthread_mutex_lock(&acl_cache->lock));
/*
Get possible access from user_list. This is or'ed to others not
fully specified
If we have cached user use it, in other case look it up.
Find acl entry in user database. Note, that find_acl_user is not the same,
because it doesn't take into account the case when user is not empty,
but acl_user->user is empty
*/
if (stage && (*cur_priv_version == priv_version))
acl_user= *cached_user;
else
{
ACL_USER *acl_user= 0;
for (uint i=0 ; i < acl_users.elements ; i++)
{
ACL_USER *acl_user_search=dynamic_element(&acl_users,i,ACL_USER*);
if (!acl_user_search->user || !strcmp(user,acl_user_search->user))
ACL_USER *user_i = dynamic_element(&acl_users,i,ACL_USER*);
if (!user_i->user || !strcmp(thd->user, user_i->user))
{
if (compare_hostname(&acl_user_search->host,host,ip))
if (compare_hostname(&user_i->host, thd->host, thd->ip))
{
/* Found mathing user */
acl_user= acl_user_search;
/* Store it as a cache */
*cached_user= acl_user;
*cur_priv_version= priv_version;
/* check password: it should be empty or valid */
if (passwd_len == user_i->salt_len)
{
if (user_i->salt_len == 0 ||
user_i->salt_len == SCRAMBLE_LENGTH &&
check_scramble(passwd, thd->scramble, user_i->salt) == 0 ||
check_scramble_323(passwd, thd->scramble_323,
(ulong *) user_i->salt, old_version) == 0)
{
acl_user= user_i;
res= 0;
}
}
else if (passwd_len == SCRAMBLE_LENGTH &&
user_i->salt_len == SCRAMBLE_LENGTH_323)
res= -1;
/* linear search complete: */
break;
}
}
}
}
/* Now we have acl_user found and may start our checks */
/*
This was moved to separate tree because of heavy HAVE_OPENSSL case.
If acl_user is not null, res is 0.
*/
if (acl_user)
{
/* Password should present for both or absend for both */
if (!acl_user->password && !*password)
password_correct=1;
else if (!acl_user->password || !*password)
{
*cached_user= 0; // Impossible to connect
}
else
{
/* 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))
password_correct=1;
}
else /* First stage - just prepare scramble */
prepare_scramble(thd,acl_user,prepared_scramble);
}
/* Old way to check password */
else
{
/* Checking the scramble at any stage. First - old clients */
if (!check_scramble(password,message,acl_user->salt,
(my_bool) old_ver))
password_correct=1;
else if (!stage) /* Here if password incorrect */
{
/* At the first stage - prepare scramble */
prepare_scramble(thd,acl_user,prepared_scramble);
}
}
}
}
/* 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
{
ulong user_access= NO_ACCESS;
Vio *vio=thd->net.vio;
/*
At this point we know that user is allowed to connect
@ -616,7 +604,8 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
We need to check for absence of SSL because without SSL
we should reject connection.
*/
if (vio_type(vio) == VIO_TYPE_SSL && SSL_get_peer_certificate(vio->ssl_))
if (vio_type(vio) == VIO_TYPE_SSL &&
SSL_get_peer_certificate(vio->ssl_))
user_access=acl_user->access;
break;
case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
@ -637,8 +626,8 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
else
{
if (global_system_variables.log_warnings)
sql_print_error("X509 ciphers mismatch: should be '%s' but is '%s'",
acl_user->ssl_cipher,
sql_print_error("X509 ciphers mismatch: should be '%s'"
"but is '%s'", acl_user->ssl_cipher,
SSL_get_cipher(vio->ssl_));
user_access=NO_ACCESS;
break;
@ -658,8 +647,8 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
if (strcmp(acl_user->x509_issuer, ptr))
{
if (global_system_variables.log_warnings)
sql_print_error("X509 issuer mismatch: should be '%s' but is '%s'",
acl_user->x509_issuer, ptr);
sql_print_error("X509 issuer mismatch: should be '%s' "
"but is '%s'", acl_user->x509_issuer, ptr);
user_access=NO_ACCESS;
free(ptr);
break;
@ -687,22 +676,22 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
}
break;
}
/* end of SSL stuff: assign result */
thd->master_access= user_access;
}
#else /* HAVE_OPENSSL */
user_access=acl_user->access;
thd->master_access= acl_user->access;
#endif /* HAVE_OPENSSL */
thd->priv_user= acl_user->user ? thd->user : (char *) "";
*mqh= acl_user->user_resource;
if (!acl_user->user)
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
if (acl_user->host.hostname)
strmake(priv_host, acl_user->host.hostname, MAX_HOSTNAME);
strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
else
*priv_host= 0;
unlock_and_exit:
*thd->priv_host= 0;
}
VOID(pthread_mutex_unlock(&acl_cache->lock));
DBUG_RETURN(user_access);
DBUG_RETURN(res);
}
@ -713,8 +702,9 @@ static byte* check_get_key(ACL_USER *buff,uint *length,
return (byte*) buff->host.hostname;
}
static void acl_update_user(const char *user, const char *host,
const char *password,
const char *password, uint password_len,
enum SSL_type ssl_type,
const char *ssl_cipher,
const char *x509_issuer,
@ -750,20 +740,9 @@ static void acl_update_user(const char *user, const char *host,
acl_user->x509_subject= (x509_subject ?
strdup_root(&mem,x509_subject) : 0);
}
if (password)
{
if (!password[0]) /* If password is empty set it to null */
{
acl_user->password=0;
acl_user->pversion=0; // just initialize
}
else
{
acl_user->password=(char*) ""; // Just point at something
get_salt_from_password(acl_user->salt,password);
acl_user->pversion=get_password_version(acl_user->password);
}
}
set_user_salt(acl_user, password, password_len);
/* search complete: */
break;
}
}
@ -772,7 +751,7 @@ static void acl_update_user(const char *user, const char *host,
static void acl_insert_user(const char *user, const char *host,
const char *password,
const char *password, uint password_len,
enum SSL_type ssl_type,
const char *ssl_cipher,
const char *x509_issuer,
@ -783,7 +762,6 @@ static void acl_insert_user(const char *user, const char *host,
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.user_resource = *mqh;
acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
@ -793,12 +771,8 @@ static void acl_insert_user(const char *user, const char *host,
acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0;
acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0;
acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
if (password)
{
acl_user.password=(char*) ""; // Just point at something
get_salt_from_password(acl_user.salt,password);
acl_user.pversion=get_password_version(password);
}
set_user_salt(&acl_user, password, password_len);
VOID(push_dynamic(&acl_users,(gptr) &acl_user));
if (!acl_user.host.hostname || acl_user.host.hostname[0] == wild_many
@ -1135,7 +1109,6 @@ bool check_change_password(THD *thd, const char *host, const char *user)
bool change_password(THD *thd, const char *host, const char *user,
char *new_password)
{
uint length=0;
DBUG_ENTER("change_password");
DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
host,user,new_password));
@ -1144,37 +1117,27 @@ bool change_password(THD *thd, const char *host, const char *user,
if (check_change_password(thd, host, user))
DBUG_RETURN(1);
/*
password should always be 0,16 or 45 chars;
Simple hack to avoid cracking
*/
length=(uint) strlen(new_password);
if (length != 45)
new_password[length & 16]=0;
VOID(pthread_mutex_lock(&acl_cache->lock));
ACL_USER *acl_user;
if (!(acl_user= find_acl_user(host, user)))
{
send_error(thd, ER_PASSWORD_NO_MATCH);
VOID(pthread_mutex_unlock(&acl_cache->lock));
send_error(thd, ER_PASSWORD_NO_MATCH);
DBUG_RETURN(1);
}
/* update loaded acl entry: */
uint new_password_len= new_password ? strlen(new_password) : 0;
set_user_salt(acl_user, new_password, new_password_len);
if (update_user_table(thd,
acl_user->host.hostname ? acl_user->host.hostname : "",
acl_user->user ? acl_user->user : "",
new_password))
new_password, new_password_len))
{
VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
send_error(thd,0); /* purecov: deadcode */
DBUG_RETURN(1); /* purecov: deadcode */
}
get_salt_from_password(acl_user->salt,new_password);
acl_user->pversion=get_password_version(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));
@ -1210,7 +1173,7 @@ find_acl_user(const char *host, const char *user)
if (!acl_user->user && !user[0] ||
acl_user->user && !strcmp(user,acl_user->user))
{
if (compare_hostname(&(acl_user->host),host,host))
if (compare_hostname(&acl_user->host,host,host))
{
DBUG_RETURN(acl_user);
}
@ -1280,7 +1243,7 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
****************************************************************************/
static bool update_user_table(THD *thd, const char *host, const char *user,
const char *new_password)
const char *new_password, uint new_password_len)
{
TABLE_LIST tables;
TABLE *table;
@ -1304,7 +1267,7 @@ static bool update_user_table(THD *thd, const char *host, const char *user,
DBUG_RETURN(1); /* purecov: deadcode */
}
store_record(table,record[1]);
table->field[2]->store(new_password,(uint) strlen(new_password), &my_charset_latin1);
table->field[2]->store(new_password, new_password_len, &my_charset_latin1);
if ((error=table->file->update_row(table->record[1],table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
@ -1352,24 +1315,24 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
{
int error = -1;
bool old_row_exists=0;
char *password,empty_string[1];
char empty_string[]= { '\0' };
char *password= empty_string;
uint password_len= 0;
char what= (revoke_grant) ? 'N' : 'Y';
DBUG_ENTER("replace_user_table");
safe_mutex_assert_owner(&acl_cache->lock);
password=empty_string;
empty_string[0]=0;
if (combo.password.str && combo.password.str[0])
{
if ((combo.password.length != HASH_PASSWORD_LENGTH)
&& combo.password.length != HASH_OLD_PASSWORD_LENGTH)
if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
{
my_printf_error(ER_PASSWORD_NO_MATCH,
"Password hash should be a %d-digit hexadecimal number",
MYF(0),HASH_PASSWORD_LENGTH);
MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
DBUG_RETURN(-1);
}
password_len= combo.password.length;
password=combo.password.str;
}
@ -1395,16 +1358,19 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
}
old_row_exists = 0;
restore_record(table,default_values); // cp empty row from default_values
table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
table->field[1]->store(combo.user.str,combo.user.length, &my_charset_latin1);
table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1);
table->field[0]->store(combo.host.str,combo.host.length,
&my_charset_latin1);
table->field[1]->store(combo.user.str,combo.user.length,
&my_charset_latin1);
table->field[2]->store(password, password_len,
&my_charset_latin1);
}
else
{
old_row_exists = 1;
store_record(table,record[1]); // Save copy for update
if (combo.password.str) // If password given
table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1);
table->field[2]->store(password, password_len, &my_charset_latin1);
}
/* Update table columns with new privileges */
@ -1501,10 +1467,8 @@ end:
if (!error)
{
acl_cache->clear(1); // Clear privilege cache
if (!combo.password.str)
password=0; // No password given on command
if (old_row_exists)
acl_update_user(combo.user.str,combo.host.str,password,
acl_update_user(combo.user.str, combo.host.str, password, password_len,
thd->lex.ssl_type,
thd->lex.ssl_cipher,
thd->lex.x509_issuer,
@ -1512,7 +1476,7 @@ end:
&thd->lex.mqh,
rights);
else
acl_insert_user(combo.user.str,combo.host.str,password,
acl_insert_user(combo.user.str, combo.host.str, password, password_len,
thd->lex.ssl_type,
thd->lex.ssl_cipher,
thd->lex.x509_issuer,
@ -2915,12 +2879,15 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
global.append ("'@'",3);
global.append(lex_user->host.str,lex_user->host.length);
global.append ('\'');
if (acl_user->password)
if (acl_user->salt_len)
{
char passd_buff[HASH_PASSWORD_LENGTH+1];
make_password_from_salt(passd_buff,acl_user->salt,acl_user->pversion);
char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
if (acl_user->salt_len == SCRAMBLE_LENGTH)
make_password_from_salt(passwd_buff, acl_user->salt);
else
make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt);
global.append(" IDENTIFIED BY PASSWORD '",25);
global.append(passd_buff);
global.append(passwd_buff);
global.append('\'');
}
/* "show grants" SSL related stuff */

View File

@ -111,9 +111,9 @@ 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
char *user;
uint8 salt[SCRAMBLE_LENGTH+1]; // scrambled password in binary form
uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 3.23, 20 - 4.1.1
enum SSL_type ssl_type;
const char *ssl_cipher, *x509_issuer, *x509_subject;
};
@ -135,11 +135,8 @@ void acl_reload(THD *thd);
void acl_free(bool end=0);
ulong acl_get(const char *host, const char *ip, const char *bin_ip,
const char *user, const char *db);
ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
const char *password,const char *scramble,
char **priv_user, char *priv_host,
bool old_ver, USER_RESOURCES *max,char* prepared_scramble,
uint *cur_priv_version, ACL_USER **cached_user);
int acl_getroot(THD *thd, USER_RESOURCES *mqh,
const char *passwd, uint passwd_len, bool old_ver);
bool acl_check_host(const char *host, const char *ip);
bool check_change_password(THD *thd, const char *host, const char *user);
bool change_password(THD *thd, const char *host, const char *user,

View File

@ -139,6 +139,7 @@ THD::THD():user_time(0), is_fatal_error(0),
set_query_id=1;
db_access=NO_ACCESS;
version=refresh_version; // For boot
*scramble= *scramble_323= '\0';
init();
/* Initialize sub structures */

View File

@ -529,10 +529,16 @@ public:
enum_tx_isolation session_tx_isolation;
/* for user variables replication*/
DYNAMIC_ARRAY user_var_events;
// extend scramble to handle new auth
char scramble[SCRAMBLE41_LENGTH+1];
// old scramble is needed to handle old clients
char old_scramble[SCRAMBLE_LENGTH+1];
/* scramble - random string sent to client on handshake */
char scramble[SCRAMBLE_LENGTH+1];
/*
The same as scramble but for old password checking routines. It always
contains first N bytes of scramble.
See check_connection() at sql_parse.cc for authentification details.
*/
char scramble_323[SCRAMBLE_LENGTH_323+1];
uint8 query_cache_type; // type of query cache processing
bool slave_thread;
bool set_query_id,locked,count_cuted_fields,some_tables_deleted;

View File

@ -178,151 +178,118 @@ end:
/*
Check if user is ok
Check if user exist and password supplied is correct.
SYNOPSIS
check_user()
thd Thread handle
command Command for connection (for log)
user Name of user trying to connect
passwd Scrambled password sent from client
db Database to connect to
check_count If set to 1, don't allow too many connection
simple_connect If 1 then client is of old type and we should connect
using the old method (no challange)
do_send_error Set to 1 if we should send error to user
prepared_scramble Buffer to store hash password of new connection
had_password Set to 1 if the user gave a password
cur_priv_version Check flag to know if someone flushed the privileges
since last code
hint_user Pointer used by acl_getroot() to remmeber user for
next call
RETURN
0 ok
thd->user, thd->master_access, thd->priv_user, thd->db and
thd->db_access are updated
1 Access denied; Error sent to client
-1 If do_send_error == 1: Failed connect, error sent to client
If do_send_error == 0: Prepare for stage of connect
thd INOUT thread handle, thd->{host,user,ip} are used
command IN originator of the check: now check_user is called
during connect and change user procedures; used for
logging.
passwd IN scrambled password recieved from client
passwd_len IN length of scrambled password
db IN database name to connect to, may be NULL
check_count IN dont know exactly
Note, that host, user and passwd may point to communication buffer.
Current implementation does not depened on that, but future changes
should be done with this in mind.
RETURN VALUE
0 OK; thd->user, thd->master_access, thd->priv_user, thd->db and
thd->db_access are updated; OK is sent to client;
1 access denied or internal error; error is sent to client
Note, that this return semantics differs from check_connection,
which returns -1 if message was already sent.
-1 acl entry for this user contains old scramble, but passwd contains
new one, error is not sent to client
*/
static int check_user(THD *thd,enum_server_command command, const char *user,
const char *passwd, const char *db, bool check_count,
bool simple_connect, bool do_send_error,
char *prepared_scramble, bool had_password,
uint *cur_priv_version, ACL_USER** hint_user)
static int check_user(THD *thd, enum enum_server_command command,
const char *passwd, uint passwd_len, const char *db,
bool check_count)
{
thd->db=0;
thd->db_length=0;
USER_RESOURCES ur;
char tmp_passwd[SCRAMBLE41_LENGTH];
DBUG_ENTER("check_user");
/*
Move password to temporary buffer as it may be stored in communication
buffer
Why this is set here? - probably to reset current DB to 'no database
selected' in case of 'change user' failure.
*/
strmake(tmp_passwd, passwd, sizeof(tmp_passwd));
passwd= tmp_passwd; // Use local copy
thd->db= 0;
thd->db_length= 0;
/* We shall avoid dupplicate user allocations here */
if (!thd->user && !(thd->user = my_strdup(user, MYF(0))))
USER_RESOURCES ur;
int res= acl_getroot(thd, &ur, passwd, passwd_len,
protocol_version == 9 ||
!(thd->client_capabilities & CLIENT_LONG_PASSWORD));
if (res == 0 && !(thd->master_access & NO_ACCESS)) // authentification is OK
{
send_error(thd,ER_OUT_OF_RESOURCES);
DBUG_RETURN(1);
}
thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user,
passwd, thd->scramble,
&thd->priv_user, thd->priv_host,
(protocol_version == 9 ||
!(thd->client_capabilities &
CLIENT_LONG_PASSWORD)),
&ur,prepared_scramble,
cur_priv_version,hint_user);
DBUG_PRINT("info",
("Capabilities: %d packet_length: %ld Host: '%s' Login user: '%s' Priv_user: '%s' Using password: %s Access: %u db: '%s'",
("Capabilities: %d packet_length: %ld Host: '%s' "
"Login user: '%s' Priv_user: '%s' Using password: %s "
"Access: %u db: '%s'",
thd->client_capabilities, thd->max_client_packet_length,
thd->host_or_ip, thd->user, thd->priv_user,
had_password ? "yes": "no",
passwd_len ? "yes": "no",
thd->master_access, thd->db ? thd->db : "*none*"));
/*
In case we're going to retry we should not send error message at this
point
*/
if (thd->master_access & NO_ACCESS)
{
if (do_send_error || !had_password || !*hint_user)
{
DBUG_PRINT("info",("Access denied"));
/*
Old client should get nicer error message if password version is
not supported
*/
if (simple_connect && *hint_user && (*hint_user)->pversion)
{
net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
}
else
{
net_printf(thd, ER_ACCESS_DENIED_ERROR,
thd->user,
thd->host_or_ip,
had_password ? ER(ER_YES) : ER(ER_NO));
mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR),
thd->user,
thd->host_or_ip,
had_password ? ER(ER_YES) : ER(ER_NO));
}
DBUG_RETURN(1); // Error already given
}
DBUG_PRINT("info",("Prepare for second part of handshake"));
DBUG_RETURN(-1); // no report error in special handshake
}
if (check_count)
{
VOID(pthread_mutex_lock(&LOCK_thread_count));
bool tmp=(thread_count - delayed_insert_threads >= max_connections &&
!(thd->master_access & SUPER_ACL));
bool count_ok= thread_count < max_connections + delayed_insert_threads ||
thd->master_access & SUPER_ACL;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
if (tmp)
{ // Too many connections
if (!count_ok)
{ // too many connections
send_error(thd, ER_CON_COUNT_ERROR);
DBUG_RETURN(1);
}
}
/* Why logging is performed before all checks've passed? */
mysql_log.write(thd,command,
(thd->priv_user == thd->user ?
(char*) "%s@%s on %s" :
(char*) "%s@%s as anonymous on %s"),
user,
thd->host_or_ip,
thd->user, thd->host_or_ip,
db ? db : (char*) "");
/* Why is it set here? */
thd->db_access=0;
/* Don't allow user to connect if he has done too many queries */
if ((ur.questions || ur.updates || ur.connections) &&
get_or_create_user_conn(thd,user,thd->host_or_ip,&ur))
get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur))
DBUG_RETURN(1);
if (thd->user_connect && thd->user_connect->user_resources.connections &&
check_for_max_user_connections(thd, thd->user_connect))
DBUG_RETURN(1);
/* Change database if necessary: OK or FAIL is sent in mysql_change_db */
if (db && db[0])
{
int error= test(mysql_change_db(thd,db));
if (error && thd->user_connect)
if (mysql_change_db(thd, db))
{
if (thd->user_connect)
decrease_user_connections(thd->user_connect);
DBUG_RETURN(error);
DBUG_RETURN(1);
}
send_ok(thd); // Ready to handle questions
thd->password= test(passwd[0]); // Remember for error messages
DBUG_RETURN(0); // ok
}
else
send_ok(thd);
thd->password= test(passwd_len); // remember for error messages
/* Ready to handle queries */
}
else if (res != -1) // authentication failure
{
net_printf(thd, ER_ACCESS_DENIED_ERROR,
thd->user,
thd->host_or_ip,
passwd_len ? ER(ER_YES) : ER(ER_NO));
mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
thd->user,
thd->host_or_ip,
passwd_len ? ER(ER_YES) : ER(ER_NO));
}
DBUG_RETURN(res);
}
/*
Check for maximum allowable user connections, if the mysqld server is
@ -525,35 +492,80 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
/*
Check connnectionn and get priviliges
Perform check for scrambled password, re-request scrambled password
from client if necessary. See also help for check_user.
SYNOPSIS
check_connections
thd Thread handle
authenticate()
RETURN VALUE
0 success, OK sent to client
-1 error, sent to client
> 0 error, not sent to client
*/
static
int
authenticate(THD *thd, enum enum_server_command command,
const char *passwd, uint passwd_len, const char *db,
bool check_count)
{
if (passwd_len != 0 &&
passwd_len != SCRAMBLE_LENGTH &&
passwd_len != SCRAMBLE_LENGTH_323)
return 1;
int res= check_user(thd, COM_CONNECT, passwd, passwd_len, db, check_count);
if (res < 0)
{
/*
This happens when client (new) sends password scrambled with
scramble(), but database holds old value (scrambled with
scramble_323()). Here we please client to send scrambled_password
in old format.
*/
char buff[NAME_LEN + 1];
/* save db because network buffer is to hold new packet */
if (db)
{
strmake(buff, db, NAME_LEN);
db= buff;
}
NET *net= &thd->net;
if (my_net_write(net, thd->scramble_323, SCRAMBLE_LENGTH_323 + 1) ||
net_flush(net) ||
my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very
{ // specific packet size
inc_host_errors(&thd->remote.sin_addr);
return ER_HANDSHAKE_ERROR;
}
/* Final attempt to check the user based on reply */
/* So as passwd is short, errcode is always sent to user and res >= 0 */
res= check_user(thd, COM_CONNECT, (char *) net->read_pos,
SCRAMBLE_LENGTH_323, db, check_count);
}
return res > 0 ? -1 : 0;
}
/*
Perform handshake, authorize client and update thd ACL variables.
SYNOPSIS
check_connection()
thd INOUT thread handle
RETURN
0 ok
-1 Error, which is sent to user
> 0 Error code (not sent to user)
0 success, OK is sent to user
-1 error, which is sent to user
> 0 error code (not sent to user)
*/
#ifndef EMBEDDED_LIBRARY
static int
check_connections(THD *thd)
check_connection(THD *thd)
{
int res;
uint connect_errors= 0;
uint cur_priv_version;
bool using_password;
NET *net= &thd->net;
char *end, *user, *passwd, *db;
char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble&hash */
ACL_USER* cached_user=NULL; /* Initialise to NULL for first stage */
DBUG_PRINT("info",("New connection received on %s",
vio_description(net->vio)));
/* Remove warning from valgrind. TODO: Fix it in password.c */
bzero((char*) &prepared_scramble[0], sizeof(prepared_scramble));
DBUG_PRINT("info",
("New connection received on %s", vio_description(net->vio)));
if (!thd->host) // If TCP/IP connection
{
char ip[30];
@ -601,6 +613,7 @@ check_connections(THD *thd)
vio_keepalive(net->vio, TRUE);
ulong pkt_len= 0;
char *end;
{
/* buff[] needs to big enough to hold the server_version variable */
char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
@ -620,15 +633,32 @@ check_connections(THD *thd)
end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
int4store((uchar*) end, thd->thread_id);
end+= 4;
memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1);
end+=SCRAMBLE_LENGTH +1;
/*
So as check_connection is the only entry point to authorization
procedure, scramble is set here. This gives us new scramble for
each handshake.
*/
create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
strmake(thd->scramble_323, thd->scramble, SCRAMBLE_LENGTH_323);
/*
Old clients does not understand long scrambles, but can ignore packet
tail: that's why first part of scramble is placed here, and second
part at the end of packet.
*/
end= strmake(end, thd->scramble_323, SCRAMBLE_LENGTH_323) + 1;
int2store(end, client_flags);
/* write server characteristics: up to 16 bytes allowed */
end[2]=(char) default_charset_info->number;
int2store(end+3, thd->server_status);
bzero(end+5, 13);
end+= 18;
/* write scramble tail */
end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323,
SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1;
// At this point we write connection message and read reply
/* At this point we write connection message and read reply */
if (net_write_command(net, (uchar) protocol_version, "", 0, buff,
(uint) (end-buff)) ||
(pkt_len= my_net_read(net)) == packet_error ||
@ -719,16 +749,7 @@ check_connections(THD *thd)
return(ER_HANDSHAKE_ERROR);
}
user= end;
passwd= strend(user)+1;
db=0;
using_password= test(passwd[0]);
if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB)
db=strend(passwd)+1;
/* We can get only old hash at this point */
if (using_password && strlen(passwd) != SCRAMBLE_LENGTH)
return ER_HANDSHAKE_ERROR;
/* why has it been put here? */
if (thd->client_capabilities & CLIENT_INTERACTIVE)
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
@ -737,60 +758,19 @@ check_connections(THD *thd)
net->return_status= &thd->server_status;
net->read_timeout=(uint) thd->variables.net_read_timeout;
/* Simple connect only for old clients. New clients always use secure auth */
bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
char *user= end;
char *passwd= strend(user)+1;
uint passwd_len= strlen(passwd);
/* Check user permissions. If password failure we'll get scramble back */
if ((res=check_user(thd, COM_CONNECT, user, passwd, db, 1, simple_connect,
simple_connect, prepared_scramble, using_password,
&cur_priv_version,
&cached_user)) < 0)
{
/* Store current used and database as they are erased with next packet */
char tmp_user[USERNAME_LENGTH+1];
char tmp_db[NAME_LEN+1];
char *db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
passwd+passwd_len+1 : 0;
/* If the client is old we just have to return error */
if (simple_connect)
return -1;
DBUG_PRINT("info",("password challenge"));
tmp_user[0]= tmp_db[0]= 0;
if (user)
strmake(tmp_user,user,USERNAME_LENGTH);
if (db)
strmake(tmp_db,db,NAME_LEN);
/* Write hash and encrypted scramble to client */
if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4) ||
net_flush(net))
{
inc_host_errors(&thd->remote.sin_addr);
return ER_HANDSHAKE_ERROR;
}
/* Reading packet back */
if ((pkt_len= my_net_read(net)) == packet_error)
{
inc_host_errors(&thd->remote.sin_addr);
return ER_HANDSHAKE_ERROR;
}
/* We have to get very specific packet size */
if (pkt_len != SCRAMBLE41_LENGTH)
{
inc_host_errors(&thd->remote.sin_addr);
return ER_HANDSHAKE_ERROR;
}
/* Final attempt to check the user based on reply */
if (check_user(thd,COM_CONNECT, tmp_user, (char*)net->read_pos,
tmp_db, 1, 0, 1, prepared_scramble, using_password,
&cur_priv_version,
&cached_user))
return -1;
}
else if (res)
return -1; // Error sent from check_user()
return 0;
if (thd->user)
x_free(thd->user);
thd->user= my_strdup(user, MYF(0));
if (!thd->user)
return(ER_OUT_OF_RESOURCES);
return authenticate(thd, COM_CONNECT, passwd, passwd_len, db, true);
}
@ -847,7 +827,7 @@ pthread_handler_decl(handle_one_connection,arg)
NET *net= &thd->net;
thd->thread_stack= (char*) &thd;
if ((error=check_connections(thd)))
if ((error=check_connection(thd)))
{ // Wrong permissions
if (error > 0)
net_printf(thd,error,thd->host_or_ip);
@ -1152,116 +1132,60 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_CHANGE_USER:
{
thd->change_user();
thd->clear_error(); // If errors from rollback
thd->clear_error(); // if errors from rollback
statistic_increment(com_other, &LOCK_status);
char *user= (char*) packet;
char *passwd= strend(user)+1;
char *db= strend(passwd)+1;
uint passwd_len= strlen(passwd);
char *db= passwd + passwd_len + 1;
/* Small check for incomming packet */
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
{
send_error(thd, ER_UNKNOWN_COM_ERROR);
break;
}
/* Save user and privileges */
uint save_master_access= thd->master_access;
uint save_db_access= thd->db_access;
uint save_db_length= thd->db_length;
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_db= thd->db;
USER_CONN *save_uc= thd->user_connect;
bool simple_connect;
bool using_password;
char prepared_scramble[SCRAMBLE41_LENGTH+4];/* Buffer for scramble,hash */
char tmp_user[USERNAME_LENGTH+1];
char tmp_db[NAME_LEN+1];
ACL_USER* cached_user ; /* Cached user */
uint cur_priv_version; /* Cached grant version */
int res;
ulong pkt_len= 0; /* Length of reply packet */
bzero((char*) prepared_scramble, sizeof(prepared_scramble));
/* Small check for incomming packet */
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
goto restore_user_err;
/* 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;
cached_user= NULL;
/* 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]);
if (simple_connect) /* Restore scramble for old clients */
memcpy(thd->scramble,thd->old_scramble,9);
/*
Check user permissions. If password failure we'll get scramble back
Do not retry if we already have sent error (result>0)
*/
if ((res=check_user(thd,COM_CHANGE_USER, user, passwd, db, 0,
simple_connect, simple_connect, prepared_scramble,
using_password, &cur_priv_version, &cached_user)) < 0)
thd->user= my_strdup(user, MYF(0));
if (!thd->user)
{
/* 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 */
tmp_user[0]= tmp_db[0]= 0;
if (user)
strmake(tmp_user,user,USERNAME_LENGTH);
if (db)
strmake(tmp_db,db,NAME_LEN);
/* 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_CHANGE_USER, tmp_user, (char*) net->read_pos,
tmp_db, 0, 0, 1, prepared_scramble, using_password,
&cur_priv_version, &cached_user))
goto restore_user;
}
else if (res)
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;
thd->user= save_user;
send_error(thd, ER_OUT_OF_RESOURCES);
break;
}
/* Bad luck we shall restore old user */
restore_user_err:
int res= authenticate(thd, COM_CHANGE_USER, passwd, passwd_len, db, false);
if (res)
{
/* authentification failure, we shall restore old user */
if (res > 0)
send_error(thd, ER_UNKNOWN_COM_ERROR);
restore_user:
x_free(thd->user);
thd->user= save_user;
thd->priv_user= save_priv_user;
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;
}
else
{
/* 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);
}
break;
}
#endif /* EMBEDDED_LIBRARY */
@ -3158,7 +3082,7 @@ error:
Check grants for commands which work only with one table and all other
tables belong to subselects.
SYNOPSYS
SYNOPSIS
single_table_command_access()
thd - Thread handler
privilege - asked privelage

View File

@ -7,7 +7,7 @@ typedef struct st_slave_info
uint32 rpl_recovery_rank, master_id;
char host[HOSTNAME_LENGTH+1];
char user[USERNAME_LENGTH+1];
char password[HASH_PASSWORD_LENGTH+1];
char password[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
uint16 port;
THD* thd;
} SLAVE_INFO;

View File

@ -2515,9 +2515,10 @@ simple_expr:
| NOW_SYM '(' expr ')'
{ $$= new Item_func_now($3); Lex->safe_to_cache_query=0;}
| PASSWORD '(' expr ')'
{ $$= new Item_func_password($3); }
| PASSWORD '(' expr ',' expr ')'
{ $$= new Item_func_password($3,$5); }
{
$$= use_old_passwords ? (Item *) new Item_func_old_password($3) :
(Item *) new Item_func_password($3);
}
| POINT_SYM '(' expr ',' expr ')'
{ $$= new Item_func_point($3,$5); }
| POINTFROMTEXT '(' expr ')'
@ -4604,11 +4605,20 @@ text_or_password:
{
if (!$3.length)
$$=$3.str;
else if (use_old_passwords)
{
char *buff= (char *)
YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
if (buff)
make_scrambled_password_323(buff, $3.str);
$$=buff;
}
else
{
char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1);
make_scrambled_password(buff,$3.str,use_old_passwords,
&YYTHD->rand);
char *buff= (char *)
YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
if (buff)
make_scrambled_password(buff, $3.str);
$$=buff;
}
}
@ -4918,13 +4928,23 @@ grant_user:
$$=$1; $1->password=$4;
if ($4.length)
{
char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1);
if (buff)
if (use_old_passwords)
{
make_scrambled_password(buff,$4.str,use_old_passwords,
&YYTHD->rand);
char *buff=
(char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
if (buff)
make_scrambled_password_323(buff, $4.str);
$1->password.str= buff;
$1->password.length=HASH_PASSWORD_LENGTH;
$1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
}
else
{
char *buff=
(char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
if (buff)
make_scrambled_password(buff, $4.str);
$1->password.str= buff;
$1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
}
}
}