diff options
author | unknown <kostja@oak.local> | 2003-07-31 17:11:52 +0400 |
---|---|---|
committer | unknown <kostja@oak.local> | 2003-07-31 17:11:52 +0400 |
commit | 4acc45d78427c27d78b5a297f713eeae522f8945 (patch) | |
tree | a08ffe6a07c8efcc13a357c5478274972fd673b0 /sql | |
parent | 79fea68bd7da9735fdbb9d6e4c40879d0e72ff70 (diff) | |
parent | 2c3cba310aa401842996a52abdbde95bb950fcc5 (diff) | |
download | mariadb-git-4acc45d78427c27d78b5a297f713eeae522f8945.tar.gz |
manual merge
BitKeeper/etc/ignore:
auto-union
BitKeeper/etc/logging_ok:
auto-union
include/mysql.h:
Auto merged
include/mysql_com.h:
Auto merged
libmysql/libmysql.c:
Auto merged
sql/item_create.cc:
Auto merged
sql/item_create.h:
Auto merged
sql/item_strfunc.cc:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/mysqld.cc:
Auto merged
sql/protocol.cc:
Auto merged
sql/set_var.cc:
Auto merged
sql/slave.cc:
Auto merged
sql/slave.h:
Auto merged
sql/sql_acl.h:
Auto merged
sql/sql_yacc.yy:
Auto merged
sql-common/client.c:
Auto merged
sql/sql_parse.cc:
merge commit
convert of database to utf8 in case of cahnge_user added
Diffstat (limited to 'sql')
45 files changed, 1083 insertions, 1322 deletions
diff --git a/sql/item_create.cc b/sql/item_create.cc index e18d1cfa189..1e98395a14d 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 1326077b096..020ceb30cfa 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 b4f4df6968d..dbc69434718 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, - ¤t_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,¤t_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 7f8d7ade67b..025c47a8504 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 c2860f4551a..4d3863d77a6 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 2c41d973f2e..8a5bf991c49 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 @@ -757,7 +754,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 8ddcbdc572f..88a81e4d377 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -257,9 +257,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; @@ -2764,12 +2765,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 @@ -3479,7 +3474,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 }; @@ -3784,9 +3780,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, @@ -3855,8 +3852,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...", @@ -3866,6 +3861,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, @@ -4638,7 +4636,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; @@ -4741,6 +4740,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 @@ -4862,9 +4862,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 257547671e5..16227aab611 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,73 +106,24 @@ 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 + hash_password() + result OUT store hash in this location + password IN plain text password to build hash + password_len IN password length (password may be not null-terminated) */ -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 -*/ - -void hash_password(ulong *result, const char *password) +void hash_password(ulong *result, const char *password, uint password_len) { register ulong nr=1345345333L, add=7, nr2=0x12345671L; ulong tmp; - for (; *password ; password++) + const char *password_end= password + password_len; + for (; password < password_end; 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; @@ -208,519 +131,388 @@ void hash_password(ulong *result, const char *password) } result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */; result[1]=nr2 & (((ulong) 1L << 31) -1L); - return; } /* - 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, strlen(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 - - RETURN - none + 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 at least + SRAMBLE_LENGTH_323 bytes long. + password IN Password to use while scrambling */ -void password_hash_stage2(char *to, const char *salt) +void 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, strlen(password)); + hash_password(hash_message, message, SCRAMBLE_LENGTH_323); + randominit(&rand_st,hash_pass[0] ^ hash_message[0], + hash_pass[1] ^ hash_message[1]); + const char *message_end= message + SCRAMBLE_LENGTH_323; + for (; message < message_end; 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; } /* - 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 scrambled message to check. + message original random message which was used for scrambling; must + be exactly SCRAMBLED_LENGTH_323 bytes long and + NULL-terminated. + hash_pass password which should be used for scrambling + All params are IN. + + 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; + + hash_password(hash_message, message, SCRAMBLE_LENGTH_323); + 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 inline 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]); } /* - Get length of password string which is stored in mysql.user table + **************** MySQL 4.1.1 authentification routines ************* +*/ +/* + 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); + const uint8 *str_end= str + len; + for (; str != str_end; ++str) + { + *to++= _dig_vec[(*str & 0xF0) >> 4]; + *to++= _dig_vec[*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; - } + register char tmp= char_val(*str++); + *to++= (tmp << 4) | 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 uchar *s1, const uchar *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 uchar *) 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 clients' reply, presumably produced by scramble() + message original random string, previously sent to client + (presumably second argument of scramble()), must be + exactly SCRAMBLE_LENGTH long and NULL-terminated. + hash_stage2 hex2octet-decoded database entry + All params are IN. + + 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 uchar *) 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 + 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); +} +/* + Convert scrambled password from binary form to asciiz hex string. SYNOPSIS - scramble() - scrambled IN Scrambled message to check - message IN Original message which was scramble - hash_pass IN Password which should be used for scrambling - old_ver IN Forse old version random number generator - - RETURN - 0 Password correct - !0 Password invalid + 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/protocol.cc b/sql/protocol.cc index e90aa7585e2..99d1e03c8a7 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -349,6 +349,25 @@ send_eof(THD *thd, bool no_flush) } DBUG_VOID_RETURN; } + +/* + Please client to send scrambled_password in old format. + SYNOPSYS + send_old_password_request() + thd thread handle + + RETURN VALUE + 0 ok + !0 error +*/ + +bool send_old_password_request(THD *thd) +{ + static char buff[1]= { (char) 254 }; + NET *net= &thd->net; + return my_net_write(net, buff, 1) || net_flush(net); +} + #endif /* EMBEDDED_LIBRARY */ /* diff --git a/sql/protocol.h b/sql/protocol.h index ffd61b3e848..405d3d4aebe 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -164,6 +164,7 @@ void net_printf(THD *thd,uint sql_errno, ...); void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L, const char *info=0); void send_eof(THD *thd, bool no_flush=0); +bool send_old_password_request(THD *thd); char *net_store_length(char *packet,ulonglong length); char *net_store_length(char *packet,uint length); char *net_store_data(char *to,const char *from, uint length); diff --git a/sql/set_var.cc b/sql/set_var.cc index 6a6e010578f..21de0eba1d2 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 978aba3384a..8f12016bab4 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 b43c4b43b50..037aabc0c9f 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -277,3 +277,4 @@ v/* "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 2eb9e6d2219..6cb09758659 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -271,3 +271,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 2a663a176f8..a3cd71ce31b 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -279,3 +279,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 f4019d63055..e8218313ef0 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -273,3 +273,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 d3a38ede5bc..b4f37231526 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -273,3 +273,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 ccff24c5759..83b076610e7 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -268,3 +268,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 52f3eb78c11..037f7e0c12d 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -277,3 +277,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 1ce052bdf22..1bd1e57b311 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -268,3 +268,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 6143ea2a1c4..2f24ffd2933 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -270,3 +270,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 8164757d823..dd929f97199 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -268,3 +268,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 747d3611cc9..a4c40628624 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -270,3 +270,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 93d86d32937..acc416149b6 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -268,3 +268,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 e9319246fc6..b09099d73b9 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -270,3 +270,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 edb5854db7e..0333e5e9232 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -270,3 +270,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 27b4d0d661f..656768a7e23 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -272,3 +272,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 60ace09ab33..9f879f41891 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -268,3 +268,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 8824d64876a..0635041f77b 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -272,3 +272,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 ddfc0a8f7de..ea557368ecc 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -270,3 +270,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"óÅÒ×ÅÒ ÚÁÐÕÝÅÎ × ÒÅÖÉÍÅ --secure-auth (ÂÅÚÏÐÁÓÎÏÊ Á×ÔÏÒÉÚÁÃÉÉ), ÎÏ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%s@%s' ÐÁÒÏÌØ ÓÏÈÒÁÎ£Î × ÓÔÁÒÏÍ ÆÏÒÍÁÔÅ; ÎÅÏÂÈÏÄÉÍÏ ÏÂÎÏ×ÉÔØ ÆÏÒÍÁÔ ÐÁÒÏÌÑ" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 9e2a37e4053..42db4c276b2 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -264,3 +264,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 ed1d8cadb80..b7cdd6fc1b4 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -276,3 +276,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 5f3a2f38109..9a311610c2d 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -269,3 +269,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 d108618834e..bbfe3e79416 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -268,3 +268,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 96b9f40feac..524fe4e4016 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -273,3 +273,4 @@ "Illegal mix of collations for operation '%s'", "Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)", "Unknown collation: '%-.64s'", +"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 37979576b73..e93c6520d87 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 668fff52d08..7e26468359c 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 598ff153cad..c6c0c5f4ab3 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,12 +68,54 @@ 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. SYNOPSIS @@ -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,126 @@ 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) + { + 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 { - 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; + 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@%s'. " + "Ignoring user. " + "You should change password for this user.", + user.user ? user.user : "", + user.host.hostname ? user.host.hostname : ""); + 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,135 +525,105 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) /* - Prepare crypted scramble to be sent to the client -*/ + Seek ACL entry for a user, check password, SSL cypher, and if + everything is OK, update THD user data and USER_RESOURCES struct. -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; + IMPLEMENTATION + 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 + acl_getroot() + thd 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 user resources; on success mqh is reset, else + unchanged + passwd 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 length of passwd, must be one of 0, 8, + SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH + 'thd' and 'mqh' are updated on success; other params are IN. + + RETURN VALUE + 0 success: thd->priv_user, thd->priv_host, thd->master_access, 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 */ + /* + here if mysqld's been started with --skip-grant-tables option. + */ + thd->priv_user= (char *) ""; // privileges for + *thd->priv_host= '\0'; // the user are unknown + thd->master_access= ~NO_ACCESS; // everything is allowed + bzero(mqh, sizeof(*mqh)); + DBUG_RETURN(0); } + + 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, + (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 */ + thd->master_access= NO_ACCESS; Vio *vio=thd->net.vio; /* At this point we know that user is allowed to connect @@ -601,48 +634,48 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, 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; + thd->master_access= acl_user->access; break; #ifdef HAVE_OPENSSL case SSL_TYPE_ANY: /* Any kind of SSL is good enough */ if (vio_type(vio) == VIO_TYPE_SSL) - user_access=acl_user->access; + thd->master_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. + 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; + if (vio_type(vio) == VIO_TYPE_SSL && + SSL_get_peer_certificate(vio->ssl_)) + thd->master_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. + 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; + 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; - } + 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_))) + thd->master_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_)); + break; + } } /* Prepare certificate (if exists) */ DBUG_PRINT("info",("checkpoint 1")); @@ -651,64 +684,58 @@ ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, /* 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 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); + free(ptr); + break; + } + thd->master_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); + 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); + } + else + thd->master_access= acl_user->access; + free(ptr); } break; -#else /* HAVE_OPENSSL */ - default: +#else /* HAVE_OPENSSL */ + default: /* - If we don't have SSL but SSL is required for this user the - authentication should fail. - */ - break; + If we don't have SSL but SSL is required for this user the + authentication should fail. + */ + thd->master_access= acl_user->access; #endif /* HAVE_OPENSSL */ } - } - - *mqh=acl_user->user_resource; - if (!acl_user->user) - *priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */ - - if (acl_user->host.hostname) - strmake(priv_host, acl_user->host.hostname, MAX_HOSTNAME); - else - *priv_host= 0; + thd->priv_user= acl_user->user ? thd->user : (char *) ""; + *mqh= acl_user->user_resource; -unlock_and_exit: + if (acl_user->host.hostname) + strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME); + else + *thd->priv_host= 0; + } VOID(pthread_mutex_unlock(&acl_cache->lock)); - DBUG_RETURN(user_access); + DBUG_RETURN(res); } @@ -719,8 +746,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, @@ -756,20 +784,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; } } @@ -778,7 +795,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, @@ -789,7 +806,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); @@ -799,12 +815,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 @@ -1141,7 +1153,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)); @@ -1150,37 +1161,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)); @@ -1216,7 +1217,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); } @@ -1286,7 +1287,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; @@ -1310,7 +1311,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 */ @@ -1358,24 +1359,23 @@ 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]; + const char *password= ""; + 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; } @@ -1400,17 +1400,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 */ @@ -1507,10 +1510,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, @@ -1518,7 +1519,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, @@ -2921,12 +2922,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 ca976f43999..e3566e4ec39 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, my_bool db_is_pattern); -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 c233ffd422a..c7fb2ce3368 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= '\0'; init(); /* Initialize sub structures */ diff --git a/sql/sql_class.h b/sql/sql_class.h index f6336cb7dd9..4eb1d6057a9 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,10 @@ 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]; + 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_crypt.cc b/sql/sql_crypt.cc index 930ecfffef7..b0b8050e311 100644 --- a/sql/sql_crypt.cc +++ b/sql/sql_crypt.cc @@ -32,7 +32,7 @@ SQL_CRYPT::SQL_CRYPT(const char *password) { ulong rand_nr[2]; - hash_password(rand_nr,password); + hash_password(rand_nr,password, strlen(password)); crypt_init(rand_nr); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 720017f399f..289addd892a 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 thread handle, thd->{host,user,ip} are used + command originator of the check: now check_user is called + during connect and change user procedures; used for + logging. + passwd scrambled password recieved from client + passwd_len length of scrambled password + db database name to connect to, may be NULL + check_count 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; 'thd' is INOUT, all other params + are 'IN'. + + 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 + Clear thd->db as it points to something, that will be freed when + connection is closed. We don't want to accidently free a wrong pointer + if connect failed. Also in case of 'CHANGE USER' failure, current + database will be switched to 'no database selected'. */ - if (thd->master_access & NO_ACCESS) + thd->db= 0; + thd->db_length= 0; + + USER_RESOURCES ur; + int res= acl_getroot(thd, &ur, passwd, passwd_len); + 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. + */ + NET *net= &thd->net; + if (opt_secure_auth_local) { - DBUG_PRINT("info",("Access denied")); + 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); + } + if (send_old_password_request(thd) || + 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); + } + /* here res is always >= 0 */ + if (res == 0) + { + 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*) ""); + /* - Old client should get nicer error message if password version is - not supported + This is the default access rights for the current database. It's + set to 0 here because we don't have an active database yet (and we + may not have an active database to set. */ - if (simple_connect && *hint_user && (*hint_user)->pversion) + 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]) { - net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); - mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE)); + if (mysql_change_db(thd, db)) + { + if (thd->user_connect) + decrease_user_connections(thd->user_connect); + DBUG_RETURN(-1); + } } else - { - net_printf(thd, ER_ACCESS_DENIED_ERROR, - thd->user, - thd->host_or_ip, - had_password ? ER(ER_YES) : ER(ER_NO)); - mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR), - thd->user, - thd->host_or_ip, - had_password ? ER(ER_YES) : ER(ER_NO)); - } - DBUG_RETURN(1); // Error already given - } - DBUG_PRINT("info",("Prepare for second part of handshake")); - DBUG_RETURN(-1); // no report error in special handshake - } - - if (check_count) - { - VOID(pthread_mutex_lock(&LOCK_thread_count)); - bool tmp=(thread_count - delayed_insert_threads >= max_connections && - !(thd->master_access & SUPER_ACL)); - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - if (tmp) - { // Too many connections - send_error(thd, ER_CON_COUNT_ERROR); - DBUG_RETURN(1); + 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,49 +551,40 @@ 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 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, thd is updated. + -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 */ - String convdb; - 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 { @@ -596,15 +613,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); @@ -618,19 +636,34 @@ 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= 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); + /* + 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, 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; - - // At this point we write connection message and read reply - if (net_write_command(net,(uchar) protocol_version, "", 0, buff, + 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) @@ -703,7 +736,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)", @@ -720,22 +753,6 @@ 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; - convdb.copy(db, strlen(db), - thd->variables.character_set_client, system_charset_info); - db= convdb.c_ptr(); - } - - /* We can get only old hash at this point */ - if (using_password && strlen(passwd) != SCRAMBLE_LENGTH) - return ER_HANDSHAKE_ERROR; - if (thd->client_capabilities & CLIENT_INTERACTIVE) thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && @@ -743,60 +760,33 @@ 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)); + 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; - /* 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) + /* Since 4.1 all database names are stored in utf8 */ + String convdb; + if (db) { - /* 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); + convdb.copy(db, strlen(db), thd->variables.character_set_client, + system_charset_info); + db= convdb.c_ptr(); + } - /* 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); + if (!(thd->user= my_strdup(user, MYF(0)))) + return (ER_OUT_OF_RESOURCES); + return check_user(thd, COM_CONNECT, passwd, passwd_len, db, true); } @@ -853,7 +843,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); @@ -1168,116 +1158,74 @@ 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; - - /* 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; + /* + 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; + /* Convert database name to utf8 */ + String convdb; + convdb.copy(db, strlen(db), thd->variables.character_set_client, + system_charset_info); + db= convdb.c_ptr(); - /* 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); + /* Small check for incomming packet */ + if ((uint) ((uchar*) db - net->read_pos) > packet_length) + { + 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 */ @@ -3179,7 +3127,7 @@ error: Check grants for commands which work only with one table and all other tables belong to subselects. - SYNOPSYS + SYNOPSIS single_table_command_access() thd - Thread handler privilege - asked privelage diff --git a/sql/sql_repl.h b/sql/sql_repl.h index e3d600b9798..b53551845bc 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -7,7 +7,7 @@ typedef struct st_slave_info uint32 rpl_recovery_rank, master_id; char host[HOSTNAME_LENGTH+1]; char user[USERNAME_LENGTH+1]; - char password[HASH_PASSWORD_LENGTH+1]; + char password[SCRAMBLED_PASSWORD_CHAR_LENGTH+1]; uint16 port; THD* thd; } SLAVE_INFO; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 90c586dc2f1..e1310b7efe6 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 ')' @@ -4454,6 +4459,7 @@ keyword: | NO_SYM {} | NONE_SYM {} | OFFSET_SYM {} + | OLD_PASSWORD {} | OPEN_SYM {} | PACK_KEYS_SYM {} | PARTIAL {} @@ -4667,15 +4673,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; } ; @@ -4983,14 +4989,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 |