diff --git a/.bzrignore b/.bzrignore
index 4e9ce4e41ab9563b0bdebeb9086830b67761bad8..7c0a871a95135f396f42a72efc7ca566dd896eef 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -622,3 +622,10 @@ vio/test-ssl
 vio/test-sslclient
 vio/test-sslserver
 vio/viotest-ssl
+start_mysqld.sh
+mysys/main.cc
+BitKeeper/post-commit
+BitKeeper/post-commit-manual
+build_tags.sh
+tests/connect_test
+BUILD/compile-pentium-maintainer
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok
index 794edcdd968a1b4f03855f40e2949449a966ce80..ac862cc97707fe3de5cd3b085fe4b0c2c9707591 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 018bcbc196374d10db53ebca18cb3ed3659e791f..f263d321a7bc7409befe6cb987c5b68f0c8c80fc 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 27d6b03bd0b165bb0e2c299229e7d4057ef03ea6..bf05f6e8e37694dca9c48cf9709ca95a3d217bea 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 5eb59a1c7ab40a790436a8aa14dabe4d271015b2..e5782235934477c171614cf65f74c65fc3eaee98 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -49,8 +49,15 @@ enum enum_server_command
 };
 
 
-#define SCRAMBLE_LENGTH   8
-#define SCRAMBLE41_LENGTH 20
+/*
+  Length of random string sent by server on handshake; this is also length of
+  obfuscated password, recieved from client 
+*/
+#define SCRAMBLE_LENGTH 20
+#define SCRAMBLE_LENGTH_323 8
+/* length of password stored in the db: new passwords are preceeded with '*' */
+#define SCRAMBLED_PASSWORD_CHAR_LENGTH (SCRAMBLE_LENGTH*2+1)
+#define SCRAMBLED_PASSWORD_CHAR_LENGTH_323 (SCRAMBLE_LENGTH_323*2)
 
 
 #define NOT_NULL_FLAG	1		/* Field can't be NULL */
@@ -301,31 +308,34 @@ 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 check_scramble_323(const char *, const char *message,
+                           unsigned long *salt);
+void get_salt_from_password_323(unsigned long *res, const char *password);
+void make_password_from_salt_323(char *to, const unsigned long *salt);
+
+void make_scrambled_password(char *to, const char *password);
+void scramble(char *to, const char *message, const char *password);
+my_bool check_scramble(const char *reply, const char *message,
+                       const unsigned char *hash_stage2);
+void get_salt_from_password(unsigned char *res, const char *password);
+void make_password_from_salt(char *to, const unsigned char *hash_stage2);
+
+/* end of password.c */
+
 char *get_tty_password(char *opt_message);
-void hash_password(unsigned long *result, const char *password);
 const char *mysql_errno_to_sqlstate(unsigned int mysql_errno);
 
 /* Some other useful functions */
diff --git a/include/mysqld_error.h b/include/mysqld_error.h
index 0468663239b55959f334467df9490d4c74428cba..2f27e3aae745e1b9904231332136ab348a2a4e31 100644
--- a/include/mysqld_error.h
+++ b/include/mysqld_error.h
@@ -287,4 +287,5 @@
 #define ER_CANT_AGGREGATE_3COLLATIONS 1268
 #define ER_CANT_AGGREGATE_NCOLLATIONS 1269
 #define ER_VARIABLE_IS_NOT_STRUCT 1270
-#define ER_ERROR_MESSAGES 271
+#define ER_SERVER_IS_IN_SECURE_AUTH_MODE 1271
+#define ER_ERROR_MESSAGES 272
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index 1461d2fa128408e36e200545661a949de21fff07..ee7fd6a2576e5a6fefe1ef110e20c826045f3a1d 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -616,41 +616,53 @@ 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])
+    if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
     {
-      /* Prepare false scramble  */
-      bfill(end, SCRAMBLE_LENGTH, 'x');
-      end+=SCRAMBLE_LENGTH;
-      *end=0;
-
+      *end++= SCRAMBLE_LENGTH;
+      scramble(end, mysql->scramble, passwd);
+      end+= SCRAMBLE_LENGTH;
     }
-    else					/* For empty password */
-      *end=0;					/* zero length scramble */
+    else
+      end= scramble_323(end, mysql->scramble_323, passwd);
   }
   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 : "");
+  end= strmov(end, db ? db : "") + 1;
 
   /* Write authentication package */
   simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1);
 
-  if (mysql_autenticate(mysql, passwd))
+  NET *net= &mysql->net;
+  ulong pkt_length= net_safe_read(mysql);
+
+  if (pkt_length == packet_error)
     goto error;
 
+  if (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);
+    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/mysql-test/r/connect.result b/mysql-test/r/connect.result
index 9c848c3434f0af8d810715b0cf9e2ac593590df2..c0608af0de26a18be2b39c359d65ef4c58b64a94 100644
--- a/mysql-test/r/connect.result
+++ b/mysql-test/r/connect.result
@@ -30,6 +30,7 @@ show tables;
 Tables_in_test
 update mysql.user set password=old_password("gambling2") where user="test";
 flush privileges;
+set password=old_password('gambling3');
 show tables;
 Tables_in_mysql
 columns_priv
diff --git a/mysql-test/r/func_crypt.result b/mysql-test/r/func_crypt.result
index 461ae1e7e0925a3923c98d0aaded18c63853ae42..bd4c6d41d398675b2639079aff73b43a1832ad24 100644
--- a/mysql-test/r/func_crypt.result
+++ b/mysql-test/r/func_crypt.result
@@ -1,15 +1,79 @@
 select length(encrypt('foo', 'ff')) <> 0;
 length(encrypt('foo', 'ff')) <> 0
 1
-select password("a",""), password("a",NULL), password("","a"), password(NULL,"a");
-password("a","")	password("a",NULL)	password("","a")	password(NULL,"a")
-*2517f7235d68d4ba2e5019c93420523101157a792c01	NULL		NULL
-select password("aaaaaaaaaaaaaaaa","a"), password("a","aaaaaaaaaaaaaaaa");
-password("aaaaaaaaaaaaaaaa","a")	password("a","aaaaaaaaaaaaaaaa")
-*2cd3b9a44e9a9994789a30f935c92f45a96c5472f381	*37c7c5c794ff144819f2531bf03c57772cd84e40db09
-select old_password('test'), length(password("1")), length(encrypt('test')), encrypt('test','aa');
-old_password('test')	length(password("1"))	length(encrypt('test'))	encrypt('test','aa')
-378b243e220ca493	45	13	aaqPiZY5xR5l.
-select old_password(""), old_password(NULL), password(""), password(NULL);
-old_password("")	old_password(NULL)	password("")	password(NULL)
-	NULL		NULL
+select password('abc');
+password('abc')
+*0d3ced9bec10a777aec23ccc353a8c08a633045e
+select password('');
+password('')
+
+select old_password('abc');
+old_password('abc')
+7cd2b5942be28759
+select old_password('');
+old_password('')
+
+select password('gabbagabbahey');
+password('gabbagabbahey')
+*b0f99d2963660dd7e16b751ec9ee2f17b6a68fa6
+select old_password('idkfa');
+old_password('idkfa')
+5c078dc54ca0fcca
+select length(password('1'));
+length(password('1'))
+41
+select length(encrypt('test'));
+length(encrypt('test'))
+13
+select encrypt('test','aa');
+encrypt('test','aa')
+aaqPiZY5xR5l.
+select old_password(NULL);
+old_password(NULL)
+NULL
+select password(NULL);
+password(NULL)
+NULL
+set global old_passwords=on;
+select password('');
+password('')
+
+select old_password('');
+old_password('')
+
+select password('idkfa');
+password('idkfa')
+*b669c9dac3aa6f2254b03cdef8dfdd6b2d1054ba
+select old_password('idkfa');
+old_password('idkfa')
+5c078dc54ca0fcca
+set old_passwords=on;
+select password('idkfa');
+password('idkfa')
+5c078dc54ca0fcca
+select old_password('idkfa');
+old_password('idkfa')
+5c078dc54ca0fcca
+set global old_passwords=off;
+select password('idkfa');
+password('idkfa')
+5c078dc54ca0fcca
+select old_password('idkfa');
+old_password('idkfa')
+5c078dc54ca0fcca
+set old_passwords=off;
+select password('idkfa ');
+password('idkfa ')
+*2dc31d90647b4c1abc9231563d2236e96c9a2db2
+select password('idkfa');
+password('idkfa')
+*b669c9dac3aa6f2254b03cdef8dfdd6b2d1054ba
+select password(' idkfa');
+password(' idkfa')
+*12b099e56bb7fe8d43c78fd834a9d1d11178d045
+select old_password('idkfa');
+old_password('idkfa')
+5c078dc54ca0fcca
+select old_password(' i 	 d k f a ');
+old_password(' i 	 d k f a ')
+5c078dc54ca0fcca
diff --git a/mysql-test/t/connect.test b/mysql-test/t/connect.test
index e6ccc52f0d4e6456aa8d0d2ee426fbf5ee056ded..7585ff0f608b74545dd92e4c6f484363f26814db 100644
--- a/mysql-test/t/connect.test
+++ b/mysql-test/t/connect.test
@@ -48,8 +48,9 @@ flush privileges;
 #connect (con1,localhost,test,gambling2,"");
 #show tables;
 connect (con1,localhost,test,gambling2,mysql);
