From 1f443c95d993a4470d23aecb3df15da32adcffa4 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 2 Oct 2002 23:43:27 +0400 Subject: [PATCH 1/4] Changeset to deal with 4.1 -> 4.0 merge include/mysql_com.h: Partial changes for new secure connection handing required to pull 4.0 -> 4.1 merge sql/item_strfunc.cc: Handling of dynamic possible length for password sql/item_strfunc.h: Larger buffer for password sql/mysqld.cc: Added option to force old password format sql/password.c: Begin implementing new hashing BitKeeper/etc/logging_ok: Logging to logging@openlogging.org accepted --- BitKeeper/etc/logging_ok | 1 + include/mysql_com.h | 1 + sql/item_strfunc.cc | 4 ++- sql/item_strfunc.h | 4 +-- sql/mysqld.cc | 9 ++++-- sql/password.c | 63 +++++++++++++++++++++++++++++++++++++--- 6 files changed, 73 insertions(+), 9 deletions(-) diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 31c8d0a3253..9f6edf9eb32 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -74,3 +74,4 @@ WAX@sergbook.mysql.com bar@bar.udmsearch.izhnet.ru nick@mysql.com bar@bar.mysql.r18.ru +peter@mysql.com diff --git a/include/mysql_com.h b/include/mysql_com.h index c259259c9e2..58c1bb062ef 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -268,6 +268,7 @@ void randominit(struct rand_struct *,unsigned long seed1, unsigned long seed2); double rnd(struct rand_struct *); void make_scrambled_password(char *to,const char *password); +uint get_password_length(); void get_salt_from_password(unsigned long *res,const char *password); void make_password_from_salt(char *to, unsigned long *hash_res); char *scramble(char *to,const char *message,const char *password, diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 243f11db106..917396d313f 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1155,7 +1155,9 @@ String *Item_func_password::val_str(String *str) if (res->length() == 0) return &empty_string; make_scrambled_password(tmp_value,res->c_ptr()); - str->set(tmp_value,16,res->charset()); + printf("Returned to the function\n"); + str->set(tmp_value,get_password_length(),res->charset()); + printf("At the exit\n"); return str; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index af94e387717..90b843f5fd7 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -224,11 +224,11 @@ public: class Item_func_password :public Item_str_func { - char tmp_value[17]; + char tmp_value[64]; /* This should be enough for new password format */ public: Item_func_password(Item *a) :Item_str_func(a) {} String *val_str(String *); - void fix_length_and_dec() { max_length = 16; } + void fix_length_and_dec() { max_length = get_password_length(); } const char *func_name() const { return "password"; } }; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 59630644e5a..30e20e79f92 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -379,6 +379,8 @@ const char *sql_mode_str="OFF"; const char *default_tx_isolation_name; enum_tx_isolation default_tx_isolation=ISO_READ_COMMITTED; +bool old_passwords=0; // force using old passwords when creating users + uint rpl_recovery_rank=0; my_string mysql_unix_port=NULL, mysql_tmpdir=NULL, allocated_mysql_tmpdir=NULL; @@ -2819,7 +2821,8 @@ enum options { OPT_INNODB_FORCE_RECOVERY, OPT_BDB_CACHE_SIZE, OPT_BDB_LOG_BUFFER_SIZE, - OPT_BDB_MAX_LOCK + OPT_BDB_MAX_LOCK, + OPT_OLD_PASSWORDS }; @@ -3535,7 +3538,9 @@ static struct my_option my_long_options[] = "The number of seconds the server waits for activity on a connection before closing it", (gptr*) &net_wait_timeout, (gptr*) &net_wait_timeout, 0, GET_ULONG, REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, - {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} + {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"old_passwords",OPT_OLD_PASSWORDS,"Use old password hash functions then storing passwords", + (gptr*) &old_passwords, (gptr*) &old_passwords, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0} }; struct show_var_st init_vars[]= { diff --git a/sql/password.c b/sql/password.c index 48181ea18e6..f3dc950f03f 100644 --- a/sql/password.c +++ b/sql/password.c @@ -37,9 +37,20 @@ #include #include #include +/* To be replaced by SHA1 as Monty will do the Merge */ +#include #include "mysql.h" + +/* Character to use as version identifier for version 4.1 */ +#define PVERSION41_CHAR '*' + + +extern uint old_passwords; /* If prior 4.1 functions to be used */ + + + void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2) { /* For mysql 3.21.# */ #ifdef HAVE_purify @@ -84,13 +95,57 @@ void hash_password(ulong *result, const char *password) return; } + void make_scrambled_password(char *to,const char *password) -{ - ulong hash_res[2]; - hash_password(hash_res,password); - sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); +{ + ulong hash_res[2]; /* Used for pre 4.1 password hashing */ + static uint salt=0; /* Salt for 4.1 version password */ + unsigned char* slt=(unsigned char*)&salt; + my_MD5_CTX context; + unsigned char digest[16]; + if (old_passwords) /* 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 */ + { + to[0]=PVERSION41_CHAR; /* New passwords have version prefix */ + /* We do not need too strong salt generation so this should be enough */ + salt+=getpid()+time(NULL)+0x01010101; + /* Use only 2 first bytes from it */ + sprintf(&(to[1]),"%02x%02x",slt[0],slt[1]); + /* Waiting for Monty to do the merge */ + my_MD5Init(&context); + /* Use Salt for Hash */ + my_MD5Update(&context,(unsigned char*)&salt,2); + + for (; *password ; password++) + { + if (*password == ' ' || *password == '\t') + continue;/* skip space in password */ + my_MD5Update(&context,(unsigned char*)&password[0],1); + } + my_MD5Final(digest,&context); + /* 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", + 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]); */ + sprintf(&to[5],"1234567890123456789012345"); + + } } +uint get_password_length() +{ + if (old_passwords) + return 16; + else return 37; +} + + inline uint char_val(char X) { return (uint) (X >= '0' && X <= '9' ? X-'0' : From a370f86622f98fa33e7085878522ff181e7effba Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 3 Oct 2002 22:42:13 +0400 Subject: [PATCH 2/4] One more commit required to merge second pack of Monty's patches sql/item_strfunc.cc: Remove some debug sql/mysql_priv.h: New password has larger length sql/mysqld.cc: New option name to be consistent sql/password.c: Clean up hash generation --- sql/item_strfunc.cc | 2 -- sql/mysql_priv.h | 3 ++- sql/mysqld.cc | 8 ++++++-- sql/password.c | 31 ++++++++++++++----------------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 7d4715d507b..da15365c236 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1267,9 +1267,7 @@ String *Item_func_password::val_str(String *str) if (res->length() == 0) return &empty_string; make_scrambled_password(tmp_value,res->c_ptr()); - printf("Returned to the function\n"); str->set(tmp_value,get_password_length(),res->charset()); - printf("At the exit\n"); return str; } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 4532646b1c2..6aa2181a88c 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -66,7 +66,8 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); ****************************************************************************/ #define ACL_CACHE_SIZE 256 -#define HASH_PASSWORD_LENGTH 16 +/* Password lengh for 4.1 version previous versions had 16 bytes password hash */ +#define HASH_PASSWORD_LENGTH 45 #define HOST_CACHE_SIZE 128 #define MAX_ACCEPT_RETRY 10 // Test accept this many times #define MAX_FIELDS_BEFORE_HASH 32 diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8c3db86e44f..c7b01d7dc54 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -278,7 +278,7 @@ static my_bool opt_noacl=0, opt_bootstrap=0, opt_myisam_log=0; my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool opt_safe_show_db=0, 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; +my_bool opt_log_slave_updates= 0, opt_old_passwords=0; volatile bool mqh_used = 0; FILE *bootstrap_file=0; @@ -396,6 +396,7 @@ const char *myisam_recover_options_str="OFF"; const char *sql_mode_str="OFF"; ulong rpl_recovery_rank=0; + my_string mysql_unix_port=NULL, opt_mysql_tmpdir=NULL, mysql_tmpdir=NULL; ulong my_bind_addr; /* the address we bind to */ DATE_FORMAT dayord; @@ -2914,7 +2915,8 @@ enum options { OPT_INNODB_FORCE_RECOVERY, OPT_BDB_CACHE_SIZE, OPT_BDB_LOG_BUFFER_SIZE, - OPT_BDB_MAX_LOCK + OPT_BDB_MAX_LOCK, + OPT_OLD_PASSWORDS }; @@ -3245,6 +3247,8 @@ struct my_option my_long_options[] = 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 old 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, "Depricated option; One should use GRANT SHOW DATABASES instead...", diff --git a/sql/password.c b/sql/password.c index f3dc950f03f..1875bb0ef04 100644 --- a/sql/password.c +++ b/sql/password.c @@ -37,8 +37,7 @@ #include #include #include -/* To be replaced by SHA1 as Monty will do the Merge */ -#include +#include #include "mysql.h" @@ -47,7 +46,7 @@ #define PVERSION41_CHAR '*' -extern uint old_passwords; /* If prior 4.1 functions to be used */ +extern my_bool opt_old_passwords; /* If prior 4.1 functions to be used */ @@ -101,9 +100,9 @@ void make_scrambled_password(char *to,const char *password) ulong hash_res[2]; /* Used for pre 4.1 password hashing */ static uint salt=0; /* Salt for 4.1 version password */ unsigned char* slt=(unsigned char*)&salt; - my_MD5_CTX context; - unsigned char digest[16]; - if (old_passwords) /* Pre 4.1 password encryption */ + SHA1_CONTEXT context; + uint8 digest[SHA1_HASH_SIZE]; + if (opt_old_passwords) /* Pre 4.1 password encryption */ { hash_password(hash_res,password); sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); @@ -115,34 +114,32 @@ void make_scrambled_password(char *to,const char *password) salt+=getpid()+time(NULL)+0x01010101; /* Use only 2 first bytes from it */ sprintf(&(to[1]),"%02x%02x",slt[0],slt[1]); - /* Waiting for Monty to do the merge */ - my_MD5Init(&context); + sha1_reset(&context); /* Use Salt for Hash */ - my_MD5Update(&context,(unsigned char*)&salt,2); + sha1_input(&context,(uint8*)&salt,2); for (; *password ; password++) { if (*password == ' ' || *password == '\t') continue;/* skip space in password */ - my_MD5Update(&context,(unsigned char*)&password[0],1); + sha1_input(&context,(int8*)&password[0],1); } - my_MD5Final(digest,&context); + sha1_result(&context,digest); /* 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", + 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]); */ - sprintf(&to[5],"1234567890123456789012345"); + digest[14],digest[15],digest[16],digest[17],digest[18],digest[19]); } } uint get_password_length() { - if (old_passwords) + if (opt_old_passwords) return 16; - else return 37; + else return SHA1_HASH_SIZE*2+4+1; } From 8640cd354413508b698817dce787827c370e7b9a Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 6 Oct 2002 13:42:16 +0400 Subject: [PATCH 3/4] One more commit to do the merge of new 4.1 tree client/mysqladmin.c: Handle new password.c prototypes include/mysql_com.h: New prototypes libmysql/Makefile.shared: Client need sha1 compiled in now libmysql/password.c: Replace copy with symlink mysql-test/r/func_crypt.result: Fix test results to handle new password function mysql-test/t/func_crypt.test: New tests for new password function sql/item_create.cc: add old_password() function sql/item_create.h: add old_password() function sql/item_strfunc.cc: add old_password() function changes to new password.c functions prototypes sql/item_strfunc.h: add old_password function handle new prototypes sql/lex.h: add OLD_PASSWORD function sql/password.c: Continue to work on new password handling sql/sql_acl.cc: Handle new passwords sql/sql_yacc.yy: Changes for new prototypes --- client/mysqladmin.c | 2 +- include/mysql_com.h | 5 +- libmysql/Makefile.shared | 2 +- libmysql/password.c | 191 --------------------------------- mysql-test/r/func_crypt.result | 6 +- mysql-test/t/func_crypt.test | 2 +- sql/item_create.cc | 7 ++ sql/item_create.h | 1 + sql/item_strfunc.cc | 18 +++- sql/item_strfunc.h | 18 +++- sql/lex.h | 1 + sql/password.c | 47 ++++++-- sql/sql_acl.cc | 17 ++- sql/sql_yacc.yy | 4 +- 14 files changed, 101 insertions(+), 220 deletions(-) mode change 100644 => 100755 libmysql/password.c diff --git a/client/mysqladmin.c b/client/mysqladmin.c index 01e36509c30..81e7e7c05de 100644 --- a/client/mysqladmin.c +++ b/client/mysqladmin.c @@ -731,7 +731,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) return 1; } if (argv[1][0]) - make_scrambled_password(crypted_pw,argv[1]); + make_scrambled_password(crypted_pw,argv[1],0); /* New passwords only */ else crypted_pw[0]=0; /* No password */ sprintf(buff,"set password='%s',sql_log_off=0",crypted_pw); diff --git a/include/mysql_com.h b/include/mysql_com.h index 5ed65e993d9..a529dbc127b 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -274,8 +274,9 @@ extern unsigned long net_buffer_length; void randominit(struct rand_struct *,unsigned long seed1, unsigned long seed2); double rnd(struct rand_struct *); -void make_scrambled_password(char *to,const char *password); -uint get_password_length(); +void make_scrambled_password(char *to,const char *password,my_bool force_old_scramble); +uint get_password_length(my_bool force_old_scramble); +uint8 get_password_version(const char* password); void get_salt_from_password(unsigned long *res,const char *password); void make_password_from_salt(char *to, unsigned long *hash_res); char *scramble(char *to,const char *message,const char *password, diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared index 0f7cb713d54..76ec06c8c51 100644 --- a/libmysql/Makefile.shared +++ b/libmysql/Makefile.shared @@ -60,7 +60,7 @@ mysysobjects1 = my_init.lo my_static.lo my_malloc.lo my_realloc.lo \ my_compress.lo array.lo my_once.lo list.lo my_net.lo \ charset.lo hash.lo mf_iocache.lo \ mf_iocache2.lo my_seek.lo \ - my_pread.lo mf_cache.lo my_vsnprintf.lo md5.lo \ + my_pread.lo mf_cache.lo my_vsnprintf.lo md5.lo sha1.lo\ my_getopt.lo my_gethostbyname.lo my_port.lo sqlobjects = net.lo diff --git a/libmysql/password.c b/libmysql/password.c old mode 100644 new mode 100755 index 9b154603b98..e69de29bb2d --- a/libmysql/password.c +++ b/libmysql/password.c @@ -1,191 +0,0 @@ -/* Copyright (C) 2000 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* password checking routines */ -/***************************************************************************** - The main idea is that no password are sent between client & server on - connection and that no password are saved in mysql in a decodable form. - - On connection a random string is generated and sent to the client. - The client generates a new string with a random generator inited with - the hash values from the password and the sent string. - This 'check' string is sent to the server where it is compared with - a string generated from the stored hash_value of the password and the - random string. - - The password is saved (in user.password) by using the PASSWORD() function in - mysql. - - Example: - update user set password=PASSWORD("hello") where user="test" - This saves a hashed number as a string in the password field. -*****************************************************************************/ - -#include -#include -#include -#include "mysql.h" - - -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 */ -#endif - rand_st->max_value= 0x3FFFFFFFL; - rand_st->max_value_dbl=(double) rand_st->max_value; - rand_st->seed1=seed1%rand_st->max_value ; - rand_st->seed2=seed2%rand_st->max_value; -} - -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; -} - -double rnd(struct rand_struct *rand_st) -{ - rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value; - rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value; - return (((double) rand_st->seed1)/rand_st->max_value_dbl); -} - -void hash_password(ulong *result, const char *password) -{ - register ulong nr=1345345333L, add=7, nr2=0x12345671L; - ulong tmp; - for (; *password ; password++) - { - if (*password == ' ' || *password == '\t') - continue; /* skipp space in password */ - tmp= (ulong) (uchar) *password; - nr^= (((nr & 63)+add)*tmp)+ (nr << 8); - nr2+=(nr2 << 8) ^ nr; - add+=tmp; - } - result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */; - result[1]=nr2 & (((ulong) 1L << 31) -1L); - return; -} - -void make_scrambled_password(char *to,const char *password) -{ - ulong hash_res[2]; - hash_password(hash_res,password); - sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); -} - -static inline uint char_val(char X) -{ - return (uint) (X >= '0' && X <= '9' ? X-'0' : - X >= 'A' && X <= 'Z' ? X-'A'+10 : - X-'a'+10); -} - -/* -** This code assumes that len(password) is divideable with 8 and that -** res is big enough (2 in mysql) -*/ - -void get_salt_from_password(ulong *res,const char *password) -{ - res[0]=res[1]=0; - if (password) - { - while (*password) - { - ulong val=0; - uint i; - for (i=0 ; i < 8 ; i++) - val=(val << 4)+char_val(*password++); - *res++=val; - } - } - return; -} - -void make_password_from_salt(char *to, ulong *hash_res) -{ - sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); -} - - -/* - * Genererate a new message based on message and password - * The same thing is done in client and server and the results are checked. - */ - -char *scramble(char *to,const char *message,const char *password, - my_bool old_ver) -{ - struct rand_struct rand_st; - ulong hash_pass[2],hash_message[2]; - if (password && password[0]) - { - char *to_start=to; - hash_password(hash_pass,password); - hash_password(hash_message,message); - if (old_ver) - old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]); - else - randominit(&rand_st,hash_pass[0] ^ hash_message[0], - hash_pass[1] ^ hash_message[1]); - while (*message++) - *to++= (char) (floor(rnd(&rand_st)*31)+64); - if (!old_ver) - { /* Make it harder to break */ - char extra=(char) (floor(rnd(&rand_st)*31)); - while (to_start != to) - *(to_start++)^=extra; - } - } - *to=0; - return to; -} - - -my_bool check_scramble(const char *scrambled, const char *message, - ulong *hash_pass, my_bool old_ver) -{ - struct rand_struct rand_st; - ulong hash_message[2]; - char buff[16],*to,extra; /* Big enough for check */ - const char *pos; - - hash_password(hash_message,message); - if (old_ver) - old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]); - else - randominit(&rand_st,hash_pass[0] ^ hash_message[0], - hash_pass[1] ^ hash_message[1]); - to=buff; - for (pos=scrambled ; *pos ; pos++) - *to++=(char) (floor(rnd(&rand_st)*31)+64); - if (old_ver) - extra=0; - else - extra=(char) (floor(rnd(&rand_st)*31)); - to=buff; - while (*scrambled) - { - if (*scrambled++ != (char) (*to++ ^ extra)) - return 1; /* Wrong password */ - } - return 0; -} diff --git a/mysql-test/r/func_crypt.result b/mysql-test/r/func_crypt.result index cf464ec21b2..ad3a64ccd1d 100644 --- a/mysql-test/r/func_crypt.result +++ b/mysql-test/r/func_crypt.result @@ -1,6 +1,6 @@ select length(encrypt('foo', 'ff')) <> 0; length(encrypt('foo', 'ff')) <> 0 1 -select password('test'),length(encrypt('test')),encrypt('test','aa'); -password('test') length(encrypt('test')) encrypt('test','aa') -378b243e220ca493 13 aaqPiZY5xR5l. +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. diff --git a/mysql-test/t/func_crypt.test b/mysql-test/t/func_crypt.test index f01504d3691..812bdade39f 100644 --- a/mysql-test/t/func_crypt.test +++ b/mysql-test/t/func_crypt.test @@ -1,3 +1,3 @@ select length(encrypt('foo', 'ff')) <> 0; --replace_result $1$aa$4OSUA5cjdx0RUQ08opV27/ aaqPiZY5xR5l. -select password('test'),length(encrypt('test')),encrypt('test','aa'); +select old_password('test'),length(password("1")),length(encrypt('test')),encrypt('test','aa'); diff --git a/sql/item_create.cc b/sql/item_create.cc index e4c9a160686..838396d9b15 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -52,6 +52,13 @@ 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); diff --git a/sql/item_create.h b/sql/item_create.h index 6d9cef04c13..937ea782273 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -66,6 +66,7 @@ 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); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 24320d98576..198ab42558d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1273,11 +1273,25 @@ String *Item_func_password::val_str(String *str) return 0; if (res->length() == 0) return &empty_string; - make_scrambled_password(tmp_value,res->c_ptr()); - str->set(tmp_value,get_password_length(),res->charset()); + make_scrambled_password(tmp_value,res->c_ptr(),opt_old_passwords); + str->set(tmp_value,get_password_length(opt_old_passwords),res->charset()); return str; } +String *Item_func_old_password::val_str(String *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); + str->set(tmp_value,16,res->charset()); + return str; +} + + + #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') String *Item_func_encrypt::val_str(String *str) diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index d416f09d458..616c9b6a11f 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -21,6 +21,9 @@ #pragma interface /* gcc class implementation */ #endif +extern my_bool opt_old_passwords; /* Need this variable for some functions */ + + class Item_str_func :public Item_func { public: @@ -247,10 +250,23 @@ class Item_func_password :public Item_str_func public: Item_func_password(Item *a) :Item_str_func(a) {} String *val_str(String *); - void fix_length_and_dec() { max_length = get_password_length(); } + void fix_length_and_dec() { max_length = get_password_length(opt_old_passwords); } const char *func_name() const { return "password"; } }; + +class Item_func_old_password :public Item_str_func +{ + char tmp_value[16]; /* old password length */ +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); } + const char *func_name() const { return "old_password"; } +}; + + + class Item_func_des_encrypt :public Item_str_func { String tmp_value; diff --git a/sql/lex.h b/sql/lex.h index 6e36e01fbe7..a6bb4605aa5 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -513,6 +513,7 @@ 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/password.c b/sql/password.c index 1875bb0ef04..ba7dc17c671 100644 --- a/sql/password.c +++ b/sql/password.c @@ -46,7 +46,6 @@ #define PVERSION41_CHAR '*' -extern my_bool opt_old_passwords; /* If prior 4.1 functions to be used */ @@ -95,14 +94,17 @@ void hash_password(ulong *result, const char *password) } -void make_scrambled_password(char *to,const char *password) + + + +void make_scrambled_password(char *to,const char *password,my_bool force_old_scramble) { ulong hash_res[2]; /* Used for pre 4.1 password hashing */ static uint salt=0; /* Salt for 4.1 version password */ unsigned char* slt=(unsigned char*)&salt; SHA1_CONTEXT context; uint8 digest[SHA1_HASH_SIZE]; - if (opt_old_passwords) /* Pre 4.1 password encryption */ + if (force_old_scramble) /* Pre 4.1 password encryption */ { hash_password(hash_res,password); sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); @@ -125,23 +127,33 @@ void make_scrambled_password(char *to,const char *password) sha1_input(&context,(int8*)&password[0],1); } sha1_result(&context,digest); + /* Hash one more time */ + sha1_reset(&context); + sha1_input(&context,digest,SHA1_HASH_SIZE); + sha1_result(&context,digest); /* Print resulting hash into the password*/ 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]); - + digest[14],digest[15],digest[16],digest[17],digest[18],digest[19]); } } -uint get_password_length() +uint get_password_length(my_bool force_old_scramble) { - if (opt_old_passwords) + if (force_old_scramble) return 16; else return SHA1_HASH_SIZE*2+4+1; } +uint8 get_password_version(const char* password) +{ + if (password==NULL) return 0; + if (password[0]==PVERSION41_CHAR) return PVERSION41_CHAR; + return 0; +} + inline uint char_val(char X) { @@ -151,15 +163,27 @@ inline uint char_val(char X) } /* -** This code assumes that len(password) is divideable with 8 and that -** res is big enough (2 in mysql) +** This code detects new version password by leading char. +** Old password has to be divisible by 8 length +** do not forget to increase array length if you need longer passwords */ void get_salt_from_password(ulong *res,const char *password) { - res[0]=res[1]=0; + bzero(res,5*sizeof(res[0])); if (password) { + 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; + } while (*password) { ulong val=0; @@ -167,13 +191,14 @@ void get_salt_from_password(ulong *res,const char *password) for (i=0 ; i < 8 ; i++) val=(val << 4)+char_val(*password++); *res++=val; - } + } } return; } void make_password_from_salt(char *to, ulong *hash_res) { + // warning this does not work for new passwords yet sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 5f8cf42c2bf..2012b565dae 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -63,7 +63,8 @@ public: uint hostname_length; USER_RESOURCES user_resource; char *user,*password; - ulong salt[2]; + ulong salt[6]; // New password has longer length + uint8 pversion; // password version enum SSL_type ssl_type; const char *ssl_cipher, *x509_issuer, *x509_subject; }; @@ -231,7 +232,7 @@ my_bool acl_init(bool dont_read_acl_tables) "Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)", user.user ? user.user : ""); /* purecov: tested */ } - else if (length % 8) // This holds true for passwords + else if (length % 8 && length!=45) // This holds true for passwords { sql_print_error( "Found invalid password for user: '%s@%s'; Ignoring user", @@ -240,6 +241,7 @@ my_bool acl_init(bool dont_read_acl_tables) continue; /* purecov: tested */ } get_salt_from_password(user.salt,user.password); + user.pversion=get_password_version(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 ? @@ -671,6 +673,7 @@ static void acl_update_user(const char *user, const char *host, { acl_user->password=(char*) ""; // Just point at something get_salt_from_password(acl_user->salt,password); + acl_user->pversion=get_password_version(acl_user->password); } } break; @@ -706,6 +709,7 @@ static void acl_insert_user(const char *user, const char *host, { acl_user.password=(char*) ""; // Just point at something get_salt_from_password(acl_user.salt,password); + acl_user.pversion=get_password_version(acl_user.password); } VOID(push_dynamic(&acl_users,(gptr) &acl_user)); @@ -1052,9 +1056,11 @@ 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 or 16 chars; simple hack to avoid cracking */ + /* password should always be 0,16 or 45 chars; simple hack to avoid cracking */ length=(uint) strlen(new_password); - new_password[length & 16]=0; + + if (length!=45) + new_password[length & 16]=0; VOID(pthread_mutex_lock(&acl_cache->lock)); ACL_USER *acl_user; @@ -1074,6 +1080,7 @@ bool change_password(THD *thd, const char *host, const char *user, 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 @@ -1081,7 +1088,7 @@ bool change_password(THD *thd, const char *host, const char *user, acl_cache->clear(1); // Clear locked hostname cache VOID(pthread_mutex_unlock(&acl_cache->lock)); - char buff[460]; + char buff[512]; /* Extend with extended password length*/ ulong query_length= my_sprintf(buff, (buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"", diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index dbc401b3ddc..2cb0904c3b5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3592,7 +3592,7 @@ text_or_password: else { char *buff=(char*) sql_alloc(HASH_PASSWORD_LENGTH+1); - make_scrambled_password(buff,$3.str); + make_scrambled_password(buff,$3.str,opt_old_passwords); $$=buff; } }; @@ -3872,7 +3872,7 @@ grant_user: char *buff=(char*) sql_alloc(HASH_PASSWORD_LENGTH+1); if (buff) { - make_scrambled_password(buff,$4.str); + make_scrambled_password(buff,$4.str,opt_old_passwords); $1->password.str=buff; $1->password.length=HASH_PASSWORD_LENGTH; } From 48edd0acae1154fce882e68a370fa630b1528390 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 19 Oct 2002 23:18:31 +0400 Subject: [PATCH 4/4] More work on secure connection protocol. commited in order to do the sync with new large pushed bunch. client/insert_test.c: Fix function call client/select_test.c: Fix function call include/mysql_com.h: New flag defined for secure connection handling sql/mysqld.cc: Add flag used for connection sql/sql_acl.cc: Partial code to find out needed connection flag based on password format --- client/insert_test.c | 1 + client/select_test.c | 1 + include/mysql_com.h | 1 + sql/mysqld.cc | 1 + sql/sql_acl.cc | 48 ++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 50 insertions(+), 2 deletions(-) diff --git a/client/insert_test.c b/client/insert_test.c index 42691df6875..d8ca71bffce 100644 --- a/client/insert_test.c +++ b/client/insert_test.c @@ -16,6 +16,7 @@ #include #include +#include "my_global.h" #include "mysql.h" #define INSERT_QUERY "insert into test (name,num) values ('item %d', %d)" diff --git a/client/select_test.c b/client/select_test.c index ee2a9192865..d7f18c0f1f0 100644 --- a/client/select_test.c +++ b/client/select_test.c @@ -19,6 +19,7 @@ #endif #include #include +#include "my_global.h" #include "mysql.h" #define SELECT_QUERY "select name from test where num = %d" diff --git a/include/mysql_com.h b/include/mysql_com.h index 3ebf9fc2120..bc273750e3a 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -97,6 +97,7 @@ enum enum_server_command #define CLIENT_IGNORE_SIGPIPE 4096 /* IGNORE sigpipes */ #define CLIENT_TRANSACTIONS 8192 /* Client knows about transactions */ #define CLIENT_PROTOCOL_41 16384 /* New 4.1 protocol */ +#define CLIENT_SECURE_CONNECTION 32768 /* New 4.1 authentication */ #define SERVER_STATUS_IN_TRANS 1 /* Transaction has started */ #define SERVER_STATUS_AUTOCOMMIT 2 /* Server in auto_commit mode */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index bf2b664f8c6..45fb7419607 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -317,6 +317,7 @@ uint volatile thread_count=0, thread_running=0, kill_cached_threads=0, ulong thd_startup_options=(OPTION_UPDATE_LOG | OPTION_AUTO_IS_NULL | OPTION_BIN_LOG | OPTION_QUOTE_SHOW_CREATE ); uint protocol_version=PROTOCOL_VERSION; +uint connection_auth_flag=0; /* Supported authentication mode */ struct system_variables global_system_variables; struct system_variables max_system_variables; ulong keybuff_size,table_cache_size, diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 2012b565dae..3b37ff552a3 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -32,6 +32,8 @@ #include #include +extern uint connection_auth_flag; + struct acl_host_and_ip { char *hostname; @@ -143,7 +145,11 @@ my_bool acl_init(bool dont_read_acl_tables) (hash_get_key) acl_entry_get_key, (void (*)(void*)) free); if (dont_read_acl_tables) + { + /* If we do not read tables use old handshake to make it quick for all clients */ + connection_auth_flag=CLIENT_LONG_PASSWORD; DBUG_RETURN(0); /* purecov: tested */ + } /* To be able to run this from boot, we allocate a temporary THD @@ -218,6 +224,7 @@ my_bool acl_init(bool dont_read_acl_tables) DBUG_PRINT("info",("user table fields: %d",table->fields)); allow_all_hosts=0; + connection_auth_flag=0; /* Reset flag as we're rereading the table */ while (!(read_record_info.read_record(&read_record_info))) { ACL_USER user; @@ -242,6 +249,18 @@ my_bool acl_init(bool dont_read_acl_tables) } get_salt_from_password(user.salt,user.password); user.pversion=get_password_version(user.password); + /* + We check the version of passwords in database. If no old passwords found we can force new handshake + if there are only old password we will force new handshake. In case of both types of passwords + found we will perform 2 stage authentication. + */ + if (user.password && user.password[0]!=0) /* empty passwords are not counted */ + { + if (user.pversion) + connection_auth_flag|=CLIENT_SECURE_CONNECTION; + else + connection_auth_flag|=CLIENT_LONG_PASSWORD; + } user.access=get_access(table,3) & GLOBAL_ACLS; user.sort=get_sort(2,user.host.hostname,user.user); user.hostname_length= (user.host.hostname ? @@ -299,7 +318,18 @@ my_bool acl_init(bool dont_read_acl_tables) sizeof(ACL_USER),(qsort_cmp) acl_compare); end_read_record(&read_record_info); freeze_size(&acl_users); - + + /* + If database is empty or has no passwords use new connection protocol + unless we're running with --old-passwords option + */ + if (!connection_auth_flag) + { + if(!opt_old_passwords) + connection_auth_flag=CLIENT_SECURE_CONNECTION; + else connection_auth_flag=CLIENT_LONG_PASSWORD; + } + printf("Set flag after read: %d\n",connection_auth_flag); init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0); VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100)); while (!(read_record_info.read_record(&read_record_info))) @@ -674,6 +704,12 @@ static void acl_update_user(const char *user, const char *host, acl_user->password=(char*) ""; // Just point at something get_salt_from_password(acl_user->salt,password); acl_user->pversion=get_password_version(acl_user->password); + // We should allow connection with authentication method matching password + if (acl_user->pversion) + connection_auth_flag|=CLIENT_SECURE_CONNECTION; + else + connection_auth_flag|=CLIENT_LONG_PASSWORD; + printf("Debug: flag set to %d\n",connection_auth_flag); } } break; @@ -1084,7 +1120,15 @@ bool change_password(THD *thd, const char *host, const char *user, if (!new_password[0]) acl_user->password=0; else - acl_user->password=(char*) ""; // Point at something + { + acl_user->password=(char*) ""; // Point at something + /* Adjust global connection options depending of client password*/ + if (acl_user->pversion) + connection_auth_flag|=CLIENT_SECURE_CONNECTION; + else + connection_auth_flag|=CLIENT_LONG_PASSWORD; + } + acl_cache->clear(1); // Clear locked hostname cache VOID(pthread_mutex_unlock(&acl_cache->lock));