Commit 35ccfd0b authored by peter@mysql.com's avatar peter@mysql.com

SCRUM: Main change for Secure connection handling. Still needs some more coding. Commit

done for merge with newer version of code.
parent 96131759
......@@ -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,&current_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,&current_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,
......
......@@ -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
*/
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 */
static uint salt=0; /* Salt for 4.1 version password */
unsigned char* slt=(unsigned char*)&salt;
SHA1_CONTEXT context;
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
......
......@@ -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'",
acl_user->x509_issuer, ptr));
if (strcmp(acl_user->x509_issuer, ptr))
/* 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)
{
user_access=NO_ACCESS;
free(ptr);
break;
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;
}
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 @@ class THD :public ilink {
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;
......
......@@ -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 @@ static int get_or_create_user_conn(THD *thd, 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_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,&current_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,&current_thd->rand);
$1->password.str=buff;
$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