+set password=old_password('gambling3');
 show tables;
-connect (con1,localhost,test,gambling2,test);
+connect (con1,localhost,test,gambling3,test);
 show tables;
 
 # Re enable this one day if error handling on connect will take place
@@ -63,7 +64,9 @@ show tables;
 #connect (con1,localhost,test,zorro,);
 #--error 1045
 
+
 # remove user 'test' so that other tests which may use 'test'
 # do not depend on this test.
+
 delete from mysql.user where user="test";
 flush privileges;
diff --git a/mysql-test/t/func_crypt.test b/mysql-test/t/func_crypt.test
index c72356bda1a09fe6d84e03482f645d947e6ab487..c1c7090cab396720f83545ae2ddc55a5f3ea12a1 100644
--- a/mysql-test/t/func_crypt.test
+++ b/mysql-test/t/func_crypt.test
@@ -4,7 +4,33 @@ select length(encrypt('foo', 'ff')) <> 0;
 --replace_result $1$aa$4OSUA5cjdx0RUQ08opV27/ aaqPiZY5xR5l.
 
 # Test new and old password handling functions 
-select password("a",""), password("a",NULL), password("","a"), password(NULL,"a");
-select password("aaaaaaaaaaaaaaaa","a"), password("a","aaaaaaaaaaaaaaaa");
-select old_password('test'), length(password("1")), length(encrypt('test')), encrypt('test','aa');
-select old_password(""), old_password(NULL), password(""), password(NULL);
+select password('abc');
+select password('');
+select old_password('abc');
+select old_password('');
+select password('gabbagabbahey');
+select old_password('idkfa');
+select length(password('1'));
+select length(encrypt('test'));
+select encrypt('test','aa');
+select old_password(NULL);
+select password(NULL);
+set global old_passwords=on;
+select password('');
+select old_password('');
+select password('idkfa');
+select old_password('idkfa');
+set old_passwords=on;
+select password('idkfa');
+select old_password('idkfa');
+set global old_passwords=off;
+select password('idkfa');
+select old_password('idkfa');
+
+# this test shows that new scrambles honor spaces in passwords:
+set old_passwords=off;
+select password('idkfa ');
+select password('idkfa');
+select password(' idkfa');
+select old_password('idkfa');
+select old_password(' i 	 d k f a ');
diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh
index 2739c45e7505ccb06ded2ee4fb342b4951103f75..c4cdc7b52d7e725133077200217623e58ea259a5 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 43dc6d8948163572db6c9096ba27113b903a2f88..8c5b29a49ffd1e91ebdb201990d9394311e1350e 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 721164c8301b7f68a610a39d2cbe87b2d8e5cd4b..9a0b7eb3fe296d5bbc6159623448571e6947f849 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -1334,76 +1334,6 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused)))
 #endif /* HAVE_OPENSSL */
 
 
-/*
-  Handle password authentication
-*/
-
-my_bool mysql_autenticate(MYSQL *mysql, const char *passwd)
-{
-  ulong pkt_length;
-  NET *net= &mysql->net;
-  char buff[SCRAMBLE41_LENGTH];
-  char password_hash[SCRAMBLE41_LENGTH]; /* Used for storage of stage1 hash */
-
-  /* We shall only query server if it expect us to do so */
-  if ((pkt_length=net_safe_read(mysql)) == packet_error)
-    goto error;
-
-  if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
-  {
-    /*
-      This should always happen with new server unless empty password
-      OK/Error packets have zero as the first char
-    */
-    if (pkt_length == 24 && net->read_pos[0])
-    {
-      /* Old passwords will have '*' at the first byte of hash */
-      if (net->read_pos[0] != '*')
-      {
-        /* Build full password hash as it is required to decode scramble */
-        password_hash_stage1(buff, passwd);
-        /* Store copy as we'll need it later */
-        memcpy(password_hash,buff,SCRAMBLE41_LENGTH);
-        /* Finally hash complete password using hash we got from server */
-        password_hash_stage2(password_hash,(const char*) net->read_pos);
-        /* Decypt and store scramble 4 = hash for stage2 */
-        password_crypt((const char*) net->read_pos+4,mysql->scramble_buff,
-		       password_hash, SCRAMBLE41_LENGTH);
-        mysql->scramble_buff[SCRAMBLE41_LENGTH]=0;
-        /* Encode scramble with password. Recycle buffer */
-        password_crypt(mysql->scramble_buff,buff,buff,SCRAMBLE41_LENGTH);
-      }
-      else
-      {
-	/* Create password to decode scramble */
-	create_key_from_old_password(passwd,password_hash);
-	/* Decypt and store scramble 4 = hash for stage2 */
-	password_crypt((const char*) net->read_pos+4,mysql->scramble_buff,
-		       password_hash, SCRAMBLE41_LENGTH);
-	mysql->scramble_buff[SCRAMBLE41_LENGTH]=0;
-	/* Finally scramble decoded scramble with password */
-	scramble(buff, mysql->scramble_buff, passwd,0);
-      }
-      /* Write second package of authentication */
-      if (my_net_write(net,buff,SCRAMBLE41_LENGTH) || net_flush(net))
-      {
-        net->last_errno= CR_SERVER_LOST;
-	strmov(net->sqlstate, unknown_sqlstate);
-        strmov(net->last_error,ER(net->last_errno));
-        goto error;
-      }
-      /* Read what server thinks about out new auth message report */
-      if (net_safe_read(mysql) == packet_error)
-	goto error;
-    }
-  }
-  return 0;
-
-error:
-  return 1;
-}
-
-
 /*
   Note that the mysql argument must be initialized with mysql_init()
   before calling mysql_real_connect !
@@ -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;
@@ -1872,7 +1823,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
 		     mysql->server_status, client_flag));
   /* This needs to be changed as it's not useful with big packets */
   if (user && user[0])
-    strmake(end,user,32);			/* Max user name */
+    strmake(end,user,USERNAME_LENGTH);          /* Max user name */
   else
     read_user_name((char*) end);
 
@@ -1881,41 +1832,27 @@ 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*/
+    if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
     {
-      end=strend(end)+1;
-      *end=0;				/* Store zero length scramble */
+      *end++= SCRAMBLE_LENGTH;
+      scramble(end, mysql->scramble, passwd);
+      end+= SCRAMBLE_LENGTH;
     }
+    else
+      end= scramble_323(end, mysql->scramble_323, passwd) + 1;
   }
   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))
   {
-    end=strmake(end+1,db,NAME_LEN);
-    mysql->db=my_strdup(db,MYF(MY_WME));
-    db=0;
+    end= strmake(end, db, NAME_LEN) + 1;
+    mysql->db= my_strdup(db,MYF(MY_WME));
+    db= 0;
   }
   /* Write authentication package */
   if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net))
@@ -1925,10 +1862,37 @@ 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);
+    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_create.cc b/sql/item_create.cc
index eaa27c1009d1c8f1e897aa8a3a67e022756bf379..67179c8d002b7230ca84a5a7a24a7a4576668f48 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -52,13 +52,6 @@ Item *create_func_ord(Item* a)
   return new Item_func_ord(a);
 }
 
-Item *create_func_old_password(Item* a)
-{
-  return new Item_func_old_password(a);
-}
-
-
-
 Item *create_func_asin(Item* a)
 {
   return new Item_func_asin(a);
@@ -332,11 +325,6 @@ Item *create_func_quarter(Item* a)
   return new Item_func_quarter(a);
 }
 
-Item *create_func_password(Item* a)
-{
-  return new Item_func_password(a);
-}
-
 Item *create_func_radians(Item *a)
 {
   return new Item_func_units((char*) "radians",a,M_PI/180,0.0);
diff --git a/sql/item_create.h b/sql/item_create.h
index 32e452c15dd85c90fd4274e3406f94d0c265e8df..434a0b134c18b1f81864c4790042afad6ec12610 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -69,14 +69,12 @@ Item *create_func_monthname(Item* a);
 Item *create_func_nullif(Item* a, Item *b);
 Item *create_func_oct(Item *);
 Item *create_func_ord(Item* a);
-Item *create_func_old_password(Item* a);
 Item *create_func_period_add(Item* a, Item *b);
 Item *create_func_period_diff(Item* a, Item *b);
 Item *create_func_pi(void);
 Item *create_func_pow(Item* a, Item *b);
 Item *create_func_current_user(void);
 Item *create_func_quarter(Item* a);
-Item *create_func_password(Item* a);
 Item *create_func_radians(Item *a);
 Item *create_func_release_lock(Item* a);
 Item *create_func_repeat(Item* a, Item *b);
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 4e35e90b42934801822c731749f4ddff6b3c10f6..0f81d285d7e26be5be6cc60acf087637e068c7bd 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1326,95 +1326,49 @@ void Item_func_trim::fix_length_and_dec()
 }
 
 
-
-
-void Item_func_password::fix_length_and_dec()
-{
-  /*
-    If PASSWORD() was called with only one argument, it depends on a random
-    number so we need to save this random number into the binary log.
-    If called with two arguments, it is repeatable.
-  */
-  if (arg_count == 1)
-  {
-    THD *thd= current_thd;
-    thd->rand_used= 1;
-    thd->rand_saved_seed1= thd->rand.seed1;
-    thd->rand_saved_seed2= thd->rand.seed2;
-  } 
-  max_length= get_password_length(use_old_passwords);
-}
-
-/*
- Password() function has 2 arguments. Second argument can be used
- to make results repeatable
-*/ 
+/* Item_func_password */
 
 String *Item_func_password::val_str(String *str)
 {
-  struct rand_struct rand_st; // local structure for 2 param version
-  ulong  seed=0;              // seed to initialise random generator to
-  
-  String *res  =args[0]->val_str(str);          
+  String *res= args[0]->val_str(str); 
   if ((null_value=args[0]->null_value))
     return 0;
-  
-  if (arg_count == 1)
-  {    
-    if (res->length() == 0)
-      return &empty_string;
-    make_scrambled_password(tmp_value,res->c_ptr(),use_old_passwords,
-                            &current_thd->rand);
-    str->set(tmp_value,get_password_length(use_old_passwords),res->charset());
-    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;
 }
 
+char *Item_func_password::alloc(THD *thd, const char *password)
+{
+  char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
+  if (buff)
+    make_scrambled_password(buff, password);
+  return buff;
+}
+
+/* Item_func_old_password */
+
 String *Item_func_old_password::val_str(String *str)
 {
-  String *res  =args[0]->val_str(str);
+  String *res= args[0]->val_str(str);
   if ((null_value=args[0]->null_value))
     return 0;
   if (res->length() == 0)
     return &empty_string;
-  make_scrambled_password(tmp_value,res->c_ptr(),1,&current_thd->rand);
-  str->set(tmp_value,16,res->charset());
+  make_scrambled_password_323(tmp_value, res->c_ptr());
+  str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, res->charset());
   return str;
 }
 
+char *Item_func_old_password::alloc(THD *thd, const char *password)
+{
+  char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
+  if (buff)
+    make_scrambled_password_323(buff, password);
+  return buff;
+}
 
 
 #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 7f8d7ade67b59ed2ed4fa7573f061a07d27d1abe..025c47a8504faf19cac5af986eef619723b4e0c0 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -254,30 +254,45 @@ 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"; }
+  static char *alloc(THD *thd, const char *password);
 };
 
 
+/*
+  Item_func_old_password -- PASSWORD() implementation used in MySQL 3.21 - 4.0
+  compatibility mode. This item is created in sql_yacc.yy when
+  'old_passwords' session variable is set, and to handle OLD_PASSWORD()
+  function.
+*/
+
 class Item_func_old_password :public Item_str_func
 {
-  char tmp_value[17]; /* old password length +1 */
+  char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1];
 public:
   Item_func_old_password(Item *a) :Item_str_func(a) {}
-  String *val_str(String *);
-  void fix_length_and_dec() { max_length = get_password_length(1); }
+  String *val_str(String *str);
+  void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; } 
   const char *func_name() const { return "old_password"; }
+  static char *alloc(THD *thd, const char *password);
 };
 
 
-
 class Item_func_des_encrypt :public Item_str_func
 {
   String tmp_value;
diff --git a/sql/lex.h b/sql/lex.h
index c2860f4551ad19b35d4e9058fd53afeba6a9b3c9..4d3863d77a60810321c54b466114defe574765be 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -288,6 +288,7 @@ static SYMBOL symbols[] = {
   { "NULL",		SYM(NULL_SYM),0,0},
   { "NUMERIC",		SYM(NUMERIC_SYM),0,0},
   { "OFFSET",		SYM(OFFSET_SYM),0,0},
+  { "OLD_PASSWORD",	SYM(OLD_PASSWORD),0,0},
   { "ON",		SYM(ON),0,0},
   { "OPEN",		SYM(OPEN_SYM),0,0},
   { "OPTIMIZE",		SYM(OPTIMIZE),0,0},
@@ -586,7 +587,6 @@ static SYMBOL sql_functions[] = {
   { "NUMPOINTS",	SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_numpoints)},
   { "OCTET_LENGTH",	SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)},
   { "OCT",		SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_oct)},
-  { "OLD_PASSWORD",     SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_old_password)},
   { "ORD",              SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ord)},
   { "OVERLAPS",		SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_overlaps)},
   { "PERIOD_ADD",	SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_add)},
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 65451ca40b528a1c693a8cfb70dc40ba4092742e..90cf2a62bf6e9d6aef6b087867090133af80531a 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
@@ -756,7 +753,7 @@ extern my_bool opt_safe_show_db, opt_local_infile, lower_case_table_names;
 extern my_bool opt_slave_compressed_protocol, use_temp_pool;
 extern my_bool opt_readonly;
 extern my_bool opt_enable_named_pipe;
-extern my_bool opt_old_passwords, use_old_passwords;
+extern my_bool opt_secure_auth;
 extern char *shared_memory_base_name, *mysqld_unix_port;
 extern bool opt_enable_shared_memory;
 
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 709ba036f7e1c0730d311a2931692d8dd5d7a3b6..cfe2605ed62f6ffbc431c38ae85fbf5b1c5d9831 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -250,9 +250,10 @@ my_bool opt_local_infile, opt_external_locking, opt_slave_compressed_protocol;
 my_bool opt_safe_user_create = 0, opt_no_mix_types = 0;
 my_bool lower_case_table_names, opt_old_rpl_compat;
 my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0;
