Commit df23d18e authored by unknown's avatar unknown

Merge bk-internal:/home/bk/mysql-4.1/

into serg.mylan:/usr/home/serg/Abk/mysql-4.1


sql/item_create.cc:
  Auto merged
sql/item_create.h:
  Auto merged
sql/item_strfunc.cc:
  Auto merged
sql/item_strfunc.h:
  Auto merged
sql/lex.h:
  Auto merged
sql/mysql_priv.h:
  Auto merged
sql/mysqld.cc:
  Auto merged
sql/sql_parse.cc:
  Auto merged
sql/sql_yacc.yy:
  Auto merged
parents 3350532e 3143ac78
......@@ -23,6 +23,7 @@
.vimrc
50
=6
BUILD/compile-pentium-maintainer
BitKeeper/etc/config
BitKeeper/etc/csets
BitKeeper/etc/csets-in
......@@ -30,6 +31,8 @@ BitKeeper/etc/csets-out
BitKeeper/etc/gone
BitKeeper/etc/level
BitKeeper/etc/pushed
BitKeeper/post-commit
BitKeeper/post-commit-manual
BitKeeper/tmp/*
BitKeeper/tmp/bkr3sAHD
BitKeeper/tmp/gone
......@@ -223,6 +226,7 @@ bkpull.log.5
bkpull.log.6
bkpush.log
build.log
build_tags.sh
client/insert_test
client/log_event.cc
client/log_event.h
......@@ -311,6 +315,8 @@ libmysql_r/conf_to_src
libmysql_r/my_static.h
libmysql_r/mysys_priv.h
libmysqld/backup_dir
libmysqld/client.c
libmysqld/client_settings.h
libmysqld/convert.cc
libmysqld/derror.cc
libmysqld/errmsg.c
......@@ -355,6 +361,7 @@ libmysqld/item_sum.cc
libmysqld/item_timefunc.cc
libmysqld/item_uniq.cc
libmysqld/key.cc
libmysqld/libmysql.c
libmysqld/lock.cc
libmysqld/log.cc
libmysqld/log_event.cc
......@@ -366,6 +373,7 @@ libmysqld/net_serv.cc
libmysqld/opt_ft.cc
libmysqld/opt_range.cc
libmysqld/opt_sum.cc
libmysqld/pack.c
libmysqld/password.c
libmysqld/procedure.cc
libmysqld/protocol.cc
......@@ -483,6 +491,7 @@ mysys/#mf_iocache.c#
mysys/charset2html
mysys/getopt.c
mysys/getopt1.c
mysys/main.cc
mysys/ste5KbMa
mysys/test_charset
mysys/test_dir
......@@ -586,6 +595,7 @@ stamp-h1
stamp-h2
stamp-h3
stamp-h4
start_mysqld.sh
strings/conf_to_src
strings/ctype_autoconf.c
strings/ctype_extra_sources.c
......@@ -612,6 +622,7 @@ support-files/mysql.spec
tags
test_xml
tests/client_test
tests/connect_test
thread_test
tmp/*
tools/my_vsnprintf.c
......@@ -623,7 +634,3 @@ vio/test-ssl
vio/test-sslclient
vio/test-sslserver
vio/viotest-ssl
libmysqld/client.c
libmysqld/client_settings.h
libmysqld/libmysql.c
libmysqld/pack.c
......@@ -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);
......
......@@ -229,7 +229,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*/
/* session-wide random string */
char scramble[SCRAMBLE_LENGTH+1];
/*
Set if this is the original connection, not a master or a slave we have
......
......@@ -49,8 +49,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 */
......@@ -302,31 +309,34 @@ extern "C" {
extern unsigned long max_allowed_packet;
extern unsigned long net_buffer_length;
void randominit(struct rand_struct *,unsigned long seed1,
/*
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,
my_bool old_ver);
my_bool check_scramble(const char *, const char *message,
unsigned long *salt,my_bool old_ver);
void create_random_string(char *to, uint length, struct rand_struct *rand_st);
void hash_password(ulong *to, const char *password, uint password_len);
void make_scrambled_password_323(char *to, const char *password);
void scramble_323(char *to, const char *message, const char *password);
my_bool check_scramble_323(const char *, const char *message,
unsigned long *salt);
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);
void 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 */
......
......@@ -290,4 +290,5 @@
#define ER_VARIABLE_IS_NOT_STRUCT 1271
#define ER_UNKNOWN_COLLATION 1272
#define ER_SLAVE_IGNORED_SSL_PARAMS 1273
#define ER_ERROR_MESSAGES 274
#define ER_SERVER_IS_IN_SECURE_AUTH_MODE 1274
#define ER_ERROR_MESSAGES 275
......@@ -604,40 +604,54 @@ 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 */
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
{
*end++= SCRAMBLE_LENGTH;
scramble(end, mysql->scramble, passwd);
end+= SCRAMBLE_LENGTH;
}
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,
(my_bool) (mysql->protocol_version == 9));
scramble_323(end, mysql->scramble, passwd);
end+= SCRAMBLE_LENGTH_323 + 1;
}
}
else
*end++= '\0'; // empty password
/* Add database if needed */
end=strmov(end+1,db ? db : "");
end= strmov(end, db ? db : "") + 1;
/* 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 (pkt_length == 1 && net->read_pos[0] == 254 &&
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, passwd);
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));
......
......@@ -30,6 +30,7 @@ show tables;
Tables_in_test
update mysql.user set password=old_password("gambling2") where user="test";
flush privileges;
set password=old_password('gambling3');
show tables;
Tables_in_mysql
columns_priv
......
select length(encrypt('foo', 'ff')) <> 0;
length(encrypt('foo', 'ff')) <> 0
1
select password("a",""), password("a",NULL), password("","a"), password(NULL,"a");
password("a","") password("a",NULL) password("","a") password(NULL,"a")
*2517f7235d68d4ba2e5019c93420523101157a792c01 NULL NULL
select password("aaaaaaaaaaaaaaaa","a"), password("a","aaaaaaaaaaaaaaaa");
password("aaaaaaaaaaaaaaaa","a") password("a","aaaaaaaaaaaaaaaa")
*2cd3b9a44e9a9994789a30f935c92f45a96c5472f381 *37c7c5c794ff144819f2531bf03c57772cd84e40db09
select old_password('test'), length(password("1")), length(encrypt('test')), encrypt('test','aa');
old_password('test') length(password("1")) length(encrypt('test')) encrypt('test','aa')
378b243e220ca493 45 13 aaqPiZY5xR5l.
select old_password(""), old_password(NULL), password(""), password(NULL);
old_password("") old_password(NULL) password("") password(NULL)
NULL NULL
select password('abc');
password('abc')
*0D3CED9BEC10A777AEC23CCC353A8C08A633045E
select password('');
password('')
select old_password('abc');
old_password('abc')
7cd2b5942be28759
select old_password('');
old_password('')
select password('gabbagabbahey');
password('gabbagabbahey')
*B0F99D2963660DD7E16B751EC9EE2F17B6A68FA6
select old_password('idkfa');
old_password('idkfa')
5c078dc54ca0fcca
select length(password('1'));
length(password('1'))
41
select length(encrypt('test'));
length(encrypt('test'))
13
select encrypt('test','aa');
encrypt('test','aa')
aaqPiZY5xR5l.
select old_password(NULL);
old_password(NULL)
NULL
select password(NULL);
password(NULL)
NULL
set global old_passwords=on;
select password('');
password('')
select old_password('');
old_password('')
select password('idkfa');
password('idkfa')
*B669C9DAC3AA6F2254B03CDEF8DFDD6B2D1054BA
select old_password('idkfa');
old_password('idkfa')
5c078dc54ca0fcca
set old_passwords=on;
select password('idkfa');
password('idkfa')
5c078dc54ca0fcca
select old_password('idkfa');
old_password('idkfa')
5c078dc54ca0fcca
set global old_passwords=off;
select password('idkfa');
password('idkfa')
5c078dc54ca0fcca
select old_password('idkfa');
old_password('idkfa')
5c078dc54ca0fcca
set old_passwords=off;
select password('idkfa ');
password('idkfa ')
*2DC31D90647B4C1ABC9231563D2236E96C9A2DB2
select password('idkfa');
password('idkfa')
*B669C9DAC3AA6F2254B03CDEF8DFDD6B2D1054BA
select password(' idkfa');
password(' idkfa')
*12B099E56BB7FE8D43C78FD834A9D1D11178D045
select old_password('idkfa');
old_password('idkfa')
5c078dc54ca0fcca
select old_password(' i d k f a ');
old_password(' i d k f a ')
5c078dc54ca0fcca
......@@ -48,8 +48,9 @@ flush privileges;
#connect (con1,localhost,test,gambling2,"");
#show tables;
connect (con1,localhost,test,gambling2,mysql);
set password=old_password('gambling3');
show tables;
connect (con1,localhost,test,gambling2,test);
connect (con1,localhost,test,gambling3,test);
show tables;
# Re enable this one day if error handling on connect will take place
......@@ -63,7 +64,9 @@ show tables;
#connect (con1,localhost,test,zorro,);
#--error 1045
# remove user 'test' so that other tests which may use 'test'
# do not depend on this test.
delete from mysql.user where user="test";
flush privileges;
......@@ -4,7 +4,33 @@ select length(encrypt('foo', 'ff')) <> 0;
--replace_result $1$aa$4OSUA5cjdx0RUQ08opV27/ aaqPiZY5xR5l.
# Test new and old password handling functions
select password("a",""), password("a",NULL), password("","a"), password(NULL,"a");
select password("aaaaaaaaaaaaaaaa","a"), password("a","aaaaaaaaaaaaaaaa");
select old_password('test'), length(password("1")), length(encrypt('test')), encrypt('test','aa');
select old_password(""), old_password(NULL), password(""), password(NULL);
select password('abc');
select password('');
select old_password('abc');
select old_password('');
select password('gabbagabbahey');
select old_password('idkfa');
select length(password('1'));
select length(encrypt('test'));
select encrypt('test','aa');
select old_password(NULL);
select password(NULL);
set global old_passwords=on;
select password('');
select old_password('');
select password('idkfa');
select old_password('idkfa');
set old_passwords=on;
select password('idkfa');
select old_password('idkfa');
set global old_passwords=off;
select password('idkfa');
select old_password('idkfa');
# this test shows that new scrambles honor spaces in passwords:
set old_passwords=off;
select password('idkfa ');
select password('idkfa');
select password(' idkfa');
select old_password('idkfa');
select old_password(' i d k f a ');
......@@ -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,"
......
......@@ -16,7 +16,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,
......
......@@ -1391,76 +1391,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 !
......@@ -1553,7 +1483,7 @@ CLI_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 ||
......@@ -1754,6 +1684,11 @@ CLI_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;
......@@ -1774,8 +1709,13 @@ CLI_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, end, SCRAMBLE_LENGTH_323);
end+= SCRAMBLE_LENGTH_323+1;
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))
......@@ -1784,6 +1724,14 @@ CLI_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;
charset_number= mysql->server_language;
/* Set character set */
......@@ -1860,9 +1808,12 @@ CLI_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;
......@@ -1949,7 +1900,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
mysql->server_status, client_flag));
/* This needs to be changed as it's not useful with big packets */
if (user && user[0])
strmake(end,user,32); /* Max user name */
strmake(end,user,USERNAME_LENGTH); /* Max user name */
else
read_user_name((char*) end);
......@@ -1958,41 +1909,30 @@ CLI_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*/
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
{
end=strend(end)+1;
*end=0; /* Store zero length scramble */
}
*end++= SCRAMBLE_LENGTH;
scramble(end, mysql->scramble, passwd);
end+= SCRAMBLE_LENGTH;
}
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,
(my_bool) (mysql->protocol_version == 9));
scramble_323(end, mysql->scramble, passwd);
end+= SCRAMBLE_LENGTH_323 + 1;
}
}
else
*end++= '\0'; /* empty password */
/* Add database if needed */
if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
{
end=strmake(end+1,db,NAME_LEN);
mysql->db=my_strdup(db,MYF(MY_WME));
db=0;
end= strmake(end, db, NAME_LEN) + 1;
mysql->db= my_strdup(db,MYF(MY_WME));
db= 0;
}
/* Write authentication package */
if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net))
......@@ -2003,8 +1943,34 @@ CLI_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 (pkt_length == 1 && net->read_pos[0] == 254 &&
mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
{
/*
By sending this very specific reply server asks us to send scrambled
password in old format.
*/
scramble_323(buff, mysql->scramble, passwd);
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;
......
......@@ -52,13 +52,6 @@ Item *create_func_ord(Item* a)
return new Item_func_ord(a);
}
Item *create_func_old_password(Item* a)
{
return new Item_func_old_password(a);
}
Item *create_func_asin(Item* a)
{
return new Item_func_asin(a);
......@@ -324,11 +317,6 @@ Item *create_func_quarter(Item* a)
return new Item_func_quarter(a);
}
Item *create_func_password(Item* a)
{
return new Item_func_password(a);
}
Item *create_func_radians(Item *a)
{
return new Item_func_units((char*) "radians",a,M_PI/180,0.0);
......
......@@ -67,14 +67,12 @@ Item *create_func_monthname(Item* a);
Item *create_func_nullif(Item* a, Item *b);
Item *create_func_oct(Item *);
Item *create_func_ord(Item* a);
Item *create_func_old_password(Item* a);
Item *create_func_period_add(Item* a, Item *b);
Item *create_func_period_diff(Item* a, Item *b);
Item *create_func_pi(void);
Item *create_func_pow(Item* a, Item *b);
Item *create_func_current_user(void);
Item *create_func_quarter(Item* a);
Item *create_func_password(Item* a);
Item *create_func_radians(Item *a);
Item *create_func_release_lock(Item* a);
Item *create_func_repeat(Item* a, Item *b);
......
......@@ -1327,95 +1327,49 @@ 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);
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;
}
char *Item_func_password::alloc(THD *thd, const char *password)
{
char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
if (buff)
make_scrambled_password(buff, password);
return buff;
}
/* Item_func_old_password */
String *Item_func_old_password::val_str(String *str)
{
String *res =args[0]->val_str(str);
String *res= args[0]->val_str(str);
if ((null_value=args[0]->null_value))
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;
}
char *Item_func_old_password::alloc(THD *thd, const char *password)
{
char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
if (buff)
make_scrambled_password_323(buff, password);
return buff;
}
#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
......
......@@ -254,30 +254,45 @@ class Item_func_rtrim :public Item_func_trim
};
/*
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"; }
static char *alloc(THD *thd, const char *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
'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"; }
static char *alloc(THD *thd, const char *password);
};
class Item_func_des_encrypt :public Item_str_func
{
String tmp_value;
......
......@@ -294,6 +294,7 @@ static SYMBOL symbols[] = {
{ "NULL", SYM(NULL_SYM),0,0},
{ "NUMERIC", SYM(NUMERIC_SYM),0,0},
{ "OFFSET", SYM(OFFSET_SYM),0,0},
{ "OLD_PASSWORD", SYM(OLD_PASSWORD),0,0},
{ "ON", SYM(ON),0,0},
{ "OPEN", SYM(OPEN_SYM),0,0},
{ "OPTIMIZE", SYM(OPTIMIZE),0,0},
......@@ -594,7 +595,6 @@ static SYMBOL sql_functions[] = {
{ "NUMPOINTS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_numpoints)},
{ "OCTET_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)},
{ "OCT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_oct)},
{ "OLD_PASSWORD", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_old_password)},
{ "ORD", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ord)},
{ "OVERLAPS", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_overlaps)},
{ "PERIOD_ADD", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_add)},
......
......@@ -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 MAX_PASSWORD_LENGTH 32
#define HOST_CACHE_SIZE 128
#define MAX_ACCEPT_RETRY 10 // Test accept this many times
......@@ -773,7 +770,7 @@ extern my_bool opt_safe_show_db, opt_local_infile, lower_case_table_names;
extern my_bool opt_slave_compressed_protocol, use_temp_pool;
extern my_bool opt_readonly;
extern my_bool opt_enable_named_pipe;
extern my_bool opt_old_passwords, use_old_passwords;
extern my_bool opt_secure_auth;
extern char *shared_memory_base_name, *mysqld_unix_port;
extern bool opt_enable_shared_memory;
......
......@@ -257,9 +257,10 @@ my_bool opt_local_infile, opt_external_locking, opt_slave_compressed_protocol;
my_bool opt_safe_user_create = 0, opt_no_mix_types = 0;
my_bool lower_case_table_names, opt_old_rpl_compat;
my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0;
my_bool opt_log_slave_updates= 0, opt_old_passwords=0, use_old_passwords=0;
my_bool opt_log_slave_updates= 0;
my_bool opt_console= 0, opt_bdb, opt_innodb, opt_isam;
my_bool opt_readonly, use_temp_pool, relay_log_purge;
my_bool opt_secure_auth= 0;
volatile bool mqh_used = 0;
uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options;
......@@ -2819,12 +2820,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
......@@ -3535,7 +3530,8 @@ enum options
OPT_EXPIRE_LOGS_DAYS,
OPT_DEFAULT_WEEK_FORMAT,
OPT_GROUP_CONCAT_MAX_LEN,
OPT_DEFAULT_COLLATION
OPT_DEFAULT_COLLATION,
OPT_SECURE_AUTH
};
......@@ -3845,9 +3841,10 @@ master-ssl",
(gptr*) &opt_no_mix_types, (gptr*) &opt_no_mix_types, 0, GET_BOOL, NO_ARG,
0, 0, 0, 0, 0, 0},
#endif
{"old-protocol", 'o', "Use the old (3.20) protocol client/server protocol.",
(gptr*) &protocol_version, (gptr*) &protocol_version, 0, GET_UINT, NO_ARG,
PROTOCOL_VERSION, 0, 0, 0, 0, 0},
{"old-passwords", OPT_OLD_PASSWORDS, "Use old password encryption method (needed for 4.0 and older clients).",
(gptr*) &global_system_variables.old_passwords,
(gptr*) &max_system_variables.old_passwords, 0, GET_BOOL, NO_ARG,
0, 0, 0, 0, 0, 0},
{"old-rpl-compat", OPT_OLD_RPL_COMPAT,
"Use old LOAD DATA format in the binary log (don't save data in file).",
(gptr*) &opt_old_rpl_compat, (gptr*) &opt_old_rpl_compat, 0, GET_BOOL,
......@@ -3914,8 +3911,6 @@ relay logs.",
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing).",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"old-passwords", OPT_OLD_PASSWORDS, "Use old password encryption method (needed for 4.0 and older clients).",
(gptr*) &opt_old_passwords, (gptr*) &opt_old_passwords, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
#ifndef TO_BE_DELETED
{"safe-show-database", OPT_SAFE_SHOW_DB,
"Deprecated option; One should use GRANT SHOW DATABASES instead...",
......@@ -3925,6 +3920,9 @@ relay logs.",
"Don't allow new user creation by the user who has no write privileges to the mysql.user table.",
(gptr*) &opt_safe_user_create, (gptr*) &opt_safe_user_create, 0, GET_BOOL,
NO_ARG, 0, 0, 0, 0, 0, 0},
{"secure-auth", OPT_SECURE_AUTH, "Disallow authentication for accounts that have old (pre-4.1) passwords.",
(gptr*) &opt_secure_auth, (gptr*) &opt_secure_auth, 0, GET_BOOL, NO_ARG,
my_bool(0), 0, 0, 0, 0, 0},
{"server-id", OPT_SERVER_ID,
"Uniquely identifies the server instance in the community of replication partners.",
(gptr*) &server_id, (gptr*) &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0,
......@@ -4714,7 +4712,8 @@ static void mysql_init_variables(void)
opt_log= opt_update_log= opt_bin_log= opt_slow_log= 0;
opt_disable_networking= opt_skip_show_db=0;
opt_logname= opt_update_logname= opt_binlog_index_name= opt_slow_logname=0;
opt_bootstrap= opt_myisam_log= use_old_passwords= 0;
opt_secure_auth= 0;
opt_bootstrap= opt_myisam_log= 0;
mqh_used= 0;
segfaulted= kill_in_progress= 0;
cleanup_done= 0;
......@@ -4818,6 +4817,7 @@ static void mysql_init_variables(void)
max_system_variables.select_limit= (ulonglong) HA_POS_ERROR;
global_system_variables.max_join_size= (ulonglong) HA_POS_ERROR;
max_system_variables.max_join_size= (ulonglong) HA_POS_ERROR;
global_system_variables.old_passwords= 0;
/* Variables that depends on compile options */
#ifndef DBUG_OFF
......@@ -4939,9 +4939,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
case 'L':
strmake(language, argument, sizeof(language)-1);
break;
case 'o':
protocol_version=PROTOCOL_VERSION-1;
break;
#ifdef HAVE_REPLICATION
case OPT_SLAVE_SKIP_ERRORS:
init_slave_skip_errors(argument);
......
......@@ -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,28 +65,18 @@
#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)
void randominit(struct rand_struct *rand_st, ulong seed1, ulong seed2)
{ /* For mysql 3.21.# */
#ifdef HAVE_purify
bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */
......@@ -94,35 +89,12 @@ void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
/*
Old (MySQL 3.20) random generation structure initialization
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)
{ /* For mysql 3.20.# */
rand_st->max_value= 0x01FFFFFFL;
rand_st->max_value_dbl=(double) rand_st->max_value;
seed1%=rand_st->max_value;
rand_st->seed1=seed1 ; rand_st->seed2=seed1/2;
}
/*
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,73 +106,24 @@ 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
hash_password()
result OUT store hash in this location
password IN plain text password to build hash
password_len IN password length (password may be not null-terminated)
*/
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
*/
void hash_password(ulong *result, const char *password)
void hash_password(ulong *result, const char *password, uint password_len)
{
register ulong nr=1345345333L, add=7, nr2=0x12345671L;
ulong tmp;
for (; *password ; password++)
const char *password_end= password + password_len;
for (; password < password_end; 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;
......@@ -208,519 +131,388 @@ void hash_password(ulong *result, const char *password)
}
result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */;
result[1]=nr2 & (((ulong) 1L << 31) -1L);
return;
}
/*
Stage one password hashing.
Used in MySQL 4.1 password handling
Create password to be stored in user database from raw string
Used for pre-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
make_scrambled_password_323()
to OUT store scrambled password here
password IN user-supplied password
*/
void password_hash_stage1(char *to, const char *password)
void make_scrambled_password_323(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);
ulong hash_res[2];
hash_password(hash_res, password, strlen(password));
sprintf(to, "%08lx%08lx", hash_res[0], hash_res[1]);
}
/*
Stage two password hashing.
Used in MySQL 4.1 password handling
Scramble string with password.
Used in pre 4.1 authentication phase.
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
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 at least
SRAMBLE_LENGTH_323 bytes long.
password IN Password to use while scrambling
*/
void password_hash_stage2(char *to, const char *salt)
void scramble_323(char *to, const char *message, const char *password)
{
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);
struct rand_struct rand_st;
ulong hash_pass[2], hash_message[2];
if (password && password[0])
{
char *to_start=to;
hash_password(hash_pass,password, strlen(password));
hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
randominit(&rand_st,hash_pass[0] ^ hash_message[0],
hash_pass[1] ^ hash_message[1]);
const char *message_end= message + SCRAMBLE_LENGTH_323;
for (; message < message_end; message++)
*to++= (char) (floor(my_rnd(&rand_st)*31)+64);
char extra=(char) (floor(my_rnd(&rand_st)*31));
while (to_start != to)
*(to_start++)^=extra;
}
*to= 0;
}
/*
Create password to be stored in user database from raw string
Handles both MySQL 4.1 and Pre-MySQL 4.1 passwords
Check scrambled message
Used in 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
check_scramble_323()
scrambled scrambled message to check.
message original random message which was used for scrambling; must
be exactly SCRAMBLED_LENGTH_323 bytes long and
NULL-terminated.
hash_pass password which should be used for scrambling
All params are IN.
RETURN VALUE
0 - password correct
!0 - password invalid
*/
void make_scrambled_password(char *to,const char *password,
my_bool force_old_scramble,
struct rand_struct *rand_st)
my_bool
check_scramble_323(const char *scrambled, const char *message,
ulong *hash_pass)
{
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 */
{
hash_password(hash_res,password);
sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
}
else /* New password 4.1 password scrambling */
struct rand_struct rand_st;
ulong hash_message[2];
char buff[16],*to,extra; /* Big enough for check */
const char *pos;
hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
randominit(&rand_st,hash_pass[0] ^ hash_message[0],
hash_pass[1] ^ hash_message[1]);
to=buff;
for (pos=scrambled ; *pos ; pos++)
*to++=(char) (floor(my_rnd(&rand_st)*31)+64);
extra=(char) (floor(my_rnd(&rand_st)*31));
to=buff;
while (*scrambled)
{
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]);
if (*scrambled++ != (char) (*to++ ^ extra))
return 1; /* Wrong password */
}
return 0;
}
static inline 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 binary string form to salt form
Used for MySQL 4.1 password handling
Convert password from hex string (as stored in mysql.user) to binary form.
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
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_bin_password(ulong *res,unsigned char *password,ulong salt)
void get_salt_from_password_323(ulong *res, const char *password)
{
unsigned char *password_end=password+SCRAMBLE41_LENGTH;
*res=salt;
res++;
/* Process password of known length*/
while (password<password_end)
res[0]= res[1]= 0;
if (password)
{
while (*password)
{
ulong val=0;
uint i;
for (i=0 ; i < 4 ; i++)
val=(val << 8)+(*password++);
for (i=0 ; i < 8 ; i++)
val=(val << 4)+char_val(*password++);
*res++=val;
}
}
}
/*
Validate password for MySQL 4.1 password handling.
Convert scrambled password from binary form to asciiz hex string.
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
make_password_from_salt_323()
to OUT store resulting string password here, at least 17 bytes
salt IN password in salt format, 2 ulongs
*/
my_bool validate_password(const char *password, const char *message,
ulong *salt)
void make_password_from_salt_323(char *to, const 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;
sprintf(to,"%08lx%08lx", salt[0], salt[1]);
}
/*
Get length of password string which is stored in mysql.user table
**************** MySQL 4.1.1 authentification routines *************
*/
/*
Generate string of printable random characters of requested length
SYNOPSIS
get_password_length()
force_old_scramble IN If we wish to use pre 4.1 scramble format
RETURN
password length >0
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
*/
int get_password_length(my_bool force_old_scramble)
void create_random_string(char *to, uint length, struct rand_struct *rand_st)
{
return (force_old_scramble) ? 16 : SHA1_HASH_SIZE*2+4+1;
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';
}
/*
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
*/
/* Character to use as version identifier for version 4.1 */
char get_password_version(const char *password)
{
if (password==NULL) return 0;
if (password[0]==PVERSION41_CHAR) return PVERSION41_CHAR;
return 0;
}
#define PVERSION41_CHAR '*'
/*
Get integer value of Hex character
Convert given octet sequence to asciiz string of hex characters;
str..str+len and 'to' may not overlap.
SYNOPSIS
char_val()
X IN Character to find value for
RETURN
Appropriate integer value
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 inline unsigned int char_val(char X)
static void
octet2hex(char *to, const uint8 *str, uint len)
{
return (uint) (X >= '0' && X <= '9' ? X-'0' :
X >= 'A' && X <= 'Z' ? X-'A'+10 :
X-'a'+10);
const uint8 *str_end= str + len;
for (; str != str_end; ++str)
{
*to++= _dig_vec[(*str & 0xF0) >> 4];
*to++= _dig_vec[*str & 0x0F];
}
*to= '\0';
}
/*
Get Binary salt from password as in mysql.user format
Convert given asciiz string of hex (0..9 a..f) characters to octet
sequence.
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
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
*/
void get_salt_from_password(ulong *res,const char *password)
static void
hex2octet(uint8 *to, const char *str, uint len)
{
if (password) /* zero salt corresponds to empty password */
const char *str_end= str + len;
while (str < str_end)
{
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;
register char tmp= char_val(*str++);
*to++= (tmp << 4) | char_val(*str++);
}
}
return;
}
/*
Get string version as stored in mysql.user from salt form
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
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
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
*/
void make_password_from_salt(char *to, ulong *hash_res,uint8 password_version)
static void
my_crypt(char *to, const uchar *s1, const uchar *s2, uint len)
{
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';
const uint8 *s1_end= s1 + len;
while (s1 < s1_end)
*to++= *s1++ ^ *s2++;
}
/*
Convert password in salted form to binary string password and hash-salt
For old password this involes one more hashing
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
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
make_scrambled_password()
buf OUT buffer of size 2*SHA1_HASH_SIZE + 2 to store hex string
password IN NULL-terminated password string
*/
void get_hash_and_password(ulong *salt, uint8 pversion, char *hash,
unsigned char *bin_password)
void
make_scrambled_password(char *to, const char *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);
}
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);
}
/*
Create key from old password to decode scramble
Used in 4.1 authentication with passwords stored old way
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 not stored
in the buf as it is not needed.
This function is used by client to create authenticated reply to the
server's greeting.
SYNOPSIS
create_key_from_old_password()
passwd IN Password used for key generation
key OUT Created 20 bytes key
RETURN
None
scramble()
buf OUT store scrambled string here. The buf must be at least
SHA1_HASH_SIZE bytes long.
message IN random message, must be exactly SCRAMBLE_LENGTH long and
NULL-terminated.
password IN users' password
*/
void create_key_from_old_password(const char *passwd, char *key)
void
scramble(char *to, const char *message, const char *password)
{
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);
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 uchar *) to, hash_stage1, SCRAMBLE_LENGTH);
}
/*
Scramble string with password
Used at pre 4.1 authentication phase.
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
scramble()
to OUT Store scrambled message here
message IN Message to scramble
password IN Password to use while scrambling
old_ver IN Forse old version random number generator
RETURN
End of scrambled string
check_scramble()
scramble clients' reply, presumably produced by scramble()
message original random string, previously sent to client
(presumably second argument of scramble()), must be
exactly SCRAMBLE_LENGTH long and NULL-terminated.
hash_stage2 hex2octet-decoded database entry
All params are IN.
RETURN VALUE
0 password is correct
!0 password is invalid
*/
char *scramble(char *to,const char *message,const char *password,
my_bool old_ver)
my_bool
check_scramble(const char *scramble, const char *message,
const uint8 *hash_stage2)
{
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);
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++)
*to++= (char) (floor(my_rnd(&rand_st)*31)+64);
if (!old_ver)
{ /* Make it harder to break */
char extra=(char) (floor(my_rnd(&rand_st)*31));
while (to_start != to)
*(to_start++)^=extra;
}
}
*to=0;
return to;
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 uchar *) 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);
}
/*
Check scrambled message
Used for pre 4.1 password handling
Convert scrambled password from asciiz hex string to binary form.
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
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
*/
my_bool check_scramble(const char *scrambled, const char *message,
ulong *hash_pass, my_bool old_ver)
void get_salt_from_password(uint8 *hash_stage2, const char *password)
{
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;
hex2octet(hash_stage2, password+1 /* skip '*' */, SHA1_HASH_SIZE * 2);
}
/* Check if this exactly N bytes. Overwise this is something fishy */
if (strlen(message_buffer)!=SCRAMBLE_LENGTH)
return 1; /* Wrong password */
/*
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
*/
hash_password(hash_message,message_buffer);
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]);
to=buff;
for (pos=scrambled ; *pos ; pos++)
*to++=(char) (floor(my_rnd(&rand_st)*31)+64);
if (old_ver)
extra=0;
else
extra=(char) (floor(my_rnd(&rand_st)*31));
to=buff;
while (*scrambled)
{
if (*scrambled++ != (char) (*to++ ^ extra))
return 1; /* Wrong password */
}
return 0;
void make_password_from_salt(char *to, const uint8 *hash_stage2)
{
*to++= PVERSION41_CHAR;
octet2hex(to, hash_stage2, SHA1_HASH_SIZE);
}
......@@ -349,6 +349,25 @@ send_eof(THD *thd, bool no_flush)
}
DBUG_VOID_RETURN;
}
/*
Please client to send scrambled_password in old format.
SYNOPSYS
send_old_password_request()
thd thread handle
RETURN VALUE
0 ok
!0 error
*/
bool send_old_password_request(THD *thd)
{
static char buff[1]= { (char) 254 };
NET *net= &thd->net;
return my_net_write(net, buff, 1) || net_flush(net);
}
#endif /* EMBEDDED_LIBRARY */
/*
......
......@@ -164,6 +164,7 @@ void net_printf(THD *thd,uint sql_errno, ...);
void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L,
const char *info=0);
void send_eof(THD *thd, bool no_flush=0);
bool send_old_password_request(THD *thd);
char *net_store_length(char *packet,ulonglong length);
char *net_store_length(char *packet,uint length);
char *net_store_data(char *to,const char *from, uint length);
......
......@@ -216,6 +216,7 @@ sys_var_thd_ulong sys_net_retry_count("net_retry_count",
&SV::net_retry_count,
fix_net_retry_count);
sys_var_thd_bool sys_new_mode("new", &SV::new_mode);
sys_var_thd_bool sys_old_passwords("old_passwords", &SV::old_passwords);
sys_var_thd_ulong sys_preload_buff_size("preload_buffer_size",
&SV::preload_buff_size);
sys_var_thd_ulong sys_read_buff_size("read_buffer_size",
......@@ -242,6 +243,7 @@ sys_var_thd_enum sys_query_cache_type("query_cache_type",
&SV::query_cache_type,
&query_cache_type_typelib);
#endif /* HAVE_QUERY_CACHE */
sys_var_bool_ptr sys_secure_auth("secure_auth", &opt_secure_auth);
sys_var_long_ptr sys_server_id("server_id",&server_id);
sys_var_bool_ptr sys_slave_compressed_protocol("slave_compressed_protocol",
&opt_slave_compressed_protocol);
......@@ -432,6 +434,7 @@ sys_var *sys_variables[]=
&sys_net_wait_timeout,
&sys_net_write_timeout,
&sys_new_mode,
&sys_old_passwords,
&sys_preload_buff_size,
&sys_pseudo_thread_id,
&sys_query_cache_size,
......@@ -450,6 +453,7 @@ sys_var *sys_variables[]=
#endif
&sys_rpl_recovery_rank,
&sys_safe_updates,
&sys_secure_auth,
&sys_select_limit,
&sys_server_id,
#ifdef HAVE_REPLICATION
......@@ -608,6 +612,7 @@ struct show_var_st init_vars[]= {
{sys_net_retry_count.name, (char*) &sys_net_retry_count, SHOW_SYS},
{sys_net_write_timeout.name,(char*) &sys_net_write_timeout, SHOW_SYS},
{sys_new_mode.name, (char*) &sys_new_mode, SHOW_SYS},
{sys_old_passwords.name, (char*) &sys_old_passwords, SHOW_SYS},
{"open_files_limit", (char*) &open_files_limit, SHOW_LONG},
{"pid_file", (char*) pidfile_name, SHOW_CHAR},
{"log_error", (char*) log_error_file, SHOW_CHAR},
......@@ -628,6 +633,7 @@ struct show_var_st init_vars[]= {
SHOW_SYS},
{sys_query_cache_size.name, (char*) &sys_query_cache_size, SHOW_SYS},
{sys_query_cache_type.name, (char*) &sys_query_cache_type, SHOW_SYS},
{"secure_auth", (char*) &sys_secure_auth, SHOW_SYS},
#endif /* HAVE_QUERY_CACHE */
#ifdef HAVE_SMEM
{"shared_memory", (char*) &opt_enable_shared_memory, SHOW_MY_BOOL},
......
......@@ -689,6 +689,9 @@ class NAMED_LIST :public ilink
}
};
/* updated in sql_acl.cc */
extern sys_var_thd_bool sys_old_passwords;
/* For sql_yacc */
struct sys_var_with_base
......
......@@ -279,3 +279,4 @@ v/*
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -273,3 +273,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -281,3 +281,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -275,3 +275,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -275,3 +275,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -270,3 +270,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -279,3 +279,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -270,3 +270,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -272,3 +272,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -270,3 +270,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -272,3 +272,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -270,3 +270,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -272,3 +272,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -272,3 +272,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -274,3 +274,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -270,3 +270,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -274,3 +274,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -272,3 +272,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
" --secure-auth ( ), '%s@%s' Σ ; "
......@@ -265,3 +265,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -278,3 +278,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -271,3 +271,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -270,3 +270,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -275,3 +275,4 @@
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"SSL parameters in CHANGE MASTER are ignored because this MySQL slave was compiled without SSL support. They can be used later when MySQL slave with SSL will be started."
"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
......@@ -1554,7 +1554,7 @@ void init_master_info_with_options(MASTER_INFO* mi)
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, MAX_PASSWORD_LENGTH);
mi->port = master_port;
mi->connect_retry = master_connect_retry;
......@@ -1707,8 +1707,8 @@ file '%s')", 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))
......
......@@ -51,7 +51,7 @@ static byte* acl_entry_get_key(acl_entry *entry,uint *length,
return (byte*) entry->key;
}
#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+17)
#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+USERNAME_LENGTH+1)
static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
static MEM_ROOT mem, memex;
......@@ -68,11 +68,53 @@ 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)
{
get_salt_from_password_323((ulong *) acl_user->salt, password);
acl_user->salt_len= SCRAMBLE_LENGTH_323;
}
else
acl_user->salt_len= 0;
}
/*
This after_update function is used when user.password is less than
SCRAMBLE_LENGTH bytes.
*/
static void restrict_update_of_old_passwords_var(THD *thd,
enum_var_type var_type)
{
if (var_type == OPT_GLOBAL)
{
pthread_mutex_lock(&LOCK_global_system_variables);
global_system_variables.old_passwords= 1;
pthread_mutex_unlock(&LOCK_global_system_variables);
}
else
thd->variables.old_passwords= 1;
}
/*
Read grant privileges from the privilege tables in the 'mysql' database.
......@@ -114,8 +156,6 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
if (!(thd=new THD))
DBUG_RETURN(1); /* purecov: inspected */
thd->store_globals();
/* Use passwords according to command line option */
use_old_passwords= opt_old_passwords;
acl_cache->clear(1); // Clear locked hostname cache
thd->db= my_strdup("mysql",MYF(0));
......@@ -172,53 +212,75 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0);
VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
if (table->field[2]->field_length == 8 &&
protocol_version == PROTOCOL_VERSION)
if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
{
sql_print_error("Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */
protocol_version=9; /* purecov: tested */
sql_print_error("Fatal error: mysql.user table is damaged or in "
"unsupported 3.20 format.");
goto end;
}
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)
pthread_mutex_lock(&LOCK_global_system_variables);
if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
{
if (opt_secure_auth)
{
pthread_mutex_unlock(&LOCK_global_system_variables);
sql_print_error("Fatal error: mysql.user table is in old format, "
"but server started with --secure-auth option.");
goto end;
}
sys_old_passwords.after_update= restrict_update_of_old_passwords_var;
if (global_system_variables.old_passwords)
pthread_mutex_unlock(&LOCK_global_system_variables);
else
{
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;
global_system_variables.old_passwords= 1;
pthread_mutex_unlock(&LOCK_global_system_variables);
sql_print_error("mysql.user table is not updated to new password format; "
"Disabling new password usage until "
"mysql_fix_privilege_tables is run");
}
thd->variables.old_passwords= 1;
}
else
{
sys_old_passwords.after_update= 0;
pthread_mutex_unlock(&LOCK_global_system_variables);
}
allow_all_hosts=0;
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_USER user;
uint length=0;
update_hostname(&user.host,get_field(&mem, table->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)
{
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 || length > 16)) ||
(user.pversion && length != 45))
{
sql_print_error(
"Found invalid password for user: '%s'@'%s'; Ignoring user",
update_hostname(&user.host, get_field(&mem, table->field[0]));
user.user= get_field(&mem, table->field[1]);
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)
{
switch (password_len) {
case 45: /* 4.1: to be removed */
sql_print_error("Found 4.1 style password for user '%s@%s'. "
"Ignoring user. "
"You should change password for this user.",
user.user ? user.user : "",
user.host.hostname ? user.host.hostname : ""); /* purecov: tested */
continue; /* purecov: tested */
user.host.hostname ? user.host.hostname : "");
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);
user.access=get_access(table,3) & GLOBAL_ACLS;
user.sort=get_sort(2,user.host.hostname,user.user);
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 ?
(uint) strlen(user.host.hostname) : 0);
if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */
......@@ -270,6 +332,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);
......@@ -469,135 +532,105 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
/*
Prepare crypted scramble to be sent to the client
*/
void prepare_scramble(THD *thd, ACL_USER *acl_user,char* prepared_scramble)
{
/* 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);
}
Seek ACL entry for a user, check password, SSL cypher, and if
everything is OK, update THD user data and USER_RESOURCES struct.
IMPLEMENTATION
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.
/*
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;
SYNOPSIS
acl_getroot()
thd 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 user resources; on success mqh is reset, else
unchanged
passwd 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 length of passwd, must be one of 0, 8,
SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH
'thd' and 'mqh' are updated on success; other params are IN.
RETURN VALUE
0 success: thd->priv_user, thd->priv_host, thd->master_access, mqh are
updated
1 user not found or authentification failure
2 user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
-1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
*/
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)
int acl_getroot(THD *thd, USER_RESOURCES *mqh,
const char *passwd, uint passwd_len)
{
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((char*) mqh, sizeof(USER_RESOURCES));
if (!initialized)
{
// If no data allow anything
DBUG_RETURN((ulong) ~NO_ACCESS); /* purecov: tested */
/*
here if mysqld's been started with --skip-grant-tables option.
*/
thd->priv_user= (char *) ""; // privileges for
*thd->priv_host= '\0'; // the user are unknown
thd->master_access= ~NO_ACCESS; // everything is allowed
bzero(mqh, sizeof(*mqh));
DBUG_RETURN(0);
}
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))
{
if (compare_hostname(&acl_user_search->host,host,ip))
{
/* Found mathing user */
acl_user= acl_user_search;
/* Store it as a cache */
*cached_user= acl_user;
*cur_priv_version= priv_version;
break;
}
}
}
}
/* Now we have acl_user found and may start our checks */
if (acl_user)
{
/* Password should present for both or absend for both */
if (!acl_user->password && !*password)
password_correct=1;
else if (!acl_user->password || !*password)
ACL_USER *user_i = dynamic_element(&acl_users,i,ACL_USER*);
if (!user_i->user || !strcmp(thd->user, user_i->user))
{
*cached_user= 0; // Impossible to connect
}
else
if (compare_hostname(&user_i->host, thd->host, thd->ip))
{
/* New version password is checked differently */
if (acl_user->pversion)
/* check password: it should be empty or valid */
if (passwd_len == user_i->salt_len)
{
if (stage) /* We check password only on the second stage */
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,
(ulong *) user_i->salt) == 0)
{
if (!validate_password(password,message,acl_user->salt))
password_correct=1;
acl_user= user_i;
res= 0;
}
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);
}
else if (passwd_len == SCRAMBLE_LENGTH &&
user_i->salt_len == SCRAMBLE_LENGTH_323)
res= -1;
else if (passwd_len == SCRAMBLE_LENGTH_323 &&
user_i->salt_len == SCRAMBLE_LENGTH)
res= 2;
/* linear search complete: */
break;
}
}
}
/*
This was moved to separate tree because of heavy HAVE_OPENSSL case.
If acl_user is not null, res is 0.
*/
/* 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 */
if (acl_user)
{
/* OK. User found and password checked continue validation */
thd->master_access= NO_ACCESS;
Vio *vio=thd->net.vio;
/*
At this point we know that user is allowed to connect
......@@ -608,12 +641,12 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
switch (acl_user->ssl_type) {
case SSL_TYPE_NOT_SPECIFIED: // Impossible
case SSL_TYPE_NONE: /* SSL is not required to connect */
user_access=acl_user->access;
thd->master_access= acl_user->access;
break;
#ifdef HAVE_OPENSSL
case SSL_TYPE_ANY: /* Any kind of SSL is good enough */
if (vio_type(vio) == VIO_TYPE_SSL)
user_access=acl_user->access;
thd->master_access= acl_user->access;
break;
case SSL_TYPE_X509: /* Client should have any valid certificate. */
/*
......@@ -626,7 +659,7 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
if (vio_type(vio) == VIO_TYPE_SSL &&
SSL_get_verify_result(vio->ssl_) == X509_V_OK &&
SSL_get_peer_certificate(vio->ssl_))
user_access=acl_user->access;
thd->master_access= acl_user->access;
break;
case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
/*
......@@ -643,14 +676,13 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)));
if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)))
user_access=acl_user->access;
thd->master_access= acl_user->access;
else
{
if (global_system_variables.log_warnings)
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;
}
}
......@@ -668,13 +700,12 @@ 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);
user_access=NO_ACCESS;
sql_print_error("X509 issuer mismatch: should be '%s' "
"but is '%s'", acl_user->x509_issuer, ptr);
free(ptr);
break;
}
user_access=acl_user->access;
thd->master_access= acl_user->access;
free(ptr);
}
DBUG_PRINT("info",("checkpoint 4"));
......@@ -689,10 +720,9 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
if (global_system_variables.log_warnings)
sql_print_error("X509 subject mismatch: '%s' vs '%s'",
acl_user->x509_subject, ptr);
user_access=NO_ACCESS;
}
else
user_access=acl_user->access;
thd->master_access= acl_user->access;
free(ptr);
}
break;
......@@ -705,20 +735,16 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
break;
#endif /* HAVE_OPENSSL */
}
}
*mqh=acl_user->user_resource;
if (!acl_user->user)
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
thd->priv_user= acl_user->user ? thd->user : (char *) "";
*mqh= acl_user->user_resource;
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);
}
......@@ -729,8 +755,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,
......@@ -766,20 +793,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;
}
}
......@@ -788,7 +804,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,
......@@ -799,7 +815,6 @@ static void acl_insert_user(const char *user, const char *host,
ACL_USER acl_user;
acl_user.user=*user ? strdup_root(&mem,user) : 0;
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);
......@@ -809,12 +824,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
......@@ -1151,7 +1162,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));
......@@ -1160,37 +1170,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)))
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));
......@@ -1230,7 +1230,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);
}
......@@ -1303,7 +1303,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;
......@@ -1345,7 +1345,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 */
......@@ -1393,24 +1393,23 @@ 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];
const char *password= "";
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;
}
......@@ -1436,16 +1435,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 */
......@@ -1542,10 +1544,8 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
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,
......@@ -1553,7 +1553,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
&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,
......@@ -3024,12 +3024,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 */
......
......@@ -111,9 +111,9 @@ class ACL_USER :public ACL_ACCESS
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, my_bool db_is_pattern);
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 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,
......
......@@ -138,6 +138,7 @@ THD::THD():user_time(0), is_fatal_error(0),
set_query_id=1;
db_access=NO_ACCESS;
version=refresh_version; // For boot
*scramble= '\0';
init();
/* Initialize sub structures */
......
......@@ -399,6 +399,7 @@ struct system_variables
my_bool log_warnings;
my_bool low_priority_updates;
my_bool new_mode;
my_bool old_passwords;
CHARSET_INFO *character_set_server;
CHARSET_INFO *character_set_database;
......@@ -556,10 +557,10 @@ class THD :public ilink
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];
uint8 query_cache_type; // type of query cache processing
bool slave_thread;
bool set_query_id,locked,count_cuted_fields,some_tables_deleted;
......
......@@ -32,7 +32,7 @@
SQL_CRYPT::SQL_CRYPT(const char *password)
{
ulong rand_nr[2];
hash_password(rand_nr,password);
hash_password(rand_nr,password, strlen(password));
crypt_init(rand_nr);
}
......
......@@ -178,153 +178,179 @@ static int get_or_create_user_conn(THD *thd, const char *user,
/*
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 thread handle, thd->{host,user,ip} are used
command originator of the check: now check_user is called
during connect and change user procedures; used for
logging.
passwd scrambled password recieved from client
passwd_len length of scrambled password
db database name to connect to, may be NULL
check_count 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; 'thd' is INOUT, all other params
are 'IN'.
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 handshake error; error is sent to client;
>0 error, 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+1];
DBUG_ENTER("check_user");
my_bool opt_secure_auth_local;
pthread_mutex_lock(&LOCK_global_system_variables);
opt_secure_auth_local= opt_secure_auth;
pthread_mutex_unlock(&LOCK_global_system_variables);
/*
Move password to temporary buffer as it may be stored in communication
buffer
If the server is running in secure auth mode, short scrambles are
forbidden.
*/
strmake(tmp_passwd, passwd, sizeof(tmp_passwd));
passwd= tmp_passwd; // Use local copy
/* We shall avoid dupplicate user allocations here */
if (!thd->user && !(thd->user = my_strdup(user, MYF(0))))
if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
{
send_error(thd,ER_OUT_OF_RESOURCES);
DBUG_RETURN(1);
net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
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'",
thd->client_capabilities, thd->max_client_packet_length,
thd->host_or_ip, thd->user, thd->priv_user,
had_password ? "yes": "no",
thd->master_access, thd->db ? thd->db : "*none*"));
if (passwd_len != 0 &&
passwd_len != SCRAMBLE_LENGTH &&
passwd_len != SCRAMBLE_LENGTH_323)
DBUG_RETURN(ER_HANDSHAKE_ERROR);
/*
In case we're going to retry we should not send error message at this
point
Clear thd->db as it points to something, that will be freed when
connection is closed. We don't want to accidently free a wrong pointer
if connect failed. Also in case of 'CHANGE USER' failure, current
database will be switched to 'no database selected'.
*/
if (thd->master_access & NO_ACCESS)
{
if (do_send_error || !had_password || !*hint_user)
thd->db= 0;
thd->db_length= 0;
USER_RESOURCES ur;
int res= acl_getroot(thd, &ur, passwd, passwd_len);
if (res == -1)
{
DBUG_PRINT("info",("Access denied"));
/*
Old client should get nicer error message if password version is
not supported
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.
*/
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 *net= &thd->net;
if (opt_secure_auth_local)
{
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));
net_printf(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
thd->user, thd->host_or_ip);
mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
thd->user, thd->host_or_ip);
DBUG_RETURN(-1);
}
DBUG_RETURN(1); // Error already given
if (send_old_password_request(thd) ||
my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very
{ // specific packet size
inc_host_errors(&thd->remote.sin_addr);
DBUG_RETURN(ER_HANDSHAKE_ERROR);
}
DBUG_PRINT("info",("Prepare for second part of handshake"));
DBUG_RETURN(-1); // no report error in special handshake
/* Final attempt to check the user based on reply */
/* So as passwd is short, errcode is always >= 0 */
res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
}
/* here res is always >= 0 */
if (res == 0)
{
if (!(thd->master_access & NO_ACCESS)) // authentification is OK
{
DBUG_PRINT("info",
("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,
passwd_len ? "yes": "no",
thd->master_access, thd->db ? thd->db : "*none*"));
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);
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*) "");
/*
This is the default access rights for the current database. It's
set to 0 here because we don't have an active database yet (and we
may not have an active database to set.
*/
thd->db_access=0;
/* Don't allow user to connect if he has done too many queries */
if ((ur.questions || ur.updates || ur.connections || max_user_connections) &&
get_or_create_user_conn(thd,user,thd->host_or_ip,&ur))
if ((ur.questions || ur.updates ||
ur.connections || max_user_connections) &&
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) ||
max_user_connections) &&
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);
}
}
else
send_ok(thd);
thd->password= test(passwd_len); // remember for error messages
/* Ready to handle queries */
DBUG_RETURN(0);
}
}
else if (res == 2) // client gave short hash, server has long hash
{
net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
DBUG_RETURN(-1);
}
send_ok(thd); // Ready to handle questions
thd->password= test(passwd[0]); // Remember for error messages
DBUG_RETURN(0); // ok
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(-1);
}
/*
Check for maximum allowable user connections, if the mysqld server is
started with corresponding variable that is greater then 0.
......@@ -526,45 +552,36 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
/*
Check connnectionn and get priviliges
Perform handshake, authorize client and update thd ACL variables.
SYNOPSIS
check_connections
thd Thread handle
check_connection()
thd 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, thd is updated.
-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;
uint connect_errors= 0;
NET *net= &thd->net;
char *end, *user, *passwd, *db;
char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble&hash */
char db_buff[NAME_LEN+1];
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];
if (vio_peer_addr(net->vio, ip, &thd->peer_port))
return (ER_BAD_HOST_ERROR);
if (!(thd->ip = my_strdup(ip,MYF(0))))
if (!(thd->ip= my_strdup(ip,MYF(0))))
return (ER_OUT_OF_RESOURCES);
thd->host_or_ip=thd->ip;
thd->host_or_ip= thd->ip;
#if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread)
/* Fast local hostname resolve for Win32 */
if (!strcmp(thd->ip,"127.0.0.1"))
......@@ -600,15 +617,16 @@ check_connections(THD *thd)
DBUG_PRINT("info",("Host: %s",thd->host));
thd->host_or_ip= thd->host;
thd->ip= 0;
bzero((char*) &thd->remote,sizeof(struct sockaddr));
bzero((char*) &thd->remote, sizeof(struct sockaddr));
}
/* Ensure that wrong hostnames doesn't cause buffer overflows */
vio_keepalive(net->vio, TRUE);
ulong pkt_len=0;
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];
char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
......@@ -622,19 +640,34 @@ check_connections(THD *thd)
client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */
#endif /* HAVE_OPENSSL */
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;
int2store(end,client_flags);
end[2]=(char) default_charset_info->number;
int2store(end+3,thd->server_status);
bzero(end+5,13);
end+=18;
end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
int4store((uchar*) end, thd->thread_id);
end+= 4;
/*
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);
/*
Old clients does not understand long scrambles, but can ignore packet
tail: that's why first part of the scramble is placed here, and second
part at the end of packet.
*/
end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
// At this point we write connection message and read reply
if (net_write_command(net,(uchar) protocol_version, "", 0, buff,
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 */
if (net_write_command(net, (uchar) protocol_version, "", 0, buff,
(uint) (end-buff)) ||
(pkt_len= my_net_read(net)) == packet_error ||
pkt_len < MIN_HANDSHAKE_SIZE)
......@@ -723,7 +756,7 @@ check_connections(THD *thd)
return(ER_HANDSHAKE_ERROR);
}
DBUG_PRINT("info", ("Reading user information over SSL layer"));
if ((pkt_len=my_net_read(net)) == packet_error ||
if ((pkt_len= my_net_read(net)) == packet_error ||
pkt_len < NORMAL_HANDSHAKE_SIZE)
{
DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
......@@ -740,25 +773,6 @@ 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;
uint32 length= copy_and_convert(db_buff, sizeof(db_buff)-1,
system_charset_info,
db, strlen(db),
thd->charset());
db_buff[length]= 0;
db= db_buff;
}
/* We can get only old hash at this point */
if (using_password && strlen(passwd) != SCRAMBLE_LENGTH)
return ER_HANDSHAKE_ERROR;
if (thd->client_capabilities & CLIENT_INTERACTIVE)
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
......@@ -766,60 +780,36 @@ 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));
/* 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];
/* If the client is old we just have to return error */
if (simple_connect)
return -1;
DBUG_PRINT("info",("password challenge"));
char *user= end;
char *passwd= strend(user)+1;
char *db= passwd;
char db_buff[NAME_LEN+1]; // buffer to store db in utf8
/*
Old clients send null-terminated string as password; new clients send
the size (1 byte) + string (not null-terminated). Hence in case of empty
password both send '\0'.
*/
uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
*passwd++ : strlen(passwd);
db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
db + passwd_len + 1 : 0;
tmp_user[0]= tmp_db[0]= 0;
if (user)
strmake(tmp_user,user,USERNAME_LENGTH);
/* Since 4.1 all database names are stored in utf8 */
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;
uint32 length= copy_and_convert(db_buff, sizeof(db_buff)-1,
system_charset_info,
db, strlen(db),
thd->charset());
db_buff[length]= 0;
db= db_buff;
}
/* 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);
if (!(thd->user= my_strdup(user, MYF(0))))
return (ER_OUT_OF_RESOURCES);
return check_user(thd, COM_CONNECT, passwd, passwd_len, db, true);
}
......@@ -876,7 +866,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);
......@@ -1188,116 +1178,72 @@ 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);
statistic_increment(com_other, &LOCK_status);
char *user= (char*) packet;
char *passwd= strend(user)+1;
char *db= strend(passwd)+1;
/*
Old clients send null-terminated string ('\0' for empty string) for
password. New clients send the size (1 byte) + string (not null
terminated, so also '\0' for empty string).
*/
char *db= passwd;
uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
*passwd++ : strlen(passwd);
db+= 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;
}
/* Convert database name to utf8 */
String convdb;
convdb.copy(db, strlen(db), thd->variables.character_set_client,
system_charset_info);
db= convdb.c_ptr();
/* Save user and privileges */
uint save_master_access=thd->master_access;
uint save_master_access= thd->master_access;
uint save_db_access= thd->db_access;
uint save_db_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)
{
/* 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;
thd->user= my_strdup(user, MYF(0));
if (!thd->user)
{
thd->user= save_user;
send_error(thd, ER_OUT_OF_RESOURCES);
break;
}
/* We have to get very specific packet size */
if (pkt_len != SCRAMBLE41_LENGTH)
goto restore_user;
int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, false);
/* 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;
if (res)
{
/* authentification failure, we shall restore old user */
if (res > 0)
send_error(thd, ER_UNKNOWN_COM_ERROR);
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;
}
else if (res)
goto restore_user;
/* Finally we've authenticated new 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);
thd->password=using_password;
break;
/* Bad luck we shall restore old user */
restore_user_err:
send_error(thd, ER_UNKNOWN_COM_ERROR);
restore_user:
x_free(thd->user);
thd->master_access=save_master_access;
thd->db_access=save_db_access;
thd->db=save_db;
thd->db_length=save_db_length;
thd->user=save_user;
thd->priv_user=save_priv_user;
}
break;
}
#endif /* EMBEDDED_LIBRARY */
......@@ -3255,7 +3201,7 @@ mysql_execute_command(THD *thd)
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
......
......@@ -506,6 +506,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token MULTIPOINT
%token MULTIPOLYGON
%token NOW_SYM
%token OLD_PASSWORD
%token PASSWORD
%token POINTFROMTEXT
%token POINT_SYM
......@@ -2625,9 +2626,13 @@ simple_expr:
| NOW_SYM '(' expr ')'
{ $$= new Item_func_now_local($3); Lex->safe_to_cache_query=0;}
| PASSWORD '(' expr ')'
{ $$= new Item_func_password($3); }
| PASSWORD '(' expr ',' expr ')'
{ $$= new Item_func_password($3,$5); }
{
$$= YYTHD->variables.old_passwords ?
(Item *) new Item_func_old_password($3) :
(Item *) new Item_func_password($3);
}
| OLD_PASSWORD '(' expr ')'
{ $$= new Item_func_old_password($3); }
| POINT_SYM '(' expr ',' expr ')'
{ $$= new Item_func_point($3,$5); }
| POINTFROMTEXT '(' expr ')'
......@@ -4535,6 +4540,7 @@ keyword:
| NO_SYM {}
| NONE_SYM {}
| OFFSET_SYM {}
| OLD_PASSWORD {}
| OPEN_SYM {}
| PACK_KEYS_SYM {}
| PARTIAL {}
......@@ -4759,15 +4765,15 @@ text_or_password:
TEXT_STRING { $$=$1.str;}
| PASSWORD '(' TEXT_STRING ')'
{
if (!$3.length)
$$=$3.str;
else
{
char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1);
make_scrambled_password(buff,$3.str,use_old_passwords,
&YYTHD->rand);
$$=buff;
$$= $3.length ? YYTHD->variables.old_passwords ?
Item_func_old_password::alloc(YYTHD, $3.str) :
Item_func_password::alloc(YYTHD, $3.str) :
$3.str;
}
| OLD_PASSWORD '(' TEXT_STRING ')'
{
$$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str) :
$3.str;
}
;
......@@ -5075,13 +5081,23 @@ grant_user:
$$=$1; $1->password=$4;
if ($4.length)
{
char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1);
if (YYTHD->variables.old_passwords)
{
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= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
}
else
{
make_scrambled_password(buff,$4.str,use_old_passwords,
&YYTHD->rand);
$1->password.str=buff;
$1->password.length=HASH_PASSWORD_LENGTH;
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;
}
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment