summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore2
-rw-r--r--BitKeeper/etc/logging_ok1
-rw-r--r--client/mysqladmin.c9
-rw-r--r--include/mysql.h4
-rw-r--r--include/mysql_com.h61
-rw-r--r--libmysql/libmysql.c58
-rw-r--r--scripts/mysql_create_system_tables.sh2
-rw-r--r--scripts/mysql_fix_privilege_tables.sql2
-rw-r--r--sql-common/client.c166
-rw-r--r--sql/item_strfunc.cc86
-rw-r--r--sql/item_strfunc.h30
-rw-r--r--sql/mysql_priv.h3
-rw-r--r--sql/mysqld.cc6
-rw-r--r--sql/password.c828
-rw-r--r--sql/slave.cc6
-rw-r--r--sql/slave.h2
-rw-r--r--sql/sql_acl.cc667
-rw-r--r--sql/sql_acl.h13
-rw-r--r--sql/sql_class.cc1
-rw-r--r--sql/sql_class.h14
-rw-r--r--sql/sql_parse.cc582
-rw-r--r--sql/sql_repl.h2
-rw-r--r--sql/sql_yacc.yy50
23 files changed, 1138 insertions, 1457 deletions
diff --git a/.bzrignore b/.bzrignore
index 4e9ce4e41ab..a23384d4170 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -622,3 +622,5 @@ vio/test-ssl
vio/test-sslclient
vio/test-sslserver
vio/viotest-ssl
+start_mysqld.sh
+mysys/main.cc
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok
index 794edcdd968..ac862cc9770 100644
--- a/BitKeeper/etc/logging_ok
+++ b/BitKeeper/etc/logging_ok
@@ -51,6 +51,7 @@ jcole@sarvik.tfr.cafe.ee
jcole@tetra.spaceapes.com
jorge@linux.jorge.mysql.com
kaj@work.mysql.com
+kostja@oak.local
lenz@kallisto.mysql.com
lenz@mysql.com
miguel@hegel.(none)
diff --git a/client/mysqladmin.c b/client/mysqladmin.c
index 018bcbc1963..f263d321a7b 100644
--- a/client/mysqladmin.c
+++ b/client/mysqladmin.c
@@ -769,9 +769,12 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv)
return 1;
}
if (argv[1][0])
- make_scrambled_password(crypted_pw,argv[1],
- (find_type(argv[0], &command_typelib, 2) ==
- ADMIN_OLD_PASSWORD), &rand_st);
+ {
+ if (find_type(argv[0], &command_typelib, 2) == ADMIN_OLD_PASSWORD)
+ make_scrambled_password_323(crypted_pw, argv[1]);
+ else
+ make_scrambled_password(crypted_pw, argv[1]);
+ }
else
crypted_pw[0]=0; /* No password */
sprintf(buff,"set password='%s',sql_log_off=0",crypted_pw);
diff --git a/include/mysql.h b/include/mysql.h
index bd63a10ba45..91ef481e7f7 100644
--- a/include/mysql.h
+++ b/include/mysql.h
@@ -227,7 +227,9 @@ typedef struct st_mysql
enum mysql_status status;
my_bool free_me; /* If free in mysql_close */
my_bool reconnect; /* set to 1 if automatic reconnect */
- char scramble_buff[21]; /* New protocol requires longer scramble*/
+
+ char scramble[SCRAMBLE_LENGTH+1]; /* for new servers */
+ char scramble_323[SCRAMBLE_LENGTH_323+1]; /* for old servers */
/*
Set if this is the original connection, not a master or a slave we have
diff --git a/include/mysql_com.h b/include/mysql_com.h
index e87001ff27d..c1f18160667 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -48,8 +48,15 @@ enum enum_server_command
};
-#define SCRAMBLE_LENGTH 8
-#define SCRAMBLE41_LENGTH 20
+/*
+ Length of random string sent by server on handshake; this is also length of
+ obfuscated password, recieved from client
+*/
+#define SCRAMBLE_LENGTH 20
+#define SCRAMBLE_LENGTH_323 8
+/* length of password stored in the db: new passwords are preceeded with '*' */
+#define SCRAMBLED_PASSWORD_CHAR_LENGTH (SCRAMBLE_LENGTH*2+1)
+#define SCRAMBLED_PASSWORD_CHAR_LENGTH_323 (SCRAMBLE_LENGTH_323*2)
#define NOT_NULL_FLAG 1 /* Field can't be NULL */
@@ -300,31 +307,35 @@ extern "C" {
extern unsigned long max_allowed_packet;
extern unsigned long net_buffer_length;
-void randominit(struct rand_struct *,unsigned long seed1,
- unsigned long seed2);
+/*
+ These functions are used for authentication by client and server and
+ implemented in sql/password.c
+*/
+
+void randominit(struct rand_struct *, unsigned long seed1,
+ unsigned long seed2);
double my_rnd(struct rand_struct *);
-void make_scrambled_password(char *to,const char *password,
- my_bool force_old_scramble,struct rand_struct *rand_st);
-int get_password_length(my_bool force_old_scramble);
-char get_password_version(const char* password);
-void create_random_string(int length,struct rand_struct *rand_st,char* target);
-my_bool validate_password(const char* password, const char* message,
- unsigned long* salt);
-void password_hash_stage1(char *to, const char *password);
-void password_hash_stage2(char *to,const char *salt);
-void password_crypt(const char* from,char* to, const char* password,int length);
-void get_hash_and_password(unsigned long* salt, unsigned char pversion,char* hash,
- unsigned char* bin_password);
-void get_salt_from_password(unsigned long *res,const char *password);
-void create_key_from_old_password(const char* password,char* key);
-void make_password_from_salt(char *to, unsigned long *hash_res,
- unsigned char password_version);
-char *scramble(char *to,const char *message,const char *password,
- my_bool old_ver);
-my_bool check_scramble(const char *, const char *message,
- unsigned long *salt,my_bool old_ver);
+void create_random_string(char *to, uint length, struct rand_struct *rand_st);
+
+void hash_password(ulong *to, const char *password);
+void make_scrambled_password_323(char *to, const char *password);
+char *scramble_323(char *to, const char *message, const char *password,
+ my_bool old_ver);
+my_bool check_scramble_323(const char *, const char *message,
+ unsigned long *salt, my_bool old_ver);
+void get_salt_from_password_323(unsigned long *res, const char *password);
+void make_password_from_salt_323(char *to, const unsigned long *salt);
+
+void make_scrambled_password(char *to, const char *password);
+char *scramble(char *to, const char *message, const char *password);
+my_bool check_scramble(const char *reply, const char *message,
+ const unsigned char *hash_stage2);
+void get_salt_from_password(unsigned char *res, const char *password);
+void make_password_from_salt(char *to, const unsigned char *hash_stage2);
+
+/* end of password.c */
+
char *get_tty_password(char *opt_message);
-void hash_password(unsigned long *result, const char *password);
const char *mysql_errno_to_sqlstate(unsigned int mysql_errno);
/* Some other useful functions */
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index 771278c1dbb..6bc38abb060 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -616,41 +616,51 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
/* Store user into the buffer */
end=strmov(end,user)+1;
- /*
- We always start with old type handshake the only difference is message sent
- If server handles secure connection type we'll not send the real scramble
- */
- if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
+ /* write scrambled password according to server capabilities */
+ if (passwd[0])
{
- if (passwd[0])
- {
- /* Prepare false scramble */
- bfill(end, SCRAMBLE_LENGTH, 'x');
- end+=SCRAMBLE_LENGTH;
- *end=0;
-
- }
- else /* For empty password */
- *end=0; /* zero length scramble */
+ /* Write NULL-terminated scrambled password: */
+ end= mysql->server_capabilities & CLIENT_SECURE_CONNECTION ?
+ scramble(end, mysql->scramble, passwd) :
+ scramble_323(end, mysql->scramble_323, passwd,
+ (my_bool) (mysql->protocol_version == 9));
}
else
- {
- /*
- Real scramble is only sent to old servers. This can be blocked
- by calling mysql_options(MYSQL *, MYSQL_SECURE_CONNECT, (char*) &1);
- */
- end=scramble(end, mysql->scramble_buff, passwd,
- (my_bool) (mysql->protocol_version == 9));
- }
+ *end= '\0'; // empty password
/* Add database if needed */
end=strmov(end+1,db ? db : "");
/* Write authentication package */
simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1);
- if (mysql_autenticate(mysql, passwd))
+ NET *net= &mysql->net;
+ ulong pkt_length= net_safe_read(mysql);
+
+ if (pkt_length == packet_error)
goto error;
+ if (net->read_pos[0] == mysql->scramble_323[0] &&
+ pkt_length == SCRAMBLE_LENGTH_323 + 1 &&
+ mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
+ {
+ /*
+ By sending this very specific reply server asks us to send scrambled
+ password in old format. The reply contains scramble_323.
+ */
+ scramble_323(buff, mysql->scramble_323, passwd,
+ (my_bool) (mysql->protocol_version == 9));
+ if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net))
+ {
+ net->last_errno= CR_SERVER_LOST;
+ strmov(net->sqlstate, unknown_sqlstate);
+ strmov(net->last_error,ER(net->last_errno));
+ goto error;
+ }
+ /* Read what server thinks about out new auth message report */
+ if (net_safe_read(mysql) == packet_error)
+ goto error;
+ }
+
/* Free old connect information */
my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR));
diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh
index 2739c45e750..c4cdc7b52d7 100644
--- a/scripts/mysql_create_system_tables.sh
+++ b/scripts/mysql_create_system_tables.sh
@@ -108,7 +108,7 @@ then
c_u="$c_u CREATE TABLE user ("
c_u="$c_u Host char(60) binary DEFAULT '' NOT NULL,"
c_u="$c_u User char(16) binary DEFAULT '' NOT NULL,"
- c_u="$c_u Password char(45) binary DEFAULT '' NOT NULL,"
+ c_u="$c_u Password char(41) binary DEFAULT '' NOT NULL,"
c_u="$c_u Select_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u Insert_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
c_u="$c_u Update_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql
index 43dc6d89481..8c5b29a49ff 100644
--- a/scripts/mysql_fix_privilege_tables.sql
+++ b/scripts/mysql_fix_privilege_tables.sql
@@ -4,7 +4,7 @@ ALTER TABLE host type=MyISAM;
ALTER TABLE func type=MyISAM;
ALTER TABLE columns_priv type=MyISAM;
ALTER TABLE tables_priv type=MyISAM;
-ALTER TABLE user change Password Password char(45) not null;
+ALTER TABLE user change Password Password char(41) not null;
ALTER TABLE user add File_priv enum('N','Y') NOT NULL;
CREATE TABLE IF NOT EXISTS func (
name char(64) DEFAULT '' NOT NULL,
diff --git a/sql-common/client.c b/sql-common/client.c
index 721164c8301..35dea62edc3 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -1335,76 +1335,6 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused)))
/*
- Handle password authentication
-*/
-
-my_bool mysql_autenticate(MYSQL *mysql, const char *passwd)
-{
- ulong pkt_length;
- NET *net= &mysql->net;
- char buff[SCRAMBLE41_LENGTH];
- char password_hash[SCRAMBLE41_LENGTH]; /* Used for storage of stage1 hash */
-
- /* We shall only query server if it expect us to do so */
- if ((pkt_length=net_safe_read(mysql)) == packet_error)
- goto error;
-
- if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
- {
- /*
- This should always happen with new server unless empty password
- OK/Error packets have zero as the first char
- */
- if (pkt_length == 24 && net->read_pos[0])
- {
- /* Old passwords will have '*' at the first byte of hash */
- if (net->read_pos[0] != '*')
- {
- /* Build full password hash as it is required to decode scramble */
- password_hash_stage1(buff, passwd);
- /* Store copy as we'll need it later */
- memcpy(password_hash,buff,SCRAMBLE41_LENGTH);
- /* Finally hash complete password using hash we got from server */
- password_hash_stage2(password_hash,(const char*) net->read_pos);
- /* Decypt and store scramble 4 = hash for stage2 */
- password_crypt((const char*) net->read_pos+4,mysql->scramble_buff,
- password_hash, SCRAMBLE41_LENGTH);
- mysql->scramble_buff[SCRAMBLE41_LENGTH]=0;
- /* Encode scramble with password. Recycle buffer */
- password_crypt(mysql->scramble_buff,buff,buff,SCRAMBLE41_LENGTH);
- }
- else
- {
- /* Create password to decode scramble */
- create_key_from_old_password(passwd,password_hash);
- /* Decypt and store scramble 4 = hash for stage2 */
- password_crypt((const char*) net->read_pos+4,mysql->scramble_buff,
- password_hash, SCRAMBLE41_LENGTH);
- mysql->scramble_buff[SCRAMBLE41_LENGTH]=0;
- /* Finally scramble decoded scramble with password */
- scramble(buff, mysql->scramble_buff, passwd,0);
- }
- /* Write second package of authentication */
- if (my_net_write(net,buff,SCRAMBLE41_LENGTH) || net_flush(net))
- {
- net->last_errno= CR_SERVER_LOST;
- strmov(net->sqlstate, unknown_sqlstate);
- strmov(net->last_error,ER(net->last_errno));
- goto error;
- }
- /* Read what server thinks about out new auth message report */
- if (net_safe_read(mysql) == packet_error)
- goto error;
- }
- }
- return 0;
-
-error:
- return 1;
-}
-
-
-/*
Note that the mysql argument must be initialized with mysql_init()
before calling mysql_real_connect !
*/
@@ -1481,7 +1411,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
mysql->server_status=SERVER_STATUS_AUTOCOMMIT;
/*
- Grab a socket and connect it to the server
+ Part 0: Grab a socket and connect it to the server
*/
#if defined(HAVE_SMEM)
if ((!mysql->options.protocol ||
@@ -1682,6 +1612,11 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
strmov(net->last_error,ER(net->last_errno));
goto error;
}
+
+ /*
+ Part 1: Connection established, read and parse first packet
+ */
+
if ((pkt_length=net_safe_read(mysql)) == packet_error)
goto error;
@@ -1702,8 +1637,14 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
end=strend((char*) net->read_pos+1);
mysql->thread_id=uint4korr(end+1);
end+=5;
- strmake(mysql->scramble_buff,end,8);
- end+=9;
+ /*
+ Scramble is split into two parts because old clients does not understand
+ long scrambles; here goes the first part.
+ */
+ strmake(mysql->scramble_323, end, SCRAMBLE_LENGTH_323);
+ end+= SCRAMBLE_LENGTH_323+1;
+ memcpy(mysql->scramble, mysql->scramble_323, SCRAMBLE_LENGTH_323);
+
if (pkt_length >= (uint) (end+1 - (char*) net->read_pos))
mysql->server_capabilities=uint2korr(end);
if (pkt_length >= (uint) (end+18 - (char*) net->read_pos))
@@ -1712,6 +1653,13 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
mysql->server_language=end[2];
mysql->server_status=uint2korr(end+3);
}
+ end+= 18;
+ if (pkt_length >= (uint) (end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1 -
+ (char *) net->read_pos))
+ strmake(mysql->scramble+SCRAMBLE_LENGTH_323, end,
+ SCRAMBLE_LENGTH-SCRAMBLE_LENGTH_323);
+ else
+ mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION;
/* Set character set */
if ((charset_name=mysql->options.charset_name))
@@ -1783,9 +1731,12 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
mysql->unix_socket=0;
strmov(mysql->server_version,(char*) net->read_pos+1);
mysql->port=port;
- client_flag|=mysql->options.client_flag;
- /* Send client information for access check */
+ /*
+ Part 2: format and send client info to the server for access check
+ */
+
+ client_flag|=mysql->options.client_flag;
client_flag|=CLIENT_CAPABILITIES;
if (client_flag & CLIENT_MULTI_QUERIES)
client_flag|= CLIENT_MULTI_RESULTS;
@@ -1881,35 +1832,18 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
#include "_cust_libmysql.h"
#endif
DBUG_PRINT("info",("user: %s",end));
- /*
- We always start with old type handshake the only difference is message sent
- If server handles secure connection type we'll not send the real scramble
- */
- if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
+ end= strend(end) + 1;
+ if (passwd[0])
{
- if (passwd[0])
- {
- /* Prepare false scramble */
- end=strend(end)+1;
- bfill(end, SCRAMBLE_LENGTH, 'x');
- end+=SCRAMBLE_LENGTH;
- *end=0;
- }
- else /* For empty password*/
- {
- end=strend(end)+1;
- *end=0; /* Store zero length scramble */
- }
+ /* Write NULL-terminated scrambled password: */
+ end= mysql->server_capabilities & CLIENT_SECURE_CONNECTION ?
+ scramble(end, mysql->scramble, passwd) :
+ scramble_323(end, mysql->scramble_323, passwd,
+ (my_bool) (mysql->protocol_version == 9));
}
else
- {
- /*
- Real scramble is only sent to old servers. This can be blocked
- by calling mysql_options(MYSQL *, MYSQL_SECURE_CONNECT, (char*) &1);
- */
- end=scramble(strend(end)+1, mysql->scramble_buff, passwd,
- (my_bool) (mysql->protocol_version == 9));
- }
+ *end= '\0'; /* empty password */
+
/* Add database if needed */
if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
{
@@ -1925,10 +1859,38 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
strmov(net->last_error,ER(net->last_errno));
goto error;
}
+
+ /*
+ Part 3: Authorization data's been sent. Now server can reply with
+ OK-packet, or re-request scrambled password.
+ */
- if (mysql_autenticate(mysql, passwd))
+ if ((pkt_length=net_safe_read(mysql)) == packet_error)
goto error;
+ if (net->read_pos[0] == mysql->scramble_323[0] &&
+ pkt_length == SCRAMBLE_LENGTH_323 + 1 &&
+ mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
+ {
+ /*
+ By sending this very specific reply server asks us to send scrambled
+ password in old format. The reply contains scramble_323.
+ */
+ scramble_323(buff, mysql->scramble_323, passwd,
+ (my_bool) (mysql->protocol_version == 9));
+ if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net))
+ {
+ net->last_errno= CR_SERVER_LOST;
+ strmov(net->sqlstate, unknown_sqlstate);
+ strmov(net->last_error,ER(net->last_errno));
+ goto error;
+ }
+ /* Read what server thinks about out new auth message report */
+ if (net_safe_read(mysql) == packet_error)
+ goto error;
+ }
+
+
if (client_flag & CLIENT_COMPRESS) /* We will use compression */
net->compress=1;
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index ae63ac85d4d..f8488565b75 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1346,97 +1346,35 @@ void Item_func_trim::fix_length_and_dec()
}
-
-
-void Item_func_password::fix_length_and_dec()
-{
- /*
- If PASSWORD() was called with only one argument, it depends on a random
- number so we need to save this random number into the binary log.
- If called with two arguments, it is repeatable.
- */
- if (arg_count == 1)
- {
- THD *thd= current_thd;
- thd->rand_used= 1;
- thd->rand_saved_seed1= thd->rand.seed1;
- thd->rand_saved_seed2= thd->rand.seed2;
- }
- max_length= get_password_length(use_old_passwords);
-}
-
-/*
- Password() function has 2 arguments. Second argument can be used
- to make results repeatable
-*/
+/* Item_func_password */
String *Item_func_password::val_str(String *str)
{
- struct rand_struct rand_st; // local structure for 2 param version
- ulong seed=0; // seed to initialise random generator to
-
- String *res =args[0]->val_str(str);
+ String *res= args[0]->val_str(str);
if ((null_value=args[0]->null_value))
return 0;
-
- if (arg_count == 1)
- {
- if (res->length() == 0)
- return &empty_string;
- make_scrambled_password(tmp_value,res->c_ptr(),use_old_passwords,
- &current_thd->rand);
- str->set(tmp_value,get_password_length(use_old_passwords),res->charset());
- return str;
- }
- else
- {
- /* We'll need the buffer to get second parameter */
- char key_buff[80];
- String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info);
- String *key =args[1]->val_str(&tmp_key_value);
-
- /* Check second argument for NULL value. First one is already checked */
- if ((null_value=args[1]->null_value))
- return 0;
-
- /* This shall be done after checking for null for proper results */
- if (res->length() == 0)
- return &empty_string;
-
- /* Generate the seed first this allows to avoid double allocation */
- char* seed_ptr=key->c_ptr();
- while (*seed_ptr)
- {
- seed=(seed*211+*seed_ptr) & 0xffffffffL; /* Use simple hashing */
- seed_ptr++;
- }
-
- /* Use constants which allow nice random values even with small seed */
- randominit(&rand_st,
- (ulong) ((ulonglong) seed*111111+33333333L) & (ulong) 0xffffffff,
- (ulong) ((ulonglong) seed*1111+55555555L) & (ulong) 0xffffffff);
-
- make_scrambled_password(tmp_value,res->c_ptr(),use_old_passwords,
- &rand_st);
- str->set(tmp_value,get_password_length(use_old_passwords),res->charset());
- return str;
- }
+ if (res->length() == 0)
+ return &empty_string;
+ make_scrambled_password(tmp_value, res->c_ptr());
+ str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, res->charset());
+ return str;
}
+/* Item_func_old_password */
+
String *Item_func_old_password::val_str(String *str)
{
- String *res =args[0]->val_str(str);
+ String *res= args[0]->val_str(str);
if ((null_value=args[0]->null_value))
return 0;
if (res->length() == 0)
return &empty_string;
- make_scrambled_password(tmp_value,res->c_ptr(),1,&current_thd->rand);
- str->set(tmp_value,16,res->charset());
+ make_scrambled_password_323(tmp_value, res->c_ptr());
+ str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, res->charset());
return str;
}
-
#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
String *Item_func_encrypt::val_str(String *str)
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 6cc6d730627..11f5a66b3d1 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -254,30 +254,44 @@ public:
};
+/*
+ Item_func_password -- new (4.1.1) PASSWORD() function implementation.
+ Returns strcat('*', octet2hex(sha1(sha1(password)))). '*' stands for new
+ password format, sha1(sha1(password) is so-called hash_stage2 value.
+ Length of returned string is always 41 byte. To find out how entire
+ authentification procedure works, see comments in password.c.
+*/
+
class Item_func_password :public Item_str_func
{
- char tmp_value[64]; /* This should be enough for new password format */
+ char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
public:
Item_func_password(Item *a) :Item_str_func(a) {}
- Item_func_password(Item *a, Item *b) :Item_str_func(a,b) {}
- String *val_str(String *);
- void fix_length_and_dec();
+ String *val_str(String *str);
+ void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; }
const char *func_name() const { return "password"; }
};
+/*
+ Item_func_old_password -- PASSWORD() implementation used in MySQL 3.21 - 4.0
+ compatibility mode. This item is created in sql_yacc.yy when
+ 'use_old_passwords' session variable is set, and to handle OLD_PASSWORD()
+ function.
+*/
+
class Item_func_old_password :public Item_str_func
{
- char tmp_value[17]; /* old password length +1 */
+ char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1];
public:
Item_func_old_password(Item *a) :Item_str_func(a) {}
- String *val_str(String *);
- void fix_length_and_dec() { max_length = get_password_length(1); }
+ String *val_str(String *str);
+ void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; }
const char *func_name() const { return "old_password"; }
+ unsigned int size_of() { return sizeof(*this);}
};
-
class Item_func_des_encrypt :public Item_str_func
{
String tmp_value;
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index aca84f1bcb3..13ff168e553 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -74,9 +74,6 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
****************************************************************************/
#define ACL_CACHE_SIZE 256
-/* Password lengh for 4.1 version previous versions had 16 bytes password hash */
-#define HASH_PASSWORD_LENGTH 45
-#define HASH_OLD_PASSWORD_LENGTH 16
#define HOST_CACHE_SIZE 128
#define MAX_ACCEPT_RETRY 10 // Test accept this many times
#define MAX_FIELDS_BEFORE_HASH 32
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 72ee3e30c63..2677973ff0e 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -2743,12 +2743,6 @@ static void create_new_thread(THD *thd)
if (thread_count-delayed_insert_threads > max_used_connections)
max_used_connections=thread_count-delayed_insert_threads;
thd->thread_id=thread_id++;
- for (uint i=0; i < 8 ; i++) // Generate password teststring
- thd->scramble[i]= (char) (my_rnd(&sql_rand)*94+33);
- thd->scramble[8]=0;
- // Back it up as old clients may need it
- memcpy(thd->old_scramble,thd->scramble,9);
-
thd->real_id=pthread_self(); // Keep purify happy
diff --git a/sql/password.c b/sql/password.c
index 257547671e5..be6514d89c6 100644
--- a/sql/password.c
+++ b/sql/password.c
@@ -29,28 +29,33 @@
The password is saved (in user.password) by using the PASSWORD() function in
mysql.
+ This is .c file because it's used in libmysqlclient, which is entirely in C.
+ (we need it to be portable to a variety of systems).
Example:
update user set password=PASSWORD("hello") where user="test"
This saves a hashed number as a string in the password field.
+ The new autentication is performed in following manner:
- New in MySQL 4.1 authentication works even more secure way.
- At the first step client sends user name to the sever, and password if
- it is empty. So in case of empty password authentication is as fast as before.
- At the second stap servers sends scramble to client, which is encoded with
- password stage2 hash stored in the password database as well as salt, needed
- for client to build stage2 password to decrypt scramble.
- Client decrypts the scramble and encrypts it once again with stage1 password.
- This information is sent to server.
- Server decrypts the scramble to get stage1 password and hashes it to get
- stage2 hash. This hash is when compared to hash stored in the database.
+ SERVER: public_seed=create_random_string()
+ send(public_seed)
- This authentication needs 2 packet round trips instead of one but it is much
- stronger. Now if one will steal mysql database content he will not be able
- to break into MySQL.
+ CLIENT: recv(public_seed)
+ hash_stage1=sha1("password")
+ hash_stage2=sha1(hash_stage1)
+ reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
- New Password handling functions by Peter Zaitsev
+ // this three steps are done in scramble()
+ send(reply)
+
+
+ SERVER: recv(reply)
+ hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
+ candidate_hash2=sha1(hash_stage1)
+ check(candidate_hash2==hash_stage2)
+
+ // this three steps are done in check_scramble()
*****************************************************************************/
@@ -60,31 +65,21 @@
#include <sha1.h>
#include "mysql.h"
-
-
-/* Character to use as version identifier for version 4.1 */
-#define PVERSION41_CHAR '*'
-
-/* Scramble length for new password version */
-
+/************ MySQL 3.23-4.0 authentification routines: untouched ***********/
/*
New (MySQL 3.21+) random generation structure initialization
-
SYNOPSIS
randominit()
rand_st OUT Structure to initialize
seed1 IN First initialization parameter
seed2 IN Second initialization parameter
-
- RETURN
- none
*/
-void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
-{ /* For mysql 3.21.# */
+void randominit(struct rand_struct *rand_st, ulong seed1, ulong seed2)
+{ /* For mysql 3.21.# */
#ifdef HAVE_purify
- bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */
+ bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */
#endif
rand_st->max_value= 0x3FFFFFFFL;
rand_st->max_value_dbl=(double) rand_st->max_value;
@@ -95,18 +90,15 @@ void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
/*
Old (MySQL 3.20) random generation structure initialization
-
+ XXX: is to be deleted very soon!
SYNOPSIS
old_randominit()
rand_st OUT Structure to initialize
seed1 IN First initialization parameter
-
- RETURN
- none
*/
-static void old_randominit(struct rand_struct *rand_st,ulong seed1)
-{ /* For mysql 3.20.# */
+static void old_randominit(struct rand_struct *rand_st, ulong seed1)
+{ /* For mysql 3.20.# */
rand_st->max_value= 0x01FFFFFFL;
rand_st->max_value_dbl=(double) rand_st->max_value;
seed1%=rand_st->max_value;
@@ -115,14 +107,12 @@ static void old_randominit(struct rand_struct *rand_st,ulong seed1)
/*
- Generate Random number
-
+ Generate random number.
SYNOPSIS
my_rnd()
rand_st INOUT Structure used for number generation
-
- RETURN
- Generated pseudo random number
+ RETURN VALUE
+ generated pseudo random number
*/
double my_rnd(struct rand_struct *rand_st)
@@ -134,63 +124,12 @@ double my_rnd(struct rand_struct *rand_st)
/*
- Generate String of printable random characters of requested length
- String will not be zero terminated.
-
+ Generate binary hash from raw text string
+ Used for Pre-4.1 password handling
SYNOPSIS
- create_random_string()
- length IN Lenght of
- rand_st INOUT Structure used for number generation
- target OUT Buffer for generation
-
- RETURN
- none
-*/
-
-void create_random_string(int length,struct rand_struct *rand_st,char *target)
-{
- char *end=target+length;
- /* Use pointer arithmetics as it is faster way to do so. */
- for (; target<end ; target++)
- *target= (char) (my_rnd(rand_st)*94+33);
-}
-
-
-/*
- Encrypt/Decrypt function used for password encryption in authentication
- Simple XOR is used here but it is OK as we crypt random strings
-
- SYNOPSIS
- password_crypt()
- from IN Data for encryption
- to OUT Encrypt data to the buffer (may be the same)
- password IN Password used for encryption (same length)
- length IN Length of data to encrypt
-
- RETURN
- none
-*/
-
-void password_crypt(const char *from,char *to, const char *password,int length)
-{
- const char *from_end=from+length;
-
- while (from < from_end)
- *to++= *(from++) ^* (password++);
-}
-
-
-/*
- Generate binary hash from raw text password
- Used for Pre-4.1 Password handling
-
- SYNOPSIS
- hash_pasword()
- result OUT Store hash in this location
- password IN Plain text password to build hash
-
- RETURN
- none
+ hash_password()
+ result OUT store hash in this location
+ password IN plain text password to build hash
*/
void hash_password(ulong *result, const char *password)
@@ -200,7 +139,7 @@ void hash_password(ulong *result, const char *password)
for (; *password ; password++)
{
if (*password == ' ' || *password == '\t')
- continue; /* skipp space in password */
+ continue; /* skip space in password */
tmp= (ulong) (uchar) *password;
nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
nr2+=(nr2 << 8) ^ nr;
@@ -213,514 +152,407 @@ void hash_password(ulong *result, const char *password)
/*
- Stage one password hashing.
- Used in MySQL 4.1 password handling
-
+ Create password to be stored in user database from raw string
+ Used for pre-4.1 password handling
SYNOPSIS
- password_hash_stage1()
- to OUT Store stage one hash to this location
- password IN Plain text password to build hash
-
- RETURN
- none
+ make_scrambled_password_323()
+ to OUT store scrambled password here
+ password IN user-supplied password
*/
-void password_hash_stage1(char *to, const char *password)
+void make_scrambled_password_323(char *to, const char *password)
{
- SHA1_CONTEXT context;
- sha1_reset(&context);
- for (; *password ; password++)
- {
- if (*password == ' ' || *password == '\t')
- continue;/* skip space in password */
- sha1_input(&context,(uint8*) &password[0],1);
- }
- sha1_result(&context,(uint8*)to);
+ ulong hash_res[2];
+ hash_password(hash_res, password);
+ sprintf(to, "%08lx%08lx", hash_res[0], hash_res[1]);
}
/*
- Stage two password hashing.
- Used in MySQL 4.1 password handling
-
+ Scramble string with password.
+ Used in pre 4.1 authentication phase.
SYNOPSIS
- password_hash_stage2()
- to INOUT Use this as stage one hash and store stage two hash here
- salt IN Salt used for stage two hashing
-
+ scramble_323()
+ to OUT Store scrambled message here. Buffer must be at least
+ SCRAMBLE_LENGTH_323+1 bytes long
+ message IN Message to scramble. Message must be exactly
+ SRAMBLE_LENGTH_323 long and NULL terminated.
+ password IN Password to use while scrambling
+ old_ver IN Force old version random number generator
RETURN
- none
+ End of scrambled string
*/
-void password_hash_stage2(char *to, const char *salt)
+char *scramble_323(char *to, const char *message, const char *password,
+ my_bool old_ver)
{
- SHA1_CONTEXT context;
- sha1_reset(&context);
- sha1_input(&context,(uint8*) salt, 4);
- sha1_input(&context,(uint8*) to, SHA1_HASH_SIZE);
- sha1_result(&context,(uint8*) to);
+ struct rand_struct rand_st;
+ ulong hash_pass[2], hash_message[2];
+
+ if (password && password[0])
+ {
+ char *to_start=to;
+ hash_password(hash_pass,password);
+ hash_password(hash_message, message);
+ if (old_ver)
+ old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
+ else
+ randominit(&rand_st,hash_pass[0] ^ hash_message[0],
+ hash_pass[1] ^ hash_message[1]);
+ while (*message++)
+ *to++= (char) (floor(my_rnd(&rand_st)*31)+64);
+ if (!old_ver)
+ { /* Make it harder to break */
+ char extra=(char) (floor(my_rnd(&rand_st)*31));
+ while (to_start != to)
+ *(to_start++)^=extra;
+ }
+ }
+ *to=0;
+ return to;
}
/*
- Create password to be stored in user database from raw string
- Handles both MySQL 4.1 and Pre-MySQL 4.1 passwords
-
+ Check scrambled message
+ Used in pre 4.1 password handling
SYNOPSIS
- make_scramble_password()
- to OUT Store scrambled password here
- password IN Raw string password
- force_old_scramle
- IN Force generation of old scramble variant
- rand_st INOUT Structure for temporary number generation.
- RETURN
- none
+ check_scramble_323()
+ scrambled IN scrambled message to check.
+ message IN original random message which was used for scrambling; must
+ be exactly SCRAMBLED_LENGTH_323 bytes long and
+ NULL-terminated.
+ hash_pass IN password which should be used for scrambling
+ old_ver IN force old (3.20) version random number generator
+ RETURN VALUE
+ 0 - password correct
+ !0 - password invalid
*/
-void make_scrambled_password(char *to,const char *password,
- my_bool force_old_scramble,
- struct rand_struct *rand_st)
+my_bool
+check_scramble_323(const char *scrambled, const char *message,
+ ulong *hash_pass, my_bool old_ver)
{
- ulong hash_res[2]; /* Used for pre 4.1 password hashing */
- unsigned short salt; /* Salt for 4.1 version password */
- uint8 digest[SHA1_HASH_SIZE];
- if (force_old_scramble) /* Pre 4.1 password encryption */
- {
- hash_password(hash_res,password);
- sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
- }
- else /* New password 4.1 password scrambling */
+ struct rand_struct rand_st;
+ ulong hash_message[2];
+ char buff[16],*to,extra; /* Big enough for check */
+ const char *pos;
+
+ /* Check if this exactly N bytes. Overwise this is something fishy */
+ if (strlen(message) != SCRAMBLE_LENGTH_323)
+ return 1; /* Wrong password */
+
+ hash_password(hash_message,message);
+ if (old_ver)
+ old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
+ else
+ randominit(&rand_st,hash_pass[0] ^ hash_message[0],
+ hash_pass[1] ^ hash_message[1]);
+ to=buff;
+ for (pos=scrambled ; *pos ; pos++)
+ *to++=(char) (floor(my_rnd(&rand_st)*31)+64);
+ if (old_ver)
+ extra=0;
+ else
+ extra=(char) (floor(my_rnd(&rand_st)*31));
+ to=buff;
+ while (*scrambled)
{
- to[0]=PVERSION41_CHAR; /* New passwords have version prefix */
- /* Rnd returns number from 0 to 1 so this would be good salt generation.*/
- salt=(unsigned short) (my_rnd(rand_st)*65535+1);
- /* Use only 2 first bytes from it */
- sprintf(to+1,"%04x",salt);
- /* First hasing is done without salt */
- password_hash_stage1((char*) digest, password);
- /* Second stage is done with salt */
- password_hash_stage2((char*) digest,(char*)to+1),
- /* Print resulting hash into the password*/
- sprintf(to+5,
- "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
- digest[0],digest[1],digest[2],digest[3],digest[4],digest[5],digest[6],
- digest[7],digest[8],digest[9],digest[10],digest[11],digest[12],digest[13],
- digest[14],digest[15],digest[16],digest[17],digest[18],digest[19]);
+ if (*scrambled++ != (char) (*to++ ^ extra))
+ return 1; /* Wrong password */
}
+ return 0;
+}
+
+static uint8 char_val(uint8 X)
+{
+ return (uint) (X >= '0' && X <= '9' ? X-'0' :
+ X >= 'A' && X <= 'Z' ? X-'A'+10 : X-'a'+10);
}
/*
- Convert password from binary string form to salt form
- Used for MySQL 4.1 password handling
-
+ Convert password from hex string (as stored in mysql.user) to binary form.
SYNOPSIS
- get_salt_from_bin_password()
- res OUT Store salt form password here
- password IN Binary password to be converted
- salt IN hashing-salt to be used for salt form generation
-
- RETURN
- none
+ get_salt_from_password_323()
+ res OUT store salt here
+ password IN password string as stored in mysql.user
+ NOTE
+ This function does not have length check for passwords. It will just crash
+ Password hashes in old format must have length divisible by 8
*/
-void get_salt_from_bin_password(ulong *res,unsigned char *password,ulong salt)
+void get_salt_from_password_323(ulong *res, const char *password)
{
- unsigned char *password_end=password+SCRAMBLE41_LENGTH;
- *res=salt;
- res++;
-
- /* Process password of known length*/
- while (password<password_end)
+ res[0]= res[1]= 0;
+ if (password)
{
- ulong val=0;
- uint i;
- for (i=0 ; i < 4 ; i++)
- val=(val << 8)+(*password++);
- *res++=val;
+ while (*password)
+ {
+ ulong val=0;
+ uint i;
+ for (i=0 ; i < 8 ; i++)
+ val=(val << 4)+char_val(*password++);
+ *res++=val;
+ }
}
}
/*
- Validate password for MySQL 4.1 password handling.
-
+ Convert scrambled password from binary form to asciiz hex string.
SYNOPSIS
- validate_password()
- password IN Encrypted Scramble which we got from the client
- message IN Original scramble which we have sent to the client before
- salt IN Password in the salted form to match to
-
- RETURN
- 0 for correct password
- !0 for invalid password
+ make_password_from_salt_323()
+ to OUT store resulting string password here, at least 17 bytes
+ salt IN password in salt format, 2 ulongs
*/
-my_bool validate_password(const char *password, const char *message,
- ulong *salt)
+void make_password_from_salt_323(char *to, const ulong *salt)
{
- char buffer[SCRAMBLE41_LENGTH]; /* Used for password validation */
- char tmpsalt[8]; /* Temporary value to convert salt to string form */
- ulong salt_candidate[6]; /* Computed candidate salt */
- ulong *sc=salt_candidate; /* we need to be able to increment */
- ulong *salt_end;
-
- /* Now we shall get stage1 encrypted password in buffer*/
- password_crypt(password,buffer,message,SCRAMBLE41_LENGTH);
-
- /* For compatibility reasons we use ulong to store salt while we need char */
- sprintf(tmpsalt,"%04x",(unsigned short)salt[0]);
-
- password_hash_stage2(buffer,tmpsalt);
- /* Convert password to salt to compare */
- get_salt_from_bin_password(salt_candidate,(uchar*) buffer,salt[0]);
-
- /* Now we shall get exactly the same password as we have stored for user */
- for (salt_end=salt+5 ; salt < salt_end; )
- if (*++salt != *++sc)
- return 1;
-
- /* Or password correct*/
- return 0;
+ sprintf(to,"%08lx%08lx", salt[0], salt[1]);
}
+/******************* MySQL 4.1.1 authentification routines ******************/
/*
- Get length of password string which is stored in mysql.user table
-
+ Generate string of printable random characters of requested length
SYNOPSIS
- get_password_length()
- force_old_scramble IN If we wish to use pre 4.1 scramble format
-
- RETURN
- password length >0
+ create_random_string()
+ to OUT buffer for generation; must be at least length+1 bytes
+ long; result string is always null-terminated
+ length IN how many random characters to put in buffer
+ rand_st INOUT structure used for number generation
*/
-int get_password_length(my_bool force_old_scramble)
+void create_random_string(char *to, uint length, struct rand_struct *rand_st)
{
- return (force_old_scramble) ? 16 : SHA1_HASH_SIZE*2+4+1;
+ char *end= to + length;
+ /* Use pointer arithmetics as it is faster way to do so. */
+ for (; to < end; to++)
+ *to= (char) (my_rnd(rand_st)*94+33);
+ *to= '\0';
}
-/*
- Get version of the password based on mysql.user password string
-
- SYNOPSIS
- get_password_version()
- password IN Password string as stored in mysql.user
-
- RETURN
- 0 for pre 4.1 passwords
- !0 password version char for newer passwords
-*/
+/* Character to use as version identifier for version 4.1 */
-char get_password_version(const char *password)
-{
- if (password==NULL) return 0;
- if (password[0]==PVERSION41_CHAR) return PVERSION41_CHAR;
- return 0;
-}
+#define PVERSION41_CHAR '*'
/*
- Get integer value of Hex character
-
+ Convert given octet sequence to asciiz string of hex characters;
+ str..str+len and 'to' may not overlap.
SYNOPSIS
- char_val()
- X IN Character to find value for
-
- RETURN
- Appropriate integer value
+ octet2hex()
+ buf OUT output buffer. Must be at least 2*len+1 bytes
+ str, len IN the beginning and the length of the input string
*/
-
-
-static inline unsigned int char_val(char X)
+static
+void
+octet2hex(char *to, const uint8 *str, uint len)
{
- return (uint) (X >= '0' && X <= '9' ? X-'0' :
- X >= 'A' && X <= 'Z' ? X-'A'+10 :
- X-'a'+10);
+ static const char alphabet[] = { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+ const uint8 *str_end= str + len;
+ for (; str != str_end; ++str)
+ {
+ *to++= alphabet[(*str & 0xF0) >> 4];
+ *to++= alphabet[*str & 0x0F];
+ }
+ *to++= '\0';
}
/*
- Get Binary salt from password as in mysql.user format
-
+ Convert given asciiz string of hex (0..9 a..f) characters to octet
+ sequence.
SYNOPSIS
- get_salt_from_password()
- res OUT Store binary salt here
- password IN Password string as stored in mysql.user
-
- RETURN
- none
-
- NOTE
- This function does not have length check for passwords. It will just crash
- Password hashes in old format must have length divisible by 8
-*/
-
-void get_salt_from_password(ulong *res,const char *password)
+ hex2octet()
+ to OUT buffer to place result; must be at least len/2 bytes
+ str, len IN begin, length for character string; str and to may not
+ overlap; len % 2 == 0
+*/
+
+static
+void
+hex2octet(uint8 *to, const char *str, uint len)
{
- if (password) /* zero salt corresponds to empty password */
+ const char *str_end= str + len;
+ while (str < str_end)
{
- if (password[0]==PVERSION41_CHAR) /* if new password */
- {
- uint val=0;
- uint i;
- password++; /* skip version identifier */
-
- /*get hashing salt from password and store in in the start of array */
- for (i=0 ; i < 4 ; i++)
- val=(val << 4)+char_val(*password++);
- *res++=val;
- }
- /* We process old passwords the same way as new ones in other case */
-#ifdef EXTRA_DEBUG
- if (strlen(password)%8!=0)
- fprintf(stderr,"Warning: Incorrect password length for salting: %d\n",
- strlen(password));
-#endif
- while (*password)
- {
- ulong val=0;
- uint i;
- for (i=0 ; i < 8 ; i++)
- val=(val << 4)+char_val(*password++);
- *res++=val;
- }
+ *to= char_val(*str++) << 4;
+ *to++|= char_val(*str++);
}
- return;
}
/*
- Get string version as stored in mysql.user from salt form
-
+ Encrypt/Decrypt function used for password encryption in authentication.
+ Simple XOR is used here but it is OK as we crypt random strings. Note,
+ that XOR(s1, XOR(s1, s2)) == s2, XOR(s1, s2) == XOR(s2, s1)
SYNOPSIS
- make_password_from_salt()
- to OUT Store resulting string password here
- hash_res IN Password in salt format
- password_version
- IN According to which version salt should be treated
-
- RETURN
- none
+ my_crypt()
+ to OUT buffer to hold crypted string; must be at least len bytes
+ long; to and s1 (or s2) may be the same.
+ s1, s2 IN input strings (of equal length)
+ len IN length of s1 and s2
*/
-void make_password_from_salt(char *to, ulong *hash_res,uint8 password_version)
+static
+void
+my_crypt(char *to, const uint8 *s1, const uint8 *s2, uint len)
{
- if (!password_version) /* Handling of old passwords. */
- sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
- else
- if (password_version==PVERSION41_CHAR)
- sprintf(to,"%c%04x%08lx%08lx%08lx%08lx%08lx",PVERSION41_CHAR,(unsigned short)hash_res[0],hash_res[1],
- hash_res[2],hash_res[3],hash_res[4],hash_res[5]);
- else /* Just use empty password if we can't handle it. This should not happen */
- to[0]='\0';
+ const uint8 *s1_end= s1 + len;
+ while (s1 < s1_end)
+ *to++= *s1++ ^ *s2++;
}
/*
- Convert password in salted form to binary string password and hash-salt
- For old password this involes one more hashing
-
+ MySQL 4.1.1 password hashing: SHA conversion (see RFC 2289, 3174) twice
+ applied to the password string, and then produced octet sequence is
+ converted to hex string.
+ The result of this function is used as return value from PASSWORD() and
+ is stored in the database.
SYNOPSIS
- get_hash_and_password()
- salt IN Salt to convert from
- pversion IN Password version to use
- hash OUT Store zero ended hash here
- bin_password OUT Store binary password here (no zero at the end)
-
- RETURN
- 0 for pre 4.1 passwords
- !0 password version char for newer passwords
+ make_scrambled_password()
+ buf OUT buffer of size 2*SHA1_HASH_SIZE + 2 to store hex string
+ password IN NULL-terminated password string
*/
-void get_hash_and_password(ulong *salt, uint8 pversion, char *hash,
- unsigned char *bin_password)
+void
+make_scrambled_password(char *to, const char *password)
{
- int t;
- ulong* salt_end;
- ulong val;
- SHA1_CONTEXT context;
-
- if (pversion) /* New password version assumed */
- {
- salt_end=salt+5;
- sprintf(hash,"%04x",(unsigned short)salt[0]);
- while (salt<salt_end)
- {
- val=*(++salt);
- for (t=3; t>=0; t--)
- {
- bin_password[t]= (char) (val & 255);
- val>>=8; /* Scroll 8 bits to get next part*/
- }
- bin_password+=4; /* Get to next 4 chars*/
- }
- }
- else
- {
- unsigned char *bp= bin_password; /* Binary password loop pointer */
-
- /* Use zero starting hash as an indication of old password */
- hash[0]=0;
- salt_end=salt+2;
- /* Encode salt using SHA1 here */
- sha1_reset(&context);
- while (salt<salt_end) /* Iterate over these elements*/
- {
- val= *salt;
- for (t=3;t>=0;t--)
- {
- bp[t]= (uchar) (val & 255);
- val>>=8; /* Scroll 8 bits to get next part*/
- }
- bp+= 4; /* Get to next 4 chars*/
- salt++;
- }
- /* Use 8 bytes of binary password for hash */
- sha1_input(&context,(uint8*)bin_password,8);
- sha1_result(&context,(uint8*)bin_password);
- }
+ SHA1_CONTEXT sha1_context;
+ uint8 hash_stage2[SHA1_HASH_SIZE];
+
+ sha1_reset(&sha1_context);
+ /* stage 1: hash password */
+ sha1_input(&sha1_context, (uint8 *) password, strlen(password));
+ sha1_result(&sha1_context, (uint8 *) to);
+ /* stage 2: hash stage1 output */
+ sha1_reset(&sha1_context);
+ sha1_input(&sha1_context, (uint8 *) to, SHA1_HASH_SIZE);
+ /* separate buffer is used to pass 'to' in octet2hex */
+ sha1_result(&sha1_context, hash_stage2);
+ /* convert hash_stage2 to hex string */
+ *to++= PVERSION41_CHAR;
+ octet2hex(to, hash_stage2, SHA1_HASH_SIZE);
}
-
+
/*
- Create key from old password to decode scramble
- Used in 4.1 authentication with passwords stored old way
-
+ Produce an obscure octet sequence from password and random
+ string, recieved from the server. This sequence corresponds to the
+ password, but password can not be easily restored from it. The sequence
+ is then sent to the server for validation. Trailing zero is stored in
+ the buf.
+ This function is used by client to create authenticated reply to the
+ server's greeting.
SYNOPSIS
- create_key_from_old_password()
- passwd IN Password used for key generation
- key OUT Created 20 bytes key
-
- RETURN
- None
+ scramble()
+ buf OUT store scrambled string here. The buf must be at least
+ SHA1_HASH_SIZE+1 bytes long.
+ message IN random message, must be exactly SCRAMBLE_LENGTH long and
+ NULL-terminated.
+ password IN users' password
+ RETURN VALUE
+ end of scrambled string
*/
-
-void create_key_from_old_password(const char *passwd, char *key)
+char *
+scramble(char *to, const char *message, const char *password)
{
- char buffer[SCRAMBLE41_LENGTH]; /* Buffer for various needs */
- ulong salt[6]; /* Salt (large for safety) */
- /* At first hash password to the string stored in password */
- make_scrambled_password(buffer,passwd,1,(struct rand_struct *)NULL);
- /* Now convert it to the salt form */
- get_salt_from_password(salt,buffer);
- /* Finally get hash and bin password from salt */
- get_hash_and_password(salt,0,buffer,(unsigned char*) key);
+ SHA1_CONTEXT sha1_context;
+ uint8 hash_stage1[SHA1_HASH_SIZE];
+ uint8 hash_stage2[SHA1_HASH_SIZE];
+
+ sha1_reset(&sha1_context);
+ /* stage 1: hash password */
+ sha1_input(&sha1_context, (uint8 *) password, strlen(password));
+ sha1_result(&sha1_context, hash_stage1);
+ /* stage 2: hash stage 1; note that hash_stage2 is stored in the database */
+ sha1_reset(&sha1_context);
+ sha1_input(&sha1_context, hash_stage1, SHA1_HASH_SIZE);
+ sha1_result(&sha1_context, hash_stage2);
+ /* create crypt string as sha1(message, hash_stage2) */;
+ sha1_reset(&sha1_context);
+ sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH);
+ sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE);
+ /* xor allows 'from' and 'to' overlap: lets take advantage of it */
+ sha1_result(&sha1_context, (uint8 *) to);
+ my_crypt(to, (const uint8 *) to, hash_stage1, SCRAMBLE_LENGTH);
+ to[SHA1_HASH_SIZE]= '\0';
+ return to + SHA1_HASH_SIZE;
}
/*
- Scramble string with password
- Used at pre 4.1 authentication phase.
-
+ Check that scrambled message corresponds to the password; the function
+ is used by server to check that recieved reply is authentic.
+ This function does not check lengths of given strings: message must be
+ null-terminated, reply and hash_stage2 must be at least SHA1_HASH_SIZE
+ long (if not, something fishy is going on).
SYNOPSIS
- scramble()
- to OUT Store scrambled message here
- message IN Message to scramble
- password IN Password to use while scrambling
- old_ver IN Forse old version random number generator
-
- RETURN
- End of scrambled string
+ check_scramble()
+ scramble IN clients' reply, presumably produced by scramble()
+ message IN original random string, previously sent to client
+ (presumably second argument of scramble()), must be
+ exactly SCRAMBLE_LENGTH long and NULL-terminated.
+ hash_stage2 IN hex2octet-decoded database entry
+ RETURN VALUE
+ 0 password is correct
+ !0 password is invalid
*/
-char *scramble(char *to,const char *message,const char *password,
- my_bool old_ver)
+my_bool
+check_scramble(const char *scramble, const char *message,
+ const uint8 *hash_stage2)
{
- struct rand_struct rand_st;
- ulong hash_pass[2],hash_message[2];
- char message_buffer[9]; /* Real message buffer */
- char *msg=message_buffer;
-
- /* We use special message buffer now as new server can provide longer hash */
-
- memcpy(message_buffer,message,8);
- message_buffer[8]=0;
-
- if (password && password[0])
- {
- char *to_start=to;
- hash_password(hash_pass,password);
- hash_password(hash_message,message_buffer);
- if (old_ver)
- old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
- else
- randominit(&rand_st,hash_pass[0] ^ hash_message[0],
- hash_pass[1] ^ hash_message[1]);
- while (*msg++)
- *to++= (char) (floor(my_rnd(&rand_st)*31)+64);
- if (!old_ver)
- { /* Make it harder to break */
- char extra=(char) (floor(my_rnd(&rand_st)*31));
- while (to_start != to)
- *(to_start++)^=extra;
- }
- }
- *to=0;
- return to;
+ SHA1_CONTEXT sha1_context;
+ uint8 buf[SHA1_HASH_SIZE];
+ uint8 hash_stage2_reassured[SHA1_HASH_SIZE];
+
+ sha1_reset(&sha1_context);
+ /* create key to encrypt scramble */
+ sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH);
+ sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE);
+ sha1_result(&sha1_context, buf);
+ /* encrypt scramble */
+ my_crypt((char *) buf, buf, (const uint8 *) scramble, SCRAMBLE_LENGTH);
+ /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */
+ sha1_reset(&sha1_context);
+ sha1_input(&sha1_context, buf, SHA1_HASH_SIZE);
+ sha1_result(&sha1_context, hash_stage2_reassured);
+ return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE);
}
/*
- Check scrambled message
- Used for pre 4.1 password handling
-
+ Convert scrambled password from asciiz hex string to binary form.
SYNOPSIS
- scramble()
- scrambled IN Scrambled message to check
- message IN Original message which was scramble
- hash_pass IN Password which should be used for scrambling
- old_ver IN Forse old version random number generator
+ get_salt_from_password()
+ res OUT buf to hold password. Must be at least SHA1_HASH_SIZE
+ bytes long.
+ password IN 4.1.1 version value of user.password
+*/
+
+void get_salt_from_password(uint8 *hash_stage2, const char *password)
+{
+ hex2octet(hash_stage2, password+1 /* skip '*' */, SHA1_HASH_SIZE * 2);
+}
- RETURN
- 0 Password correct
- !0 Password invalid
+/*
+ Convert scrambled password from binary form to asciiz hex string.
+ SYNOPSIS
+ make_password_from_salt()
+ to OUT store resulting string here, 2*SHA1_HASH_SIZE+2 bytes
+ salt IN password in salt format
*/
-my_bool check_scramble(const char *scrambled, const char *message,
- ulong *hash_pass, my_bool old_ver)
+void make_password_from_salt(char *to, const uint8 *hash_stage2)
{
- struct rand_struct rand_st;
- ulong hash_message[2];
- char buff[16],*to,extra; /* Big enough for check */
- const char *pos;
- char message_buffer[SCRAMBLE_LENGTH+1]; /* Copy of message */
-
- /* We need to copy the message as this function can be called for MySQL 4.1
- scramble which is not zero ended and can have zeroes inside
- We could just write zero to proper place in original message but
- this would make it harder to understand code for next generations
- */
-
- memcpy(message_buffer,message,SCRAMBLE_LENGTH); /* Ignore the rest */
- message_buffer[SCRAMBLE_LENGTH]=0;
-
- /* Check if this exactly N bytes. Overwise this is something fishy */
- if (strlen(message_buffer)!=SCRAMBLE_LENGTH)
- return 1; /* Wrong password */
-
- hash_password(hash_message,message_buffer);
- if (old_ver)
- old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
- else
- randominit(&rand_st,hash_pass[0] ^ hash_message[0],
- hash_pass[1] ^ hash_message[1]);
- to=buff;
- for (pos=scrambled ; *pos ; pos++)
- *to++=(char) (floor(my_rnd(&rand_st)*31)+64);
- if (old_ver)
- extra=0;
- else
- extra=(char) (floor(my_rnd(&rand_st)*31));
- to=buff;
- while (*scrambled)
- {
- if (*scrambled++ != (char) (*to++ ^ extra))
- return 1; /* Wrong password */
- }
- return 0;
+ *to++= PVERSION41_CHAR;
+ octet2hex(to, hash_stage2, SHA1_HASH_SIZE);
}
diff --git a/sql/slave.cc b/sql/slave.cc
index c45c11f8bef..91376df5590 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1459,7 +1459,7 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname,
if (master_user)
strmake(mi->user, master_user, sizeof(mi->user) - 1);
if (master_password)
- strmake(mi->password, master_password, HASH_PASSWORD_LENGTH);
+ strmake(mi->password, master_password, SCRAMBLED_PASSWORD_CHAR_LENGTH);
mi->port = master_port;
mi->connect_retry = master_connect_retry;
}
@@ -1483,8 +1483,8 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname,
master_host) ||
init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file,
master_user) ||
- init_strvar_from_file(mi->password, HASH_PASSWORD_LENGTH+1, &mi->file,
- master_password) ||
+ init_strvar_from_file(mi->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1,
+ &mi->file, master_password) ||
init_intvar_from_file(&port, &mi->file, master_port) ||
init_intvar_from_file(&connect_retry, &mi->file,
master_connect_retry))
diff --git a/sql/slave.h b/sql/slave.h
index 429456eb0bb..d3565565ded 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -292,7 +292,7 @@ typedef struct st_master_info
/* the variables below are needed because we can change masters on the fly */
char host[HOSTNAME_LENGTH+1];
char user[USERNAME_LENGTH+1];
- char password[HASH_PASSWORD_LENGTH+1];
+ char password[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
pthread_mutex_t data_lock,run_lock;
pthread_cond_t data_cond,start_cond,stop_cond;
THD *io_thd;
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 1bdca7167e8..bbc6b74c3a9 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -68,12 +68,37 @@ static ulong get_sort(uint count,...);
static void init_check_host(void);
static ACL_USER *find_acl_user(const char *host, const char *user);
static bool update_user_table(THD *thd, const char *host, const char *user,
- const char *new_password);
+ const char *new_password, uint new_password_len);
static void update_hostname(acl_host_and_ip *host, const char *hostname);
static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
const char *ip);
/*
+ Convert scrambled password to binary form, according to scramble type,
+ Binary form is stored in user.salt.
+*/
+
+static
+void
+set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
+{
+ if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
+ {
+ get_salt_from_password(acl_user->salt, password);
+ acl_user->salt_len= SCRAMBLE_LENGTH;
+ }
+ else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323
+ || password_len == 8 && protocol_version == 9)
+ {
+ get_salt_from_password_323((ulong *) acl_user->salt, password);
+ acl_user->salt_len= password_len/2;
+ }
+ else
+ acl_user->salt_len= 0;
+}
+
+
+/*
Read grant privileges from the privilege tables in the 'mysql' database.
SYNOPSIS
@@ -175,16 +200,19 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
if (table->field[2]->field_length == 8 &&
protocol_version == PROTOCOL_VERSION)
{
- sql_print_error(
- "Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */
+ sql_print_error("Old 'user' table. "
+ "(Check README or the Reference manual). "
+ "Continuing --old-protocol"); /* purecov: tested */
protocol_version=9; /* purecov: tested */
}
DBUG_PRINT("info",("user table fields: %d, password length: %d",
table->fields, table->field[2]->field_length));
- if (table->field[2]->field_length < 45 && !use_old_passwords)
+ if (table->field[2]->field_length < 41 && !use_old_passwords)
{
- sql_print_error("mysql.user table is not updated to new password format; Disabling new password usage until mysql_fix_privilege_tables is run");
+ sql_print_error("mysql.user table is not updated to new password format; "
+ "Disabling new password usage until "
+ "mysql_fix_privilege_tables is run");
use_old_passwords= 1;
}
@@ -192,83 +220,88 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_USER user;
- uint length=0;
- update_hostname(&user.host,get_field(&mem, table->field[0]));
- user.user=get_field(&mem, table->field[1]);
- user.password=get_field(&mem, table->field[2]);
- if (user.password && (length=(uint) strlen(user.password)) == 8 &&
- protocol_version == PROTOCOL_VERSION)
- {
- sql_print_error(
- "Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)",
- user.user ? user.user : ""); /* purecov: tested */
- }
- else /* non empty and not short passwords */
- {
- user.pversion=get_password_version(user.password);
- /* Only passwords of specific lengths depending on version are allowed */
- if ( (!user.pversion && length % 8) || (user.pversion && length!=45 ))
- {
- sql_print_error(
- "Found invalid password for user: '%s@%s'; Ignoring user",
- user.user ? user.user : "",
- user.host.hostname ? user.host.hostname : ""); /* purecov: tested */
- continue; /* purecov: tested */
+ update_hostname(&user.host, get_field(&mem, table->field[0]));
+ user.user= get_field(&mem, table->field[1]);
+ const char *password= get_field(&mem, table->field[2]);
+ uint password_len= password ? strlen(password) : 0;
+ set_user_salt(&user, password, password_len);
+ if (user.salt_len == 0 && password_len != 0)
+ {
+ switch (password_len) {
+ case 8: /* 3.20: to be removed */
+ sql_print_error("Found old style password for user '%s'. "
+ "Ignoring user. (You may want to restart mysqld "
+ "using --old-protocol) ",
+ user.user ? user.user : "");
+ break;
+ case 45: /* 4.1: to be removed */
+ sql_print_error("Found 4.1 style password for user '%s'. "
+ "Ignoring user. "
+ "You should change password for this user.",
+ user.user ? user.user : "");
+ break;
+ default:
+ sql_print_error("Found invalid password for user: '%s@%s'; "
+ "Ignoring user", user.user ? user.user : "",
+ user.host.hostname ? user.host.hostname : "");
+ break;
}
}
- get_salt_from_password(user.salt,user.password);
- user.access=get_access(table,3) & GLOBAL_ACLS;
- user.sort=get_sort(2,user.host.hostname,user.user);
- user.hostname_length= (user.host.hostname ?
- (uint) strlen(user.host.hostname) : 0);
- if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */
- {
- char *ssl_type=get_field(&mem, table->field[24]);
- if (!ssl_type)
- user.ssl_type=SSL_TYPE_NONE;
- else if (!strcmp(ssl_type, "ANY"))
- user.ssl_type=SSL_TYPE_ANY;
- else if (!strcmp(ssl_type, "X509"))
- user.ssl_type=SSL_TYPE_X509;
- else /* !strcmp(ssl_type, "SPECIFIED") */
- user.ssl_type=SSL_TYPE_SPECIFIED;
-
- user.ssl_cipher= get_field(&mem, table->field[25]);
- user.x509_issuer= get_field(&mem, table->field[26]);
- user.x509_subject= get_field(&mem, table->field[27]);
-
- char *ptr = get_field(&mem, table->field[28]);
- user.user_resource.questions=atoi(ptr);
- ptr = get_field(&mem, table->field[29]);
- user.user_resource.updates=atoi(ptr);
- ptr = get_field(&mem, table->field[30]);
- user.user_resource.connections=atoi(ptr);
- if (user.user_resource.questions || user.user_resource.updates ||
- user.user_resource.connections)
- mqh_used=1;
- }
- else
+ else // password is correct
{
- user.ssl_type=SSL_TYPE_NONE;
- bzero(&(user.user_resource),sizeof(user.user_resource));
-#ifndef TO_BE_REMOVED
- if (table->fields <= 13)
- { // Without grant
- if (user.access & CREATE_ACL)
- user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
+ user.access= get_access(table,3) & GLOBAL_ACLS;
+ user.sort= get_sort(2,user.host.hostname,user.user);
+ user.hostname_length= (user.host.hostname ?
+ (uint) strlen(user.host.hostname) : 0);
+ if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */
+ {
+ char *ssl_type=get_field(&mem, table->field[24]);
+ if (!ssl_type)
+ user.ssl_type=SSL_TYPE_NONE;
+ else if (!strcmp(ssl_type, "ANY"))
+ user.ssl_type=SSL_TYPE_ANY;
+ else if (!strcmp(ssl_type, "X509"))
+ user.ssl_type=SSL_TYPE_X509;
+ else /* !strcmp(ssl_type, "SPECIFIED") */
+ user.ssl_type=SSL_TYPE_SPECIFIED;
+
+ user.ssl_cipher= get_field(&mem, table->field[25]);
+ user.x509_issuer= get_field(&mem, table->field[26]);
+ user.x509_subject= get_field(&mem, table->field[27]);
+
+ char *ptr = get_field(&mem, table->field[28]);
+ user.user_resource.questions=atoi(ptr);
+ ptr = get_field(&mem, table->field[29]);
+ user.user_resource.updates=atoi(ptr);
+ ptr = get_field(&mem, table->field[30]);
+ user.user_resource.connections=atoi(ptr);
+ if (user.user_resource.questions || user.user_resource.updates ||
+ user.user_resource.connections)
+ mqh_used=1;
}
- /* Convert old privileges */
- user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
- if (user.access & FILE_ACL)
- user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
- if (user.access & PROCESS_ACL)
- user.access|= SUPER_ACL | EXECUTE_ACL;
+ else
+ {
+ user.ssl_type=SSL_TYPE_NONE;
+ bzero(&(user.user_resource),sizeof(user.user_resource));
+#ifndef TO_BE_REMOVED
+ if (table->fields <= 13)
+ { // Without grant
+ if (user.access & CREATE_ACL)
+ user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
+ }
+ /* Convert old privileges */
+ user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
+ if (user.access & FILE_ACL)
+ user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
+ if (user.access & PROCESS_ACL)
+ user.access|= SUPER_ACL | EXECUTE_ACL;
#endif
+ }
+ VOID(push_dynamic(&acl_users,(gptr) &user));
+ if (!user.host.hostname || user.host.hostname[0] == wild_many &&
+ !user.host.hostname[1])
+ allow_all_hosts=1; // Anyone can connect
}
- VOID(push_dynamic(&acl_users,(gptr) &user));
- if (!user.host.hostname || user.host.hostname[0] == wild_many &&
- !user.host.hostname[1])
- allow_all_hosts=1; // Anyone can connect
}
qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
sizeof(ACL_USER),(qsort_cmp) acl_compare);
@@ -462,247 +495,203 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
/*
- Prepare crypted scramble to be sent to the client
-*/
-
-void prepare_scramble(THD *thd, ACL_USER *acl_user,char* prepared_scramble)
-{
- /* Binary password format to be used for generation*/
- char bin_password[SCRAMBLE41_LENGTH];
- /* Generate new long scramble for the thread */
- create_random_string(SCRAMBLE41_LENGTH,&thd->rand,thd->scramble);
- thd->scramble[SCRAMBLE41_LENGTH]=0;
- /* Get binary form, First 4 bytes of prepared scramble is salt */
- get_hash_and_password(acl_user->salt,acl_user->pversion,prepared_scramble,
- (unsigned char*) bin_password);
- /* Store "*" as identifier for old passwords */
- if (!acl_user->pversion)
- prepared_scramble[0]='*';
- /* Finally encrypt password to get prepared scramble */
- password_crypt(thd->scramble, prepared_scramble+4, bin_password,
- SCRAMBLE41_LENGTH);
-}
-
-
-/*
- Get master privilges for user (priviliges for all tables).
- Required before connecting to MySQL
-
- As we have 2 stage handshake now we cache user not to lookup
- it second time. At the second stage we do not lookup user in case
- we already know it;
-
+ Seek ACL entry for a user, check password, SSL cypher, and if
+ everything is OK, update THD user data and USER_RESOURCES struct.
+ This function does not check if the user has any sensible privileges:
+ only user's existence and validity is checked.
+ Note, that entire operation is protected by acl_cache_lock.
+ SYNOPSIS
+ thd INOUT thread handle. If all checks are OK,
+ thd->priv_user, thd->master_access are updated.
+ thd->host, thd->ip, thd->user are used for checks.
+ mqh OUT user resources; on success mqh is reset, else
+ unchanged
+ passwd IN scrambled & crypted password, recieved from client
+ (to check): thd->scramble or thd->scramble_323 is
+ used to decrypt passwd, so they must contain
+ original random string,
+ passwd_len IN length of passwd, must be one of 0, 8,
+ SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH
+ old_version IN if old (3.20) protocol is used
+ RETURN VALUE
+ 0 success: thread data and mqh are updated
+ 1 user not found or authentification failure
+ -1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
*/
-ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
- const char *password,const char *message,char **priv_user,
- char *priv_host, bool old_ver, USER_RESOURCES *mqh,
- char *prepared_scramble, uint *cur_priv_version,
- ACL_USER **cached_user)
+int
+acl_getroot(THD *thd, USER_RESOURCES *mqh,
+ const char *passwd, uint passwd_len, bool old_version)
{
- ulong user_access=NO_ACCESS;
- *priv_user= (char*) user;
- bool password_correct= 0;
- int stage= (*cached_user != NULL); /* NULL passed as first stage */
- ACL_USER *acl_user= NULL;
DBUG_ENTER("acl_getroot");
- bzero(mqh,sizeof(USER_RESOURCES));
- if (!initialized)
- {
- // If no data allow anything
- DBUG_RETURN((ulong) ~NO_ACCESS); /* purecov: tested */
+ if (!initialized) /* if no data allow anything */
+ {
+ DBUG_RETURN(1);
}
+
+ int res= 1;
+
VOID(pthread_mutex_lock(&acl_cache->lock));
/*
- Get possible access from user_list. This is or'ed to others not
- fully specified
-
- If we have cached user use it, in other case look it up.
+ Find acl entry in user database. Note, that find_acl_user is not the same,
+ because it doesn't take into account the case when user is not empty,
+ but acl_user->user is empty
*/
- if (stage && (*cur_priv_version == priv_version))
- acl_user= *cached_user;
- else
+ ACL_USER *acl_user= 0;
+ for (uint i=0 ; i < acl_users.elements ; i++)
{
- for (uint i=0 ; i < acl_users.elements ; i++)
+ ACL_USER *user_i = dynamic_element(&acl_users,i,ACL_USER*);
+ if (!user_i->user || !strcmp(thd->user, user_i->user))
{
- ACL_USER *acl_user_search=dynamic_element(&acl_users,i,ACL_USER*);
- if (!acl_user_search->user || !strcmp(user,acl_user_search->user))
+ if (compare_hostname(&user_i->host, thd->host, thd->ip))
{
- if (compare_hostname(&acl_user_search->host,host,ip))
+ /* check password: it should be empty or valid */
+ if (passwd_len == user_i->salt_len)
{
- /* Found mathing user */
- acl_user= acl_user_search;
- /* Store it as a cache */
- *cached_user= acl_user;
- *cur_priv_version= priv_version;
- break;
+ if (user_i->salt_len == 0 ||
+ user_i->salt_len == SCRAMBLE_LENGTH &&
+ check_scramble(passwd, thd->scramble, user_i->salt) == 0 ||
+ check_scramble_323(passwd, thd->scramble_323,
+ (ulong *) user_i->salt, old_version) == 0)
+ {
+ acl_user= user_i;
+ res= 0;
+ }
}
+ else if (passwd_len == SCRAMBLE_LENGTH &&
+ user_i->salt_len == SCRAMBLE_LENGTH_323)
+ res= -1;
+ /* linear search complete: */
+ break;
}
}
}
-
- /* Now we have acl_user found and may start our checks */
+ /*
+ This was moved to separate tree because of heavy HAVE_OPENSSL case.
+ If acl_user is not null, res is 0.
+ */
if (acl_user)
{
- /* Password should present for both or absend for both */
- if (!acl_user->password && !*password)
- password_correct=1;
- else if (!acl_user->password || !*password)
- {
- *cached_user= 0; // Impossible to connect
- }
- else
- {
- /* New version password is checked differently */
- if (acl_user->pversion)
- {
- if (stage) /* We check password only on the second stage */
- {
- if (!validate_password(password,message,acl_user->salt))
- password_correct=1;
- }
- else /* First stage - just prepare scramble */
- prepare_scramble(thd,acl_user,prepared_scramble);
- }
- /* Old way to check password */
- else
- {
- /* Checking the scramble at any stage. First - old clients */
- if (!check_scramble(password,message,acl_user->salt,
- (my_bool) old_ver))
- password_correct=1;
- else if (!stage) /* Here if password incorrect */
- {
- /* At the first stage - prepare scramble */
- prepare_scramble(thd,acl_user,prepared_scramble);
- }
- }
- }
- }
-
- /* If user not found password_correct will also be zero */
- if (!password_correct)
- goto unlock_and_exit;
-
- /* OK. User found and password checked continue validation */
-
+ /* OK. User found and password checked continue validation */
#ifdef HAVE_OPENSSL
- {
- Vio *vio=thd->net.vio;
- /*
- At 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.
-
- We need to check for absence of SSL because without SSL
- we should reject connection.
- */
- if (vio_type(vio) == VIO_TYPE_SSL && SSL_get_peer_certificate(vio->ssl_))
- user_access=acl_user->access;
- break;
- case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
+ {
+ ulong user_access= NO_ACCESS;
+ Vio *vio=thd->net.vio;
/*
- 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.
+ At 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 (vio_type(vio) != VIO_TYPE_SSL)
- break;
- 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
- {
- if (global_system_variables.log_warnings)
- sql_print_error("X509 ciphers mismatch: should be '%s' but is '%s'",
- acl_user->ssl_cipher,
- SSL_get_cipher(vio->ssl_));
- user_access=NO_ACCESS;
- break;
- }
- }
- /* 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))
- {
- if (global_system_variables.log_warnings)
- sql_print_error("X509 issuer mismatch: should be '%s' but is '%s'",
- acl_user->x509_issuer, ptr);
- user_access=NO_ACCESS;
- free(ptr);
- break;
- }
- 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'",
- acl_user->x509_subject, ptr));
- if (strcmp(acl_user->x509_subject,ptr))
- {
- if (global_system_variables.log_warnings)
- sql_print_error("X509 subject mismatch: '%s' vs '%s'",
- acl_user->x509_subject, ptr);
- user_access=NO_ACCESS;
- }
- else
- user_access=acl_user->access;
- free(ptr);
+ 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.
+
+ We need to check for absence of SSL because without SSL
+ we should reject connection.
+ */
+ if (vio_type(vio) == VIO_TYPE_SSL &&
+ SSL_get_peer_certificate(vio->ssl_))
+ 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 (vio_type(vio) != VIO_TYPE_SSL)
+ break;
+ 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
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_error("X509 ciphers mismatch: should be '%s'"
+ "but is '%s'", acl_user->ssl_cipher,
+ SSL_get_cipher(vio->ssl_));
+ user_access=NO_ACCESS;
+ break;
+ }
+ }
+ /* 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))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_error("X509 issuer mismatch: should be '%s' "
+ "but is '%s'", acl_user->x509_issuer, ptr);
+ user_access=NO_ACCESS;
+ free(ptr);
+ break;
+ }
+ 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'",
+ acl_user->x509_subject, ptr));
+ if (strcmp(acl_user->x509_subject,ptr))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_error("X509 subject mismatch: '%s' vs '%s'",
+ acl_user->x509_subject, ptr);
+ user_access=NO_ACCESS;
+ }
+ else
+ user_access=acl_user->access;
+ free(ptr);
+ }
+ break;
}
- break;
+ /* end of SSL stuff: assign result */
+ thd->master_access= user_access;
}
- }
#else /* HAVE_OPENSSL */
- user_access=acl_user->access;
+ thd->master_access= acl_user->access;
#endif /* HAVE_OPENSSL */
- *mqh=acl_user->user_resource;
- if (!acl_user->user)
- *priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
-
- if (acl_user->host.hostname)
- strmake(priv_host, acl_user->host.hostname, MAX_HOSTNAME);
- else
- *priv_host= 0;
+ thd->priv_user= acl_user->user ? thd->user : (char *) "";
+ *mqh= acl_user->user_resource;
-unlock_and_exit:
+ if (acl_user->host.hostname)
+ strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
+ else
+ *thd->priv_host= 0;
+ }
VOID(pthread_mutex_unlock(&acl_cache->lock));
- DBUG_RETURN(user_access);
+ DBUG_RETURN(res);
}
@@ -713,8 +702,9 @@ static byte* check_get_key(ACL_USER *buff,uint *length,
return (byte*) buff->host.hostname;
}
+
static void acl_update_user(const char *user, const char *host,
- const char *password,
+ const char *password, uint password_len,
enum SSL_type ssl_type,
const char *ssl_cipher,
const char *x509_issuer,
@@ -750,20 +740,9 @@ static void acl_update_user(const char *user, const char *host,
acl_user->x509_subject= (x509_subject ?
strdup_root(&mem,x509_subject) : 0);
}
- if (password)
- {
- if (!password[0]) /* If password is empty set it to null */
- {
- acl_user->password=0;
- acl_user->pversion=0; // just initialize
- }
- else
- {
- acl_user->password=(char*) ""; // Just point at something
- get_salt_from_password(acl_user->salt,password);
- acl_user->pversion=get_password_version(acl_user->password);
- }
- }
+
+ set_user_salt(acl_user, password, password_len);
+ /* search complete: */
break;
}
}
@@ -772,7 +751,7 @@ static void acl_update_user(const char *user, const char *host,
static void acl_insert_user(const char *user, const char *host,
- const char *password,
+ const char *password, uint password_len,
enum SSL_type ssl_type,
const char *ssl_cipher,
const char *x509_issuer,
@@ -783,7 +762,6 @@ static void acl_insert_user(const char *user, const char *host,
ACL_USER acl_user;
acl_user.user=strdup_root(&mem,user);
update_hostname(&acl_user.host,strdup_root(&mem,host));
- acl_user.password=0;
acl_user.access=privileges;
acl_user.user_resource = *mqh;
acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
@@ -793,12 +771,8 @@ static void acl_insert_user(const char *user, const char *host,
acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0;
acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0;
acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
- if (password)
- {
- acl_user.password=(char*) ""; // Just point at something
- get_salt_from_password(acl_user.salt,password);
- acl_user.pversion=get_password_version(password);
- }
+
+ set_user_salt(&acl_user, password, password_len);
VOID(push_dynamic(&acl_users,(gptr) &acl_user));
if (!acl_user.host.hostname || acl_user.host.hostname[0] == wild_many
@@ -1135,7 +1109,6 @@ bool check_change_password(THD *thd, const char *host, const char *user)
bool change_password(THD *thd, const char *host, const char *user,
char *new_password)
{
- uint length=0;
DBUG_ENTER("change_password");
DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
host,user,new_password));
@@ -1144,37 +1117,27 @@ bool change_password(THD *thd, const char *host, const char *user,
if (check_change_password(thd, host, user))
DBUG_RETURN(1);
- /*
- password should always be 0,16 or 45 chars;
- Simple hack to avoid cracking
- */
- length=(uint) strlen(new_password);
- if (length != 45)
- new_password[length & 16]=0;
-
VOID(pthread_mutex_lock(&acl_cache->lock));
ACL_USER *acl_user;
- if (!(acl_user= find_acl_user(host,user)))
+ if (!(acl_user= find_acl_user(host, user)))
{
- send_error(thd, ER_PASSWORD_NO_MATCH);
VOID(pthread_mutex_unlock(&acl_cache->lock));
+ send_error(thd, ER_PASSWORD_NO_MATCH);
DBUG_RETURN(1);
}
+ /* update loaded acl entry: */
+ uint new_password_len= new_password ? strlen(new_password) : 0;
+ set_user_salt(acl_user, new_password, new_password_len);
+
if (update_user_table(thd,
acl_user->host.hostname ? acl_user->host.hostname : "",
acl_user->user ? acl_user->user : "",
- new_password))
+ new_password, new_password_len))
{
VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
send_error(thd,0); /* purecov: deadcode */
DBUG_RETURN(1); /* purecov: deadcode */
}
- get_salt_from_password(acl_user->salt,new_password);
- acl_user->pversion=get_password_version(new_password);
- if (!new_password[0])
- acl_user->password=0;
- else
- acl_user->password=(char*) ""; // Point at something
acl_cache->clear(1); // Clear locked hostname cache
VOID(pthread_mutex_unlock(&acl_cache->lock));
@@ -1210,7 +1173,7 @@ find_acl_user(const char *host, const char *user)
if (!acl_user->user && !user[0] ||
acl_user->user && !strcmp(user,acl_user->user))
{
- if (compare_hostname(&(acl_user->host),host,host))
+ if (compare_hostname(&acl_user->host,host,host))
{
DBUG_RETURN(acl_user);
}
@@ -1280,7 +1243,7 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
****************************************************************************/
static bool update_user_table(THD *thd, const char *host, const char *user,
- const char *new_password)
+ const char *new_password, uint new_password_len)
{
TABLE_LIST tables;
TABLE *table;
@@ -1304,7 +1267,7 @@ static bool update_user_table(THD *thd, const char *host, const char *user,
DBUG_RETURN(1); /* purecov: deadcode */
}
store_record(table,record[1]);
- table->field[2]->store(new_password,(uint) strlen(new_password), &my_charset_latin1);
+ table->field[2]->store(new_password, new_password_len, &my_charset_latin1);
if ((error=table->file->update_row(table->record[1],table->record[0])))
{
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
@@ -1352,24 +1315,24 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
{
int error = -1;
bool old_row_exists=0;
- char *password,empty_string[1];
+ char empty_string[]= { '\0' };
+ char *password= empty_string;
+ uint password_len= 0;
char what= (revoke_grant) ? 'N' : 'Y';
DBUG_ENTER("replace_user_table");
safe_mutex_assert_owner(&acl_cache->lock);
- password=empty_string;
- empty_string[0]=0;
-
if (combo.password.str && combo.password.str[0])
{
- if ((combo.password.length != HASH_PASSWORD_LENGTH)
- && combo.password.length != HASH_OLD_PASSWORD_LENGTH)
+ if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
+ combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
{
my_printf_error(ER_PASSWORD_NO_MATCH,
"Password hash should be a %d-digit hexadecimal number",
- MYF(0),HASH_PASSWORD_LENGTH);
+ MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
DBUG_RETURN(-1);
}
+ password_len= combo.password.length;
password=combo.password.str;
}
@@ -1394,17 +1357,20 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
goto end;
}
old_row_exists = 0;
- restore_record(table,default_values); // cp empty row from default_values
- table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
- table->field[1]->store(combo.user.str,combo.user.length, &my_charset_latin1);
- table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1);
+ restore_record(table,default_values); // cp empty row from default_values
+ table->field[0]->store(combo.host.str,combo.host.length,
+ &my_charset_latin1);
+ table->field[1]->store(combo.user.str,combo.user.length,
+ &my_charset_latin1);
+ table->field[2]->store(password, password_len,
+ &my_charset_latin1);
}
else
{
old_row_exists = 1;
store_record(table,record[1]); // Save copy for update
if (combo.password.str) // If password given
- table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1);
+ table->field[2]->store(password, password_len, &my_charset_latin1);
}
/* Update table columns with new privileges */
@@ -1501,10 +1467,8 @@ end:
if (!error)
{
acl_cache->clear(1); // Clear privilege cache
- if (!combo.password.str)
- password=0; // No password given on command
if (old_row_exists)
- acl_update_user(combo.user.str,combo.host.str,password,
+ acl_update_user(combo.user.str, combo.host.str, password, password_len,
thd->lex.ssl_type,
thd->lex.ssl_cipher,
thd->lex.x509_issuer,
@@ -1512,7 +1476,7 @@ end:
&thd->lex.mqh,
rights);
else
- acl_insert_user(combo.user.str,combo.host.str,password,
+ acl_insert_user(combo.user.str, combo.host.str, password, password_len,
thd->lex.ssl_type,
thd->lex.ssl_cipher,
thd->lex.x509_issuer,
@@ -2915,12 +2879,15 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
global.append ("'@'",3);
global.append(lex_user->host.str,lex_user->host.length);
global.append ('\'');
- if (acl_user->password)
+ if (acl_user->salt_len)
{
- char passd_buff[HASH_PASSWORD_LENGTH+1];
- make_password_from_salt(passd_buff,acl_user->salt,acl_user->pversion);
+ char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
+ if (acl_user->salt_len == SCRAMBLE_LENGTH)
+ make_password_from_salt(passwd_buff, acl_user->salt);
+ else
+ make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt);
global.append(" IDENTIFIED BY PASSWORD '",25);
- global.append(passd_buff);
+ global.append(passwd_buff);
global.append('\'');
}
/* "show grants" SSL related stuff */
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index e6c6771253c..3370797820a 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -111,9 +111,9 @@ public:
acl_host_and_ip host;
uint hostname_length;
USER_RESOURCES user_resource;
- char *user,*password;
- ulong salt[6]; // New password has longer length
- uint8 pversion; // password version
+ char *user;
+ uint8 salt[SCRAMBLE_LENGTH+1]; // scrambled password in binary form
+ uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 3.23, 20 - 4.1.1
enum SSL_type ssl_type;
const char *ssl_cipher, *x509_issuer, *x509_subject;
};
@@ -135,11 +135,8 @@ void acl_reload(THD *thd);
void acl_free(bool end=0);
ulong acl_get(const char *host, const char *ip, const char *bin_ip,
const char *user, const char *db);
-ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
- const char *password,const char *scramble,
- char **priv_user, char *priv_host,
- bool old_ver, USER_RESOURCES *max,char* prepared_scramble,
- uint *cur_priv_version, ACL_USER **cached_user);
+int acl_getroot(THD *thd, USER_RESOURCES *mqh,
+ const char *passwd, uint passwd_len, bool old_ver);
bool acl_check_host(const char *host, const char *ip);
bool check_change_password(THD *thd, const char *host, const char *user);
bool change_password(THD *thd, const char *host, const char *user,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index c233ffd422a..ebb3e819ddc 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -139,6 +139,7 @@ THD::THD():user_time(0), is_fatal_error(0),
set_query_id=1;
db_access=NO_ACCESS;
version=refresh_version; // For boot
+ *scramble= *scramble_323= '\0';
init();
/* Initialize sub structures */
diff --git a/sql/sql_class.h b/sql/sql_class.h
index ccfe2555518..d962cc8086e 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -529,10 +529,16 @@ public:
enum_tx_isolation session_tx_isolation;
/* for user variables replication*/
DYNAMIC_ARRAY user_var_events;
- // extend scramble to handle new auth
- char scramble[SCRAMBLE41_LENGTH+1];
- // old scramble is needed to handle old clients
- char old_scramble[SCRAMBLE_LENGTH+1];
+
+ /* scramble - random string sent to client on handshake */
+ char scramble[SCRAMBLE_LENGTH+1];
+ /*
+ The same as scramble but for old password checking routines. It always
+ contains first N bytes of scramble.
+ See check_connection() at sql_parse.cc for authentification details.
+ */
+ char scramble_323[SCRAMBLE_LENGTH_323+1];
+
uint8 query_cache_type; // type of query cache processing
bool slave_thread;
bool set_query_id,locked,count_cuted_fields,some_tables_deleted;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 5070466007e..384ec2bd4dd 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -178,152 +178,119 @@ end:
/*
- Check if user is ok
-
+ Check if user exist and password supplied is correct.
SYNOPSIS
check_user()
- thd Thread handle
- command Command for connection (for log)
- user Name of user trying to connect
- passwd Scrambled password sent from client
- db Database to connect to
- check_count If set to 1, don't allow too many connection
- simple_connect If 1 then client is of old type and we should connect
- using the old method (no challange)
- do_send_error Set to 1 if we should send error to user
- prepared_scramble Buffer to store hash password of new connection
- had_password Set to 1 if the user gave a password
- cur_priv_version Check flag to know if someone flushed the privileges
- since last code
- hint_user Pointer used by acl_getroot() to remmeber user for
- next call
-
- RETURN
- 0 ok
- thd->user, thd->master_access, thd->priv_user, thd->db and
- thd->db_access are updated
- 1 Access denied; Error sent to client
- -1 If do_send_error == 1: Failed connect, error sent to client
- If do_send_error == 0: Prepare for stage of connect
+ thd INOUT thread handle, thd->{host,user,ip} are used
+ command IN originator of the check: now check_user is called
+ during connect and change user procedures; used for
+ logging.
+ passwd IN scrambled password recieved from client
+ passwd_len IN length of scrambled password
+ db IN database name to connect to, may be NULL
+ check_count IN dont know exactly
+ Note, that host, user and passwd may point to communication buffer.
+ Current implementation does not depened on that, but future changes
+ should be done with this in mind.
+ RETURN VALUE
+ 0 OK; thd->user, thd->master_access, thd->priv_user, thd->db and
+ thd->db_access are updated; OK is sent to client;
+ 1 access denied or internal error; error is sent to client
+ Note, that this return semantics differs from check_connection,
+ which returns -1 if message was already sent.
+ -1 acl entry for this user contains old scramble, but passwd contains
+ new one, error is not sent to client
*/
-static int check_user(THD *thd,enum_server_command command, const char *user,
- const char *passwd, const char *db, bool check_count,
- bool simple_connect, bool do_send_error,
- char *prepared_scramble, bool had_password,
- uint *cur_priv_version, ACL_USER** hint_user)
+static int check_user(THD *thd, enum enum_server_command command,
+ const char *passwd, uint passwd_len, const char *db,
+ bool check_count)
{
- thd->db=0;
- thd->db_length=0;
- USER_RESOURCES ur;
- char tmp_passwd[SCRAMBLE41_LENGTH];
DBUG_ENTER("check_user");
-
- /*
- Move password to temporary buffer as it may be stored in communication
- buffer
- */
- strmake(tmp_passwd, passwd, sizeof(tmp_passwd));
- passwd= tmp_passwd; // Use local copy
-
- /* We shall avoid dupplicate user allocations here */
- if (!thd->user && !(thd->user = my_strdup(user, MYF(0))))
- {
- send_error(thd,ER_OUT_OF_RESOURCES);
- DBUG_RETURN(1);
- }
- thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user,
- passwd, thd->scramble,
- &thd->priv_user, thd->priv_host,
- (protocol_version == 9 ||
- !(thd->client_capabilities &
- CLIENT_LONG_PASSWORD)),
- &ur,prepared_scramble,
- cur_priv_version,hint_user);
-
- DBUG_PRINT("info",
- ("Capabilities: %d packet_length: %ld Host: '%s' Login user: '%s' Priv_user: '%s' Using password: %s Access: %u db: '%s'",
- thd->client_capabilities, thd->max_client_packet_length,
- thd->host_or_ip, thd->user, thd->priv_user,
- had_password ? "yes": "no",
- thd->master_access, thd->db ? thd->db : "*none*"));
/*
- In case we're going to retry we should not send error message at this
- point
+ Why this is set here? - probably to reset current DB to 'no database
+ selected' in case of 'change user' failure.
*/
- if (thd->master_access & NO_ACCESS)
+ thd->db= 0;
+ thd->db_length= 0;
+
+ USER_RESOURCES ur;
+ int res= acl_getroot(thd, &ur, passwd, passwd_len,
+ protocol_version == 9 ||
+ !(thd->client_capabilities & CLIENT_LONG_PASSWORD));
+ if (res == 0 && !(thd->master_access & NO_ACCESS)) // authentification is OK
{
- if (do_send_error || !had_password || !*hint_user)
+ DBUG_PRINT("info",
+ ("Capabilities: %d packet_length: %ld Host: '%s' "
+ "Login user: '%s' Priv_user: '%s' Using password: %s "
+ "Access: %u db: '%s'",
+ thd->client_capabilities, thd->max_client_packet_length,
+ thd->host_or_ip, thd->user, thd->priv_user,
+ passwd_len ? "yes": "no",
+ thd->master_access, thd->db ? thd->db : "*none*"));
+
+ if (check_count)
{
- DBUG_PRINT("info",("Access denied"));
- /*
- Old client should get nicer error message if password version is
- not supported
- */
- if (simple_connect && *hint_user && (*hint_user)->pversion)
- {
- net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
- mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
+ bool count_ok= thread_count < max_connections + delayed_insert_threads ||
+ thd->master_access & SUPER_ACL;
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ if (!count_ok)
+ { // too many connections
+ send_error(thd, ER_CON_COUNT_ERROR);
+ DBUG_RETURN(1);
}
- else
- {
- net_printf(thd, ER_ACCESS_DENIED_ERROR,
- thd->user,
- thd->host_or_ip,
- had_password ? ER(ER_YES) : ER(ER_NO));
- mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR),
- thd->user,
- thd->host_or_ip,
- had_password ? ER(ER_YES) : ER(ER_NO));
- }
- DBUG_RETURN(1); // Error already given
}
- DBUG_PRINT("info",("Prepare for second part of handshake"));
- DBUG_RETURN(-1); // no report error in special handshake
- }
- if (check_count)
- {
- VOID(pthread_mutex_lock(&LOCK_thread_count));
- bool tmp=(thread_count - delayed_insert_threads >= max_connections &&
- !(thd->master_access & SUPER_ACL));
- VOID(pthread_mutex_unlock(&LOCK_thread_count));
- if (tmp)
- { // Too many connections
- send_error(thd, ER_CON_COUNT_ERROR);
+ /* Why logging is performed before all checks've passed? */
+ mysql_log.write(thd,command,
+ (thd->priv_user == thd->user ?
+ (char*) "%s@%s on %s" :
+ (char*) "%s@%s as anonymous on %s"),
+ thd->user, thd->host_or_ip,
+ db ? db : (char*) "");
+
+ /* Why is it set here? */
+ thd->db_access=0;
+
+ /* Don't allow user to connect if he has done too many queries */
+ if ((ur.questions || ur.updates || ur.connections) &&
+ get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur))
+ DBUG_RETURN(1);
+ if (thd->user_connect && thd->user_connect->user_resources.connections &&
+ check_for_max_user_connections(thd, thd->user_connect))
DBUG_RETURN(1);
+
+ /* Change database if necessary: OK or FAIL is sent in mysql_change_db */
+ if (db && db[0])
+ {
+ if (mysql_change_db(thd, db))
+ {
+ if (thd->user_connect)
+ decrease_user_connections(thd->user_connect);
+ DBUG_RETURN(1);
+ }
}
+ else
+ send_ok(thd);
+ thd->password= test(passwd_len); // remember for error messages
+ /* Ready to handle queries */
}
- mysql_log.write(thd,command,
- (thd->priv_user == thd->user ?
- (char*) "%s@%s on %s" :
- (char*) "%s@%s as anonymous on %s"),
- user,
- thd->host_or_ip,
- db ? db : (char*) "");
- thd->db_access=0;
- /* Don't allow user to connect if he has done too many queries */
- if ((ur.questions || ur.updates || ur.connections) &&
- get_or_create_user_conn(thd,user,thd->host_or_ip,&ur))
- DBUG_RETURN(1);
- if (thd->user_connect && thd->user_connect->user_resources.connections &&
- check_for_max_user_connections(thd, thd->user_connect))
- DBUG_RETURN(1);
-
- if (db && db[0])
+ else if (res != -1) // authentication failure
{
- int error= test(mysql_change_db(thd,db));
- if (error && thd->user_connect)
- decrease_user_connections(thd->user_connect);
- DBUG_RETURN(error);
- }
- send_ok(thd); // Ready to handle questions
- thd->password= test(passwd[0]); // Remember for error messages
- DBUG_RETURN(0); // ok
+ net_printf(thd, ER_ACCESS_DENIED_ERROR,
+ thd->user,
+ thd->host_or_ip,
+ passwd_len ? ER(ER_YES) : ER(ER_NO));
+ mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
+ thd->user,
+ thd->host_or_ip,
+ passwd_len ? ER(ER_YES) : ER(ER_NO));
+ }
+ DBUG_RETURN(res);
}
-
/*
Check for maximum allowable user connections, if the mysqld server is
started with corresponding variable that is greater then 0.
@@ -525,48 +492,93 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
/*
- Check connnectionn and get priviliges
-
+ Perform check for scrambled password, re-request scrambled password
+ from client if necessary. See also help for check_user.
SYNOPSIS
- check_connections
- thd Thread handle
+ authenticate()
+ RETURN VALUE
+ 0 success, OK sent to client
+ -1 error, sent to client
+ > 0 error, not sent to client
+*/
+
+static
+int
+authenticate(THD *thd, enum enum_server_command command,
+ const char *passwd, uint passwd_len, const char *db,
+ bool check_count)
+{
+ if (passwd_len != 0 &&
+ passwd_len != SCRAMBLE_LENGTH &&
+ passwd_len != SCRAMBLE_LENGTH_323)
+ return 1;
+ int res= check_user(thd, COM_CONNECT, passwd, passwd_len, db, check_count);
+ if (res < 0)
+ {
+ /*
+ This happens when client (new) sends password scrambled with
+ scramble(), but database holds old value (scrambled with
+ scramble_323()). Here we please client to send scrambled_password
+ in old format.
+ */
+ char buff[NAME_LEN + 1];
+ /* save db because network buffer is to hold new packet */
+ if (db)
+ {
+ strmake(buff, db, NAME_LEN);
+ db= buff;
+ }
+ NET *net= &thd->net;
+ if (my_net_write(net, thd->scramble_323, SCRAMBLE_LENGTH_323 + 1) ||
+ net_flush(net) ||
+ my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very
+ { // specific packet size
+ inc_host_errors(&thd->remote.sin_addr);
+ return ER_HANDSHAKE_ERROR;
+ }
+ /* Final attempt to check the user based on reply */
+ /* So as passwd is short, errcode is always sent to user and res >= 0 */
+ res= check_user(thd, COM_CONNECT, (char *) net->read_pos,
+ SCRAMBLE_LENGTH_323, db, check_count);
+ }
+ return res > 0 ? -1 : 0;
+}
+
+/*
+ Perform handshake, authorize client and update thd ACL variables.
+ SYNOPSIS
+ check_connection()
+ thd INOUT thread handle
RETURN
- 0 ok
- -1 Error, which is sent to user
- > 0 Error code (not sent to user)
+ 0 success, OK is sent to user
+ -1 error, which is sent to user
+ > 0 error code (not sent to user)
*/
#ifndef EMBEDDED_LIBRARY
static int
-check_connections(THD *thd)
+check_connection(THD *thd)
{
- int res;
- uint connect_errors=0;
- uint cur_priv_version;
- bool using_password;
+ uint connect_errors= 0;
NET *net= &thd->net;
- char *end, *user, *passwd, *db;
- char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble&hash */
- ACL_USER* cached_user=NULL; /* Initialise to NULL for first stage */
- DBUG_PRINT("info",("New connection received on %s",
- vio_description(net->vio)));
-
- /* Remove warning from valgrind. TODO: Fix it in password.c */
- bzero((char*) &prepared_scramble[0], sizeof(prepared_scramble));
+
+ DBUG_PRINT("info",
+ ("New connection received on %s", vio_description(net->vio)));
+
if (!thd->host) // If TCP/IP connection
{
char ip[30];
if (vio_peer_addr(net->vio, ip, &thd->peer_port))
return (ER_BAD_HOST_ERROR);
- if (!(thd->ip = my_strdup(ip,MYF(0))))
+ if (!(thd->ip= my_strdup(ip,MYF(0))))
return (ER_OUT_OF_RESOURCES);
- thd->host_or_ip=thd->ip;
+ thd->host_or_ip= thd->ip;
#if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread)
/* Fast local hostname resolve for Win32 */
if (!strcmp(thd->ip,"127.0.0.1"))
- thd->host=(char*) localhost;
+ thd->host= (char *) localhost;
else
#endif
{
@@ -595,15 +607,16 @@ check_connections(THD *thd)
DBUG_PRINT("info",("Host: %s",thd->host));
thd->host_or_ip= thd->host;
thd->ip= 0;
- bzero((char*) &thd->remote,sizeof(struct sockaddr));
+ bzero((char*) &thd->remote, sizeof(struct sockaddr));
}
/* Ensure that wrong hostnames doesn't cause buffer overflows */
vio_keepalive(net->vio, TRUE);
- ulong pkt_len=0;
+ ulong pkt_len= 0;
+ char *end;
{
/* buff[] needs to big enough to hold the server_version variable */
- char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+64];
+ char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
@@ -617,19 +630,36 @@ check_connections(THD *thd)
client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */
#endif /* HAVE_OPENSSL */
- end=strnmov(buff,server_version,SERVER_VERSION_LENGTH)+1;
- int4store((uchar*) end,thd->thread_id);
- end+=4;
- memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1);
- end+=SCRAMBLE_LENGTH +1;
- int2store(end,client_flags);
- end[2]=(char) default_charset_info->number;
- int2store(end+3,thd->server_status);
- bzero(end+5,13);
- end+=18;
+ end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
+ int4store((uchar*) end, thd->thread_id);
+ end+= 4;
+ /*
+ So as check_connection is the only entry point to authorization
+ procedure, scramble is set here. This gives us new scramble for
+ each handshake.
+ */
+ create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
+ strmake(thd->scramble_323, thd->scramble, SCRAMBLE_LENGTH_323);
- // At this point we write connection message and read reply
- if (net_write_command(net,(uchar) protocol_version, "", 0, buff,
+ /*
+ Old clients does not understand long scrambles, but can ignore packet
+ tail: that's why first part of scramble is placed here, and second
+ part at the end of packet.
+ */
+ end= strmake(end, thd->scramble_323, SCRAMBLE_LENGTH_323) + 1;
+
+ int2store(end, client_flags);
+ /* write server characteristics: up to 16 bytes allowed */
+ end[2]=(char) default_charset_info->number;
+ int2store(end+3, thd->server_status);
+ bzero(end+5, 13);
+ end+= 18;
+ /* write scramble tail */
+ end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323,
+ SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1;
+
+ /* At this point we write connection message and read reply */
+ if (net_write_command(net, (uchar) protocol_version, "", 0, buff,
(uint) (end-buff)) ||
(pkt_len= my_net_read(net)) == packet_error ||
pkt_len < MIN_HANDSHAKE_SIZE)
@@ -702,7 +732,7 @@ check_connections(THD *thd)
return(ER_HANDSHAKE_ERROR);
}
DBUG_PRINT("info", ("Reading user information over SSL layer"));
- if ((pkt_len=my_net_read(net)) == packet_error ||
+ if ((pkt_len= my_net_read(net)) == packet_error ||
pkt_len < NORMAL_HANDSHAKE_SIZE)
{
DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
@@ -719,16 +749,7 @@ check_connections(THD *thd)
return(ER_HANDSHAKE_ERROR);
}
- user= end;
- passwd= strend(user)+1;
- db=0;
- using_password= test(passwd[0]);
- if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB)
- db=strend(passwd)+1;
-
- /* We can get only old hash at this point */
- if (using_password && strlen(passwd) != SCRAMBLE_LENGTH)
- return ER_HANDSHAKE_ERROR;
+ /* why has it been put here? */
if (thd->client_capabilities & CLIENT_INTERACTIVE)
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
@@ -737,60 +758,19 @@ check_connections(THD *thd)
net->return_status= &thd->server_status;
net->read_timeout=(uint) thd->variables.net_read_timeout;
- /* Simple connect only for old clients. New clients always use secure auth */
- bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
-
- /* Check user permissions. If password failure we'll get scramble back */
- if ((res=check_user(thd, COM_CONNECT, user, passwd, db, 1, simple_connect,
- simple_connect, prepared_scramble, using_password,
- &cur_priv_version,
- &cached_user)) < 0)
- {
- /* Store current used and database as they are erased with next packet */
- char tmp_user[USERNAME_LENGTH+1];
- char tmp_db[NAME_LEN+1];
-
- /* If the client is old we just have to return error */
- if (simple_connect)
- return -1;
+ char *user= end;
+ char *passwd= strend(user)+1;
+ uint passwd_len= strlen(passwd);
- DBUG_PRINT("info",("password challenge"));
+ char *db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
+ passwd+passwd_len+1 : 0;
- tmp_user[0]= tmp_db[0]= 0;
- if (user)
- strmake(tmp_user,user,USERNAME_LENGTH);
- if (db)
- strmake(tmp_db,db,NAME_LEN);
-
- /* Write hash and encrypted scramble to client */
- if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4) ||
- net_flush(net))
- {
- inc_host_errors(&thd->remote.sin_addr);
- return ER_HANDSHAKE_ERROR;
- }
- /* Reading packet back */
- if ((pkt_len= my_net_read(net)) == packet_error)
- {
- inc_host_errors(&thd->remote.sin_addr);
- return ER_HANDSHAKE_ERROR;
- }
- /* We have to get very specific packet size */
- if (pkt_len != SCRAMBLE41_LENGTH)
- {
- inc_host_errors(&thd->remote.sin_addr);
- return ER_HANDSHAKE_ERROR;
- }
- /* Final attempt to check the user based on reply */
- if (check_user(thd,COM_CONNECT, tmp_user, (char*)net->read_pos,
- tmp_db, 1, 0, 1, prepared_scramble, using_password,
- &cur_priv_version,
- &cached_user))
- return -1;
- }
- else if (res)
- return -1; // Error sent from check_user()
- return 0;
+ if (thd->user)
+ x_free(thd->user);
+ thd->user= my_strdup(user, MYF(0));
+ if (!thd->user)
+ return(ER_OUT_OF_RESOURCES);
+ return authenticate(thd, COM_CONNECT, passwd, passwd_len, db, true);
}
@@ -847,7 +827,7 @@ pthread_handler_decl(handle_one_connection,arg)
NET *net= &thd->net;
thd->thread_stack= (char*) &thd;
- if ((error=check_connections(thd)))
+ if ((error=check_connection(thd)))
{ // Wrong permissions
if (error > 0)
net_printf(thd,error,thd->host_or_ip);
@@ -1152,116 +1132,60 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_CHANGE_USER:
{
thd->change_user();
- thd->clear_error(); // If errors from rollback
+ thd->clear_error(); // if errors from rollback
- statistic_increment(com_other,&LOCK_status);
- char *user= (char*) packet;
+ statistic_increment(com_other, &LOCK_status);
+ char *user= (char*) packet;
char *passwd= strend(user)+1;
- char *db= strend(passwd)+1;
+ uint passwd_len= strlen(passwd);
+ char *db= passwd + passwd_len + 1;
- /* Save user and privileges */
- uint save_master_access=thd->master_access;
- uint save_db_access= thd->db_access;
- uint save_db_length= thd->db_length;
- char *save_user= thd->user;
- thd->user=NULL; /* Needed for check_user to allocate new user */
- char *save_priv_user= thd->priv_user;
- char *save_db= thd->db;
- USER_CONN *save_uc= thd->user_connect;
- bool simple_connect;
- bool using_password;
- char prepared_scramble[SCRAMBLE41_LENGTH+4];/* Buffer for scramble,hash */
- char tmp_user[USERNAME_LENGTH+1];
- char tmp_db[NAME_LEN+1];
- ACL_USER* cached_user ; /* Cached user */
- uint cur_priv_version; /* Cached grant version */
- int res;
- ulong pkt_len= 0; /* Length of reply packet */
-
- bzero((char*) prepared_scramble, sizeof(prepared_scramble));
/* Small check for incomming packet */
-
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
- goto restore_user_err;
-
- /* Now we shall basically perform authentication again */
-
- /* We can get only old hash at this point */
- if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH)
- goto restore_user_err;
-
- cached_user= NULL;
-
- /* Simple connect only for old clients. New clients always use sec. auth*/
- simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
-
- /* Store information if we used password. passwd will be dammaged */
- using_password=test(passwd[0]);
-
- if (simple_connect) /* Restore scramble for old clients */
- memcpy(thd->scramble,thd->old_scramble,9);
+ {
+ send_error(thd, ER_UNKNOWN_COM_ERROR);
+ break;
+ }
- /*
- Check user permissions. If password failure we'll get scramble back
- Do not retry if we already have sent error (result>0)
- */
- if ((res=check_user(thd,COM_CHANGE_USER, user, passwd, db, 0,
- simple_connect, simple_connect, prepared_scramble,
- using_password, &cur_priv_version, &cached_user)) < 0)
+ /* Save user and privileges */
+ uint save_master_access= thd->master_access;
+ uint save_db_access= thd->db_access;
+ uint save_db_length= thd->db_length;
+ char *save_user= thd->user;
+ char *save_priv_user= thd->priv_user;
+ char *save_db= thd->db;
+ USER_CONN *save_uc= thd->user_connect;
+ thd->user= my_strdup(user, MYF(0));
+ if (!thd->user)
{
- /* If the client is old we just have to have auth failure */
- if (simple_connect)
- goto restore_user; /* Error is already reported */
-
- /* Store current used and database as they are erased with next packet */
- tmp_user[0]= tmp_db[0]= 0;
- if (user)
- strmake(tmp_user,user,USERNAME_LENGTH);
- if (db)
- strmake(tmp_db,db,NAME_LEN);
-
- /* Write hash and encrypted scramble to client */
- if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4) ||
- net_flush(net))
- goto restore_user_err;
-
- /* Reading packet back */
- if ((pkt_len=my_net_read(net)) == packet_error)
- goto restore_user_err;
-
- /* We have to get very specific packet size */
- if (pkt_len != SCRAMBLE41_LENGTH)
- goto restore_user;
-
- /* Final attempt to check the user based on reply */
- if (check_user(thd,COM_CHANGE_USER, tmp_user, (char*) net->read_pos,
- tmp_db, 0, 0, 1, prepared_scramble, using_password,
- &cur_priv_version, &cached_user))
- goto restore_user;
+ thd->user= save_user;
+ send_error(thd, ER_OUT_OF_RESOURCES);
+ break;
}
- else if (res)
- goto restore_user;
-
- /* Finally we've authenticated new user */
- if (max_connections && save_uc)
- decrease_user_connections(save_uc);
- x_free((gptr) save_db);
- x_free((gptr) save_user);
- thd->password=using_password;
- break;
- /* Bad luck we shall restore old user */
-restore_user_err:
- send_error(thd, ER_UNKNOWN_COM_ERROR);
+ int res= authenticate(thd, COM_CHANGE_USER, passwd, passwd_len, db, false);
-restore_user:
- x_free(thd->user);
- thd->master_access=save_master_access;
- thd->db_access=save_db_access;
- thd->db=save_db;
- thd->db_length=save_db_length;
- thd->user=save_user;
- thd->priv_user=save_priv_user;
+ if (res)
+ {
+ /* authentification failure, we shall restore old user */
+ if (res > 0)
+ send_error(thd, ER_UNKNOWN_COM_ERROR);
+ x_free(thd->user);
+ thd->user= save_user;
+ thd->priv_user= save_priv_user;
+ thd->master_access= save_master_access;
+ thd->db_access= save_db_access;
+ thd->db= save_db;
+ thd->db_length= save_db_length;
+ }
+ else
+ {
+ /* we've authenticated new user */
+ if (max_connections && save_uc)
+ decrease_user_connections(save_uc);
+ x_free((gptr) save_db);
+ x_free((gptr) save_user);
+ }
break;
}
#endif /* EMBEDDED_LIBRARY */
@@ -3158,7 +3082,7 @@ error:
Check grants for commands which work only with one table and all other
tables belong to subselects.
- SYNOPSYS
+ SYNOPSIS
single_table_command_access()
thd - Thread handler
privilege - asked privelage
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index e3d600b9798..b53551845bc 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -7,7 +7,7 @@ typedef struct st_slave_info
uint32 rpl_recovery_rank, master_id;
char host[HOSTNAME_LENGTH+1];
char user[USERNAME_LENGTH+1];
- char password[HASH_PASSWORD_LENGTH+1];
+ char password[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
uint16 port;
THD* thd;
} SLAVE_INFO;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 1d605abe8a3..c8c9eb97a6a 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -2515,9 +2515,10 @@ simple_expr:
| NOW_SYM '(' expr ')'
{ $$= new Item_func_now($3); Lex->safe_to_cache_query=0;}
| PASSWORD '(' expr ')'
- { $$= new Item_func_password($3); }
- | PASSWORD '(' expr ',' expr ')'
- { $$= new Item_func_password($3,$5); }
+ {
+ $$= use_old_passwords ? (Item *) new Item_func_old_password($3) :
+ (Item *) new Item_func_password($3);
+ }
| POINT_SYM '(' expr ',' expr ')'
{ $$= new Item_func_point($3,$5); }
| POINTFROMTEXT '(' expr ')'
@@ -4604,13 +4605,22 @@ text_or_password:
{
if (!$3.length)
$$=$3.str;
- else
+ else if (use_old_passwords)
{
- char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1);
- make_scrambled_password(buff,$3.str,use_old_passwords,
- &YYTHD->rand);
+ char *buff= (char *)
+ YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
+ if (buff)
+ make_scrambled_password_323(buff, $3.str);
$$=buff;
}
+ else
+ {
+ char *buff= (char *)
+ YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
+ if (buff)
+ make_scrambled_password(buff, $3.str);
+ $$=buff;
+ }
}
;
@@ -4918,14 +4928,24 @@ grant_user:
$$=$1; $1->password=$4;
if ($4.length)
{
- char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1);
- if (buff)
- {
- make_scrambled_password(buff,$4.str,use_old_passwords,
- &YYTHD->rand);
- $1->password.str=buff;
- $1->password.length=HASH_PASSWORD_LENGTH;
- }
+ if (use_old_passwords)
+ {
+ char *buff=
+ (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
+ if (buff)
+ make_scrambled_password_323(buff, $4.str);
+ $1->password.str= buff;
+ $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ }
+ else
+ {
+ char *buff=
+ (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
+ if (buff)
+ make_scrambled_password(buff, $4.str);
+ $1->password.str= buff;
+ $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
+ }
}
}
| user IDENTIFIED_SYM BY PASSWORD TEXT_STRING