-my_bool opt_log_slave_updates= 0, opt_old_passwords=0, use_old_passwords=0;
+my_bool opt_log_slave_updates= 0;
 my_bool	opt_console= 0, opt_bdb, opt_innodb, opt_isam;
 my_bool opt_readonly, use_temp_pool, relay_log_purge;
+my_bool opt_secure_auth= 0;
 volatile bool mqh_used = 0;
 
 uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options;
@@ -2757,12 +2758,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
 
@@ -3472,7 +3467,8 @@ enum options
   OPT_EXPIRE_LOGS_DAYS,
   OPT_DEFAULT_WEEK_FORMAT,
   OPT_GROUP_CONCAT_MAX_LEN,
-  OPT_DEFAULT_COLLATION
+  OPT_DEFAULT_COLLATION,
+  OPT_SECURE_AUTH
 };
 
 
@@ -3773,9 +3769,10 @@ Does nothing yet.",
    (gptr*) &opt_no_mix_types, (gptr*) &opt_no_mix_types, 0, GET_BOOL, NO_ARG,
    0, 0, 0, 0, 0, 0},
 #endif
-  {"old-protocol", 'o', "Use the old (3.20) protocol client/server protocol.",
-   (gptr*) &protocol_version, (gptr*) &protocol_version, 0, GET_UINT, NO_ARG,
-   PROTOCOL_VERSION, 0, 0, 0, 0, 0},
+  {"old-passwords", OPT_OLD_PASSWORDS, "Use old password encryption method (needed for 4.0 and older clients).",
+   (gptr*) &global_system_variables.old_passwords,
+   (gptr*) &max_system_variables.old_passwords, 0, GET_BOOL, NO_ARG,
+   0, 0, 0, 0, 0, 0},
   {"old-rpl-compat", OPT_OLD_RPL_COMPAT,
    "Use old LOAD DATA format in the binary log (don't save data in file).",
    (gptr*) &opt_old_rpl_compat, (gptr*) &opt_old_rpl_compat, 0, GET_BOOL,
@@ -3844,8 +3841,6 @@ relay logs.",
    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
   {"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing).",
    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
-  {"old-passwords", OPT_OLD_PASSWORDS, "Use old password encryption method (needed for 4.0 and older clients).",
-   (gptr*) &opt_old_passwords, (gptr*) &opt_old_passwords, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 #ifndef TO_BE_DELETED
   {"safe-show-database", OPT_SAFE_SHOW_DB,
    "Deprecated option; One should use GRANT SHOW DATABASES instead...",
@@ -3855,6 +3850,9 @@ relay logs.",
    "Don't allow new user creation by the user who has no write privileges to the mysql.user table.",
    (gptr*) &opt_safe_user_create, (gptr*) &opt_safe_user_create, 0, GET_BOOL,
    NO_ARG, 0, 0, 0, 0, 0, 0},
+  {"secure-auth", OPT_SECURE_AUTH, "Disallow authentication for accounts that have old (pre-4.1) passwords.",
+   (gptr*) &opt_secure_auth, (gptr*) &opt_secure_auth, 0, GET_BOOL, NO_ARG,
+   my_bool(0), 0, 0, 0, 0, 0},
   {"server-id",	OPT_SERVER_ID,
    "Uniquely identifies the server instance in the community of replication partners.",
    (gptr*) &server_id, (gptr*) &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0,
@@ -4624,7 +4622,8 @@ static void mysql_init_variables(void)
   opt_log= opt_update_log= opt_bin_log= opt_slow_log= 0;
   opt_disable_networking= opt_skip_show_db=0;
   opt_logname= opt_update_logname= opt_binlog_index_name= opt_slow_logname=0;
-  opt_bootstrap= opt_myisam_log= use_old_passwords= 0;
+  opt_secure_auth= 0;
+  opt_bootstrap= opt_myisam_log= 0;
   mqh_used= 0;
   segfaulted= kill_in_progress= 0;
   cleanup_done= 0;
@@ -4727,6 +4726,7 @@ static void mysql_init_variables(void)
   max_system_variables.select_limit=    (ulonglong) HA_POS_ERROR;
   global_system_variables.max_join_size= (ulonglong) HA_POS_ERROR;
   max_system_variables.max_join_size=   (ulonglong) HA_POS_ERROR;
+  global_system_variables.old_passwords= 0;
 
   /* Variables that depends on compile options */
 #ifndef DBUG_OFF
@@ -4848,9 +4848,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
   case 'L':
     strmake(language, argument, sizeof(language)-1);
     break;
-  case 'o':
-    protocol_version=PROTOCOL_VERSION-1;
-    break;
 #ifdef HAVE_REPLICATION
   case OPT_SLAVE_SKIP_ERRORS:
     init_slave_skip_errors(argument);
diff --git a/sql/password.c b/sql/password.c
index 257547671e5413fd4c1e9161b4c6af7bcfe9121c..2e9139c12aa019fa2907aecf130312ecb46b70e2 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;
@@ -94,35 +89,12 @@ void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
 
 
 /*
-  Old (MySQL 3.20) random generation structure initialization
-
-  SYNOPSIS
-    old_randominit()
-    rand_st    OUT  Structure to initialize
-    seed1      IN   First initialization parameter
-
-  RETURN
-    none
-*/
-
-static void old_randominit(struct rand_struct *rand_st,ulong seed1)
-{						/* For mysql 3.20.# */
-  rand_st->max_value= 0x01FFFFFFL;
-  rand_st->max_value_dbl=(double) rand_st->max_value;
-  seed1%=rand_st->max_value;
-  rand_st->seed1=seed1 ; rand_st->seed2=seed1/2;
-}
-
-
-/*
-  Generate Random number
-
+    Generate random number.
   SYNOPSIS
     my_rnd()
     rand_st    INOUT  Structure used for number generation
-
-  RETURN
-    Generated pseudo random number
+  RETURN VALUE
+    generated pseudo random number
 */
 
 double my_rnd(struct rand_struct *rand_st)
@@ -134,63 +106,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 +121,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 +134,388 @@ 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
   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)
 {
-  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);
+    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);
+    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
+  RETURN VALUE
+    0 - password correct
+   !0 - password invalid
 */
 
-void make_scrambled_password(char *to,const char *password,
-                             my_bool force_old_scramble,
-                             struct rand_struct *rand_st)
+my_bool
+check_scramble_323(const char *scrambled, const char *message,
+                   ulong *hash_pass)
 {
-  ulong hash_res[2];   /* Used for pre 4.1 password hashing */
-  unsigned short salt; /* Salt for 4.1 version password */
-  uint8 digest[SHA1_HASH_SIZE];
-  if (force_old_scramble) /* Pre 4.1 password encryption */
-  {
-    hash_password(hash_res,password);
-    sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
-  }
-  else /* New password 4.1 password scrambling */
+  struct rand_struct rand_st;
+  ulong hash_message[2];
+  char buff[16],*to,extra;                      /* Big enough for check */
+  const char *pos;
+  
+  /* 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);
+  randominit(&rand_st,hash_pass[0] ^ hash_message[0],
+             hash_pass[1] ^ hash_message[1]);
+  to=buff;
+  for (pos=scrambled ; *pos ; pos++)
+    *to++=(char) (floor(my_rnd(&rand_st)*31)+64);
+  extra=(char) (floor(my_rnd(&rand_st)*31));
+  to=buff;
+  while (*scrambled)
   {
-    to[0]=PVERSION41_CHAR; /* New passwords have version prefix */
-   /* Rnd returns number from 0 to 1 so this would be good salt generation.*/
-    salt=(unsigned short) (my_rnd(rand_st)*65535+1);
-    /* Use only 2 first bytes from it */
-    sprintf(to+1,"%04x",salt);
-    /* First hasing is done without salt */
-    password_hash_stage1((char*) digest, password);
-    /* Second stage is done with salt */
-    password_hash_stage2((char*) digest,(char*)to+1),
-    /* Print resulting hash into the password*/
-    sprintf(to+5,
-      "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
-      digest[0],digest[1],digest[2],digest[3],digest[4],digest[5],digest[6],
-      digest[7],digest[8],digest[9],digest[10],digest[11],digest[12],digest[13],
-      digest[14],digest[15],digest[16],digest[17],digest[18],digest[19]);
+    if (*scrambled++ != (char) (*to++ ^ extra))
+      return 1;                                 /* Wrong password */
   }
+  return 0;
 }
 
+static 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 not stored
+    in the buf as it is not needed.
+    This function is used by client to create authenticated reply to the
+    server's greeting.
   SYNOPSIS
-    create_key_from_old_password()
-    passwd    IN  Password used for key generation
-    key       OUT Created 20 bytes key
-
-  RETURN
-    None
+    scramble()
+    buf       OUT store scrambled string here. The buf must be at least 
+                  SHA1_HASH_SIZE bytes long. 
+    message   IN  random message, must be exactly SCRAMBLE_LENGTH long and 
+                  NULL-terminated.
+    password  IN  users' password 
 */
 
-
-void create_key_from_old_password(const char *passwd, char *key)
+void
+scramble(char *to, const char *message, const char *password)
 {
-  char  buffer[SCRAMBLE41_LENGTH]; /* Buffer for various needs */
-  ulong salt[6];    /* Salt (large for safety) */
-  /* At first hash password to the string stored in password */
-  make_scrambled_password(buffer,passwd,1,(struct rand_struct *)NULL);
-  /* Now convert it to the salt form */
-  get_salt_from_password(salt,buffer);
-  /* Finally get hash and bin password from salt */
-  get_hash_and_password(salt,0,buffer,(unsigned char*) key);
+  SHA1_CONTEXT sha1_context;
+  uint8 hash_stage1[SHA1_HASH_SIZE];
+  uint8 hash_stage2[SHA1_HASH_SIZE];
+
+  sha1_reset(&sha1_context);
+  /* stage 1: hash password */
+  sha1_input(&sha1_context, (uint8 *) password, strlen(password));
+  sha1_result(&sha1_context, hash_stage1);
+  /* stage 2: hash stage 1; note that hash_stage2 is stored in the database */
+  sha1_reset(&sha1_context);
+  sha1_input(&sha1_context, hash_stage1, SHA1_HASH_SIZE);
+  sha1_result(&sha1_context, hash_stage2);
+  /* create crypt string as sha1(message, hash_stage2) */;
+  sha1_reset(&sha1_context);
+  sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH);
+  sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE);
+  /* xor allows 'from' and 'to' overlap: lets take advantage of it */
+  sha1_result(&sha1_context, (uint8 *) to);
+  my_crypt(to, (const uint8 *) to, hash_stage1, SCRAMBLE_LENGTH);
 }
 
 
 /*
-  Scramble string with password
-  Used at pre 4.1 authentication phase.
-
+    Check that scrambled message corresponds to the password; the function
+    is used by server to check that recieved reply is authentic.
+    This function does not check lengths of given strings: message must be
+    null-terminated, reply and hash_stage2 must be at least SHA1_HASH_SIZE
+    long (if not, something fishy is going on).
   SYNOPSIS
-    scramble()
-    to        OUT Store scrambled message here
-    message   IN  Message to scramble
-    password  IN  Password to use while scrambling
-    old_ver   IN  Forse old version random number generator
-
-  RETURN
-    End of scrambled string
+    check_scramble()
+    scramble     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/set_var.cc b/sql/set_var.cc
index cb6c875d5132d03cd4308969e76b2de12ea38c7c..31706b2173442d42b29ba9a59832a5105f72096e 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -208,6 +208,7 @@ sys_var_thd_ulong	sys_net_retry_count("net_retry_count",
 					    &SV::net_retry_count,
 					    fix_net_retry_count);
 sys_var_thd_bool	sys_new_mode("new", &SV::new_mode);
+sys_var_thd_bool	sys_old_passwords("old_passwords", &SV::old_passwords);
 sys_var_thd_ulong       sys_preload_buff_size("preload_buffer_size",
                                               &SV::preload_buff_size);
 sys_var_thd_ulong	sys_read_buff_size("read_buffer_size",
@@ -234,6 +235,7 @@ sys_var_thd_enum	sys_query_cache_type("query_cache_type",
 					     &SV::query_cache_type,
 					     &query_cache_type_typelib);
 #endif /* HAVE_QUERY_CACHE */
