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/mysqladmin.c b/client/mysqladmin.c index 00af8c592ec..e9572e0ad8d 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/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 82bcaab2340..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 */ @@ -279,7 +280,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); +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 26262d9e618..ca84258193b 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 2ef95bb8746..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()); + 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 b98be7829fb..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: @@ -243,14 +246,27 @@ 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(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/mysql_priv.h b/sql/mysql_priv.h index d1a3c7e235a..242eba11785 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 e6c2c198722..45fb7419607 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; @@ -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, @@ -395,8 +396,8 @@ 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; -MY_TMPDIR mysql_tmpdir_list; + +my_string mysql_unix_port=NULL, opt_mysql_tmpdir=NULL, mysql_tmpdir=NULL; ulong my_bind_addr; /* the address we bind to */ char *my_bind_addr_str; DATE_FORMAT dayord; @@ -853,7 +854,7 @@ void clean_up(bool print_message) if (defaults_argv) free_defaults(defaults_argv); my_free(charsets_list, MYF(MY_ALLOW_ZERO_PTR)); - free_tmpdir(&mysql_tmpdir_list); + my_free(mysql_tmpdir,MYF(MY_ALLOW_ZERO_PTR)); my_free(slave_load_tmpdir,MYF(MY_ALLOW_ZERO_PTR)); x_free(opt_bin_logname); x_free(opt_relay_logname); @@ -1835,6 +1836,17 @@ int main(int argc, char **argv) load_defaults(MYSQL_CONFIG_NAME,load_default_groups,&argc,&argv); defaults_argv=argv; + /* Get default temporary directory */ + opt_mysql_tmpdir=getenv("TMPDIR"); /* Use this if possible */ +#if defined( __WIN__) || defined(OS2) + if (!opt_mysql_tmpdir) + opt_mysql_tmpdir=getenv("TEMP"); + if (!opt_mysql_tmpdir) + opt_mysql_tmpdir=getenv("TMP"); +#endif + if (!opt_mysql_tmpdir || !opt_mysql_tmpdir[0]) + opt_mysql_tmpdir=(char*) P_tmpdir; /* purecov: inspected */ + set_options(); get_options(argc,argv); if (opt_log || opt_update_log || opt_slow_log || opt_bin_log) @@ -2894,7 +2906,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 }; @@ -2931,13 +2944,13 @@ struct my_option my_long_options[] = #endif /* HAVE_BERKELEY_DB */ {"skip-bdb", OPT_BDB_SKIP, "Don't use berkeley db (will save memory)", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"big-tables", OPT_BIG_TABLES, + {"big-tables", OPT_BIG_TABLES, "Allow big result sets by saving all temporary sets on file (Solves most 'table full' errors)", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"binlog-do-db", OPT_BINLOG_DO_DB, "Tells the master it should log updates for the specified database, and exclude all others not explicitly mentioned.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"binlog-ignore-db", OPT_BINLOG_IGNORE_DB, + {"binlog-ignore-db", OPT_BINLOG_IGNORE_DB, "Tells the master that updates to the given database should not be logged tothe binary log", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"bind-address", OPT_BIND_ADDRESS, "IP address to bind to", @@ -3225,6 +3238,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, "Deprecated option; One should use GRANT SHOW DATABASES instead...", @@ -3306,23 +3321,16 @@ struct my_option my_long_options[] = #ifdef HAVE_OPENSSL #include "sslopt-longopts.h" #endif - {"temp-pool", OPT_TEMP_POOL, + {"temp-pool", OPT_TEMP_POOL, "Using this option will cause most temporary files created to use a small set of names, rather than a unique name for each new file.", (gptr*) &use_temp_pool, (gptr*) &use_temp_pool, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, - {"tmpdir", 't', - "Path for temporary files. Several paths may be specified, separated by a " -#if defined( __WIN__) || defined(OS2) - "semicolon (;)" -#else - "colon (:)" -#endif - ", in this case they are used in a round-robin fashion.", - (gptr*) &opt_mysql_tmpdir, + {"tmpdir", 't', "Path for temporary files", (gptr*) &opt_mysql_tmpdir, (gptr*) &opt_mysql_tmpdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"transaction-isolation", OPT_TX_ISOLATION, - "Default transaction isolation level", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, - 0, 0, 0, 0, 0}, + "Default transaction isolation level", 0, 0, 0, GET_NO_ARG, REQUIRED_ARG, 0, + 0, 0, 0, + 0, 0}, {"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running", (gptr*) &opt_external_locking, (gptr*) &opt_external_locking, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -3375,7 +3383,7 @@ struct my_option my_long_options[] = (gptr*) &connect_timeout, (gptr*) &connect_timeout, 0, GET_ULONG, REQUIRED_ARG, CONNECT_TIMEOUT, 2, LONG_TIMEOUT, 0, 1, 0 }, {"delayed_insert_timeout", OPT_DELAYED_INSERT_TIMEOUT, - "How long a INSERT DELAYED thread should wait for INSERT statements before terminating.", + "Ho wlong a INSERT DELAYED thread should wait for INSERT statements before terminating.", (gptr*) &delayed_insert_timeout, (gptr*) &delayed_insert_timeout, 0, GET_ULONG, REQUIRED_ARG, DELAYED_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, {"delayed_insert_limit", OPT_DELAYED_INSERT_LIMIT, @@ -4171,6 +4179,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case (int) OPT_SKIP_SHOW_DB: opt_skip_show_db=1; opt_specialflag|=SPECIAL_SKIP_SHOW_DB; + mysql_port=0; break; #ifdef ONE_THREAD case (int) OPT_ONE_THREAD: @@ -4494,7 +4503,9 @@ static void fix_paths(void) charsets_dir=mysql_charsets_dir; } - if (init_tmpdir(&mysql_tmpdir_list, opt_mysql_tmpdir)) + char *end=convert_dirname(buff, opt_mysql_tmpdir, NullS); + if (!(mysql_tmpdir= my_memdup((byte*) buff,(uint) (end-buff)+1, + MYF(MY_FAE)))) exit(1); if (!slave_load_tmpdir) { diff --git a/sql/password.c b/sql/password.c index 48181ea18e6..ba7dc17c671 100644 --- a/sql/password.c +++ b/sql/password.c @@ -37,9 +37,18 @@ #include #include #include +#include #include "mysql.h" + +/* Character to use as version identifier for version 4.1 */ +#define PVERSION41_CHAR '*' + + + + + void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2) { /* For mysql 3.21.# */ #ifdef HAVE_purify @@ -84,13 +93,68 @@ 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]); + + + + +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 (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 */ + { + 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]); + sha1_reset(&context); + /* Use Salt for Hash */ + sha1_input(&context,(uint8*)&salt,2); + + for (; *password ; password++) + { + if (*password == ' ' || *password == '\t') + continue;/* skip space in password */ + sha1_input(&context,(int8*)&password[0],1); + } + sha1_result(&context,digest); + /* Hash one more time */ + sha1_reset(&context); + sha1_input(&context,digest,SHA1_HASH_SIZE); + sha1_result(&context,digest); + /* 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]); + } } +uint get_password_length(my_bool force_old_scramble) +{ + if (force_old_scramble) + return 16; + else return SHA1_HASH_SIZE*2+4+1; +} + +uint8 get_password_version(const char* password) +{ + if (password==NULL) return 0; + if (password[0]==PVERSION41_CHAR) return PVERSION41_CHAR; + return 0; +} + + inline uint char_val(char X) { return (uint) (X >= '0' && X <= '9' ? X-'0' : @@ -99,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; @@ -115,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..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; @@ -63,7 +65,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; }; @@ -142,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 @@ -217,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; @@ -231,7 +239,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 +248,19 @@ 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); + /* + 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 ? @@ -297,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))) @@ -671,6 +703,13 @@ 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; @@ -706,6 +745,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 +1092,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,14 +1116,23 @@ 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 - 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)); - 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 4f75716ff56..338f6ce3a2d 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3650,7 +3650,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; } } @@ -3941,7 +3941,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; }