SCRUM: Main change for Secure connection handling. Still needs some more coding. Commit
done for merge with newer version of code. client/mysqladmin.c: Support for new password format include/mysql.h: Increase buffer as new scramble is larger include/mysql_com.h: Add new prototypes for new auth handling libmysql/libmysql.c: New handshake handling code mysql-test/install_test_db.sh: Extend password length to handle new passwords mysql-test/t/rpl000017-slave.sh: Adjust test to work with longer password scripts/mysql_fix_privilege_tables.sh: Increase password length at priv table convertion scripts/mysql_install_db.sh: Longer passwords sql/item_strfunc.cc: New password generation function needs seed for password generation sql/mini_client.cc: Change MiniClient to handle new auth in replication sql/mysqld.cc: Remove unneeded flag (was added earlier for same change) sql/password.c: A lot of changes to handle new authentication and doccumentation sql/sql_acl.cc: Password handling function adjustment sql/sql_acl.h: Change prototype sql/sql_class.h: Extend scramble length sql/sql_parse.cc: Handling server side of new authentication sql/sql_yacc.yy: Adjustment for new prototypes
This commit is contained in:
parent
85bbdcf016
commit
8c8b97fdf4
@ -87,7 +87,7 @@ enum commands {
|
||||
ADMIN_FLUSH_HOSTS, ADMIN_FLUSH_TABLES, ADMIN_PASSWORD,
|
||||
ADMIN_PING, ADMIN_EXTENDED_STATUS, ADMIN_FLUSH_STATUS,
|
||||
ADMIN_FLUSH_PRIVILEGES, ADMIN_START_SLAVE, ADMIN_STOP_SLAVE,
|
||||
ADMIN_FLUSH_THREADS
|
||||
ADMIN_FLUSH_THREADS, ADMIN_OLD_PASSWORD
|
||||
};
|
||||
static const char *command_names[]= {
|
||||
"create", "drop", "shutdown",
|
||||
@ -97,7 +97,7 @@ static const char *command_names[]= {
|
||||
"flush-hosts", "flush-tables", "password",
|
||||
"ping", "extended-status", "flush-status",
|
||||
"flush-privileges", "start-slave", "stop-slave",
|
||||
"flush-threads",
|
||||
"flush-threads","old-password",
|
||||
NullS
|
||||
};
|
||||
|
||||
@ -419,7 +419,8 @@ static my_bool sql_connect(MYSQL *mysql, uint wait)
|
||||
static int execute_commands(MYSQL *mysql,int argc, char **argv)
|
||||
{
|
||||
const char *status;
|
||||
|
||||
struct rand_struct rand_st;
|
||||
|
||||
for (; argc > 0 ; argv++,argc--)
|
||||
{
|
||||
switch (find_type(argv[0],&command_typelib,2)) {
|
||||
@ -721,17 +722,23 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ADMIN_OLD_PASSWORD:
|
||||
case ADMIN_PASSWORD:
|
||||
{
|
||||
char buff[128],crypted_pw[33];
|
||||
|
||||
char buff[128],crypted_pw[64];
|
||||
time_t start_time;
|
||||
/* Do initialization the same way as we do in mysqld */
|
||||
start_time=time((time_t*) 0);
|
||||
randominit(&rand_st,(ulong) start_time,(ulong) start_time/2);
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
my_printf_error(0,"Too few arguments to change password",MYF(ME_BELL));
|
||||
return 1;
|
||||
}
|
||||
if (argv[1][0])
|
||||
make_scrambled_password(crypted_pw,argv[1],0); /* New passwords only */
|
||||
make_scrambled_password(crypted_pw,argv[1],(find_type(argv[0],&command_typelib,2)
|
||||
==ADMIN_OLD_PASSWORD),&rand_st);
|
||||
else
|
||||
crypted_pw[0]=0; /* No password */
|
||||
sprintf(buff,"set password='%s',sql_log_off=0",crypted_pw);
|
||||
@ -836,7 +843,8 @@ static void usage(void)
|
||||
kill id,id,... Kill mysql threads");
|
||||
#if MYSQL_VERSION_ID >= 32200
|
||||
puts("\
|
||||
password new-password Change old password to new-password");
|
||||
password new-password Change old password to new-password, MySQL 4.1 hashing.\n\
|
||||
old-password new-password Change old password to new-password in old format.\n");
|
||||
#endif
|
||||
puts("\
|
||||
ping Check if mysqld is alive\n\
|
||||
|
@ -181,7 +181,7 @@ 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[9];
|
||||
char scramble_buff[21]; /* New protocol requires longer scramble*/
|
||||
|
||||
/*
|
||||
Set if this is the original connection, not a master or a slave we have
|
||||
|
@ -280,10 +280,17 @@ extern unsigned long net_buffer_length;
|
||||
void randominit(struct rand_struct *,unsigned long seed1,
|
||||
unsigned long seed2);
|
||||
double rnd(struct rand_struct *);
|
||||
void make_scrambled_password(char *to,const char *password,my_bool force_old_scramble);
|
||||
void make_scrambled_password(char *to,const char *password,my_bool force_old_scramble,struct rand_struct *rand_st);
|
||||
uint get_password_length(my_bool force_old_scramble);
|
||||
uint8 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, ulong* 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(ulong* salt, uint8 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, uint8 password_version);
|
||||
char *scramble(char *to,const char *message,const char *password,
|
||||
my_bool old_ver);
|
||||
|
@ -67,7 +67,7 @@ ulong net_write_timeout= NET_WRITE_TIMEOUT;
|
||||
|
||||
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG \
|
||||
| CLIENT_LOCAL_FILES | CLIENT_TRANSACTIONS \
|
||||
| CLIENT_PROTOCOL_41)
|
||||
| CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION)
|
||||
|
||||
|
||||
#ifdef __WIN__
|
||||
@ -1585,6 +1585,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
|
||||
{
|
||||
char buff[NAME_LEN+USERNAME_LENGTH+100],charset_name_buff[16];
|
||||
char *end,*host_info,*charset_name;
|
||||
char password_hash[20]; /* Used for tmp storage of stage1 hash */
|
||||
my_socket sock;
|
||||
uint32 ip_addr;
|
||||
struct sockaddr_in sock_addr;
|
||||
@ -1789,7 +1790,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
|
||||
if ((pkt_length=net_safe_read(mysql)) == packet_error)
|
||||
goto error;
|
||||
|
||||
/* Check if version of protocoll matches current one */
|
||||
/* Check if version of protocol matches current one */
|
||||
|
||||
mysql->protocol_version= net->read_pos[0];
|
||||
DBUG_DUMP("packet",(char*) net->read_pos,10);
|
||||
@ -1855,7 +1856,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
/* Save connection information */
|
||||
if (!user) user="";
|
||||
if (!passwd) passwd="";
|
||||
@ -1962,32 +1963,105 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
|
||||
DBUG_PRINT("info",("Server version = '%s' capabilites: %ld status: %d client_flag: %d",
|
||||
mysql->server_version,mysql->server_capabilities,
|
||||
mysql->server_status, client_flag));
|
||||
|
||||
int3store(buff+2,max_allowed_packet);
|
||||
if (user && user[0])
|
||||
strmake(buff+5,user,32); /* Max user name */
|
||||
else
|
||||
read_user_name((char*) buff+5);
|
||||
/* We have to handle different version of handshake here */
|
||||
#ifdef _CUSTOMCONFIG_
|
||||
#include "_cust_libmysql.h";
|
||||
#endif
|
||||
DBUG_PRINT("info",("user: %s",buff+5));
|
||||
end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
/*
|
||||
We always start with old type handshake the only difference is message sent
|
||||
If server handles secure connection type we'll not send the real scramble
|
||||
*/
|
||||
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||
{
|
||||
if (passwd[0])
|
||||
{
|
||||
/* Use something for not empty password not to match it against empty one */
|
||||
end=scramble(strend(buff+5)+1, mysql->scramble_buff,"~MySQL#!",
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
}
|
||||
else /* For empty password*/
|
||||
{
|
||||
end=strend(buff+5)+1;
|
||||
*end=0; /* Store zero length scramble */
|
||||
}
|
||||
}
|
||||
/* Real scramble is sent only for servers. This is to be blocked by option */
|
||||
else
|
||||
end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
|
||||
/* Add database if needed */
|
||||
if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
|
||||
{
|
||||
end=strmake(end+1,db,NAME_LEN);
|
||||
end=strmake(end+1,db,NAME_LEN);
|
||||
mysql->db=my_strdup(db,MYF(MY_WME));
|
||||
db=0;
|
||||
}
|
||||
/* Write authentication package */
|
||||
if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net))
|
||||
{
|
||||
net->last_errno= CR_SERVER_LOST;
|
||||
net->last_errno= CR_SERVER_LOST;
|
||||
strmov(net->last_error,ER(net->last_errno));
|
||||
goto error;
|
||||
}
|
||||
if (net_safe_read(mysql) == packet_error)
|
||||
|
||||
/* We shall only query sever if it expect us to do so */
|
||||
|
||||
if ( (pkt_length=net_safe_read(mysql)) == packet_error)
|
||||
goto error;
|
||||
|
||||
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||
{
|
||||
/* This should basically always happen with new server unless empty password */
|
||||
if (pkt_length==24) /* We have new hash back */
|
||||
{
|
||||
/* Old passwords will have zero at the first byte of hash */
|
||||
if (net->read_pos[0])
|
||||
{
|
||||
/* Build full password hash as it is required to decode scramble */
|
||||
password_hash_stage1(buff, passwd);
|
||||
/* Store copy as we'll need it later */
|
||||
memcpy(password_hash,buff,20);
|
||||
/* Finally hash complete password using hash we got from server */
|
||||
password_hash_stage2(password_hash,net->read_pos);
|
||||
/* Decypt and store scramble 4 = hash for stage2 */
|
||||
password_crypt(net->read_pos+4,mysql->scramble_buff,password_hash,20);
|
||||
mysql->scramble_buff[20]=0;
|
||||
/* Encode scramble with password. Recycle buffer */
|
||||
password_crypt(mysql->scramble_buff,buff,buff,20);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create password to decode scramble */
|
||||
create_key_from_old_password(passwd,password_hash);
|
||||
/* Decypt and store scramble 4 = hash for stage2 */
|
||||
password_crypt(net->read_pos+4,mysql->scramble_buff,password_hash,20);
|
||||
mysql->scramble_buff[20]=0;
|
||||
/* Finally scramble decoded scramble with password */
|
||||
scramble(buff, mysql->scramble_buff, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
}
|
||||
/* Write second package of authentication */
|
||||
if (my_net_write(net,buff,20) || net_flush(net))
|
||||
{
|
||||
net->last_errno= CR_SERVER_LOST;
|
||||
strmov(net->last_error,ER(net->last_errno));
|
||||
goto error;
|
||||
}
|
||||
/* Read What server thinks about out new auth message report */
|
||||
if (net_safe_read(mysql) == packet_error)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* End of authentication part of handshake */
|
||||
|
||||
if (client_flag & CLIENT_COMPRESS) /* We will use compression */
|
||||
net->compress=1;
|
||||
if (db && mysql_select_db(mysql,db))
|
||||
|
@ -123,7 +123,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(16) binary DEFAULT '' NOT NULL,"
|
||||
c_u="$c_u Password char(45) 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,"
|
||||
|
@ -5,7 +5,7 @@ master-bin.000001
|
||||
4
|
||||
127.0.0.1
|
||||
replicate
|
||||
aaaaaaaaaaaaaaabthispartofthepasswordisnotused
|
||||
aaaaaaaaaaaaaaab
|
||||
$MASTER_MYPORT
|
||||
1
|
||||
0
|
||||
|
@ -170,6 +170,7 @@ fi
|
||||
|
||||
@bindir@/mysql -f --user=root --password="$root_password" --host="$host" mysql <<END_OF_DATA
|
||||
alter table user
|
||||
change password password char(45) not null,
|
||||
add max_questions int(11) NOT NULL AFTER x509_subject,
|
||||
add max_updates int(11) unsigned NOT NULL AFTER max_questions,
|
||||
add max_connections int(11) unsigned NOT NULL AFTER max_updates;
|
||||
|
@ -213,7 +213,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(16) binary DEFAULT '' NOT NULL,"
|
||||
c_u="$c_u Password char(45) 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,"
|
||||
|
@ -1279,7 +1279,7 @@ String *Item_func_password::val_str(String *str)
|
||||
return 0;
|
||||
if (res->length() == 0)
|
||||
return &empty_string;
|
||||
make_scrambled_password(tmp_value,res->c_ptr(),opt_old_passwords);
|
||||
make_scrambled_password(tmp_value,res->c_ptr(),opt_old_passwords,¤t_thd->rand);
|
||||
str->set(tmp_value,get_password_length(opt_old_passwords),res->charset());
|
||||
return str;
|
||||
}
|
||||
@ -1291,7 +1291,7 @@ String *Item_func_old_password::val_str(String *str)
|
||||
return 0;
|
||||
if (res->length() == 0)
|
||||
return &empty_string;
|
||||
make_scrambled_password(tmp_value,res->c_ptr(),1);
|
||||
make_scrambled_password(tmp_value,res->c_ptr(),1,¤t_thd->rand);
|
||||
str->set(tmp_value,16,res->charset());
|
||||
return str;
|
||||
}
|
||||
|
@ -87,7 +87,9 @@ static MYSQL_DATA *mc_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
|
||||
|
||||
|
||||
|
||||
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES)
|
||||
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | \
|
||||
CLIENT_LOCAL_FILES | CLIENT_SECURE_CONNECTION)
|
||||
|
||||
|
||||
#if defined(MSDOS) || defined(__WIN__)
|
||||
#define perror(A)
|
||||
@ -488,6 +490,7 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user,
|
||||
uint net_read_timeout)
|
||||
{
|
||||
char buff[NAME_LEN+USERNAME_LENGTH+100],*end,*host_info;
|
||||
char password_hash[20];
|
||||
my_socket sock;
|
||||
ulong ip_addr;
|
||||
struct sockaddr_in sock_addr;
|
||||
@ -510,7 +513,6 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user,
|
||||
user ? user : "(Null)",
|
||||
net_read_timeout,
|
||||
(uint) slave_net_timeout));
|
||||
|
||||
net->vio = 0; /* If something goes wrong */
|
||||
mysql->charset=default_charset_info; /* Set character set */
|
||||
if (!port)
|
||||
@ -799,22 +801,96 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user,
|
||||
}
|
||||
|
||||
DBUG_PRINT("info",("user: %s",buff+5));
|
||||
end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
if (db)
|
||||
|
||||
/*
|
||||
We always start with old type handshake the only difference is message sent
|
||||
If server handles secure connection type we'll not send the real scramble
|
||||
*/
|
||||
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||
{
|
||||
if (passwd[0])
|
||||
{
|
||||
/* Use something for not empty password not to match it against empty one */
|
||||
end=scramble(strend(buff+5)+1, mysql->scramble_buff,"~MySQL#!",
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
}
|
||||
else /* For empty password*/
|
||||
{
|
||||
end=strend(buff+5)+1;
|
||||
*end=0; /* Store zero length scramble */
|
||||
}
|
||||
}
|
||||
/* Real scramble is sent only for old servers. This is to be blocked by option */
|
||||
else
|
||||
end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* Write authentication package */
|
||||
if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net))
|
||||
{
|
||||
net->last_errno= CR_SERVER_LOST;
|
||||
strmov(net->last_error,ER(net->last_errno));
|
||||
strmov(net->last_error,ER(net->last_errno));
|
||||
goto error;
|
||||
}
|
||||
if (mc_net_safe_read(mysql) == packet_error)
|
||||
|
||||
/* We shall only query sever if it expect us to do so */
|
||||
|
||||
if ( (pkt_length=mc_net_safe_read(mysql)) == packet_error)
|
||||
goto error;
|
||||
|
||||
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||
{
|
||||
/* This should basically always happen with new server unless empty password */
|
||||
if (pkt_length==24) /* We have new hash back */
|
||||
{
|
||||
/* Old passwords will have zero at the first byte of hash */
|
||||
if (net->read_pos[0])
|
||||
{
|
||||
/* Build full password hash as it is required to decode scramble */
|
||||
password_hash_stage1(buff, passwd);
|
||||
/* Store copy as we'll need it later */
|
||||
memcpy(password_hash,buff,20);
|
||||
/* Finally hash complete password using hash we got from server */
|
||||
password_hash_stage2(password_hash,(char*)net->read_pos);
|
||||
/* Decypt and store scramble 4 = hash for stage2 */
|
||||
password_crypt((char*)net->read_pos+4,mysql->scramble_buff,password_hash,20);
|
||||
mysql->scramble_buff[20]=0;
|
||||
/* Encode scramble with password. Recycle buffer */
|
||||
password_crypt(mysql->scramble_buff,buff,buff,20);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create password to decode scramble */
|
||||
create_key_from_old_password(passwd,password_hash);
|
||||
/* Decypt and store scramble 4 = hash for stage2 */
|
||||
password_crypt((char*)net->read_pos+4,mysql->scramble_buff,password_hash,20);
|
||||
mysql->scramble_buff[20]=0;
|
||||
/* Finally scramble decoded scramble with password */
|
||||
scramble(buff, mysql->scramble_buff, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
}
|
||||
/* Write second package of authentication */
|
||||
if (my_net_write(net,buff,20) || net_flush(net))
|
||||
{
|
||||
net->last_errno= CR_SERVER_LOST;
|
||||
strmov(net->last_error,ER(net->last_errno));
|
||||
goto error;
|
||||
}
|
||||
/* Read What server thinks about out new auth message report */
|
||||
if (mc_net_safe_read(mysql) == packet_error)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* End of authentication part of handshake */
|
||||
|
||||
if (client_flag & CLIENT_COMPRESS) /* We will use compression */
|
||||
net->compress=1;
|
||||
DBUG_PRINT("exit",("Mysql handler: %lx",mysql));
|
||||
|
@ -317,7 +317,6 @@ uint volatile thread_count=0, thread_running=0, kill_cached_threads=0,
|
||||
ulong thd_startup_options=(OPTION_UPDATE_LOG | OPTION_AUTO_IS_NULL |
|
||||
OPTION_BIN_LOG | OPTION_QUOTE_SHOW_CREATE );
|
||||
uint protocol_version=PROTOCOL_VERSION;
|
||||
uint connection_auth_flag=0; /* Supported authentication mode */
|
||||
struct system_variables global_system_variables;
|
||||
struct system_variables max_system_variables;
|
||||
ulong keybuff_size,table_cache_size,
|
||||
|
543
sql/password.c
543
sql/password.c
@ -32,6 +32,24 @@
|
||||
Example:
|
||||
update user set password=PASSWORD("hello") where user="test"
|
||||
This saves a hashed number as a string in the password field.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include <my_global.h>
|
||||
@ -45,10 +63,23 @@
|
||||
/* Character to use as version identifier for version 4.1 */
|
||||
#define PVERSION41_CHAR '*'
|
||||
|
||||
/* Scramble length for new password version */
|
||||
#define SCRAMBLE41_LENGTH 20
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
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)
|
||||
{ /* For mysql 3.21.# */
|
||||
#ifdef HAVE_purify
|
||||
@ -60,6 +91,19 @@ void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
|
||||
rand_st->seed2=seed2%rand_st->max_value;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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;
|
||||
@ -68,6 +112,18 @@ static void old_randominit(struct rand_struct *rand_st,ulong seed1)
|
||||
rand_st->seed1=seed1 ; rand_st->seed2=seed1/2;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Generate Random number
|
||||
|
||||
SYNOPSIS
|
||||
rnd()
|
||||
rand_st INOUT Structure used for number generation
|
||||
|
||||
RETURN
|
||||
Generated pseudo random number
|
||||
*/
|
||||
|
||||
double rnd(struct rand_struct *rand_st)
|
||||
{
|
||||
rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value;
|
||||
@ -75,6 +131,75 @@ double rnd(struct rand_struct *rand_st)
|
||||
return (((double) rand_st->seed1)/rand_st->max_value_dbl);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Generate String of printable random characters of requested length
|
||||
String will not be zero terminated.
|
||||
|
||||
SYNOPSIS
|
||||
create_random_string()
|
||||
length IN Lenght of
|
||||
rand_st INOUT Structure used for number generation
|
||||
target OUT Buffer for generation
|
||||
|
||||
RETURN
|
||||
none
|
||||
*/
|
||||
|
||||
void create_random_string(int length,struct rand_struct *rand_st,char* target)
|
||||
{
|
||||
char* end=target+length;
|
||||
/* Use pointer arithmetics as it is faster way to do so. */
|
||||
while (target<end)
|
||||
{
|
||||
*target=rnd(rand_st)*94+33;
|
||||
target++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
inline 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;
|
||||
from++;
|
||||
to++;
|
||||
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)
|
||||
{
|
||||
register ulong nr=1345345333L, add=7, nr2=0x12345671L;
|
||||
@ -94,15 +219,75 @@ void hash_password(ulong *result, const char *password)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Stage one password hashing.
|
||||
Used in MySQL 4.1 password handling
|
||||
|
||||
SYNOPSIS
|
||||
password_hash_stage1()
|
||||
to OUT Store stage one hash to this location
|
||||
password IN Plain text password to build hash
|
||||
|
||||
RETURN
|
||||
none
|
||||
*/
|
||||
|
||||
|
||||
|
||||
void make_scrambled_password(char *to,const char *password,my_bool force_old_scramble)
|
||||
{
|
||||
ulong hash_res[2]; /* Used for pre 4.1 password hashing */
|
||||
static uint salt=0; /* Salt for 4.1 version password */
|
||||
unsigned char* slt=(unsigned char*)&salt;
|
||||
inline void password_hash_stage1(char *to, const char *password)
|
||||
{
|
||||
SHA1_CONTEXT context;
|
||||
sha1_reset(&context);
|
||||
for (; *password ; password++)
|
||||
{
|
||||
if (*password == ' ' || *password == '\t')
|
||||
continue;/* skip space in password */
|
||||
sha1_input(&context,(int8*)&password[0],1);
|
||||
}
|
||||
sha1_result(&context,(uint8*)to);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Stage two password hashing.
|
||||
Used in MySQL 4.1 password handling
|
||||
|
||||
SYNOPSIS
|
||||
password_hash_stage2()
|
||||
to INOUT Use this as stage one hash and store stage two hash here
|
||||
salt IN Salt used for stage two hashing
|
||||
|
||||
RETURN
|
||||
none
|
||||
*/
|
||||
|
||||
inline void password_hash_stage2(char *to,const char *salt)
|
||||
{
|
||||
SHA1_CONTEXT context;
|
||||
sha1_reset(&context);
|
||||
sha1_input(&context,(uint8*)salt,4);
|
||||
sha1_input(&context,to,SHA1_HASH_SIZE);
|
||||
sha1_result(&context,(uint8*)to);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Create password to be stored in user database from raw string
|
||||
Handles both MySQL 4.1 and Pre-MySQL 4.1 passwords
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
void make_scrambled_password(char *to,const char *password,my_bool force_old_scramble,struct rand_struct *rand_st)
|
||||
{
|
||||
ulong hash_res[2]; /* Used for pre 4.1 password hashing */
|
||||
unsigned short salt; /* Salt for 4.1 version password */
|
||||
uint8 digest[SHA1_HASH_SIZE];
|
||||
if (force_old_scramble) /* Pre 4.1 password encryption */
|
||||
{
|
||||
@ -112,27 +297,16 @@ void make_scrambled_password(char *to,const char *password,my_bool force_old_scr
|
||||
else /* New password 4.1 password scrambling */
|
||||
{
|
||||
to[0]=PVERSION41_CHAR; /* New passwords have version prefix */
|
||||
/* We do not need too strong salt generation so this should be enough */
|
||||
salt+=getpid()+time(NULL)+0x01010101;
|
||||
/* Random returns number from 0 to 1 so this would be good salt generation.*/
|
||||
salt=rnd(rand_st)*65535+1;
|
||||
/* Use only 2 first bytes from it */
|
||||
sprintf(&(to[1]),"%02x%02x",slt[0],slt[1]);
|
||||
sha1_reset(&context);
|
||||
/* Use Salt for Hash */
|
||||
sha1_input(&context,(uint8*)&salt,2);
|
||||
|
||||
for (; *password ; password++)
|
||||
{
|
||||
if (*password == ' ' || *password == '\t')
|
||||
continue;/* skip space in password */
|
||||
sha1_input(&context,(int8*)&password[0],1);
|
||||
}
|
||||
sha1_result(&context,digest);
|
||||
/* Hash one more time */
|
||||
sha1_reset(&context);
|
||||
sha1_input(&context,digest,SHA1_HASH_SIZE);
|
||||
sha1_result(&context,digest);
|
||||
sprintf(to+1,"%04x",salt);
|
||||
/* First hasing is done without salt */
|
||||
password_hash_stage1(digest,password);
|
||||
/* Second stage is done with salt */
|
||||
password_hash_stage2(digest,(char*)to+1),
|
||||
/* Print resulting hash into the password*/
|
||||
sprintf(&(to[5]),
|
||||
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],
|
||||
@ -140,14 +314,111 @@ void make_scrambled_password(char *to,const char *password,my_bool force_old_scr
|
||||
}
|
||||
}
|
||||
|
||||
uint get_password_length(my_bool force_old_scramble)
|
||||
|
||||
/*
|
||||
Convert password from binary string form to salt form
|
||||
Used for MySQL 4.1 password handling
|
||||
|
||||
SYNOPSIS
|
||||
get_salt_from_bin_password()
|
||||
res OUT Store salt form password here
|
||||
password IN Binary password to be converted
|
||||
salt IN hashing-salt to be used for salt form generation
|
||||
|
||||
RETURN
|
||||
none
|
||||
*/
|
||||
|
||||
void get_salt_from_bin_password(ulong *res,unsigned char *password,ulong salt)
|
||||
{
|
||||
unsigned char* password_end=password+SCRAMBLE41_LENGTH;
|
||||
*res=salt;
|
||||
res++;
|
||||
bzero(res,5*sizeof(res[0]));
|
||||
|
||||
/* Process password of known length*/
|
||||
while (password<password_end)
|
||||
{
|
||||
ulong val=0;
|
||||
uint i;
|
||||
for (i=0 ; i < 4 ; i++)
|
||||
val=(val << 8)+(*password++);
|
||||
*res++=val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Validate password for MySQL 4.1 password handling.
|
||||
|
||||
SYNOPSIS
|
||||
validate_password()
|
||||
password IN Encrypted Scramble which we got from the client
|
||||
message IN Original scramble which we have sent to the client before
|
||||
salt IN Password in the salted form to match to
|
||||
|
||||
RETURN
|
||||
0 for correct password
|
||||
!0 for invalid password
|
||||
*/
|
||||
|
||||
my_bool validate_password(const char* password, const char* message, ulong* salt)
|
||||
{
|
||||
char buffer[SCRAMBLE41_LENGTH]; /* Used for password validation */
|
||||
char tmpsalt[8]; /* Temporary value to convert salt to string form */
|
||||
int i;
|
||||
ulong salt_candidate[6]; /* Computed candidate salt */
|
||||
|
||||
/* 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,buffer,salt[0]);
|
||||
|
||||
/* Now we shall get exactly the same password as we have stored for user */
|
||||
for(i=1;i<6;i++)
|
||||
if (salt[i]!=salt_candidate[i]) return 1;
|
||||
/* Or password correct*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get length of password string which is stored in mysql.user table
|
||||
|
||||
SYNOPSIS
|
||||
get_password_length()
|
||||
force_old_scramble IN If we wish to use pre 4.1 scramble format
|
||||
|
||||
RETURN
|
||||
password length >0
|
||||
*/
|
||||
|
||||
inline uint get_password_length(my_bool force_old_scramble)
|
||||
{
|
||||
if (force_old_scramble)
|
||||
return 16;
|
||||
else return SHA1_HASH_SIZE*2+4+1;
|
||||
}
|
||||
|
||||
uint8 get_password_version(const char* password)
|
||||
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
inline uint8 get_password_version(const char* password)
|
||||
{
|
||||
if (password==NULL) return 0;
|
||||
if (password[0]==PVERSION41_CHAR) return PVERSION41_CHAR;
|
||||
@ -155,6 +426,19 @@ uint8 get_password_version(const char* password)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get integer value of Hex character
|
||||
|
||||
SYNOPSIS
|
||||
char_val()
|
||||
X IN Character to find value for
|
||||
|
||||
RETURN
|
||||
Appropriate integer value
|
||||
*/
|
||||
|
||||
|
||||
|
||||
inline uint char_val(char X)
|
||||
{
|
||||
return (uint) (X >= '0' && X <= '9' ? X-'0' :
|
||||
@ -162,30 +446,45 @@ inline uint char_val(char X)
|
||||
X-'a'+10);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This code detects new version password by leading char.
|
||||
** Old password has to be divisible by 8 length
|
||||
** do not forget to increase array length if you need longer passwords
|
||||
** THIS FUNCTION DOES NOT HAVE ANY LENGTH CHECK
|
||||
Get Binary salt from password as in mysql.user format
|
||||
|
||||
SYNOPSIS
|
||||
get_salt_from_password()
|
||||
res OUT Store binary salt here
|
||||
password IN Password string as stored in mysql.user
|
||||
|
||||
RETURN
|
||||
none
|
||||
|
||||
NOTE
|
||||
This function does not have length check for passwords. It will just crash
|
||||
Password hashes in old format must have length divisible by 8
|
||||
*/
|
||||
|
||||
void get_salt_from_password(ulong *res,const char *password)
|
||||
{
|
||||
bzero(res,6*sizeof(res[0]));
|
||||
if (password) // zero salt corresponds to empty password
|
||||
if (password) /* zero salt corresponds to empty password */
|
||||
{
|
||||
if (password[0]==PVERSION41_CHAR) // if new password
|
||||
if (password[0]==PVERSION41_CHAR) /* if new password */
|
||||
{
|
||||
uint val=0;
|
||||
uint i;
|
||||
password++; // skip version identifier.
|
||||
password++; /* skip version identifier */
|
||||
|
||||
//get hashing salt from password and store in in the start of array
|
||||
/*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
|
||||
/* 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;
|
||||
@ -198,40 +497,168 @@ void get_salt_from_password(ulong *res,const char *password)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get string version as stored in mysql.user from salt form
|
||||
|
||||
SYNOPSIS
|
||||
make_password_from_salt()
|
||||
to OUT Store resulting string password here
|
||||
hash_res IN Password in salt format
|
||||
password_version
|
||||
IN According to which version salt should be treated
|
||||
|
||||
RETURN
|
||||
none
|
||||
*/
|
||||
|
||||
void make_password_from_salt(char *to, ulong *hash_res,uint8 password_version)
|
||||
{
|
||||
if (!password_version) // Handling of old passwords.
|
||||
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",(uint)hash_res[0],hash_res[1],
|
||||
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
|
||||
else /* Just use empty password if we can't handle it. This should not happen */
|
||||
to[0]='\0';
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Genererate a new message based on message and password
|
||||
* The same thing is done in client and server and the results are checked.
|
||||
*/
|
||||
Convert password in salted form to binary string password and hash-salt
|
||||
For old password this involes one more hashing
|
||||
|
||||
SYNOPSIS
|
||||
get_hash_and_password()
|
||||
salt IN Salt to convert from
|
||||
pversion IN Password version to use
|
||||
hash OUT Store zero ended hash here
|
||||
bin_password OUT Store binary password here (no zero at the end)
|
||||
|
||||
RETURN
|
||||
0 for pre 4.1 passwords
|
||||
!0 password version char for newer passwords
|
||||
*/
|
||||
|
||||
void get_hash_and_password(ulong* salt, uint8 pversion, char* hash, unsigned char* bin_password)
|
||||
{
|
||||
int t;
|
||||
ulong* salt_end;
|
||||
ulong val;
|
||||
SHA1_CONTEXT context;
|
||||
unsigned char* bp; /* Binary password loop pointer */
|
||||
|
||||
if (pversion) /* New password version assumed */
|
||||
{
|
||||
salt_end=salt+6;
|
||||
sprintf(hash,"%04x",(unsigned short)salt[0]);
|
||||
salt++; /* position to the second element */
|
||||
while (salt<salt_end) /* Iterate over these elements*/
|
||||
{
|
||||
val=*salt;
|
||||
for(t=3;t>=0;t--)
|
||||
{
|
||||
bin_password[t]=val%256;
|
||||
val>>=8; /* Scroll 8 bits to get next part*/
|
||||
}
|
||||
bin_password+=4; /* Get to next 4 chars*/
|
||||
salt++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use zero starting hash as an indication of old password */
|
||||
hash[0]=0;
|
||||
salt_end=salt+2;
|
||||
bp=bin_password;
|
||||
/* 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]=val%256;
|
||||
|
||||
val>>=8; /* Scroll 8 bits to get next part*/
|
||||
}
|
||||
bp+=4; /* Get to next 4 chars*/
|
||||
salt++;
|
||||
}
|
||||
/* Use 8 bytes of binary password for hash */
|
||||
sha1_input(&context,(uint8*)bin_password,8);
|
||||
sha1_result(&context,(uint8*)bin_password);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Create key from old password to decode scramble
|
||||
Used in 4.1 authentication with passwords stored old way
|
||||
|
||||
SYNOPSIS
|
||||
create_key_from_old_password()
|
||||
passwd IN Password used for key generation
|
||||
key OUT Created 20 bytes key
|
||||
|
||||
RETURN
|
||||
None
|
||||
*/
|
||||
|
||||
|
||||
void create_key_from_old_password(const char* passwd, char* key)
|
||||
{
|
||||
char buffer[20]; /* Buffer for various needs */
|
||||
ulong salt[6]; /* Salt (large for safety) */
|
||||
/* At first hash password to the string stored in password */
|
||||
make_scrambled_password(buffer,passwd,1,(struct rand_struct *)NULL);
|
||||
/* Now convert it to the salt form */
|
||||
get_salt_from_password(salt,buffer);
|
||||
/* Finally get hash and bin password from salt */
|
||||
get_hash_and_password(salt,0,buffer,(unsigned char*) key);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Scramble string with password
|
||||
Used at pre 4.1 authentication phase.
|
||||
|
||||
SYNOPSIS
|
||||
scramble()
|
||||
to OUT Store scrambled message here
|
||||
message IN Message to scramble
|
||||
password IN Password to use while scrambling
|
||||
old_ver IN Forse old version random number generator
|
||||
|
||||
RETURN
|
||||
End of scrambled string
|
||||
*/
|
||||
|
||||
char *scramble(char *to,const char *message,const char *password,
|
||||
my_bool old_ver)
|
||||
{
|
||||
struct rand_struct rand_st;
|
||||
ulong hash_pass[2],hash_message[2];
|
||||
char message_buffer[9]; /* Real message buffer */
|
||||
char* msg=message_buffer;
|
||||
|
||||
/* We use special message buffer now as new server can provide longer hash */
|
||||
|
||||
memcpy(message_buffer,message,8);
|
||||
message_buffer[8]=0;
|
||||
|
||||
if (password && password[0])
|
||||
{
|
||||
char *to_start=to;
|
||||
hash_password(hash_pass,password);
|
||||
hash_password(hash_message,message);
|
||||
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 (*message++)
|
||||
while (*msg++)
|
||||
*to++= (char) (floor(rnd(&rand_st)*31)+64);
|
||||
if (!old_ver)
|
||||
{ /* Make it harder to break */
|
||||
@ -245,6 +672,22 @@ char *scramble(char *to,const char *message,const char *password,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check scrambled message
|
||||
Used for pre 4.1 password handling
|
||||
|
||||
SYNOPSIS
|
||||
scramble()
|
||||
scrambled IN Scrambled message to check
|
||||
message IN Original message which was scramble
|
||||
hash_pass IN Password which should be used for scrambling
|
||||
old_ver IN Forse old version random number generator
|
||||
|
||||
RETURN
|
||||
0 Password correct
|
||||
!0 Password invalid
|
||||
*/
|
||||
|
||||
my_bool check_scramble(const char *scrambled, const char *message,
|
||||
ulong *hash_pass, my_bool old_ver)
|
||||
{
|
||||
@ -252,8 +695,12 @@ my_bool check_scramble(const char *scrambled, const char *message,
|
||||
ulong hash_message[2];
|
||||
char buff[16],*to,extra; /* Big enough for check */
|
||||
const char *pos;
|
||||
char message_buffer[9]; /* Copy of message */
|
||||
|
||||
hash_password(hash_message,message);
|
||||
memcpy(message_buffer,message,8); /* Old auth uses 8 bytes at maximum */
|
||||
message_buffer[8]=0;
|
||||
|
||||
hash_password(hash_message,message_buffer);
|
||||
if (old_ver)
|
||||
old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
|
||||
else
|
||||
|
280
sql/sql_acl.cc
280
sql/sql_acl.cc
@ -32,7 +32,6 @@
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
extern uint connection_auth_flag; // any better way to do it ?
|
||||
|
||||
struct acl_host_and_ip
|
||||
{
|
||||
@ -146,8 +145,6 @@ my_bool acl_init(bool dont_read_acl_tables)
|
||||
(void (*)(void*)) free);
|
||||
if (dont_read_acl_tables)
|
||||
{
|
||||
/* If we do not read tables use old handshake to make it quick for all clients */
|
||||
connection_auth_flag=CLIENT_LONG_PASSWORD;
|
||||
DBUG_RETURN(0); /* purecov: tested */
|
||||
}
|
||||
|
||||
@ -224,7 +221,6 @@ my_bool acl_init(bool dont_read_acl_tables)
|
||||
|
||||
DBUG_PRINT("info",("user table fields: %d",table->fields));
|
||||
allow_all_hosts=0;
|
||||
connection_auth_flag=0; /* Reset flag as we're rereading the table */
|
||||
while (!(read_record_info.read_record(&read_record_info)))
|
||||
{
|
||||
ACL_USER user;
|
||||
@ -239,28 +235,20 @@ my_bool acl_init(bool dont_read_acl_tables)
|
||||
"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 if (length % 8 && length!=45) // This holds true for passwords
|
||||
else /* non emptpy and not short passwords */
|
||||
{
|
||||
sql_print_error(
|
||||
"Found invalid password for user: '%s@%s'; Ignoring user",
|
||||
user.user ? user.user : "",
|
||||
user.host.hostname ? user.host.hostname : ""); /* purecov: tested */
|
||||
continue; /* purecov: tested */
|
||||
user.pversion=get_password_version(user.password);
|
||||
/* Only passwords of specific lengths depending on version are allowed */
|
||||
if ( (!user.pversion && length % 8) || (user.pversion && length!=45 ))
|
||||
{
|
||||
sql_print_error(
|
||||
"Found invalid password for user: '%s@%s'; Ignoring user",
|
||||
user.user ? user.user : "",
|
||||
user.host.hostname ? user.host.hostname : ""); /* purecov: tested */
|
||||
continue; /* purecov: tested */
|
||||
}
|
||||
}
|
||||
get_salt_from_password(user.salt,user.password);
|
||||
user.pversion=get_password_version(user.password);
|
||||
/*
|
||||
We check the version of passwords in database. If no old passwords found we can force new handshake
|
||||
if there are only old password we will force new handshake. In case of both types of passwords
|
||||
found we will perform 2 stage authentication.
|
||||
*/
|
||||
if (user.password && user.password[0]!=0) /* empty passwords are not counted */
|
||||
{
|
||||
if (user.pversion)
|
||||
connection_auth_flag|=CLIENT_SECURE_CONNECTION;
|
||||
else
|
||||
connection_auth_flag|=CLIENT_LONG_PASSWORD;
|
||||
}
|
||||
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);
|
||||
user.hostname_length= (user.host.hostname ?
|
||||
@ -319,17 +307,6 @@ my_bool acl_init(bool dont_read_acl_tables)
|
||||
end_read_record(&read_record_info);
|
||||
freeze_size(&acl_users);
|
||||
|
||||
/*
|
||||
If database is empty or has no passwords use new connection protocol
|
||||
unless we're running with --old-passwords option
|
||||
*/
|
||||
if (!connection_auth_flag)
|
||||
{
|
||||
if(!opt_old_passwords)
|
||||
connection_auth_flag=CLIENT_SECURE_CONNECTION;
|
||||
else connection_auth_flag=CLIENT_LONG_PASSWORD;
|
||||
}
|
||||
printf("Set flag after read: %d\n",connection_auth_flag); /* DEBUG to be removed */
|
||||
init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0);
|
||||
VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100));
|
||||
while (!(read_record_info.read_record(&read_record_info)))
|
||||
@ -509,6 +486,26 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
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[20];
|
||||
/* Generate new long scramble for the thread */
|
||||
create_random_string(20,&thd->rand,thd->scramble);
|
||||
thd->scramble[20]=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);
|
||||
/* Finally encrypt password to get prepared scramble */
|
||||
password_crypt(thd->scramble,prepared_scramble+4,bin_password,20);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Get master privilges for user (priviliges for all tables).
|
||||
@ -517,10 +514,11 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
|
||||
|
||||
ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
|
||||
const char *password,const char *message,char **priv_user,
|
||||
bool old_ver, USER_RESOURCES *mqh)
|
||||
bool old_ver, USER_RESOURCES *mqh,char* prepared_scramble,int stage)
|
||||
{
|
||||
ulong user_access=NO_ACCESS;
|
||||
*priv_user=(char*) user;
|
||||
bool password_correct=0;
|
||||
DBUG_ENTER("acl_getroot");
|
||||
|
||||
bzero(mqh,sizeof(USER_RESOURCES));
|
||||
@ -543,98 +541,130 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
|
||||
if (compare_hostname(&acl_user->host,host,ip))
|
||||
{
|
||||
if (!acl_user->password && !*password ||
|
||||
(acl_user->password && *password &&
|
||||
!check_scramble(password,message,acl_user->salt,
|
||||
(my_bool) old_ver)))
|
||||
{
|
||||
(acl_user->password && *password))
|
||||
{
|
||||
/* Quick check and accept for empty passwords*/
|
||||
if (!acl_user->password && !*password)
|
||||
password_correct=1;
|
||||
else
|
||||
{
|
||||
/* New version password is checked differently */
|
||||
if (acl_user->pversion)
|
||||
{
|
||||
if (stage) /* We check password only on the second stage */
|
||||
{
|
||||
if (!validate_password(password,message,acl_user->salt))
|
||||
password_correct=1;
|
||||
}
|
||||
else /* First stage - just prepare scramble */
|
||||
prepare_scramble(thd,acl_user,prepared_scramble);
|
||||
}
|
||||
/* Old way to check password */
|
||||
else
|
||||
{
|
||||
/* Checking the scramble at any stage. First - old clients */
|
||||
if (!check_scramble(password,message,acl_user->salt,
|
||||
(my_bool) old_ver))
|
||||
password_correct=1;
|
||||
else /* Password incorrect */
|
||||
/* At the first stage - prepare scramble */
|
||||
if (!stage)
|
||||
prepare_scramble(thd,acl_user,prepared_scramble);
|
||||
}
|
||||
}
|
||||
/* If password correct continue with checking other limitations */
|
||||
if (password_correct)
|
||||
{
|
||||
#ifdef HAVE_OPENSSL
|
||||
Vio *vio=thd->net.vio;
|
||||
/*
|
||||
In this point we know that user is allowed to connect
|
||||
from given host by given username/password pair. Now
|
||||
we check if SSL is required, if user is using SSL and
|
||||
if X509 certificate attributes are OK
|
||||
*/
|
||||
switch (acl_user->ssl_type) {
|
||||
case SSL_TYPE_NOT_SPECIFIED: // Impossible
|
||||
case SSL_TYPE_NONE: /* SSL is not required to connect */
|
||||
user_access=acl_user->access;
|
||||
break;
|
||||
case SSL_TYPE_ANY: /* Any kind of SSL is good enough */
|
||||
if (vio_type(vio) == VIO_TYPE_SSL)
|
||||
user_access=acl_user->access;
|
||||
break;
|
||||
case SSL_TYPE_X509: /* Client should have any valid certificate. */
|
||||
/*
|
||||
Connections with non-valid certificates are dropped already
|
||||
in sslaccept() anyway, so we do not check validity here.
|
||||
Vio *vio=thd->net.vio;
|
||||
/*
|
||||
In this point we know that user is allowed to connect
|
||||
from given host by given username/password pair. Now
|
||||
we check if SSL is required, if user is using SSL and
|
||||
if X509 certificate attributes are OK
|
||||
*/
|
||||
if (SSL_get_peer_certificate(vio->ssl_))
|
||||
switch (acl_user->ssl_type) {
|
||||
case SSL_TYPE_NOT_SPECIFIED: // Impossible
|
||||
case SSL_TYPE_NONE: /* SSL is not required to connect */
|
||||
user_access=acl_user->access;
|
||||
break;
|
||||
case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
|
||||
/*
|
||||
We do not check for absence of SSL because without SSL it does
|
||||
not pass all checks here anyway.
|
||||
If cipher name is specified, we compare it to actual cipher in
|
||||
use.
|
||||
*/
|
||||
if (acl_user->ssl_cipher)
|
||||
{
|
||||
DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
|
||||
acl_user->ssl_cipher,
|
||||
SSL_get_cipher(vio->ssl_)));
|
||||
if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)))
|
||||
user_access=acl_user->access;
|
||||
else
|
||||
break;
|
||||
case SSL_TYPE_ANY: /* Any kind of SSL is good enough */
|
||||
if (vio_type(vio) == VIO_TYPE_SSL)
|
||||
user_access=acl_user->access;
|
||||
break;
|
||||
case SSL_TYPE_X509: /* Client should have any valid certificate. */
|
||||
/*
|
||||
Connections with non-valid certificates are dropped already
|
||||
in sslaccept() anyway, so we do not check validity here.
|
||||
*/
|
||||
if (SSL_get_peer_certificate(vio->ssl_))
|
||||
user_access=acl_user->access;
|
||||
break;
|
||||
case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
|
||||
/*
|
||||
We do not check for absence of SSL because without SSL it does
|
||||
not pass all checks here anyway.
|
||||
If cipher name is specified, we compare it to actual cipher in
|
||||
use.
|
||||
*/
|
||||
if (acl_user->ssl_cipher)
|
||||
{
|
||||
user_access=NO_ACCESS;
|
||||
break;
|
||||
DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
|
||||
acl_user->ssl_cipher,
|
||||
SSL_get_cipher(vio->ssl_)));
|
||||
if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)))
|
||||
user_access=acl_user->access;
|
||||
else
|
||||
{
|
||||
user_access=NO_ACCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Prepare certificate (if exists) */
|
||||
DBUG_PRINT("info",("checkpoint 1"));
|
||||
X509* cert=SSL_get_peer_certificate(vio->ssl_);
|
||||
DBUG_PRINT("info",("checkpoint 2"));
|
||||
/* If X509 issuer is speified, we check it... */
|
||||
if (acl_user->x509_issuer)
|
||||
{
|
||||
DBUG_PRINT("info",("checkpoint 3"));
|
||||
char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
|
||||
DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
|
||||
/* Prepare certificate (if exists) */
|
||||
DBUG_PRINT("info",("checkpoint 1"));
|
||||
X509* cert=SSL_get_peer_certificate(vio->ssl_);
|
||||
DBUG_PRINT("info",("checkpoint 2"));
|
||||
/* If X509 issuer is speified, we check it... */
|
||||
if (acl_user->x509_issuer)
|
||||
{
|
||||
DBUG_PRINT("info",("checkpoint 3"));
|
||||
char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
|
||||
DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
|
||||
acl_user->x509_issuer, ptr));
|
||||
if (strcmp(acl_user->x509_issuer, ptr))
|
||||
{
|
||||
user_access=NO_ACCESS;
|
||||
free(ptr);
|
||||
break;
|
||||
if (strcmp(acl_user->x509_issuer, ptr))
|
||||
{
|
||||
user_access=NO_ACCESS;
|
||||
free(ptr);
|
||||
break;
|
||||
}
|
||||
user_access=acl_user->access;
|
||||
free(ptr);
|
||||
}
|
||||
user_access=acl_user->access;
|
||||
free(ptr);
|
||||
}
|
||||
DBUG_PRINT("info",("checkpoint 4"));
|
||||
/* X509 subject is specified, we check it .. */
|
||||
if (acl_user->x509_subject)
|
||||
{
|
||||
char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
||||
DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
|
||||
DBUG_PRINT("info",("checkpoint 4"));
|
||||
/* X509 subject is specified, we check it .. */
|
||||
if (acl_user->x509_subject)
|
||||
{
|
||||
char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
||||
DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
|
||||
acl_user->x509_subject, ptr));
|
||||
if (strcmp(acl_user->x509_subject,ptr))
|
||||
user_access=NO_ACCESS;
|
||||
else
|
||||
user_access=acl_user->access;
|
||||
free(ptr);
|
||||
if (strcmp(acl_user->x509_subject,ptr))
|
||||
user_access=NO_ACCESS;
|
||||
else
|
||||
user_access=acl_user->access;
|
||||
free(ptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#else /* HAVE_OPENSSL */
|
||||
user_access=acl_user->access;
|
||||
user_access=acl_user->access;
|
||||
#endif /* HAVE_OPENSSL */
|
||||
*mqh=acl_user->user_resource;
|
||||
if (!acl_user->user)
|
||||
*mqh=acl_user->user_resource;
|
||||
if (!acl_user->user)
|
||||
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
|
||||
break;
|
||||
}
|
||||
break;
|
||||
} // correct password
|
||||
} // found matching user
|
||||
|
||||
#ifndef ALLOW_DOWNGRADE_OF_USERS
|
||||
break; // Wrong password breaks loop /* purecov: inspected */
|
||||
#endif
|
||||
@ -704,12 +734,6 @@ static void acl_update_user(const char *user, const char *host,
|
||||
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);
|
||||
// We should allow connection with authentication method matching password
|
||||
if (acl_user->pversion)
|
||||
connection_auth_flag|=CLIENT_SECURE_CONNECTION;
|
||||
else
|
||||
connection_auth_flag|=CLIENT_LONG_PASSWORD;
|
||||
printf("Debug: flag set to %d\n",connection_auth_flag);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -746,10 +770,6 @@ static void acl_insert_user(const char *user, const char *host,
|
||||
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);
|
||||
if (acl_user.pversion)
|
||||
connection_auth_flag|=CLIENT_SECURE_CONNECTION;
|
||||
else
|
||||
connection_auth_flag|=CLIENT_LONG_PASSWORD;
|
||||
}
|
||||
|
||||
VOID(push_dynamic(&acl_users,(gptr) &acl_user));
|
||||
@ -1124,14 +1144,7 @@ bool change_password(THD *thd, const char *host, const char *user,
|
||||
if (!new_password[0])
|
||||
acl_user->password=0;
|
||||
else
|
||||
{
|
||||
acl_user->password=(char*) ""; // Point at something
|
||||
/* Adjust global connection options depending of client password*/
|
||||
if (acl_user->pversion)
|
||||
connection_auth_flag|=CLIENT_SECURE_CONNECTION;
|
||||
else
|
||||
connection_auth_flag|=CLIENT_LONG_PASSWORD;
|
||||
}
|
||||
|
||||
acl_cache->clear(1); // Clear locked hostname cache
|
||||
VOID(pthread_mutex_unlock(&acl_cache->lock));
|
||||
@ -2241,7 +2254,6 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list,
|
||||
bool create_new_users=0;
|
||||
TABLE_LIST tables[2];
|
||||
DBUG_ENTER("mysql_grant");
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
send_error(thd, ER_UNKNOWN_COM_ERROR); /* purecov: tested */
|
||||
|
@ -88,7 +88,7 @@ ulong acl_get(const char *host, const char *ip, const char *bin_ip,
|
||||
const char *user, const char *db);
|
||||
ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
|
||||
const char *password,const char *scramble,char **priv_user,
|
||||
bool old_ver, USER_RESOURCES *max);
|
||||
bool old_ver, USER_RESOURCES *max,char* prepared_scramble, int stage);
|
||||
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,
|
||||
|
@ -496,7 +496,7 @@ public:
|
||||
uint select_number; //number of select (used for EXPLAIN)
|
||||
/* variables.transaction_isolation is reset to this after each commit */
|
||||
enum_tx_isolation session_tx_isolation;
|
||||
char scramble[9];
|
||||
char scramble[21]; // extend scramble to handle new auth
|
||||
uint8 query_cache_type; // type of query cache processing
|
||||
bool slave_thread;
|
||||
bool set_query_id,locked,count_cuted_fields,some_tables_deleted;
|
||||
|
138
sql/sql_parse.cc
138
sql/sql_parse.cc
@ -45,13 +45,13 @@
|
||||
#define MIN_HANDSHAKE_SIZE 6
|
||||
#endif /* HAVE_OPENSSL */
|
||||
#define SCRAMBLE_LENGTH 8
|
||||
#define SCRAMBLE41_LENGTH 20
|
||||
|
||||
#define MEM_ROOT_BLOCK_SIZE 8192
|
||||
#define MEM_ROOT_PREALLOC 8192
|
||||
#define TRANS_MEM_ROOT_BLOCK_SIZE 4096
|
||||
#define TRANS_MEM_ROOT_PREALLOC 4096
|
||||
|
||||
extern uint connection_auth_flag;
|
||||
|
||||
extern int yyparse(void);
|
||||
extern "C" pthread_mutex_t THR_LOCK_keycache;
|
||||
@ -180,40 +180,51 @@ end:
|
||||
*/
|
||||
|
||||
static bool check_user(THD *thd,enum_server_command command, const char *user,
|
||||
const char *passwd, const char *db, bool check_count)
|
||||
const char *passwd, const char *db, bool check_count,
|
||||
bool do_send_error, char* crypted_scramble,int stage,
|
||||
bool had_password)
|
||||
{
|
||||
thd->db=0;
|
||||
thd->db_length=0;
|
||||
USER_RESOURCES ur;
|
||||
|
||||
if (!(thd->user = my_strdup(user, MYF(0))))
|
||||
{
|
||||
send_error(thd,ER_OUT_OF_RESOURCES);
|
||||
return 1;
|
||||
}
|
||||
/* We shall avoid dupplicate user allocations here */
|
||||
if (!(thd->user))
|
||||
if (!(thd->user = my_strdup(user, MYF(0))))
|
||||
{
|
||||
send_error(thd,ER_OUT_OF_RESOURCES);
|
||||
return 1;
|
||||
}
|
||||
thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user,
|
||||
passwd, thd->scramble, &thd->priv_user,
|
||||
protocol_version == 9 ||
|
||||
!(thd->client_capabilities &
|
||||
CLIENT_LONG_PASSWORD),&ur);
|
||||
CLIENT_LONG_PASSWORD),&ur,crypted_scramble,stage);
|
||||
DBUG_PRINT("info",
|
||||
("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'",
|
||||
thd->client_capabilities, thd->max_client_packet_length,
|
||||
thd->host_or_ip, thd->priv_user,
|
||||
passwd[0] ? "yes": "no",
|
||||
had_password ? "yes": "no",
|
||||
thd->master_access, thd->db ? thd->db : "*none*"));
|
||||
|
||||
/* in case we're going to retry we should not send error message at this point */
|
||||
if (thd->master_access & NO_ACCESS)
|
||||
{
|
||||
net_printf(thd, ER_ACCESS_DENIED_ERROR,
|
||||
thd->user,
|
||||
if (do_send_error)
|
||||
{
|
||||
net_printf(thd, ER_ACCESS_DENIED_ERROR,
|
||||
thd->user,
|
||||
thd->host_or_ip,
|
||||
passwd[0] ? ER(ER_YES) : ER(ER_NO));
|
||||
mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR),
|
||||
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,
|
||||
passwd[0] ? ER(ER_YES) : ER(ER_NO));
|
||||
return(1); // Error already given
|
||||
had_password ? ER(ER_YES) : ER(ER_NO));
|
||||
return(1); // Error already given
|
||||
}
|
||||
else
|
||||
return(-1); // do not report error in special handshake
|
||||
}
|
||||
|
||||
if (check_count)
|
||||
{
|
||||
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
||||
@ -505,9 +516,10 @@ check_connections(THD *thd)
|
||||
ulong pkt_len=0;
|
||||
{
|
||||
/* buff[] needs to big enough to hold the server_version variable */
|
||||
char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+32],*end;
|
||||
char buff[SERVER_VERSION_LENGTH +
|
||||
SCRAMBLE_LENGTH+64],*end;
|
||||
int client_flags = CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
|
||||
CLIENT_PROTOCOL_41 | connection_auth_flag;
|
||||
CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION;
|
||||
|
||||
if (opt_using_transactions)
|
||||
client_flags|=CLIENT_TRANSACTIONS;
|
||||
@ -529,6 +541,8 @@ check_connections(THD *thd)
|
||||
int2store(end+3,thd->server_status);
|
||||
bzero(end+5,13);
|
||||
end+=18;
|
||||
|
||||
// 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 ||
|
||||
@ -581,19 +595,82 @@ check_connections(THD *thd)
|
||||
char *user= (char*) net->read_pos+5;
|
||||
char *passwd= strend(user)+1;
|
||||
char *db=0;
|
||||
if (passwd[0] && strlen(passwd) != SCRAMBLE_LENGTH)
|
||||
return ER_HANDSHAKE_ERROR;
|
||||
if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB)
|
||||
db=strend(passwd)+1;
|
||||
if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB)
|
||||
db=strend(passwd)+1;
|
||||
|
||||
/* We can get only old hash at this point */
|
||||
if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH)
|
||||
return ER_HANDSHAKE_ERROR;
|
||||
|
||||
if (thd->client_capabilities & CLIENT_INTERACTIVE)
|
||||
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
|
||||
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
|
||||
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
|
||||
opt_using_transactions)
|
||||
thd->net.return_status= &thd->server_status;
|
||||
opt_using_transactions)
|
||||
thd->net.return_status= &thd->server_status;
|
||||
net->read_timeout=(uint) thd->variables.net_read_timeout;
|
||||
if (check_user(thd,COM_CONNECT, user, passwd, db, 1))
|
||||
return (-1);
|
||||
thd->password=test(passwd[0]);
|
||||
|
||||
char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble and hash */
|
||||
|
||||
/* Simple connect only for old clients. New clients always use secure auth */
|
||||
bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
|
||||
|
||||
/* Store information if we used password. passwd will be dammaged */
|
||||
bool using_password=test(passwd[0]);
|
||||
|
||||
/* Check user permissions. If password failure we'll get scramble back */
|
||||
if (check_user(thd,COM_CONNECT, user, passwd, db, 1, simple_connect,
|
||||
prepared_scramble,0,using_password))
|
||||
{
|
||||
/* If The client is old we just have to return error */
|
||||
if (simple_connect)
|
||||
return -1;
|
||||
|
||||
/* Store current used and database as they are erased with next packet */
|
||||
|
||||
char tmp_user[USERNAME_LENGTH+1];
|
||||
char tmp_db[NAME_LEN+1];
|
||||
|
||||
if (user)
|
||||
{
|
||||
strncpy(tmp_user,user,USERNAME_LENGTH+1);
|
||||
/* Extra safety if we have too long data */
|
||||
tmp_user[USERNAME_LENGTH]=0;
|
||||
}
|
||||
else
|
||||
tmp_user[0]=0;
|
||||
if (db)
|
||||
{
|
||||
strncpy(tmp_db,db,NAME_LEN+1);
|
||||
tmp_db[NAME_LEN]=0;
|
||||
}
|
||||
else
|
||||
tmp_db[0]=0;
|
||||
|
||||
/* Write hash and encrypted scramble to client */
|
||||
if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4)
|
||||
|| net_flush(net))
|
||||
{
|
||||
inc_host_errors(&thd->remote.sin_addr);
|
||||
return ER_HANDSHAKE_ERROR;
|
||||
}
|
||||
/* Reading packet back */
|
||||
if ((pkt_len=my_net_read(net)) == packet_error)
|
||||
{
|
||||
inc_host_errors(&thd->remote.sin_addr);
|
||||
return ER_HANDSHAKE_ERROR;
|
||||
}
|
||||
/* We have to get very specific packet size */
|
||||
if (pkt_len!=SCRAMBLE41_LENGTH)
|
||||
{
|
||||
inc_host_errors(&thd->remote.sin_addr);
|
||||
return ER_HANDSHAKE_ERROR;
|
||||
}
|
||||
/* Final attempt to check the user based on reply */
|
||||
if (check_user(thd,COM_CONNECT, tmp_user, (char*)net->read_pos,
|
||||
tmp_db, 1, 1,prepared_scramble,1,using_password))
|
||||
return -1;
|
||||
}
|
||||
thd->password=using_password;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -954,7 +1031,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
send_error(thd, ER_UNKNOWN_COM_ERROR);
|
||||
break;
|
||||
}
|
||||
if (check_user(thd, COM_CHANGE_USER, user, passwd, db, 0))
|
||||
/* WARNING THIS HAS TO BE REWRITTEN */
|
||||
char tmp_buffer[64];
|
||||
printf("Change user called: %s %s %s\n",user,passwd,db);
|
||||
if (check_user(thd, COM_CHANGE_USER, user, passwd, db, 0,1,tmp_buffer,0,1))
|
||||
{ // Restore old user
|
||||
x_free(thd->user);
|
||||
x_free(thd->db);
|
||||
|
@ -3745,7 +3745,7 @@ text_or_password:
|
||||
else
|
||||
{
|
||||
char *buff=(char*) sql_alloc(HASH_PASSWORD_LENGTH+1);
|
||||
make_scrambled_password(buff,$3.str,opt_old_passwords);
|
||||
make_scrambled_password(buff,$3.str,opt_old_passwords,¤t_thd->rand);
|
||||
$$=buff;
|
||||
}
|
||||
}
|
||||
@ -4041,7 +4041,7 @@ grant_user:
|
||||
char *buff=(char*) sql_alloc(HASH_PASSWORD_LENGTH+1);
|
||||
if (buff)
|
||||
{
|
||||
make_scrambled_password(buff,$4.str,opt_old_passwords);
|
||||
make_scrambled_password(buff,$4.str,opt_old_passwords,¤t_thd->rand);
|
||||
$1->password.str=buff;
|
||||
$1->password.length=HASH_PASSWORD_LENGTH;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user