+sys_var_bool_ptr	sys_secure_auth("secure_auth", &opt_secure_auth);
 sys_var_long_ptr	sys_server_id("server_id",&server_id);
 sys_var_bool_ptr	sys_slave_compressed_protocol("slave_compressed_protocol",
 						      &opt_slave_compressed_protocol);
@@ -423,6 +425,7 @@ sys_var *sys_variables[]=
   &sys_net_wait_timeout,
   &sys_net_write_timeout,
   &sys_new_mode,
+  &sys_old_passwords,
   &sys_preload_buff_size,
   &sys_pseudo_thread_id,
   &sys_query_cache_size,
@@ -441,6 +444,7 @@ sys_var *sys_variables[]=
 #endif
   &sys_rpl_recovery_rank,
   &sys_safe_updates,
+  &sys_secure_auth,
   &sys_select_limit,
   &sys_server_id,
 #ifdef HAVE_REPLICATION
@@ -598,6 +602,7 @@ struct show_var_st init_vars[]= {
   {sys_net_retry_count.name,  (char*) &sys_net_retry_count,	    SHOW_SYS},
   {sys_net_write_timeout.name,(char*) &sys_net_write_timeout,       SHOW_SYS},
   {sys_new_mode.name,         (char*) &sys_new_mode,                SHOW_SYS},
+  {sys_old_passwords.name,    (char*) &sys_old_passwords,           SHOW_SYS},
   {"open_files_limit",	      (char*) &open_files_limit,	    SHOW_LONG},
   {"pid_file",                (char*) pidfile_name,                 SHOW_CHAR},
   {"log_error",               (char*) log_error_file,               SHOW_CHAR},
@@ -618,6 +623,7 @@ struct show_var_st init_vars[]= {
    SHOW_SYS},
   {sys_query_cache_size.name, (char*) &sys_query_cache_size,	    SHOW_SYS},
   {sys_query_cache_type.name, (char*) &sys_query_cache_type,        SHOW_SYS},
+  {"secure_auth",             (char*) &sys_secure_auth,             SHOW_SYS},
 #endif /* HAVE_QUERY_CACHE */
 #ifdef HAVE_SMEM
   {"shared_memory",           (char*) &opt_enable_shared_memory,    SHOW_MY_BOOL},
diff --git a/sql/set_var.h b/sql/set_var.h
index 978aba3384a0c8c6e256ad6684a6173f6960fe97..8f12016bab48a974e51bf0a4f57bfdc27bb83a67 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -692,6 +692,9 @@ public:
   }
 };
 
