Commit 8c8b97fd authored by unknown's avatar unknown

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
parent 85bbdcf0
...@@ -87,7 +87,7 @@ enum commands { ...@@ -87,7 +87,7 @@ enum commands {
ADMIN_FLUSH_HOSTS, ADMIN_FLUSH_TABLES, ADMIN_PASSWORD, ADMIN_FLUSH_HOSTS, ADMIN_FLUSH_TABLES, ADMIN_PASSWORD,
ADMIN_PING, ADMIN_EXTENDED_STATUS, ADMIN_FLUSH_STATUS, ADMIN_PING, ADMIN_EXTENDED_STATUS, ADMIN_FLUSH_STATUS,
ADMIN_FLUSH_PRIVILEGES, ADMIN_START_SLAVE, ADMIN_STOP_SLAVE, ADMIN_FLUSH_PRIVILEGES, ADMIN_START_SLAVE, ADMIN_STOP_SLAVE,
ADMIN_FLUSH_THREADS ADMIN_FLUSH_THREADS, ADMIN_OLD_PASSWORD
}; };
static const char *command_names[]= { static const char *command_names[]= {
"create", "drop", "shutdown", "create", "drop", "shutdown",
...@@ -97,7 +97,7 @@ static const char *command_names[]= { ...@@ -97,7 +97,7 @@ static const char *command_names[]= {
"flush-hosts", "flush-tables", "password", "flush-hosts", "flush-tables", "password",
"ping", "extended-status", "flush-status", "ping", "extended-status", "flush-status",
"flush-privileges", "start-slave", "stop-slave", "flush-privileges", "start-slave", "stop-slave",
"flush-threads", "flush-threads","old-password",
NullS NullS
}; };
...@@ -419,7 +419,8 @@ static my_bool sql_connect(MYSQL *mysql, uint wait) ...@@ -419,7 +419,8 @@ static my_bool sql_connect(MYSQL *mysql, uint wait)
static int execute_commands(MYSQL *mysql,int argc, char **argv) static int execute_commands(MYSQL *mysql,int argc, char **argv)
{ {
const char *status; const char *status;
struct rand_struct rand_st;
for (; argc > 0 ; argv++,argc--) for (; argc > 0 ; argv++,argc--)
{ {
switch (find_type(argv[0],&command_typelib,2)) { switch (find_type(argv[0],&command_typelib,2)) {
...@@ -721,17 +722,23 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) ...@@ -721,17 +722,23 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv)
} }
break; break;
} }
case ADMIN_OLD_PASSWORD:
case ADMIN_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) if (argc < 2)
{ {
my_printf_error(0,"Too few arguments to change password",MYF(ME_BELL)); my_printf_error(0,"Too few arguments to change password",MYF(ME_BELL));
return 1; return 1;
} }
if (argv[1][0]) 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 else
crypted_pw[0]=0; /* No password */ crypted_pw[0]=0; /* No password */
sprintf(buff,"set password='%s',sql_log_off=0",crypted_pw); sprintf(buff,"set password='%s',sql_log_off=0",crypted_pw);
...@@ -836,7 +843,8 @@ static void usage(void) ...@@ -836,7 +843,8 @@ static void usage(void)
kill id,id,... Kill mysql threads"); kill id,id,... Kill mysql threads");
#if MYSQL_VERSION_ID >= 32200 #if MYSQL_VERSION_ID >= 32200
puts("\ 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 #endif
puts("\ puts("\
ping Check if mysqld is alive\n\ ping Check if mysqld is alive\n\
......
...@@ -181,7 +181,7 @@ typedef struct st_mysql ...@@ -181,7 +181,7 @@ typedef struct st_mysql
enum mysql_status status; enum mysql_status status;
my_bool free_me; /* If free in mysql_close */ my_bool free_me; /* If free in mysql_close */
my_bool reconnect; /* set to 1 if automatic reconnect */ 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 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; ...@@ -280,10 +280,17 @@ extern unsigned long net_buffer_length;
void randominit(struct rand_struct *,unsigned long seed1, void randominit(struct rand_struct *,unsigned long seed1,
unsigned long seed2); unsigned long seed2);
double rnd(struct rand_struct *); double rnd(struct rand_struct *);
void make_scrambled_password(char *to,const char *password,my_bool force_old_scramble); 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); uint get_password_length(my_bool force_old_scramble);
uint8 get_password_version(const char* password); 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 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); void make_password_from_salt(char *to, unsigned long *hash_res, uint8 password_version);
char *scramble(char *to,const char *message,const char *password, char *scramble(char *to,const char *message,const char *password,
my_bool old_ver); my_bool old_ver);
......
...@@ -67,7 +67,7 @@ ulong net_write_timeout= NET_WRITE_TIMEOUT; ...@@ -67,7 +67,7 @@ ulong net_write_timeout= NET_WRITE_TIMEOUT;
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG \ #define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG \
| CLIENT_LOCAL_FILES | CLIENT_TRANSACTIONS \ | CLIENT_LOCAL_FILES | CLIENT_TRANSACTIONS \
| CLIENT_PROTOCOL_41) | CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION)
#ifdef __WIN__ #ifdef __WIN__
...@@ -1585,6 +1585,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, ...@@ -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 buff[NAME_LEN+USERNAME_LENGTH+100],charset_name_buff[16];
char *end,*host_info,*charset_name; char *end,*host_info,*charset_name;
char password_hash[20]; /* Used for tmp storage of stage1 hash */
my_socket sock; my_socket sock;
uint32 ip_addr; uint32 ip_addr;
struct sockaddr_in sock_addr; struct sockaddr_in sock_addr;
...@@ -1789,7 +1790,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, ...@@ -1789,7 +1790,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
if ((pkt_length=net_safe_read(mysql)) == packet_error) if ((pkt_length=net_safe_read(mysql)) == packet_error)
goto 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]; mysql->protocol_version= net->read_pos[0];
DBUG_DUMP("packet",(char*) net->read_pos,10); DBUG_DUMP("packet",(char*) net->read_pos,10);
...@@ -1855,7 +1856,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, ...@@ -1855,7 +1856,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
} }
goto error; goto error;
} }
/* Save connection information */ /* Save connection information */
if (!user) user=""; if (!user) user="";
if (!passwd) passwd=""; if (!passwd) passwd="";
...@@ -1962,32 +1963,105 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, ...@@ -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", DBUG_PRINT("info",("Server version = '%s' capabilites: %ld status: %d client_flag: %d",
mysql->server_version,mysql->server_capabilities, mysql->server_version,mysql->server_capabilities,
mysql->server_status, client_flag)); mysql->server_status, client_flag));
int3store(buff+2,max_allowed_packet); int3store(buff+2,max_allowed_packet);
if (user && user[0]) if (user && user[0])
strmake(buff+5,user,32); /* Max user name */ strmake(buff+5,user,32); /* Max user name */
else else
read_user_name((char*) buff+5); read_user_name((char*) buff+5);
/* We have to handle different version of handshake here */
#ifdef _CUSTOMCONFIG_ #ifdef _CUSTOMCONFIG_
#include "_cust_libmysql.h"; #include "_cust_libmysql.h";
#endif #endif
DBUG_PRINT("info",("user: %s",buff+5)); 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)) if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
{ {
end=strmake(end+1,db,NAME_LEN); end=strmake(end+1,db,NAME_LEN);
mysql->db=my_strdup(db,MYF(MY_WME)); mysql->db=my_strdup(db,MYF(MY_WME));
db=0; db=0;
} }
/* Write authentication package */
if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net)) 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)); strmov(net->last_error,ER(net->last_errno));
goto error; 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; 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 */ if (client_flag & CLIENT_COMPRESS) /* We will use compression */
net->compress=1; net->compress=1;
if (db && mysql_select_db(mysql,db)) if (db && mysql_select_db(mysql,db))
......
...@@ -123,7 +123,7 @@ then ...@@ -123,7 +123,7 @@ then
c_u="$c_u CREATE TABLE user (" c_u="$c_u CREATE TABLE user ("
c_u="$c_u Host char(60) binary DEFAULT '' NOT NULL," 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 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 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 Insert_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u Update_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 ...@@ -5,7 +5,7 @@ master-bin.000001
4 4
127.0.0.1 127.0.0.1
replicate replicate
aaaaaaaaaaaaaaabthispartofthepasswordisnotused aaaaaaaaaaaaaaab
$MASTER_MYPORT $MASTER_MYPORT
1 1
0 0
......
...@@ -170,6 +170,7 @@ fi ...@@ -170,6 +170,7 @@ fi
@bindir@/mysql -f --user=root --password="$root_password" --host="$host" mysql <<END_OF_DATA @bindir@/mysql -f --user=root --password="$root_password" --host="$host" mysql <<END_OF_DATA
alter table user alter table user
change password password char(45) not null,
add max_questions int(11) NOT NULL AFTER x509_subject, add max_questions int(11) NOT NULL AFTER x509_subject,
add max_updates int(11) unsigned NOT NULL AFTER max_questions, add max_updates int(11) unsigned NOT NULL AFTER max_questions,
add max_connections int(11) unsigned NOT NULL AFTER max_updates; add max_connections int(11) unsigned NOT NULL AFTER max_updates;
......
...@@ -213,7 +213,7 @@ then ...@@ -213,7 +213,7 @@ then
c_u="$c_u CREATE TABLE user (" c_u="$c_u CREATE TABLE user ("
c_u="$c_u Host char(60) binary DEFAULT '' NOT NULL," 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 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 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 Insert_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u Update_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) ...@@ -1279,7 +1279,7 @@ String *Item_func_password::val_str(String *str)
return 0; return 0;
if (res->length() == 0) if (res->length() == 0)
return &empty_string; 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,&current_thd->rand);
str->set(tmp_value,get_password_length(opt_old_passwords),res->charset()); str->set(tmp_value,get_password_length(opt_old_passwords),res->charset());
return str; return str;
} }
...@@ -1291,7 +1291,7 @@ String *Item_func_old_password::val_str(String *str) ...@@ -1291,7 +1291,7 @@ String *Item_func_old_password::val_str(String *str)
return 0; return 0;
if (res->length() == 0) if (res->length() == 0)
return &empty_string; return &empty_string;
make_scrambled_password(tmp_value,res->c_ptr(),1); make_scrambled_password(tmp_value,res->c_ptr(),1,&current_thd->rand);
str->set(tmp_value,16,res->charset()); str->set(tmp_value,16,res->charset());
return str; return str;
} }
......
...@@ -87,7 +87,9 @@ static MYSQL_DATA *mc_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, ...@@ -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__) #if defined(MSDOS) || defined(__WIN__)
#define perror(A) #define perror(A)
...@@ -488,6 +490,7 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, ...@@ -488,6 +490,7 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user,
uint net_read_timeout) uint net_read_timeout)
{ {
char buff[NAME_LEN+USERNAME_LENGTH+100],*end,*host_info; char buff[NAME_LEN+USERNAME_LENGTH+100],*end,*host_info;
char password_hash[20];
my_socket sock; my_socket sock;
ulong ip_addr; ulong ip_addr;
struct sockaddr_in sock_addr; struct sockaddr_in sock_addr;
...@@ -510,7 +513,6 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, ...@@ -510,7 +513,6 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user,
user ? user : "(Null)", user ? user : "(Null)",
net_read_timeout, net_read_timeout,
(uint) slave_net_timeout)); (uint) slave_net_timeout));
net->vio = 0; /* If something goes wrong */ net->vio = 0; /* If something goes wrong */
mysql->charset=default_charset_info; /* Set character set */ mysql->charset=default_charset_info; /* Set character set */
if (!port) if (!port)
...@@ -799,22 +801,96 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, ...@@ -799,22 +801,96 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user,
} }
DBUG_PRINT("info",("user: %s",buff+5)); 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); end=strmake(end+1,db,NAME_LEN);
mysql->db=my_strdup(db,MYF(MY_WME)); mysql->db=my_strdup(db,MYF(MY_WME));
db=0; db=0;
} }
/* Write authentication package */
if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net)) 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)); strmov(net->last_error,ER(net->last_errno));
goto error; 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; 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 */ if (client_flag & CLIENT_COMPRESS) /* We will use compression */
net->compress=1; net->compress=1;
DBUG_PRINT("exit",("Mysql handler: %lx",mysql)); DBUG_PRINT("exit",("Mysql handler: %lx",mysql));
......
...@@ -317,7 +317,6 @@ uint volatile thread_count=0, thread_running=0, kill_cached_threads=0, ...@@ -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 | ulong thd_startup_options=(OPTION_UPDATE_LOG | OPTION_AUTO_IS_NULL |
OPTION_BIN_LOG | OPTION_QUOTE_SHOW_CREATE ); OPTION_BIN_LOG | OPTION_QUOTE_SHOW_CREATE );
uint protocol_version=PROTOCOL_VERSION; uint protocol_version=PROTOCOL_VERSION;
uint connection_auth_flag=0; /* Supported authentication mode */
struct system_variables global_system_variables; struct system_variables global_system_variables;
struct system_variables max_system_variables; struct system_variables max_system_variables;
ulong keybuff_size,table_cache_size, ulong keybuff_size,table_cache_size,
......
...@@ -32,6 +32,24 @@ ...@@ -32,6 +32,24 @@
Example: Example:
update user set password=PASSWORD("hello") where user="test" update user set password=PASSWORD("hello") where user="test"
This saves a hashed number as a string in the password field. 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> #include <my_global.h>
...@@ -45,10 +63,23 @@ ...@@ -45,10 +63,23 @@
/* Character to use as version identifier for version 4.1 */ /* Character to use as version identifier for version 4.1 */
#define PVERSION41_CHAR '*' #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) void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
{ /* For mysql 3.21.# */ { /* For mysql 3.21.# */
#ifdef HAVE_purify #ifdef HAVE_purify
...@@ -60,6 +91,19 @@ void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2) ...@@ -60,6 +91,19 @@ void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
rand_st->seed2=seed2%rand_st->max_value; 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) static void old_randominit(struct rand_struct *rand_st,ulong seed1)
{ /* For mysql 3.20.# */ { /* For mysql 3.20.# */
rand_st->max_value= 0x01FFFFFFL; rand_st->max_value= 0x01FFFFFFL;
...@@ -68,6 +112,18 @@ static void old_randominit(struct rand_struct *rand_st,ulong seed1) ...@@ -68,6 +112,18 @@ static void old_randominit(struct rand_struct *rand_st,ulong seed1)
rand_st->seed1=seed1 ; rand_st->seed2=seed1/2; 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) double rnd(struct rand_struct *rand_st)
{ {
rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value; 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) ...@@ -75,6 +131,75 @@ double rnd(struct rand_struct *rand_st)
return (((double) rand_st->seed1)/rand_st->max_value_dbl); 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) void hash_password(ulong *result, const char *password)
{ {
register ulong nr=1345345333L, add=7, nr2=0x12345671L; register ulong nr=1345345333L, add=7, nr2=0x12345671L;
...@@ -94,15 +219,75 @@ void hash_password(ulong *result, const char *password) ...@@ -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
*/
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);
}
void make_scrambled_password(char *to,const char *password,my_bool force_old_scramble) /*
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 */ ulong hash_res[2]; /* Used for pre 4.1 password hashing */
static uint salt=0; /* Salt for 4.1 version password */ unsigned short salt; /* Salt for 4.1 version password */
unsigned char* slt=(unsigned char*)&salt;
SHA1_CONTEXT context;
uint8 digest[SHA1_HASH_SIZE]; uint8 digest[SHA1_HASH_SIZE];
if (force_old_scramble) /* Pre 4.1 password encryption */ 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 ...@@ -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 */ else /* New password 4.1 password scrambling */
{ {
to[0]=PVERSION41_CHAR; /* New passwords have version prefix */ to[0]=PVERSION41_CHAR; /* New passwords have version prefix */
/* We do not need too strong salt generation so this should be enough */ /* Random returns number from 0 to 1 so this would be good salt generation.*/
salt+=getpid()+time(NULL)+0x01010101; salt=rnd(rand_st)*65535+1;
/* Use only 2 first bytes from it */ /* Use only 2 first bytes from it */
sprintf(&(to[1]),"%02x%02x",slt[0],slt[1]); sprintf(to+1,"%04x",salt);
sha1_reset(&context); /* First hasing is done without salt */
/* Use Salt for Hash */ password_hash_stage1(digest,password);
sha1_input(&context,(uint8*)&salt,2); /* Second stage is done with salt */
password_hash_stage2(digest,(char*)to+1),
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);
/* Print resulting hash into the password*/ /* 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", "%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[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[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 ...@@ -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) if (force_old_scramble)
return 16; return 16;
else return SHA1_HASH_SIZE*2+4+1; 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==NULL) return 0;
if (password[0]==PVERSION41_CHAR) return PVERSION41_CHAR; if (password[0]==PVERSION41_CHAR) return PVERSION41_CHAR;
...@@ -155,6 +426,19 @@ uint8 get_password_version(const char* password) ...@@ -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) inline uint char_val(char X)
{ {
return (uint) (X >= '0' && X <= '9' ? X-'0' : return (uint) (X >= '0' && X <= '9' ? X-'0' :
...@@ -162,30 +446,45 @@ inline uint char_val(char X) ...@@ -162,30 +446,45 @@ inline uint char_val(char X)
X-'a'+10); X-'a'+10);
} }
/* /*
** This code detects new version password by leading char. Get Binary salt from password as in mysql.user format
** Old password has to be divisible by 8 length
** do not forget to increase array length if you need longer passwords SYNOPSIS
** THIS FUNCTION DOES NOT HAVE ANY LENGTH CHECK 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) void get_salt_from_password(ulong *res,const char *password)
{ {
bzero(res,6*sizeof(res[0])); 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 val=0;
uint i; 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++) for (i=0 ; i < 4 ; i++)
val=(val << 4)+char_val(*password++); val=(val << 4)+char_val(*password++);
*res++=val; *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) while (*password)
{ {
ulong val=0; ulong val=0;
...@@ -198,40 +497,168 @@ void get_salt_from_password(ulong *res,const char *password) ...@@ -198,40 +497,168 @@ void get_salt_from_password(ulong *res,const char *password)
return; 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) 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]); sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
else else
if (password_version==PVERSION41_CHAR) 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]); 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'; to[0]='\0';
} }
/* /*
* Genererate a new message based on message and password Convert password in salted form to binary string password and hash-salt
* The same thing is done in client and server and the results are checked. 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, char *scramble(char *to,const char *message,const char *password,
my_bool old_ver) my_bool old_ver)
{ {
struct rand_struct rand_st; struct rand_struct rand_st;
ulong hash_pass[2],hash_message[2]; 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]) if (password && password[0])
{ {
char *to_start=to; char *to_start=to;
hash_password(hash_pass,password); hash_password(hash_pass,password);
hash_password(hash_message,message); hash_password(hash_message,message_buffer);
if (old_ver) if (old_ver)
old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]); old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
else else
randominit(&rand_st,hash_pass[0] ^ hash_message[0], randominit(&rand_st,hash_pass[0] ^ hash_message[0],
hash_pass[1] ^ hash_message[1]); hash_pass[1] ^ hash_message[1]);
while (*message++) while (*msg++)
*to++= (char) (floor(rnd(&rand_st)*31)+64); *to++= (char) (floor(rnd(&rand_st)*31)+64);
if (!old_ver) if (!old_ver)
{ /* Make it harder to break */ { /* Make it harder to break */
...@@ -245,6 +672,22 @@ char *scramble(char *to,const char *message,const char *password, ...@@ -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, my_bool check_scramble(const char *scrambled, const char *message,
ulong *hash_pass, my_bool old_ver) ulong *hash_pass, my_bool old_ver)
{ {
...@@ -252,8 +695,12 @@ my_bool check_scramble(const char *scrambled, const char *message, ...@@ -252,8 +695,12 @@ my_bool check_scramble(const char *scrambled, const char *message,
ulong hash_message[2]; ulong hash_message[2];
char buff[16],*to,extra; /* Big enough for check */ char buff[16],*to,extra; /* Big enough for check */
const char *pos; 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) if (old_ver)
old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]); old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
else else
......
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
#include <assert.h> #include <assert.h>
#include <stdarg.h> #include <stdarg.h>
extern uint connection_auth_flag; // any better way to do it ?
struct acl_host_and_ip struct acl_host_and_ip
{ {
...@@ -146,8 +145,6 @@ my_bool acl_init(bool dont_read_acl_tables) ...@@ -146,8 +145,6 @@ my_bool acl_init(bool dont_read_acl_tables)
(void (*)(void*)) free); (void (*)(void*)) free);
if (dont_read_acl_tables) 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 */ DBUG_RETURN(0); /* purecov: tested */
} }
...@@ -224,7 +221,6 @@ my_bool acl_init(bool dont_read_acl_tables) ...@@ -224,7 +221,6 @@ my_bool acl_init(bool dont_read_acl_tables)
DBUG_PRINT("info",("user table fields: %d",table->fields)); DBUG_PRINT("info",("user table fields: %d",table->fields));
allow_all_hosts=0; 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))) while (!(read_record_info.read_record(&read_record_info)))
{ {
ACL_USER user; ACL_USER user;
...@@ -239,28 +235,20 @@ my_bool acl_init(bool dont_read_acl_tables) ...@@ -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)", "Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)",
user.user ? user.user : ""); /* purecov: tested */ 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( user.pversion=get_password_version(user.password);
"Found invalid password for user: '%s@%s'; Ignoring user", /* Only passwords of specific lengths depending on version are allowed */
user.user ? user.user : "", if ( (!user.pversion && length % 8) || (user.pversion && length!=45 ))
user.host.hostname ? user.host.hostname : ""); /* purecov: tested */ {
continue; /* purecov: tested */ 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); 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;
}
user.access=get_access(table,3) & GLOBAL_ACLS; user.access=get_access(table,3) & GLOBAL_ACLS;
user.sort=get_sort(2,user.host.hostname,user.user); user.sort=get_sort(2,user.host.hostname,user.user);
user.hostname_length= (user.host.hostname ? user.hostname_length= (user.host.hostname ?
...@@ -319,17 +307,6 @@ my_bool acl_init(bool dont_read_acl_tables) ...@@ -319,17 +307,6 @@ my_bool acl_init(bool dont_read_acl_tables)
end_read_record(&read_record_info); end_read_record(&read_record_info);
freeze_size(&acl_users); 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); 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)); VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100));
while (!(read_record_info.read_record(&read_record_info))) while (!(read_record_info.read_record(&read_record_info)))
...@@ -509,6 +486,26 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) ...@@ -509,6 +486,26 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
return 0; 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). Get master privilges for user (priviliges for all tables).
...@@ -517,10 +514,11 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) ...@@ -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, ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
const char *password,const char *message,char **priv_user, const char *password,const char *message,char **priv_user,
bool old_ver, USER_RESOURCES *mqh) bool old_ver, USER_RESOURCES *mqh,char* prepared_scramble,int stage)
{ {
ulong user_access=NO_ACCESS; ulong user_access=NO_ACCESS;
*priv_user=(char*) user; *priv_user=(char*) user;
bool password_correct=0;
DBUG_ENTER("acl_getroot"); DBUG_ENTER("acl_getroot");
bzero(mqh,sizeof(USER_RESOURCES)); bzero(mqh,sizeof(USER_RESOURCES));
...@@ -543,98 +541,130 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ...@@ -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 (compare_hostname(&acl_user->host,host,ip))
{ {
if (!acl_user->password && !*password || if (!acl_user->password && !*password ||
(acl_user->password && *password && (acl_user->password && *password))
!check_scramble(password,message,acl_user->salt, {
(my_bool) old_ver))) /* 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 #ifdef HAVE_OPENSSL
Vio *vio=thd->net.vio; Vio *vio=thd->net.vio;
/* /*
In this point we know that user is allowed to connect In this point we know that user is allowed to connect
from given host by given username/password pair. Now from given host by given username/password pair. Now
we check if SSL is required, if user is using SSL and we check if SSL is required, if user is using SSL and
if X509 certificate attributes are OK if X509 certificate attributes are OK
*/
switch (acl_user->ssl_type) {
case SSL_TYPE_NOT_SPECIFIED: // Impossible
case SSL_TYPE_NONE: /* SSL is not required to connect */
user_access=acl_user->access;
break;
case SSL_TYPE_ANY: /* Any kind of SSL is good enough */
if (vio_type(vio) == VIO_TYPE_SSL)
user_access=acl_user->access;
break;
case SSL_TYPE_X509: /* Client should have any valid certificate. */
/*
Connections with non-valid certificates are dropped already
in sslaccept() anyway, so we do not check validity here.
*/ */
if (SSL_get_peer_certificate(vio->ssl_)) 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; user_access=acl_user->access;
break; break;
case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */ case SSL_TYPE_ANY: /* Any kind of SSL is good enough */
/* if (vio_type(vio) == VIO_TYPE_SSL)
We do not check for absence of SSL because without SSL it does user_access=acl_user->access;
not pass all checks here anyway. break;
If cipher name is specified, we compare it to actual cipher in case SSL_TYPE_X509: /* Client should have any valid certificate. */
use. /*
*/ Connections with non-valid certificates are dropped already
if (acl_user->ssl_cipher) in sslaccept() anyway, so we do not check validity here.
{ */
DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'", if (SSL_get_peer_certificate(vio->ssl_))
acl_user->ssl_cipher, user_access=acl_user->access;
SSL_get_cipher(vio->ssl_))); break;
if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_))) case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
user_access=acl_user->access; /*
else 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; DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
break; 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) */
/* Prepare certificate (if exists) */ DBUG_PRINT("info",("checkpoint 1"));
DBUG_PRINT("info",("checkpoint 1")); X509* cert=SSL_get_peer_certificate(vio->ssl_);
X509* cert=SSL_get_peer_certificate(vio->ssl_); DBUG_PRINT("info",("checkpoint 2"));
DBUG_PRINT("info",("checkpoint 2")); /* If X509 issuer is speified, we check it... */
/* If X509 issuer is speified, we check it... */ if (acl_user->x509_issuer)
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; DBUG_PRINT("info",("checkpoint 3"));
free(ptr); char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
break; DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
acl_user->x509_issuer, ptr));
if (strcmp(acl_user->x509_issuer, ptr))
{
user_access=NO_ACCESS;
free(ptr);
break;
}
user_access=acl_user->access;
free(ptr);
} }
user_access=acl_user->access; DBUG_PRINT("info",("checkpoint 4"));
free(ptr); /* X509 subject is specified, we check it .. */
} if (acl_user->x509_subject)
DBUG_PRINT("info",("checkpoint 4")); {
/* X509 subject is specified, we check it .. */ char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
if (acl_user->x509_subject) DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
{
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)); acl_user->x509_subject, ptr));
if (strcmp(acl_user->x509_subject,ptr)) if (strcmp(acl_user->x509_subject,ptr))
user_access=NO_ACCESS; user_access=NO_ACCESS;
else else
user_access=acl_user->access; user_access=acl_user->access;
free(ptr); free(ptr);
}
break;
} }
break;
}
#else /* HAVE_OPENSSL */ #else /* HAVE_OPENSSL */
user_access=acl_user->access; user_access=acl_user->access;
#endif /* HAVE_OPENSSL */ #endif /* HAVE_OPENSSL */
*mqh=acl_user->user_resource; *mqh=acl_user->user_resource;
if (!acl_user->user) if (!acl_user->user)
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */ *priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
break; break;
} } // correct password
} // found matching user
#ifndef ALLOW_DOWNGRADE_OF_USERS #ifndef ALLOW_DOWNGRADE_OF_USERS
break; // Wrong password breaks loop /* purecov: inspected */ break; // Wrong password breaks loop /* purecov: inspected */
#endif #endif
...@@ -704,12 +734,6 @@ static void acl_update_user(const char *user, const char *host, ...@@ -704,12 +734,6 @@ static void acl_update_user(const char *user, const char *host,
acl_user->password=(char*) ""; // Just point at something acl_user->password=(char*) ""; // Just point at something
get_salt_from_password(acl_user->salt,password); get_salt_from_password(acl_user->salt,password);
acl_user->pversion=get_password_version(acl_user->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; break;
...@@ -746,10 +770,6 @@ static void acl_insert_user(const char *user, const char *host, ...@@ -746,10 +770,6 @@ static void acl_insert_user(const char *user, const char *host,
acl_user.password=(char*) ""; // Just point at something acl_user.password=(char*) ""; // Just point at something
get_salt_from_password(acl_user.salt,password); get_salt_from_password(acl_user.salt,password);
acl_user.pversion=get_password_version(acl_user.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)); VOID(push_dynamic(&acl_users,(gptr) &acl_user));
...@@ -1124,14 +1144,7 @@ bool change_password(THD *thd, const char *host, const char *user, ...@@ -1124,14 +1144,7 @@ bool change_password(THD *thd, const char *host, const char *user,
if (!new_password[0]) if (!new_password[0])
acl_user->password=0; acl_user->password=0;
else else
{
acl_user->password=(char*) ""; // Point at something 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 acl_cache->clear(1); // Clear locked hostname cache
VOID(pthread_mutex_unlock(&acl_cache->lock)); VOID(pthread_mutex_unlock(&acl_cache->lock));
...@@ -2241,7 +2254,6 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, ...@@ -2241,7 +2254,6 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list,
bool create_new_users=0; bool create_new_users=0;
TABLE_LIST tables[2]; TABLE_LIST tables[2];
DBUG_ENTER("mysql_grant"); DBUG_ENTER("mysql_grant");
if (!initialized) if (!initialized)
{ {
send_error(thd, ER_UNKNOWN_COM_ERROR); /* purecov: tested */ 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, ...@@ -88,7 +88,7 @@ ulong acl_get(const char *host, const char *ip, const char *bin_ip,
const char *user, const char *db); const char *user, const char *db);
ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
const char *password,const char *scramble,char **priv_user, const char *password,const char *scramble,char **priv_user,
bool old_ver, USER_RESOURCES *max); bool old_ver, USER_RESOURCES *max,char* prepared_scramble, int stage);
bool acl_check_host(const char *host, const char *ip); bool acl_check_host(const char *host, const char *ip);
bool check_change_password(THD *thd, const char *host, const char *user); bool check_change_password(THD *thd, const char *host, const char *user);
bool change_password(THD *thd, const char *host, const char *user, bool change_password(THD *thd, const char *host, const char *user,
......
...@@ -496,7 +496,7 @@ public: ...@@ -496,7 +496,7 @@ public:
uint select_number; //number of select (used for EXPLAIN) uint select_number; //number of select (used for EXPLAIN)
/* variables.transaction_isolation is reset to this after each commit */ /* variables.transaction_isolation is reset to this after each commit */
enum_tx_isolation session_tx_isolation; 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 uint8 query_cache_type; // type of query cache processing
bool slave_thread; bool slave_thread;
bool set_query_id,locked,count_cuted_fields,some_tables_deleted; bool set_query_id,locked,count_cuted_fields,some_tables_deleted;
......
...@@ -45,13 +45,13 @@ ...@@ -45,13 +45,13 @@
#define MIN_HANDSHAKE_SIZE 6 #define MIN_HANDSHAKE_SIZE 6
#endif /* HAVE_OPENSSL */ #endif /* HAVE_OPENSSL */
#define SCRAMBLE_LENGTH 8 #define SCRAMBLE_LENGTH 8
#define SCRAMBLE41_LENGTH 20
#define MEM_ROOT_BLOCK_SIZE 8192 #define MEM_ROOT_BLOCK_SIZE 8192
#define MEM_ROOT_PREALLOC 8192 #define MEM_ROOT_PREALLOC 8192
#define TRANS_MEM_ROOT_BLOCK_SIZE 4096 #define TRANS_MEM_ROOT_BLOCK_SIZE 4096
#define TRANS_MEM_ROOT_PREALLOC 4096 #define TRANS_MEM_ROOT_PREALLOC 4096
extern uint connection_auth_flag;
extern int yyparse(void); extern int yyparse(void);
extern "C" pthread_mutex_t THR_LOCK_keycache; extern "C" pthread_mutex_t THR_LOCK_keycache;
...@@ -180,40 +180,51 @@ end: ...@@ -180,40 +180,51 @@ end:
*/ */
static bool check_user(THD *thd,enum_server_command command, const char *user, 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=0;
thd->db_length=0; thd->db_length=0;
USER_RESOURCES ur; USER_RESOURCES ur;
/* We shall avoid dupplicate user allocations here */
if (!(thd->user = my_strdup(user, MYF(0)))) if (!(thd->user))
{ if (!(thd->user = my_strdup(user, MYF(0))))
send_error(thd,ER_OUT_OF_RESOURCES); {
return 1; send_error(thd,ER_OUT_OF_RESOURCES);
} return 1;
}
thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user, thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user,
passwd, thd->scramble, &thd->priv_user, passwd, thd->scramble, &thd->priv_user,
protocol_version == 9 || protocol_version == 9 ||
!(thd->client_capabilities & !(thd->client_capabilities &
CLIENT_LONG_PASSWORD),&ur); CLIENT_LONG_PASSWORD),&ur,crypted_scramble,stage);
DBUG_PRINT("info", DBUG_PRINT("info",
("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'", ("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'",
thd->client_capabilities, thd->max_client_packet_length, thd->client_capabilities, thd->max_client_packet_length,
thd->host_or_ip, thd->priv_user, thd->host_or_ip, thd->priv_user,
passwd[0] ? "yes": "no", had_password ? "yes": "no",
thd->master_access, thd->db ? thd->db : "*none*")); thd->master_access, thd->db ? thd->db : "*none*"));
/* in case we're going to retry we should not send error message at this point */
if (thd->master_access & NO_ACCESS) if (thd->master_access & NO_ACCESS)
{ {
net_printf(thd, ER_ACCESS_DENIED_ERROR, if (do_send_error)
thd->user, {
net_printf(thd, ER_ACCESS_DENIED_ERROR,
thd->user,
thd->host_or_ip, thd->host_or_ip,
passwd[0] ? ER(ER_YES) : ER(ER_NO)); had_password ? ER(ER_YES) : ER(ER_NO));
mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR), mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR),
thd->user, thd->user,
thd->host_or_ip, thd->host_or_ip,
passwd[0] ? ER(ER_YES) : ER(ER_NO)); had_password ? ER(ER_YES) : ER(ER_NO));
return(1); // Error already given return(1); // Error already given
}
else
return(-1); // do not report error in special handshake
} }
if (check_count) if (check_count)
{ {
VOID(pthread_mutex_lock(&LOCK_thread_count)); VOID(pthread_mutex_lock(&LOCK_thread_count));
...@@ -505,9 +516,10 @@ check_connections(THD *thd) ...@@ -505,9 +516,10 @@ check_connections(THD *thd)
ulong pkt_len=0; ulong pkt_len=0;
{ {
/* buff[] needs to big enough to hold the server_version variable */ /* 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 | 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) if (opt_using_transactions)
client_flags|=CLIENT_TRANSACTIONS; client_flags|=CLIENT_TRANSACTIONS;
...@@ -529,6 +541,8 @@ check_connections(THD *thd) ...@@ -529,6 +541,8 @@ check_connections(THD *thd)
int2store(end+3,thd->server_status); int2store(end+3,thd->server_status);
bzero(end+5,13); bzero(end+5,13);
end+=18; end+=18;
// At this point we write connection message and read reply
if (net_write_command(net,(uchar) protocol_version, "", 0, buff, if (net_write_command(net,(uchar) protocol_version, "", 0, buff,
(uint) (end-buff)) || (uint) (end-buff)) ||
(pkt_len= my_net_read(net)) == packet_error || (pkt_len= my_net_read(net)) == packet_error ||
...@@ -581,19 +595,82 @@ check_connections(THD *thd) ...@@ -581,19 +595,82 @@ check_connections(THD *thd)
char *user= (char*) net->read_pos+5; char *user= (char*) net->read_pos+5;
char *passwd= strend(user)+1; char *passwd= strend(user)+1;
char *db=0; char *db=0;
if (passwd[0] && strlen(passwd) != SCRAMBLE_LENGTH) if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB)
return ER_HANDSHAKE_ERROR; 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) if (thd->client_capabilities & CLIENT_INTERACTIVE)
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
opt_using_transactions) opt_using_transactions)
thd->net.return_status= &thd->server_status; thd->net.return_status= &thd->server_status;
net->read_timeout=(uint) thd->variables.net_read_timeout; net->read_timeout=(uint) thd->variables.net_read_timeout;
if (check_user(thd,COM_CONNECT, user, passwd, db, 1))
return (-1); char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble and hash */
thd->password=test(passwd[0]);
/* 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; return 0;
} }
...@@ -954,7 +1031,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -954,7 +1031,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
send_error(thd, ER_UNKNOWN_COM_ERROR); send_error(thd, ER_UNKNOWN_COM_ERROR);
break; 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 { // Restore old user
x_free(thd->user); x_free(thd->user);
x_free(thd->db); x_free(thd->db);
......
...@@ -3745,7 +3745,7 @@ text_or_password: ...@@ -3745,7 +3745,7 @@ text_or_password:
else else
{ {
char *buff=(char*) sql_alloc(HASH_PASSWORD_LENGTH+1); 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,&current_thd->rand);
$$=buff; $$=buff;
} }
} }
...@@ -4041,7 +4041,7 @@ grant_user: ...@@ -4041,7 +4041,7 @@ grant_user:
char *buff=(char*) sql_alloc(HASH_PASSWORD_LENGTH+1); char *buff=(char*) sql_alloc(HASH_PASSWORD_LENGTH+1);
if (buff) if (buff)
{ {
make_scrambled_password(buff,$4.str,opt_old_passwords); make_scrambled_password(buff,$4.str,opt_old_passwords,&current_thd->rand);
$1->password.str=buff; $1->password.str=buff;
$1->password.length=HASH_PASSWORD_LENGTH; $1->password.length=HASH_PASSWORD_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