+/* updated in sql_acl.cc */
+
+extern sys_var_thd_bool sys_old_passwords;
 
 /*
   Prototypes for helper functions
@@ -705,6 +708,7 @@ void fix_delay_key_write(THD *thd, enum_var_type type);
 ulong fix_sql_mode(ulong sql_mode);
 extern sys_var_str sys_charset_system;
 CHARSET_INFO *get_old_charset_by_name(const char *old_name);
+
 gptr find_named(I_List<NAMED_LIST> *list, const char *name, uint length);
 void delete_elements(I_List<NAMED_LIST> *list, void (*free_element)(gptr));
 
diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt
index 6f42243ba2b46f2b0ee951aeaae227f250abceab..682d46fdebe581496f60276fce913c0c88efc8da 100644
--- a/sql/share/czech/errmsg.txt
+++ b/sql/share/czech/errmsg.txt
@@ -276,3 +276,4 @@ v/*
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt
index 25a5020a30ff9d5d140d72c0af609e67ad1316a2..353a9c47266a4f671589588bcb83b2b5112b9459 100644
--- a/sql/share/danish/errmsg.txt
+++ b/sql/share/danish/errmsg.txt
@@ -270,3 +270,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
index 6aba93c246c8673cd1c845aa47efe9c8edb1c0ed..20e38ef3ee13b3f5b9ab9686b99a49a40f2c1d60 100644
--- a/sql/share/dutch/errmsg.txt
+++ b/sql/share/dutch/errmsg.txt
@@ -278,3 +278,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
index 57336647f2a881b4c5750af2b249d8b0f9cf6b9d..ab75780237e9a4d3fb3e2c898b7106d15c3366f8 100644
--- a/sql/share/english/errmsg.txt
+++ b/sql/share/english/errmsg.txt
@@ -272,3 +272,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt
index 94e19fc6e9d69d11f57ad26dced9a20d7322be52..9474055c10b74313d6365a5133f9e9e30ed5af78 100644
--- a/sql/share/estonian/errmsg.txt
+++ b/sql/share/estonian/errmsg.txt
@@ -272,3 +272,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index dd062cc074ae100921127ca823b253c8d96b77cc..c2954891cd640d491e4280d7665d8f2d0ae79afa 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -267,3 +267,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
index e7cdf3f6f0d91709ecadb0703a7962108227d40b..b9057823d9a286ca837fe06c2ffe317715ac822d 100644
--- a/sql/share/german/errmsg.txt
+++ b/sql/share/german/errmsg.txt
@@ -276,3 +276,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index ef423ae275800480e77f72d19f82383b99b3f9fb..fe05c554abe03a34a3dde942a6988a6f8536fd04 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -267,3 +267,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
index 6b27cdc255108b03b7e1b6e97db9df9f64349bb0..a58093c08d84e58e13a8fc8fa47e7ed6855337d5 100644
--- a/sql/share/hungarian/errmsg.txt
+++ b/sql/share/hungarian/errmsg.txt
@@ -269,3 +269,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
index 8b0598f64b5ab1c1080c7ca7927778a24127f0bb..705c23057b608443d7a250211dc331286699a7c4 100644
--- a/sql/share/italian/errmsg.txt
+++ b/sql/share/italian/errmsg.txt
@@ -267,3 +267,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
index 011d6a1faac083180ec837b4adf0e261ae082fd1..6bc104a7e961d701f7b1d6f37dc83bd5f4d215d4 100644
--- a/sql/share/japanese/errmsg.txt
+++ b/sql/share/japanese/errmsg.txt
@@ -269,3 +269,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
index be7fee7cb455c2b4acdc183979ff4c6f8584a6e3..f07001188301aee99c7a9b8894d19361bb2a0054 100644
--- a/sql/share/korean/errmsg.txt
+++ b/sql/share/korean/errmsg.txt
@@ -267,3 +267,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
index 96c11871502faa8b5c20cfd3fdbe1b0fb671323e..d565150dfe35daee4b668b027ac9e055430189cd 100644
--- a/sql/share/norwegian-ny/errmsg.txt
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -269,3 +269,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
index c505d512e9e8d1d216cc59c321258d677ce8da65..91059ff33dd7f8aa4f58d84aa505c29f8e71d94e 100644
--- a/sql/share/norwegian/errmsg.txt
+++ b/sql/share/norwegian/errmsg.txt
@@ -269,3 +269,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
index 33b2fbdb8a8877b9150d0ab4cafbf5983ad17a40..1cfc91ff06db3d2bf85004dd6697b94878c7a8b9 100644
--- a/sql/share/polish/errmsg.txt
+++ b/sql/share/polish/errmsg.txt
@@ -271,3 +271,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index e8a3205ec5dea846a0ebc0cb0fcf1ce7a310f94d..7fcf8651f30e48bac6637894f13a78bfc4cd925d 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -267,3 +267,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index 6602b5d430c4c5cbfd51478390dba1a96dc5bc39..b6d034f5adac1b3ebfc803d8c0ce09138fb0ee8f 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -271,3 +271,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
index 70ff979bfdfd334b5dc7fbf511f6e1b3ddf5cf0d..8410804d1f678bcea6f580c9dd0ecce666495389 100644
--- a/sql/share/russian/errmsg.txt
+++ b/sql/share/russian/errmsg.txt
@@ -269,3 +269,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Сервер запущен в режиме --secure-auth (безопасной авторизации), но для пользователя '%s@%s' пароль сохранён в старом формате; необходимо обновить формат пароля"
diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt
index f4ea3ac3c8286ccdce768d215867d023abc99669..fa027fa77a46e02e005a31cd796eb18f873d872f 100644
--- a/sql/share/serbian/errmsg.txt
+++ b/sql/share/serbian/errmsg.txt
@@ -263,3 +263,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
index 2ba4f2f05489fa39ab6e87198a513e5bff497dca..a7fef685639c5a7a317f4d70bfa96c978554f2e3 100644
--- a/sql/share/slovak/errmsg.txt
+++ b/sql/share/slovak/errmsg.txt
@@ -275,3 +275,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index c7f8d51dee9abf30bff867d82f37976e0aa51468..6e187f31b11ab5dccbf661f0b60cbe360997848d 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -268,3 +268,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index 0d51cb4b54331f193d881e861a2df8722e23f3c3..78268f459c4ebb81323a97c1f34566103f6baec3 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -267,3 +267,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt
index 4e22dbfcf3e11611578176d092c8fd51d5bdfcea..13fc5f249baf82a63d43409e6745b5060a96bc3f 100644
--- a/sql/share/ukrainian/errmsg.txt
+++ b/sql/share/ukrainian/errmsg.txt
@@ -272,3 +272,4 @@
 "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'",
 "Illegal mix of collations for operation '%s'",
 "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
+"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format"
diff --git a/sql/slave.cc b/sql/slave.cc
index c45c11f8bef56cdac9832fd5c931d73dc0b2328c..91376df55905a59157883050eb46d1fd3d84bd00 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 429456eb0bb9d2077b4f13fb93dc88a6a5de82d8..d3565565dedd9960564fa85c2aed92848e140f2a 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 1bdca7167e80ee236e42a8ee375ed13f5bd869ad..ee544335a99e4ae879a7c6048c5a7f3cde18dbda 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -51,7 +51,7 @@ static byte* acl_entry_get_key(acl_entry *entry,uint *length,
   return (byte*) entry->key;
 }
 
-#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+17)
+#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+USERNAME_LENGTH+1)
 
 static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
 static MEM_ROOT mem, memex;
@@ -68,11 +68,53 @@ static ulong get_sort(uint count,...);
 static void init_check_host(void);
 static ACL_USER *find_acl_user(const char *host, const char *user);
 static bool update_user_table(THD *thd, const char *host, const char *user,
-			      const char *new_password);
+			      const char *new_password, uint new_password_len);
 static void update_hostname(acl_host_and_ip *host, const char *hostname);
 static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
 			     const char *ip);
 
+/*
+  Convert scrambled password to binary form, according to scramble type, 
+  Binary form is stored in user.salt.
+*/
+
+static
+void
+set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
+{
+  if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
+  {
+    get_salt_from_password(acl_user->salt, password);
+    acl_user->salt_len= SCRAMBLE_LENGTH;
+  }
+  else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
+  {
+    get_salt_from_password_323((ulong *) acl_user->salt, password);
+    acl_user->salt_len= SCRAMBLE_LENGTH_323;
+  }
+  else
+    acl_user->salt_len= 0;
+}
+
+/*
+  This after_update function is used when user.password is less than
+  SCRAMBLE_LENGTH bytes.
+*/
+
+static void restrict_update_of_old_passwords_var(THD *thd,
+                                                 enum_var_type var_type)
+{
+  if (var_type == OPT_GLOBAL)
+  {
+    pthread_mutex_lock(&LOCK_global_system_variables);
+    global_system_variables.old_passwords= 1;
+    pthread_mutex_unlock(&LOCK_global_system_variables);
+  }
+  else
+    thd->variables.old_passwords= 1;
+}
+
+
 /*
   Read grant privileges from the privilege tables in the 'mysql' database.
 
@@ -114,8 +156,6 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
   if (!(thd=new THD))
     DBUG_RETURN(1); /* purecov: inspected */
   thd->store_globals();
-  /* Use passwords according to command line option */
-  use_old_passwords= opt_old_passwords;
 
   acl_cache->clear(1);				// Clear locked hostname cache
   thd->db= my_strdup("mysql",MYF(0));
@@ -172,103 +212,125 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
 
   init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0);
   VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
-  if (table->field[2]->field_length == 8 &&
-      protocol_version == PROTOCOL_VERSION)
+  if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
   {
-    sql_print_error(
-	    "Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */
-    protocol_version=9; /* purecov: tested */
+    sql_print_error("Fatal error: mysql.user table is damaged or in "
+                    "unsupported 3.20 format.");
+    goto end;
   }
 
   DBUG_PRINT("info",("user table fields: %d, password length: %d",
 		     table->fields, table->field[2]->field_length));
-  if (table->field[2]->field_length < 45 && !use_old_passwords)
+  
+  pthread_mutex_lock(&LOCK_global_system_variables);
+  if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH)
   {
-    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;
+    if (opt_secure_auth)
+    {
+      pthread_mutex_unlock(&LOCK_global_system_variables);
+      sql_print_error("Fatal error: mysql.user table is in old format, "
+                      "but server started with --secure-auth option.");
+      goto end;
+    }
+    sys_old_passwords.after_update= restrict_update_of_old_passwords_var;
+    if (global_system_variables.old_passwords)
+      pthread_mutex_unlock(&LOCK_global_system_variables);
+    else
+    {
+      global_system_variables.old_passwords= 1;
+      pthread_mutex_unlock(&LOCK_global_system_variables);
+      sql_print_error("mysql.user table is not updated to new password format; "
+                      "Disabling new password usage until "
+                      "mysql_fix_privilege_tables is run");
+    }
+    thd->variables.old_passwords= 1;
+  }
+  else
+  {
+    sys_old_passwords.after_update= 0;
+    pthread_mutex_unlock(&LOCK_global_system_variables);
   }
 
   allow_all_hosts=0;
   while (!(read_record_info.read_record(&read_record_info)))
   {
     ACL_USER user;
-    uint length=0;
-    update_hostname(&user.host,get_field(&mem, table->field[0]));
-    user.user=get_field(&mem, table->field[1]);
-    user.password=get_field(&mem, table->field[2]);
-    if (user.password && (length=(uint) strlen(user.password)) == 8 &&
-	protocol_version == PROTOCOL_VERSION)
-    {
-      sql_print_error(
-		      "Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)",
-		      user.user ? user.user : ""); /* purecov: tested */
-    }
-    else  /* non empty and not short passwords */
-    {
-      user.pversion=get_password_version(user.password);
-      /* Only passwords of specific lengths depending on version are allowed */
-      if ( (!user.pversion && length % 8) ||  (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 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 +524,205 @@ 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
+ RETURN VALUE
+    0  success: thread data and mqh are updated
+    1  user not found or authentification failure
+    2  user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
+   -1  user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
 */
 
-ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
-		  const char *password,const char *message,char **priv_user,
-		  char *priv_host, bool old_ver, USER_RESOURCES  *mqh,
-		  char *prepared_scramble, uint *cur_priv_version,
-		  ACL_USER **cached_user)
+int acl_getroot(THD *thd, USER_RESOURCES  *mqh,
+                const char *passwd, uint passwd_len)
 {
-  ulong user_access=NO_ACCESS;
-  *priv_user= (char*) user;
-  bool password_correct= 0;
-  int stage= (*cached_user != NULL); /* NULL passed as first stage */
-  ACL_USER *acl_user= NULL;
   DBUG_ENTER("acl_getroot");
 
-  bzero(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) == 0)
+          {
+            acl_user= user_i;
+            res= 0;
+          }
         }
+        else if (passwd_len == SCRAMBLE_LENGTH &&
+                 user_i->salt_len == SCRAMBLE_LENGTH_323)
+          res= -1;
+        else if (passwd_len == SCRAMBLE_LENGTH_323 &&
+                 user_i->salt_len == SCRAMBLE_LENGTH)
+          res= 2;
+        /* linear search complete: */
+        break;
       }
     }
   }
-
-  /* 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 */
+    thd->priv_user= acl_user->user ? thd->user : (char *) "";
+    *mqh= acl_user->user_resource;
 
-  if (acl_user->host.hostname)
-    strmake(priv_host, acl_user->host.hostname, MAX_HOSTNAME);
-  else
-    *priv_host= 0;
-
-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 +733,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 +771,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 +782,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 +793,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 +802,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 +1140,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 +1148,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 +1204,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 +1274,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 +1298,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 +1346,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 +1388,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 +1498,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 +1507,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 +2910,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 e6c6771253c42184aa7d05537c9da138786de3b5..b4ee1a9b15f0997dddc1421937f9d9ecb7d35ede 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 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 c233ffd422a90e6ef4b57ba86f7bf91c1bfeaa6a..ebb3e819ddcadfd7abf86e79f5d157e9204349de 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 f6336cb7dd99544d7f45659ad0e3b83e49c7619e..e80b5d9c8d5691805846e23037266e70fecfea86 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -386,6 +386,7 @@ struct system_variables
   my_bool log_warnings;
   my_bool low_priority_updates;
   my_bool new_mode;
+  my_bool old_passwords;
   
   CHARSET_INFO	*character_set_server;
   CHARSET_INFO	*character_set_database;
@@ -544,10 +545,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 9e670a97e92dde21e049aef47016d82ceaf5f202..7878f2c8a29914b3ef1d93a050cab84c7761d352 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -178,152 +178,178 @@ 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 handshake error; error is sent to client;
+   >0  error, not sent to client
 */
 
-static int check_user(THD *thd,enum_server_command command, const char *user,
-		       const char *passwd, const char *db, bool check_count,
-                       bool simple_connect, bool do_send_error, 
-                       char *prepared_scramble, bool had_password,
-                       uint *cur_priv_version, ACL_USER** hint_user)
+static int check_user(THD *thd, enum enum_server_command command, 
+                      const char *passwd, uint passwd_len, const char *db,
+                      bool check_count)
 {
-  thd->db=0;
-  thd->db_length=0;
-  USER_RESOURCES ur;
-  char tmp_passwd[SCRAMBLE41_LENGTH];
   DBUG_ENTER("check_user");
   
+  my_bool opt_secure_auth_local;
+  pthread_mutex_lock(&LOCK_global_system_variables);
+  opt_secure_auth_local= opt_secure_auth;
+  pthread_mutex_unlock(&LOCK_global_system_variables);
+  
   /*
-    Move password to temporary buffer as it may be stored in communication
-    buffer
+    If the server is running in secure auth mode, short scrambles are 
+    forbidden.
   */
-  strmake(tmp_passwd, passwd, sizeof(tmp_passwd));
-  passwd= tmp_passwd;				// Use local copy
-
-  /* We shall avoid dupplicate user allocations here */
-  if (!thd->user && !(thd->user = my_strdup(user, MYF(0))))
+  if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
   {
-    send_error(thd,ER_OUT_OF_RESOURCES);
-    DBUG_RETURN(1);
+    net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
+    mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+    DBUG_RETURN(-1);
   }
-  thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user,
-				 passwd, thd->scramble,
-				 &thd->priv_user, thd->priv_host,
-				 (protocol_version == 9 ||
-				  !(thd->client_capabilities &
-				    CLIENT_LONG_PASSWORD)),
-				 &ur,prepared_scramble,
-				 cur_priv_version,hint_user);
-
-  DBUG_PRINT("info",
-	     ("Capabilities: %d  packet_length: %ld  Host: '%s'  Login user: '%s'  Priv_user: '%s'  Using password: %s  Access: %u  db: '%s'",
-	      thd->client_capabilities, thd->max_client_packet_length,
-	      thd->host_or_ip, thd->user, thd->priv_user,
-	      had_password ? "yes": "no",
-	      thd->master_access, thd->db ? thd->db : "*none*"));
+  if (passwd_len != 0 &&
+      passwd_len != SCRAMBLE_LENGTH &&
+      passwd_len != SCRAMBLE_LENGTH_323)
+    DBUG_RETURN(ER_HANDSHAKE_ERROR);
 
   /*
-    In case we're going to retry we should not send error message at this
-    point
+    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;
+  
+  char buff[NAME_LEN + 1]; /* to conditionally save db */
+  
+  USER_RESOURCES ur;
+  int res= acl_getroot(thd, &ur, passwd, passwd_len);
+  if (res == -1)
   {
-    if (do_send_error || !had_password || !*hint_user)
+    /*
+      This happens when client (new) sends password scrambled with
+      scramble(), but database holds old value (scrambled with
+      scramble_323()). Here we please client to send scrambled_password
+      in old format.
+    */
+    if (opt_secure_auth_local)
     {
-      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));
-      }
-      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
+      net_printf(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
+                 thd->user, thd->host_or_ip);
+      mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
+                      thd->user, thd->host_or_ip);
+      DBUG_RETURN(-1);
     }
-    DBUG_PRINT("info",("Prepare for second part of handshake"));
-    DBUG_RETURN(-1);			// no report error in special handshake
+    /* 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);
+      DBUG_RETURN(ER_HANDSHAKE_ERROR);
+    }
+    /* Final attempt to check the user based on reply */
+    /* So as passwd is short, errcode is always >= 0 */
+    res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
   }
-
-  if (check_count)
+  /* here res is always >= 0 */
+  if (res == 0)
   {
-    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);
-      DBUG_RETURN(1);
+    if (!(thd->master_access & NO_ACCESS)) // authentification is OK 
+    {
+      DBUG_PRINT("info",
+                 ("Capabilities: %d  packet_length: %ld  Host: '%s'  "
+                  "Login user: '%s' Priv_user: '%s'  Using password: %s "
+                  "Access: %u  db: '%s'",
+                  thd->client_capabilities, thd->max_client_packet_length,
+                  thd->host_or_ip, thd->user, thd->priv_user,
+                  passwd_len ? "yes": "no",
+                  thd->master_access, thd->db ? thd->db : "*none*"));
+
+      if (check_count)
+      {
+        VOID(pthread_mutex_lock(&LOCK_thread_count));
+        bool 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);
+        }
+      }
+
+      /* 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 */
+      DBUG_RETURN(0);
     }
   }
-  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 == 2) // client gave short hash, server has long hash
   {
-    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_NOT_SUPPORTED_AUTH_MODE);
+    mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
+    DBUG_RETURN(-1);
+  }
+  net_printf(thd, ER_ACCESS_DENIED_ERROR,
+             thd->user,
+             thd->host_or_ip,
+             passwd_len ? ER(ER_YES) : ER(ER_NO));
+  mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
+                  thd->user,
+                  thd->host_or_ip,
+                  passwd_len ? ER(ER_YES) : ER(ER_NO));
+  DBUG_RETURN(-1);
 }
 
-
 /*
   Check for maximum allowable user connections, if the mysqld server is
   started with corresponding variable that is greater then 0.
@@ -525,48 +551,39 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
 
 
 /*
-  Check connnectionn and get priviliges
-
+    Perform handshake, authorize client and update thd ACL variables.
   SYNOPSIS
-    check_connections
-    thd		Thread handle
-
+    check_connection()
+    thd  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 +612,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 +635,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 the 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 +737,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 +754,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 +763,25 @@ check_connections(THD *thd)
     net->return_status= &thd->server_status;
   net->read_timeout=(uint) thd->variables.net_read_timeout;
 
-  /* Simple connect only for old clients. New clients always use secure auth */
-  bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
-
-  /* Check user permissions. If password failure we'll get scramble back */
-  if ((res=check_user(thd, COM_CONNECT, user, passwd, db, 1, simple_connect,
-		      simple_connect, prepared_scramble, using_password,
-		      &cur_priv_version,
-		      &cached_user)) < 0)
-  {
-    /* Store current used and database as they are erased with next packet */
-    char tmp_user[USERNAME_LENGTH+1];
-    char tmp_db[NAME_LEN+1];
-
-    /* If the client is old we just have to return error */
-    if (simple_connect)
-      return -1;
-
-    DBUG_PRINT("info",("password challenge"));
-
-    tmp_user[0]= tmp_db[0]= 0;
-    if (user)
-      strmake(tmp_user,user,USERNAME_LENGTH);
-    if (db)
-      strmake(tmp_db,db,NAME_LEN);
+  char *user= end;
+  char *passwd= strend(user)+1;
+  char *db= passwd;
+  /* 
+    Old clients send null-terminated string as password; new clients send
+    the size (1 byte) + string (not null-terminated). Hence in case of empty
+    password both send '\0'.
+  */
+  uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? 
+    *passwd++ : strlen(passwd);
+  db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
+    db + passwd_len + 1 : 0;
 
-    /* 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 check_user(thd, COM_CONNECT, passwd, passwd_len, db, true);
 }
 
 
@@ -847,7 +838,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);
@@ -1157,116 +1148,67 @@ 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;
+    /* 
+      Old clients send null-terminated string ('\0' for empty string) for
+      password.  New clients send the size (1 byte) + string (not null
+      terminated, so also '\0' for empty string).
+    */
+    char *db= passwd;
+    uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? 
+      *passwd++ : strlen(passwd);
+    db+= passwd_len + 1;
 
-    /* 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= check_user(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 */
@@ -3168,7 +3110,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 e3d600b979825ca21e88ccf56db9ec5a6a85ee00..b53551845bccfed7a9df603f0f8ba4f43c45047c 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 0bb20b740161e7f98c1d0f7aa6f3ea2ae2daf6f3..9bbd2ae123541c477e9b33e56f69992f6689649e 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -499,6 +499,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
 %token  MULTIPOINT
 %token  MULTIPOLYGON
 %token	NOW_SYM
+%token	OLD_PASSWORD
 %token	PASSWORD
 %token	POINTFROMTEXT
 %token	POINT_SYM
@@ -2538,9 +2539,13 @@ 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); }
+	  {
+	    $$= YYTHD->variables.old_passwords ?
+              (Item *) new Item_func_old_password($3) :
+	      (Item *) new Item_func_password($3);
+	  }
+	| OLD_PASSWORD '(' expr ')'
+	  { $$=  new Item_func_old_password($3); }
 	| POINT_SYM '(' expr ',' expr ')'
 	  { $$= new Item_func_point($3,$5); }
  	| POINTFROMTEXT '(' expr ')'
@@ -4456,6 +4461,7 @@ keyword:
 	| NO_SYM		{}
 	| NONE_SYM		{}
 	| OFFSET_SYM		{}
+	| OLD_PASSWORD		{}
 	| OPEN_SYM		{}
 	| PACK_KEYS_SYM		{}
 	| PARTIAL		{}
@@ -4669,15 +4675,15 @@ text_or_password:
 	TEXT_STRING { $$=$1.str;}
 	| PASSWORD '(' TEXT_STRING ')'
 	  {
-	    if (!$3.length)
-	      $$=$3.str;
-	    else
-	    {
-	      char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1);
-	      make_scrambled_password(buff,$3.str,use_old_passwords,
-				      &YYTHD->rand);
-	      $$=buff;
-	    }
+	    $$= $3.length ? YYTHD->variables.old_passwords ?
+	        Item_func_old_password::alloc(YYTHD, $3.str) :
+	        Item_func_password::alloc(YYTHD, $3.str) :
+	      $3.str;
+	  }
+	| OLD_PASSWORD '(' TEXT_STRING ')'
+	  {
+	    $$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str) :
+	      $3.str;
 	  }
           ;
 
@@ -4985,14 +4991,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 (YYTHD->variables.old_passwords)
+             {
+               char *buff= 
+                 (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
+               if (buff)
+                 make_scrambled_password_323(buff, $4.str);
+               $1->password.str= buff;
+               $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+             }
+             else
+             {
+               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